initial Commit
233
foxyproxy/_locales/de/messages.json
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "Über"
|
||||
},
|
||||
"add": {
|
||||
"message": "Hinzufügen"
|
||||
},
|
||||
"addHostTo": {
|
||||
"message": "Host Muster hinzufügen zu..."
|
||||
},
|
||||
"all": {
|
||||
"message": "Alle"
|
||||
},
|
||||
"city": {
|
||||
"message": "Stadt"
|
||||
},
|
||||
"color": {
|
||||
"message": "Farbe"
|
||||
},
|
||||
"confirmDeleteBrowserData": {
|
||||
"message": "Sind Sie sicher, dass Sie Cookies, indexedDB-Speicher und lokalen DOM-Speicher löschen möchten?"
|
||||
},
|
||||
"country": {
|
||||
"message": "Land"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Löschen"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Browserdaten löschen"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "Sind Sie sicher, dass Sie löschen wollen?"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Deaktivieren"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deaktiviert"
|
||||
},
|
||||
"documentURL": {
|
||||
"message": "Dokumenten-URL"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "Synchronisation einschalten"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "Synchronisieren globaler Ausnahmen, Proxys & Muster"
|
||||
},
|
||||
"error": {
|
||||
"message": "Bei dem Vorgang ist ein Fehler aufgetreten"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "Ausschließen"
|
||||
},
|
||||
"export": {
|
||||
"message": "Export"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Einfach zu bedienendes, fortschrittliches Proxy-Management-Tool für jedermann"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "Es gab einen Fehler beim Parsen der Datei"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "Es gab einen Fehler beim Lesen der Datei"
|
||||
},
|
||||
"fileSizeError": {
|
||||
"message": "Die Dateigröße ist größer als die erlaubten $1kb"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "Nicht unterstütztes Dateiformat"
|
||||
},
|
||||
"foxyProxyAccount": {
|
||||
"message": "FoxyProxy Konto"
|
||||
},
|
||||
"fromOlderVersions": {
|
||||
"message": "Von alten Versionen"
|
||||
},
|
||||
"fromURL": {
|
||||
"message": "Von URL"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "Standort ermitteln"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "Globale Ausnahmen"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "Die hier eingegebenen Muster werden nicht weitergegeben."
|
||||
},
|
||||
"help": {
|
||||
"message": "Hilfe"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Hostname"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "Fehlender Hostname - Port"
|
||||
},
|
||||
"import": {
|
||||
"message": "Import"
|
||||
},
|
||||
"importAs": {
|
||||
"message": "Importieren als"
|
||||
},
|
||||
"include": {
|
||||
"message": "Einbeziehen"
|
||||
},
|
||||
"incognitoAccess": {
|
||||
"message": "Proxy-Einstellungen erfordern die Erlaubnis zur Verwendung beim privaten Surfen."
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "WebRTC einschränken"
|
||||
},
|
||||
"location": {
|
||||
"message": "Ort"
|
||||
},
|
||||
"log": {
|
||||
"message": "Log"
|
||||
},
|
||||
"method": {
|
||||
"message": "Methode"
|
||||
},
|
||||
"options": {
|
||||
"message": "Optionen"
|
||||
},
|
||||
"pacUrlError": {
|
||||
"message": "Fehlende PAC-URL für PAC-Typ"
|
||||
},
|
||||
"password": {
|
||||
"message": "Passwort"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Muster"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Muster"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "Einfacher Host"
|
||||
},
|
||||
"port": {
|
||||
"message": "Port"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "Proxy"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "Proxy by Patterns"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "Proxy DNS"
|
||||
},
|
||||
"proxyDnsDescription": {
|
||||
"message": "Verwenden Sie den Proxy-Server, um bestimmte DNS-Anfragen aufzulösen (nur für SOCKS in Firefox)"
|
||||
},
|
||||
"proxyDuplicateError": {
|
||||
"message": "Doppelter Hostname:Port oder PAC URL"
|
||||
},
|
||||
"proxyList": {
|
||||
"message": "Proxy Liste"
|
||||
},
|
||||
"proxyPortError": {
|
||||
"message": "Fehlender Port"
|
||||
},
|
||||
"proxyTypeError": {
|
||||
"message": "Unbekannter Typ"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "Schnelles Hinzufügen"
|
||||
},
|
||||
"random": {
|
||||
"message": "Zufällig"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "Fehler beim Erstellen eines regulären Ausdrucks für ein Muster"
|
||||
},
|
||||
"resetWebRTC": {
|
||||
"message": "WebRTC zurücksetzen"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "Standardeinstellungen wiederherstellen"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "Sind Sie sicher, dass Sie alle Einstellungen auf die Standardwerte zurücksetzen wollen?"
|
||||
},
|
||||
"result": {
|
||||
"message": "Ergebnis"
|
||||
},
|
||||
"save": {
|
||||
"message": "Speichern"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "Synchronisationsfehler"
|
||||
},
|
||||
"test": {
|
||||
"message": "Test"
|
||||
},
|
||||
"tester": {
|
||||
"message": "Tester"
|
||||
},
|
||||
"time": {
|
||||
"message": "Zeit"
|
||||
},
|
||||
"title": {
|
||||
"message": "Titel"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Passwortsichtbarkeit umschalten"
|
||||
},
|
||||
"type": {
|
||||
"message": "Typ"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Fehlender Benutzername oder Passwort"
|
||||
},
|
||||
"username": {
|
||||
"message": "Benutzername"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "Wildcard"
|
||||
}
|
||||
}
|
||||
368
foxyproxy/_locales/en/messages.json
Normal file
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "About"
|
||||
},
|
||||
"add": {
|
||||
"message": "Add"
|
||||
},
|
||||
"addContainerPrompt": {
|
||||
"message": "Please enter the Container ID number."
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
},
|
||||
"altServer": {
|
||||
"message": "Alt Server"
|
||||
},
|
||||
"autoBackup": {
|
||||
"message": "Auto Backup"
|
||||
},
|
||||
"autoBackupDescription": {
|
||||
"message": "Automatically backup settings to the Downloads folder on save"
|
||||
},
|
||||
"back": {
|
||||
"message": "Back"
|
||||
},
|
||||
"bulkEdit": {
|
||||
"message": "Bulk Edit"
|
||||
},
|
||||
"city": {
|
||||
"message": "City"
|
||||
},
|
||||
"closeAll": {
|
||||
"message": "Close All"
|
||||
},
|
||||
"color": {
|
||||
"message": "Color"
|
||||
},
|
||||
"container": {
|
||||
"message": "Container"
|
||||
},
|
||||
"containerDescription": {
|
||||
"message": "Incognito and Container proxy (Firefox only)"
|
||||
},
|
||||
"controlledByOtherExtensions": {
|
||||
"message": "Settings are controlled by other extensions."
|
||||
},
|
||||
"country": {
|
||||
"message": "Country"
|
||||
},
|
||||
"default": {
|
||||
"message": "Default"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Delete"
|
||||
},
|
||||
"deleteBrowsingData": {
|
||||
"message": "Delete Browsing Data"
|
||||
},
|
||||
"deleteBrowsingDataConfirm": {
|
||||
"message": "Are you sure you want to delete cookies, indexedDB storage, DOM local storage?"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "Are you sure you want to delete?"
|
||||
},
|
||||
"deleteProxy": {
|
||||
"message": "Delete Proxy"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Disable"
|
||||
},
|
||||
"disableNonProxied": {
|
||||
"message": "Disable Non-Proxied"
|
||||
},
|
||||
"documentUrl": {
|
||||
"message": "Document URL"
|
||||
},
|
||||
"duplicate": {
|
||||
"message": "Duplicate"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "Enable Sync"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "Synchronise global exclude, proxies & patterns"
|
||||
},
|
||||
"error": {
|
||||
"message": "There was an error with the operation"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "Exclude"
|
||||
},
|
||||
"excludeHost": {
|
||||
"message": "Exclude Host"
|
||||
},
|
||||
"export": {
|
||||
"message": "Export"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Easy to use advanced Proxy Management tool for everyone"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "There was an error parsing the file"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "There was an error with reading the file"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "Unsupported File Format"
|
||||
},
|
||||
"getAssociatedDomains": {
|
||||
"message": "Get Associated Domains"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "Get Location"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "Global Exclude"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "List of hosts that should not be proxied"
|
||||
},
|
||||
"help": {
|
||||
"message": "Help"
|
||||
},
|
||||
"hostDotError": {
|
||||
"message": "'*' in host must be the only character or be followed by '.'"
|
||||
},
|
||||
"hostError": {
|
||||
"message": "'*' in host must be at the start"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Hostname"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "Missing Hostname - Port"
|
||||
},
|
||||
"import": {
|
||||
"message": "Import"
|
||||
},
|
||||
"importFoxyProxyAccount": {
|
||||
"message": "Import FoxyProxy Account"
|
||||
},
|
||||
"importFromOlderVersions": {
|
||||
"message": "Import from older versions"
|
||||
},
|
||||
"importFromURL": {
|
||||
"message": "Import from URL"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "Import Proxy List"
|
||||
},
|
||||
"include": {
|
||||
"message": "Include"
|
||||
},
|
||||
"includeHost": {
|
||||
"message": "Include Host"
|
||||
},
|
||||
"incognito": {
|
||||
"message": "Incognito"
|
||||
},
|
||||
"incognitoAccessError": {
|
||||
"message": "Firefox requires private browsing permission for proxy settings."
|
||||
},
|
||||
"invalidPatternError": {
|
||||
"message": "Invalid Pattern"
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "Limit WebRTC"
|
||||
},
|
||||
"limitWebRTCDescription": {
|
||||
"message": "Change WebRTC browser settings"
|
||||
},
|
||||
"location": {
|
||||
"message": "Location"
|
||||
},
|
||||
"log": {
|
||||
"message": "Log"
|
||||
},
|
||||
"mainServer": {
|
||||
"message": "Main Server"
|
||||
},
|
||||
"method": {
|
||||
"message": "Method"
|
||||
},
|
||||
"more": {
|
||||
"message": "More"
|
||||
},
|
||||
"movePattern": {
|
||||
"message": "Move Pattern"
|
||||
},
|
||||
"moveProxy": {
|
||||
"message": "Move Proxy"
|
||||
},
|
||||
"notAvailable": {
|
||||
"message": "Proxy data is not available on Chrome (Firefox Only)"
|
||||
},
|
||||
"openAll": {
|
||||
"message": "Open All"
|
||||
},
|
||||
"openLinkTabProxy": {
|
||||
"message": "Open Link in New Tab Proxy"
|
||||
},
|
||||
"options": {
|
||||
"message": "Options"
|
||||
},
|
||||
"password": {
|
||||
"message": "Password"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Pattern"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Patterns"
|
||||
},
|
||||
"ping": {
|
||||
"message": "Ping"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "Plain Host"
|
||||
},
|
||||
"port": {
|
||||
"message": "Port"
|
||||
},
|
||||
"portError": {
|
||||
"message": "Host must not include a port number"
|
||||
},
|
||||
"proxies": {
|
||||
"message": "Proxies"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "Proxy"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "Proxy by Patterns"
|
||||
},
|
||||
"proxyByPatternsDescription": {
|
||||
"message": "Test a URL against active proxy patterns"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "Proxy DNS"
|
||||
},
|
||||
"proxyOnly": {
|
||||
"message": "Proxy Only"
|
||||
},
|
||||
"publicInterfaceOnly": {
|
||||
"message": "Public Interface Only"
|
||||
},
|
||||
"publicPrivateInterfaces": {
|
||||
"message": "Public and Private Interfaces"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "Quick Add"
|
||||
},
|
||||
"random": {
|
||||
"message": "Random"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "Error creating RegExp for pattern"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "Restore Defaults"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "Are you sure you want to restore all settings to Defaults?"
|
||||
},
|
||||
"result": {
|
||||
"message": "Result"
|
||||
},
|
||||
"saveOptions": {
|
||||
"message": "Save"
|
||||
},
|
||||
"schemeError": {
|
||||
"message": "'*' in scheme must be the only character"
|
||||
},
|
||||
"server": {
|
||||
"message": "Server"
|
||||
},
|
||||
"setPassword": {
|
||||
"message": "Set Password"
|
||||
},
|
||||
"setPort": {
|
||||
"message": "Set Port"
|
||||
},
|
||||
"setProxy": {
|
||||
"message": "Set Proxy"
|
||||
},
|
||||
"setTabProxy": {
|
||||
"message": "Set Tab Proxy"
|
||||
},
|
||||
"setTitle": {
|
||||
"message": "Set Title"
|
||||
},
|
||||
"setType": {
|
||||
"message": "Set Type"
|
||||
},
|
||||
"setUsername": {
|
||||
"message": "Set Username"
|
||||
},
|
||||
"shortcut": {
|
||||
"message": "Keyboard Shortcut"
|
||||
},
|
||||
"shortcutDescription": {
|
||||
"message": "Preselect a proxy to associate with a keyboard shortcut action"
|
||||
},
|
||||
"storeLocally": {
|
||||
"message": "Store Locally"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "Sync Error"
|
||||
},
|
||||
"tabProxy": {
|
||||
"message": "Tab Proxy"
|
||||
},
|
||||
"test": {
|
||||
"message": "Test"
|
||||
},
|
||||
"tester": {
|
||||
"message": "Pattern Tester"
|
||||
},
|
||||
"testerDescription": {
|
||||
"message": "Test a pattern against URLs"
|
||||
},
|
||||
"theme": {
|
||||
"message": "Theme"
|
||||
},
|
||||
"time": {
|
||||
"message": "Time"
|
||||
},
|
||||
"title": {
|
||||
"message": "Title"
|
||||
},
|
||||
"togglePassword": {
|
||||
"message": "Toggle Password"
|
||||
},
|
||||
"type": {
|
||||
"message": "Type"
|
||||
},
|
||||
"unsetTabProxy": {
|
||||
"message": "Unset Tab Proxy"
|
||||
},
|
||||
"unsupportedSchemeError": {
|
||||
"message": "Unsupported scheme"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Missing Username or Password"
|
||||
},
|
||||
"username": {
|
||||
"message": "Username"
|
||||
},
|
||||
"view": {
|
||||
"message": "View"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "Wildcard"
|
||||
}
|
||||
}
|
||||
398
foxyproxy/_locales/es/messages.json
Normal file
@@ -0,0 +1,398 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "Acerca de"
|
||||
},
|
||||
"active": {
|
||||
"message": "Habilitado"
|
||||
},
|
||||
"activeNote": {
|
||||
"message": "FoxyProxy ignora todo en esta página a menos que se establezca"
|
||||
},
|
||||
"add": {
|
||||
"message": "Añadir"
|
||||
},
|
||||
"addBlacklist": {
|
||||
"message": "Añadir patrones negros para evitar que este proxy sea usado en localhost y direcciones IP de intranet/privadas"
|
||||
},
|
||||
"addBlacklistTip": {
|
||||
"message": "Añadir patrones a la lista negra para localhost, 127.0.0.1, 192.168.*.*, 172.16.*.*, y 10.*.*.*"
|
||||
},
|
||||
"addProxy": {
|
||||
"message": "Añadir Proxy"
|
||||
},
|
||||
"addWhitelist": {
|
||||
"message": "Añadir patrón a la lista blanca para coincidir con todas las URLs"
|
||||
},
|
||||
"addWhitelistTip": {
|
||||
"message": "Añadir patrón a la lista blanca *"
|
||||
},
|
||||
"authError": {
|
||||
"message": "$1 está rechazando las conexiones\nVerifica el nombre de usuario/contraseña del proxy"
|
||||
},
|
||||
"back": {
|
||||
"message": "Volver"
|
||||
},
|
||||
"black": {
|
||||
"message": "Negro"
|
||||
},
|
||||
"blackPatterns": {
|
||||
"message": "Patrones Negros"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Limpiar"
|
||||
},
|
||||
"clickTest": {
|
||||
"message": "Pulsa 'Probar' cuando esté listo"
|
||||
},
|
||||
"color": {
|
||||
"message": "Color"
|
||||
},
|
||||
"complete": {
|
||||
"message": "completo"
|
||||
},
|
||||
"completeFormat": {
|
||||
"message": "Con este formato, puedes especificar todos los ajustes del proxy. pero sólo el protocolo y servidor son requeridos."
|
||||
},
|
||||
"confirmDelete": {
|
||||
"message": "¿Seguro que quieres eliminar?"
|
||||
},
|
||||
"confirmOverwrite": {
|
||||
"message": "¿Seguro que quieres sobreescribir los proxies existentes?"
|
||||
},
|
||||
"confirmTransferToLocal": {
|
||||
"message": "¿Te gustaría transferir los ajustes sincronizados a tu perfil local?\nLos Datos Existentes serán fusionados."
|
||||
},
|
||||
"confirmTransferToSync": {
|
||||
"message": "¿Te gustaría transferir los ajustes locales a tu perfil sincronizado?\nLos Datos Existentes serán fusionados."
|
||||
},
|
||||
"delete": {
|
||||
"message": "Eliminar"
|
||||
},
|
||||
"deleteAll": {
|
||||
"message": "Eliminar Todo"
|
||||
},
|
||||
"deleteAllmessage": {
|
||||
"message": "Toda la información fue eliminada"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Eliminar Información del Navegador"
|
||||
},
|
||||
"deleteBrowserDataDescription": {
|
||||
"message": "Caché, cookies, almacenamiento de BD indexada, Almacenamiento local DOM, datos de extensiones, datos del servicio trabajador."
|
||||
},
|
||||
"deleteBrowserDataNotDescription": {
|
||||
"message": "Contraseñas almacenadas, historial de navegación y formularios, historial de descargas, webSQL, y certificados vínculo-servidor."
|
||||
},
|
||||
"deleteNot": {
|
||||
"message": "No Eliminar"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Deshabilitado"
|
||||
},
|
||||
"done": {
|
||||
"message": "¡Hecho!"
|
||||
},
|
||||
"down": {
|
||||
"message": "mover abajo"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"editPatterns": {
|
||||
"message": "Editar Patrones"
|
||||
},
|
||||
"editPatternsFor": {
|
||||
"message": "Editar patrones de $1"
|
||||
},
|
||||
"editProxy": {
|
||||
"message": "Editar Proxy $1"
|
||||
},
|
||||
"enterUrl": {
|
||||
"message": "Ingrese URL para probar"
|
||||
},
|
||||
"enterUrlNote": {
|
||||
"message": "(rutas y parámetros de consultas son ignorados)"
|
||||
},
|
||||
"error": {
|
||||
"message": "Error"
|
||||
},
|
||||
"errorEmpty": {
|
||||
"message": "El campo no puede estar vacío."
|
||||
},
|
||||
"errorFetch": {
|
||||
"message": "Hubo un error en la operación"
|
||||
},
|
||||
"errorNoSettings": {
|
||||
"message": "No se encontraron ajustes. Por favor reinstala FoxyProxy."
|
||||
},
|
||||
"errorPattern": {
|
||||
"message": "Por favor ingresa un patrón"
|
||||
},
|
||||
"errorProtocol": {
|
||||
"message": "El protocolo no coincide."
|
||||
},
|
||||
"errorSlash": {
|
||||
"message": "No pongas diagonales en los comodines de patrones. No puedes coincidir rutas URL debido a restricciones de Firefox."
|
||||
},
|
||||
"errorWas": {
|
||||
"message": "Hubo un error. Por favor intenta nuevamente."
|
||||
},
|
||||
"examples": {
|
||||
"message": "Ejemplos"
|
||||
},
|
||||
"export": {
|
||||
"message": "Exportar Ajustes"
|
||||
},
|
||||
"exportPatterns": {
|
||||
"message": "Exportar Patrones"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Herramienta Avanzada de Gestión de Proxies fácil de usar para todos"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Básico"
|
||||
},
|
||||
"forAll": {
|
||||
"message": "para todas las URLs"
|
||||
},
|
||||
"formats": {
|
||||
"message": "Formatos"
|
||||
},
|
||||
"help": {
|
||||
"message": "Ayuda"
|
||||
},
|
||||
"import": {
|
||||
"message": "Importar Ajustes"
|
||||
},
|
||||
"importBW": {
|
||||
"message": "Importados $1 patrones blancos y $2 negros."
|
||||
},
|
||||
"importEnd": {
|
||||
"message": "Los ajustes fueron importados exitosamente"
|
||||
},
|
||||
"importEndSlash": {
|
||||
"message": "Importación terminada. Las diagonales en patrones no están soportadas debido a un error de Firefox. Por favor revisa tus patrones y elimina las diagonales si contiene."
|
||||
},
|
||||
"importPatterns": {
|
||||
"message": "Importar Patrones"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "Importar Lista de Proxies"
|
||||
},
|
||||
"importSucceeded": {
|
||||
"message": "Leídos y almacenados $1 proxies."
|
||||
},
|
||||
"imported": {
|
||||
"message": "Importado"
|
||||
},
|
||||
"importedPattern": {
|
||||
"message": "Este patrón fue importado desde una versión más vieja de FoxyProxy y modificado durante la importación. Aquí está el patrón original, sin cambios:"
|
||||
},
|
||||
"importsSkipped": {
|
||||
"message": "Omitidas $1 líneas porque no pudieron ser convertidas:\n\n$2"
|
||||
},
|
||||
"ip": {
|
||||
"message": "Dirección IP del Proxy o nombre DNS"
|
||||
},
|
||||
"ipPort": {
|
||||
"message": "ip:puerto"
|
||||
},
|
||||
"ipPortUsernamePassword": {
|
||||
"message": "ip:puerto:nombre de usuario:contraseña"
|
||||
},
|
||||
"log": {
|
||||
"message": "Registros"
|
||||
},
|
||||
"logSize": {
|
||||
"message": "Tamaño del registro"
|
||||
},
|
||||
"matchPattern": {
|
||||
"message": "Coincidir Patrones"
|
||||
},
|
||||
"matchedURLs": {
|
||||
"message": "URLs Que Coincidieron con Patrones"
|
||||
},
|
||||
"modeDisabled": {
|
||||
"message": "Apagar (Usar Ajustes de Firefox)"
|
||||
},
|
||||
"modePatterns": {
|
||||
"message": "Usar Proxies habilitados Por patrones y Órden"
|
||||
},
|
||||
"myIP": {
|
||||
"message": "¿Cuál es mi IP?"
|
||||
},
|
||||
"name": {
|
||||
"message": "nombre"
|
||||
},
|
||||
"newBlack": {
|
||||
"message": "Nuevo Negro"
|
||||
},
|
||||
"newWhite": {
|
||||
"message": "Nuevo Blanco"
|
||||
},
|
||||
"noLocal": {
|
||||
"message": "No usar para localhost y direcciones IP de intranet/privadas"
|
||||
},
|
||||
"noMatch": {
|
||||
"message": "Sin Coincidencias"
|
||||
},
|
||||
"noProxies": {
|
||||
"message": "No tienes ningún ajuste de proxy."
|
||||
},
|
||||
"none": {
|
||||
"message": "Ninguno"
|
||||
},
|
||||
"notApplicable": {
|
||||
"message": "n/a"
|
||||
},
|
||||
"off": {
|
||||
"message": "Apagado"
|
||||
},
|
||||
"ok": {
|
||||
"message": "OK"
|
||||
},
|
||||
"on": {
|
||||
"message": "Endendido"
|
||||
},
|
||||
"options": {
|
||||
"message": "Opciones"
|
||||
},
|
||||
"optionsPage": {
|
||||
"message": "Opciones de FoxyProxy"
|
||||
},
|
||||
"overwritProxiesHelp1": {
|
||||
"message": "marca para eliminar todos los proxies existentes y reemplazarlos con los proxies de esta lista."
|
||||
},
|
||||
"overwritProxiesHelp2": {
|
||||
"message": "Desmarca para añadir los proxies de esta lista a los proxies existentes."
|
||||
},
|
||||
"overwriteProxies": {
|
||||
"message": "Sobreescribir proxies existentes"
|
||||
},
|
||||
"password": {
|
||||
"message": "Contraseña (opcional)"
|
||||
},
|
||||
"pasteList": {
|
||||
"message": "Pega una lista de proxies debajo."
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Patrón"
|
||||
},
|
||||
"patternCheatSheet": {
|
||||
"message": "Hoja de Atajos de Patrones"
|
||||
},
|
||||
"patternDetail": {
|
||||
"message": "Ingrese detalles del patrón."
|
||||
},
|
||||
"patternHelp": {
|
||||
"message": "Ayuda de Patrones"
|
||||
},
|
||||
"patternMatch": {
|
||||
"message": "¡El patrón coincide con la URL!"
|
||||
},
|
||||
"patternNotMatch": {
|
||||
"message": "¡El Patrón no coincide con la URL."
|
||||
},
|
||||
"patternNote": {
|
||||
"message": "(sin rutas, ni parámetros de consulta)"
|
||||
},
|
||||
"patternShortcuts": {
|
||||
"message": "Atajos de patrón"
|
||||
},
|
||||
"patternTester": {
|
||||
"message": "Probador de Patrones"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Patrones"
|
||||
},
|
||||
"patternsChanged": {
|
||||
"message": "Algunos patrones fueron cambiados porque contenían diagonales. Las diagonales en patrones ya no están soportadas."
|
||||
},
|
||||
"port": {
|
||||
"message": "Puerto"
|
||||
},
|
||||
"protocol": {
|
||||
"message": "Protocolo"
|
||||
},
|
||||
"proxyAddress": {
|
||||
"message": "Dirección del Proxy"
|
||||
},
|
||||
"proxyTitle": {
|
||||
"message": "Título del Proxy"
|
||||
},
|
||||
"proxyType": {
|
||||
"message": "Tipo de Proxy"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "Recargar"
|
||||
},
|
||||
"save": {
|
||||
"message": "Guardar"
|
||||
},
|
||||
"saveAdd": {
|
||||
"message": "Guardar y Añadir otro"
|
||||
},
|
||||
"saveEditPattern": {
|
||||
"message": "Guardar y Editar patrones"
|
||||
},
|
||||
"simple": {
|
||||
"message": "simple"
|
||||
},
|
||||
"simpleFormat": {
|
||||
"message": "la dirección ip/servidor y puerto son requeridos. nombre de usuario:contraseña es opcional."
|
||||
},
|
||||
"syncSettings": {
|
||||
"message": "Sincronizar Ajustes"
|
||||
},
|
||||
"syncSettingsHelp": {
|
||||
"message": "Activa para usar Firefox Sync (los ajustes serán sincronizados en todos tus navegadores Firefox). Apaga para almacenar los ajustes localmente. Ten en cuenta que puedes tener dos diferentes instancias de ajustes, una sincronizada y una local."
|
||||
},
|
||||
"test": {
|
||||
"message": "Probar"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "Tiempo"
|
||||
},
|
||||
"title": {
|
||||
"message": "Título o Descripción (opcional)"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Ver Contraseña"
|
||||
},
|
||||
"type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"unmatchedURLs": {
|
||||
"message": "URLs Que No Coincidieron con Patrones"
|
||||
},
|
||||
"up": {
|
||||
"message": "mover arriba"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Por favor ingresa Nombre de usuario y Contraseña."
|
||||
},
|
||||
"username": {
|
||||
"message": "Nombre de usuario (opcional)"
|
||||
},
|
||||
"welcome": {
|
||||
"message": "Bienvenido"
|
||||
},
|
||||
"white": {
|
||||
"message": "Blanco"
|
||||
},
|
||||
"whiteBlack": {
|
||||
"message": "Blanco/Negro"
|
||||
},
|
||||
"whitePatterns": {
|
||||
"message": "Patrones Blancos"
|
||||
}
|
||||
}
|
||||
239
foxyproxy/_locales/fa/messages.json
Normal file
@@ -0,0 +1,239 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "درباره"
|
||||
},
|
||||
"add": {
|
||||
"message": "افزودن"
|
||||
},
|
||||
"addHostTo": {
|
||||
"message": "افزودن الگوی میزبان به ..."
|
||||
},
|
||||
"all": {
|
||||
"message": "همه"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "لغو"
|
||||
},
|
||||
"city": {
|
||||
"message": "شهر"
|
||||
},
|
||||
"color": {
|
||||
"message": "رنگ"
|
||||
},
|
||||
"confirmDeleteBrowserData": {
|
||||
"message": "آیا مطمئنید که میخواهید کوکیها، فضای ذخیرهسازی indexedDB و فضای ذخیرهسازی محلی DOM را حذف کنید؟"
|
||||
},
|
||||
"country": {
|
||||
"message": "کشور"
|
||||
},
|
||||
"delete": {
|
||||
"message": "حذف"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "حذف دادهٔ مرورگر"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "آیا مطمئنید که میخواهید حذفش کنید؟"
|
||||
},
|
||||
"disable": {
|
||||
"message": "از کار انداختن"
|
||||
},
|
||||
"documentURL": {
|
||||
"message": "نشانی مستندات"
|
||||
},
|
||||
"domain": {
|
||||
"message": "دامنه"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "به کار انداختن همگامسازی"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "همگامسازی استثناهای عمومی، پیشکارها و الگوها"
|
||||
},
|
||||
"error": {
|
||||
"message": "هنگام انجام عملیات خطایی رخ داد"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "بجز"
|
||||
},
|
||||
"export": {
|
||||
"message": "برونریزی"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "ابزاری آسان و پیشرفتهٔ مدیریت پیشکار برای همه"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "هنگام تجزیهٔ پرونده خطایی رخ داد"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "هنگام خواندن پرونده خطایی رخ داد"
|
||||
},
|
||||
"fileSizeError": {
|
||||
"message": "پرونده با اندازهٔ بزرگتر از 1$ کیلوبایت مجاز نیست"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "قالب پرونده پشتیبانی نشده"
|
||||
},
|
||||
"foxyProxyAccount": {
|
||||
"message": "حساب فاکسی پروکسی"
|
||||
},
|
||||
"fromOlderVersions": {
|
||||
"message": "از نگارشهای قدیمیتر"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "دریافت مکان"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "استثناهای عمومی"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "الگوهایی که اینجا وارد میکنید، از پیشکار رد نخواهند شد."
|
||||
},
|
||||
"globalExcludeError": {
|
||||
"message": "خطای الگوی استثناهای عمومی"
|
||||
},
|
||||
"help": {
|
||||
"message": "راهنما"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "نام میزبان"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "نام میزبان یا درگاه مشخص نشده"
|
||||
},
|
||||
"import": {
|
||||
"message": "درونریزی"
|
||||
},
|
||||
"importAs": {
|
||||
"message": "درونریزی به عنوان"
|
||||
},
|
||||
"include": {
|
||||
"message": "شامل"
|
||||
},
|
||||
"incognitoAccess": {
|
||||
"message": "تنظیمات پیشکار به مجوز مرور خصوصی نیاز دارد."
|
||||
},
|
||||
"ip": {
|
||||
"message": "آیپی"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "محدود کردن WebRTC"
|
||||
},
|
||||
"location": {
|
||||
"message": "مکان"
|
||||
},
|
||||
"log": {
|
||||
"message": "گزارشات"
|
||||
},
|
||||
"method": {
|
||||
"message": "شیوه"
|
||||
},
|
||||
"ok": {
|
||||
"message": "قبول"
|
||||
},
|
||||
"options": {
|
||||
"message": "گزینهها"
|
||||
},
|
||||
"pacUrlError": {
|
||||
"message": "نشانی PAC برای گونهٔ PAC مشخص نشده"
|
||||
},
|
||||
"password": {
|
||||
"message": "گذرواژه"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "الگو"
|
||||
},
|
||||
"patternError": {
|
||||
"message": "خطای الگو"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "الگوها"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "میزبان ساده"
|
||||
},
|
||||
"port": {
|
||||
"message": "درگاه"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "پیشکار"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "پیشکار بر اساس الگوها"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "پیشکار ساناد (فقط ساکس۵)"
|
||||
},
|
||||
"proxyList": {
|
||||
"message": "فهرست پیشکار"
|
||||
},
|
||||
"proxyPortError": {
|
||||
"message": "درگاه مشخص نشده"
|
||||
},
|
||||
"proxyTypeError": {
|
||||
"message": "گونهٔ ناشناخته"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "افزودن سریع"
|
||||
},
|
||||
"random": {
|
||||
"message": "تصادفی"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "خطا در ایجاد عبارت باقاعده برای الگو"
|
||||
},
|
||||
"resetWebRTC": {
|
||||
"message": "بازنشانی WebRTC"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "بازگردانی به پیشگزیدهها"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "آیا مطمئنید که میخواهید همه تنظیمات را به حالت پیشگزیده بازگردانید؟"
|
||||
},
|
||||
"result": {
|
||||
"message": "نتیجه"
|
||||
},
|
||||
"save": {
|
||||
"message": "ذخیره"
|
||||
},
|
||||
"selectCountry": {
|
||||
"message": "گزینش کشور"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "خطای همگامسازی"
|
||||
},
|
||||
"test": {
|
||||
"message": "آزمودن"
|
||||
},
|
||||
"tester": {
|
||||
"message": "آزمایشگر"
|
||||
},
|
||||
"time": {
|
||||
"message": "زمان"
|
||||
},
|
||||
"title": {
|
||||
"message": "عنوان"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "تغییر حالت گذرواژه"
|
||||
},
|
||||
"type": {
|
||||
"message": "گونه"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "لطفاً نام کاربری و گذرواژه را تکمیل کنید."
|
||||
},
|
||||
"username": {
|
||||
"message": "نام کاربری"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "نویسهٔ همگانی"
|
||||
}
|
||||
}
|
||||
395
foxyproxy/_locales/fr/messages.json
Normal file
@@ -0,0 +1,395 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "A propos"
|
||||
},
|
||||
"active": {
|
||||
"message": "Activer"
|
||||
},
|
||||
"activeNote": {
|
||||
"message": "FoxyProxy ignore tous sur cette page à moins d'être explicitement configuré pour"
|
||||
},
|
||||
"add": {
|
||||
"message": "Ajouter"
|
||||
},
|
||||
"addBlacklist": {
|
||||
"message": "Ajouter des modèles d'exclusions pour empêcher ce proxy d'être utiliser pour localhost et des adresses IP privées ou d'intranet"
|
||||
},
|
||||
"addBlacklistTip": {
|
||||
"message": "Ajouter des modèles d'exclusions pour localhost, 127.0.0.1, 192.168.*.*, 172.16.*.*, & 10.*.*.*"
|
||||
},
|
||||
"addProxy": {
|
||||
"message": "Ajouter un Proxy"
|
||||
},
|
||||
"addWhitelist": {
|
||||
"message": "Ajouter un modèle d'inclusion qui correspond à toutes les urls"
|
||||
},
|
||||
"addWhitelistTip": {
|
||||
"message": "Ajouter un modèle d'inclusion *"
|
||||
},
|
||||
"authError": {
|
||||
"message": "$1 refuse la connection\nVérifiez les identifiants du proxy"
|
||||
},
|
||||
"back": {
|
||||
"message": "Retour"
|
||||
},
|
||||
"black": {
|
||||
"message": "Exclusion"
|
||||
},
|
||||
"blackPatterns": {
|
||||
"message": "Modèle d'exclusion"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Annuler"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Effacer"
|
||||
},
|
||||
"clickTest": {
|
||||
"message": "Cliquez sur 'Test' lorsque vous êtes prêt"
|
||||
},
|
||||
"color": {
|
||||
"message": "Couleur"
|
||||
},
|
||||
"complete": {
|
||||
"message": "complet"
|
||||
},
|
||||
"completeFormat": {
|
||||
"message": "Dans ce format, vous pouvez spécifier tous les paramètres du proxy. Mais seulement le protocole et le nom du serveur sont requis."
|
||||
},
|
||||
"confirmDelete": {
|
||||
"message": "Etes-vous sûr de vouloir supprimer ?"
|
||||
},
|
||||
"confirmOverwrite": {
|
||||
"message": "Etes-vous sûr de vouloir ecraser les proxys existants ?"
|
||||
},
|
||||
"confirmTransferToLocal": {
|
||||
"message": "Voulez-vous appliquez les paramètres synchronisés au profil local ?\nLes données existantes seront fusionnées."
|
||||
},
|
||||
"confirmTransferToSync": {
|
||||
"message": "Voulez-vous appliquez les paramètres locaux au profil synchronisé ?\nLes données existantes seront fusionnées."
|
||||
},
|
||||
"delete": {
|
||||
"message": "Supprimer"
|
||||
},
|
||||
"deleteAll": {
|
||||
"message": "Tous supprimer"
|
||||
},
|
||||
"deleteAllmessage": {
|
||||
"message": "Toutes les données ont été supprimées"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Supprimer les données du navigateur"
|
||||
},
|
||||
"deleteBrowserDataDescription": {
|
||||
"message": "Cache, cookies, base de donnée indexée, cache local du DOM, données du plugin, données du service."
|
||||
},
|
||||
"deleteBrowserDataNotDescription": {
|
||||
"message": "Mot de passe sauvegardés, historique et formulaire du navigateur, historique de téléchargement, webSQL, et certificats de serveur."
|
||||
},
|
||||
"deleteNot": {
|
||||
"message": "Ne Pas Supprimer"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Désactiver"
|
||||
},
|
||||
"done": {
|
||||
"message": "Fait !"
|
||||
},
|
||||
"down": {
|
||||
"message": "descendre"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Modifier"
|
||||
},
|
||||
"editPatterns": {
|
||||
"message": "Editer les Modèles"
|
||||
},
|
||||
"editPatternsFor": {
|
||||
"message": "Editer les Modèles de $1"
|
||||
},
|
||||
"editProxy": {
|
||||
"message": "Modifier le Proxy $1"
|
||||
},
|
||||
"enterUrl": {
|
||||
"message": "Entrer l'URL à tester"
|
||||
},
|
||||
"enterUrlNote": {
|
||||
"message": "(les chemins d'URL et les paramètres sont ignorés)"
|
||||
},
|
||||
"erroNoSettings": {
|
||||
"message": "Aucun paramètre n'a été trouvé. Essayez de réinstaller FoxyProxy."
|
||||
},
|
||||
"error": {
|
||||
"message": "Erreur"
|
||||
},
|
||||
"errorEmpty": {
|
||||
"message": "Le champ ne peut pas être vide."
|
||||
},
|
||||
"errorFetch": {
|
||||
"message": "Une erreur est survenue durant l'opération"
|
||||
},
|
||||
"errorPattern": {
|
||||
"message": "Merci d'entrer un modèle"
|
||||
},
|
||||
"errorProtocol": {
|
||||
"message": "Le protocole ne correspond pas."
|
||||
},
|
||||
"errorSlash": {
|
||||
"message": "Pas de barre oblique dans des modèles génériques. Vous ne pouvez pas associer d'URL à cause des restrictions de Firefox."
|
||||
},
|
||||
"errorWas": {
|
||||
"message": "Une erreur est survenue. Merci de réessayer."
|
||||
},
|
||||
"examples": {
|
||||
"message": "Exemples"
|
||||
},
|
||||
"export": {
|
||||
"message": "Exporter les paramètres"
|
||||
},
|
||||
"exportPatterns": {
|
||||
"message": "Exporter les Modèles"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Gestionnaire de proxy avancé facile d'utilisation"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basique"
|
||||
},
|
||||
"forAll": {
|
||||
"message": "pour toutes les URLs"
|
||||
},
|
||||
"formats": {
|
||||
"message": "Formats"
|
||||
},
|
||||
"help": {
|
||||
"message": "Aide"
|
||||
},
|
||||
"import": {
|
||||
"message": "Importer les paramètres"
|
||||
},
|
||||
"importBW": {
|
||||
"message": "$1 modèle(s) d'inclusions et $2 modèle(s) d'exclusions ont été importés."
|
||||
},
|
||||
"importEnd": {
|
||||
"message": "Les paramètres ont été correctement importés"
|
||||
},
|
||||
"importEndSlash": {
|
||||
"message": "Import fini. Les barres obliques ne sont plus supportées dans les modèles à cause d'un bug de Firefox. Merci de revoir votre modèle et de supprimer les éventuelles barres obliques."
|
||||
},
|
||||
"importPatterns": {
|
||||
"message": "Importer les Modèles"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "Importer la liste des Proxy"
|
||||
},
|
||||
"importSucceeded": {
|
||||
"message": "$1 proxys interprétés et stockés."
|
||||
},
|
||||
"imported": {
|
||||
"message": "Importé"
|
||||
},
|
||||
"importedPattern": {
|
||||
"message": "Ce modèle a été importé à partir d'une ancienne version de FoxyProxy et a été modifié lors de l'import. Voici le modèle original non modifié:"
|
||||
},
|
||||
"importsSkipped": {
|
||||
"message": "$1 lignes ignorées car elles ne pouvaient pas être interprétées:\n\n$2"
|
||||
},
|
||||
"ip": {
|
||||
"message": "Adresse IP du Proxy ou nom du DNS"
|
||||
},
|
||||
"ipPort": {
|
||||
"message": "ip:port"
|
||||
},
|
||||
"ipPortUsernamePassword": {
|
||||
"message": "ip:port:login:mot de passe"
|
||||
},
|
||||
"log": {
|
||||
"message": "Journal"
|
||||
},
|
||||
"logSize": {
|
||||
"message": "Taille du Journal"
|
||||
},
|
||||
"matchPattern": {
|
||||
"message": "Modèle de correspondance"
|
||||
},
|
||||
"matchedURLs": {
|
||||
"message": "URLs qui correspondent aux Modèles"
|
||||
},
|
||||
"modeDisabled": {
|
||||
"message": "Désactiver (Utiliser les paramètres de firefox)"
|
||||
},
|
||||
"modePatterns": {
|
||||
"message": "Utiliser les proxys activés par modèle et ordre"
|
||||
},
|
||||
"myIP": {
|
||||
"message": "Quel est mon IP ?"
|
||||
},
|
||||
"name": {
|
||||
"message": "Nom"
|
||||
},
|
||||
"newBlack": {
|
||||
"message": "Nouveau Modèle d'Exclusion"
|
||||
},
|
||||
"newWhite": {
|
||||
"message": "Nouveau Modèle d'Inclusion"
|
||||
},
|
||||
"noLocal": {
|
||||
"message": "Ne pas utiliser pour localhost et des adresses IP privées ou d'intranet"
|
||||
},
|
||||
"noMatch": {
|
||||
"message": "Pas de Correspondance"
|
||||
},
|
||||
"noProxies": {
|
||||
"message": "Vous n'avez aucun paramètres de proxy."
|
||||
},
|
||||
"none": {
|
||||
"message": "Aucun"
|
||||
},
|
||||
"notApplicable": {
|
||||
"message": "s/o"
|
||||
},
|
||||
"off": {
|
||||
"message": "Desactiver"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Accepter"
|
||||
},
|
||||
"on": {
|
||||
"message": "Activer"
|
||||
},
|
||||
"options": {
|
||||
"message": "Options"
|
||||
},
|
||||
"optionsPage": {
|
||||
"message": "Options FoxyProxy"
|
||||
},
|
||||
"overwritProxiesHelp1": {
|
||||
"message": "Cochez pour supprimer tous les proxys existants et les remplacer par ceux de la liste."
|
||||
},
|
||||
"overwritProxiesHelp2": {
|
||||
"message": "Décocher pour ajouter les proxys de la liste à ceux déjà existant."
|
||||
},
|
||||
"overwriteProxies": {
|
||||
"message": "Ecraser les proxys existants"
|
||||
},
|
||||
"password": {
|
||||
"message": "Mot de passe (optionnel)"
|
||||
},
|
||||
"pasteList": {
|
||||
"message": "Coller une liste de proxy ci-dessous."
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Modèles"
|
||||
},
|
||||
"patternCheatSheet": {
|
||||
"message": "Aide-Mémoire pour les Modèles"
|
||||
},
|
||||
"patternDetail": {
|
||||
"message": "Entrer les informations du modèle."
|
||||
},
|
||||
"patternHelp": {
|
||||
"message": "Aide Modèle"
|
||||
},
|
||||
"patternMatch": {
|
||||
"message": "Ce modèle correspond à l'URL!"
|
||||
},
|
||||
"patternNotMatch": {
|
||||
"message": "Ce modèle ne correspond pas à l'URL."
|
||||
},
|
||||
"patternNote": {
|
||||
"message": "(pas de chemins d'URL, pas de paramètres de requêtes)"
|
||||
},
|
||||
"patternShortcuts": {
|
||||
"message": "Raccourcis de Modèle"
|
||||
},
|
||||
"patternTester": {
|
||||
"message": "Testeur de Modèles"
|
||||
},
|
||||
"patternsChanged": {
|
||||
"message": "Certains modèles ont été changés car ils contenaient des barres obliques. Elles ne sont plus supportées."
|
||||
},
|
||||
"port": {
|
||||
"message": "Port"
|
||||
},
|
||||
"protocol": {
|
||||
"message": "Protocole"
|
||||
},
|
||||
"proxyAddress": {
|
||||
"message": "Adresse du Proxy"
|
||||
},
|
||||
"proxyTitle": {
|
||||
"message": "Nom du Proxy"
|
||||
},
|
||||
"proxyType": {
|
||||
"message": "Type de Proxy"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "Rafraîchir"
|
||||
},
|
||||
"save": {
|
||||
"message": "Sauvegarder"
|
||||
},
|
||||
"saveAdd": {
|
||||
"message": "Sauvegarder et Ajouter un autre"
|
||||
},
|
||||
"saveEditPattern": {
|
||||
"message": "Sauvegarder et Modifier les Modèles"
|
||||
},
|
||||
"simple": {
|
||||
"message": "simple"
|
||||
},
|
||||
"simpleFormat": {
|
||||
"message": "l'adresse ip ou nom du serveur et le port sont requis. login:mot de passe sont optionnels."
|
||||
},
|
||||
"syncSettings": {
|
||||
"message": "Synchroniser les Paramètres"
|
||||
},
|
||||
"syncSettingsHelp": {
|
||||
"message": "Activer pour utiliser Firefox Sync (les paramètres sont synchronisés entre tous vos navigateurs Firefox). Desactiver pour sauvegarder les paramètres localement. A noter que deux profils de paramètres peuvent coexister, un synchronisé et un local"
|
||||
},
|
||||
"test": {
|
||||
"message": "Tester"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "Date"
|
||||
},
|
||||
"title": {
|
||||
"message": "Nom ou Description (optionnel)"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Afficher/Masquer le mot de passe"
|
||||
},
|
||||
"type": {
|
||||
"message": "Type"
|
||||
},
|
||||
"unmatchedURLs": {
|
||||
"message": "URLs qui ne correspondent pas aux Modèles"
|
||||
},
|
||||
"up": {
|
||||
"message": "remonter"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Merci d'entrer votre nom d'utilisateur et mot de passe."
|
||||
},
|
||||
"username": {
|
||||
"message": "Nom d'utilisateur (optionnel)"
|
||||
},
|
||||
"welcome": {
|
||||
"message": "Bienvenu"
|
||||
},
|
||||
"white": {
|
||||
"message": "Inclusion"
|
||||
},
|
||||
"whiteBlack": {
|
||||
"message": "Inclusion/Exclusion"
|
||||
},
|
||||
"whitePatterns": {
|
||||
"message": "Modèle d'inclusion"
|
||||
}
|
||||
}
|
||||
272
foxyproxy/_locales/ja/messages.json
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "About"
|
||||
},
|
||||
"add": {
|
||||
"message": "追加"
|
||||
},
|
||||
"all": {
|
||||
"message": "全て"
|
||||
},
|
||||
"autoBackup": {
|
||||
"message": "自動バックアップ"
|
||||
},
|
||||
"autoBackupDescription": {
|
||||
"message": "保存時に設定をダウンロードフォルダーに自動でバックアップする"
|
||||
},
|
||||
"back": {
|
||||
"message": "戻る"
|
||||
},
|
||||
"city": {
|
||||
"message": "市区町村"
|
||||
},
|
||||
"color": {
|
||||
"message": "色"
|
||||
},
|
||||
"container": {
|
||||
"message": "コンテナー"
|
||||
},
|
||||
"containerDescription": {
|
||||
"message": "Incognito/コンテナープロキシー (Firefox のみ)"
|
||||
},
|
||||
"controlledByOtherExtensions": {
|
||||
"message": "他の拡張機能によって設定が制御されています"
|
||||
},
|
||||
"country": {
|
||||
"message": "国"
|
||||
},
|
||||
"default": {
|
||||
"message": "デフォルト"
|
||||
},
|
||||
"delete": {
|
||||
"message": "削除"
|
||||
},
|
||||
"deleteBrowsingData": {
|
||||
"message": "閲覧データを削除"
|
||||
},
|
||||
"deleteBrowsingDataConfirm": {
|
||||
"message": "本当に Cookie、indexedDB ストレージ、DOM ローカルストレージを削除しますか?"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "本当に削除しますか?"
|
||||
},
|
||||
"disable": {
|
||||
"message": "無効"
|
||||
},
|
||||
"documentUrl": {
|
||||
"message": "ドキュメント URL"
|
||||
},
|
||||
"duplicate": {
|
||||
"message": "複製"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "同期を有効化"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "グローバル除外、プロキシー、パターンを同期する"
|
||||
},
|
||||
"error": {
|
||||
"message": "操作中にエラーが発生しました"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "除く"
|
||||
},
|
||||
"excludeHost": {
|
||||
"message": "ホストを除外"
|
||||
},
|
||||
"export": {
|
||||
"message": "エクスポート"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "誰でも使いやすい先進的なプロキシー管理ツール"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "ファイルの解析中にエラーが発生しました"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "ファイルの読み取り中にエラーが発生しました"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "サポートされていないファイルフォーマットです"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "位置情報を取得"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "グローバル除外"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "プロキシーを使用しないホストのリスト"
|
||||
},
|
||||
"help": {
|
||||
"message": "ヘルプ"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "ホスト名"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "ホスト名 - ポートが入力されていません"
|
||||
},
|
||||
"import": {
|
||||
"message": "インポート"
|
||||
},
|
||||
"importFoxyProxyAccount": {
|
||||
"message": "FoxyProxy アカウントからインポート"
|
||||
},
|
||||
"importFromOlderVersions": {
|
||||
"message": "以前のバージョンからインポート"
|
||||
},
|
||||
"importFromURL": {
|
||||
"message": "URL からインポート"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "プロキシーリストをインポート"
|
||||
},
|
||||
"include": {
|
||||
"message": "含む"
|
||||
},
|
||||
"incognito": {
|
||||
"message": "Incognito"
|
||||
},
|
||||
"incognitoAccessError": {
|
||||
"message": "Firefox でプロキシーを設定するためにはプライベートブラウジングでの許可設定が必要です"
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "WebRTC を制限"
|
||||
},
|
||||
"limitWebRTCDescription": {
|
||||
"message": "WebRTC ブラウザー設定を切り替える"
|
||||
},
|
||||
"location": {
|
||||
"message": "位置情報"
|
||||
},
|
||||
"log": {
|
||||
"message": "ログ"
|
||||
},
|
||||
"method": {
|
||||
"message": "メソッド"
|
||||
},
|
||||
"more": {
|
||||
"message": "詳細"
|
||||
},
|
||||
"notAvailable": {
|
||||
"message": "Chrome では利用できません (Firefox のみ)"
|
||||
},
|
||||
"options": {
|
||||
"message": "オプション"
|
||||
},
|
||||
"password": {
|
||||
"message": "パスワード"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "パターン"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "パターン"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "プレーンホスト"
|
||||
},
|
||||
"port": {
|
||||
"message": "ポート"
|
||||
},
|
||||
"proxies": {
|
||||
"message": "プロキシー"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "プロキシー"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "パターンによるプロキシー"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "DNS でプロキシーを使用"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "クイック追加"
|
||||
},
|
||||
"random": {
|
||||
"message": "ランダム"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "パターンの正規表現を作成中にエラーが発生しました"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "規定値を復元"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "本当に全ての設定を規定値に復元しますか?"
|
||||
},
|
||||
"result": {
|
||||
"message": "結果"
|
||||
},
|
||||
"saveOptions": {
|
||||
"message": "保存"
|
||||
},
|
||||
"setProxy": {
|
||||
"message": "プロキシーを設定"
|
||||
},
|
||||
"setTabProxy": {
|
||||
"message": "タブプロキシーを設定"
|
||||
},
|
||||
"shortcut": {
|
||||
"message": "キーボードショートカット"
|
||||
},
|
||||
"shortcutDescription": {
|
||||
"message": "キーボードショートカットアクションに関連付けるプロキシーをあらかじめ選択する"
|
||||
},
|
||||
"storeLocally": {
|
||||
"message": "ローカルに保存"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "同期エラー"
|
||||
},
|
||||
"test": {
|
||||
"message": "テスト"
|
||||
},
|
||||
"tester": {
|
||||
"message": "パターンテスター"
|
||||
},
|
||||
"theme": {
|
||||
"message": "テーマ"
|
||||
},
|
||||
"time": {
|
||||
"message": "時刻"
|
||||
},
|
||||
"title": {
|
||||
"message": "タイトル"
|
||||
},
|
||||
"togglePassword": {
|
||||
"message": "パスワードを切り替え"
|
||||
},
|
||||
"type": {
|
||||
"message": "種類"
|
||||
},
|
||||
"unsetTabProxy": {
|
||||
"message": "タブプロキシーを解除"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "ユーザー名もしくはパスワードが入力されていません"
|
||||
},
|
||||
"username": {
|
||||
"message": "ユーザー名"
|
||||
},
|
||||
"view": {
|
||||
"message": "閲覧"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "ワイルドカード"
|
||||
}
|
||||
}
|
||||
236
foxyproxy/_locales/pl/messages.json
Normal file
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "O aplikacji"
|
||||
},
|
||||
"add": {
|
||||
"message": "Dodaj"
|
||||
},
|
||||
"addHostTo": {
|
||||
"message": "Dodaj wzorzec hosta do ..."
|
||||
},
|
||||
"all": {
|
||||
"message": "Wszystko"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Anuluj"
|
||||
},
|
||||
"city": {
|
||||
"message": "Miasto"
|
||||
},
|
||||
"color": {
|
||||
"message": "Kolor"
|
||||
},
|
||||
"confirmDeleteBrowserData": {
|
||||
"message": "Czy na pewno chcesz usunąć ciasteczka, magazyn indexedDB, lokalny magazyn DOM?"
|
||||
},
|
||||
"country": {
|
||||
"message": "Kraj"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Usuń"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Usuń dane przeglądarki"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "Czy na pewno chcesz usunąć?"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Wyłącz"
|
||||
},
|
||||
"documentURL": {
|
||||
"message": "URL dokumentu"
|
||||
},
|
||||
"domain": {
|
||||
"message": "Domena"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "Włącz synchronizację"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "Synchronizacja globalnych wykluczeń, proxy i wzorców"
|
||||
},
|
||||
"error": {
|
||||
"message": "Wystąpił błąd podczas realizacji zadania."
|
||||
},
|
||||
"exclude": {
|
||||
"message": "Wyklucz"
|
||||
},
|
||||
"export": {
|
||||
"message": "Eksportuj"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Łatwe w użyciu zaawansowane narzędzie do zarządzania proxy dla każdego"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "Wystąpił błąd podczas przetwarzania pliku"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "Wystąpił błąd w odczycie pliku"
|
||||
},
|
||||
"fileSizeError": {
|
||||
"message": "Rozmiar pliku jest większy niż dozwolony $1kb"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "Nieobsługiwany format pliku"
|
||||
},
|
||||
"foxyProxyAccount": {
|
||||
"message": "Konto FoxyProxy"
|
||||
},
|
||||
"fromOlderVersions": {
|
||||
"message": "Ze starszych wersji"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "Uzyskaj lokalizację"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "Globalne wykluczenia"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "Wzorce wprowadzone tutaj nigdy nie będą pośredniczyć"
|
||||
},
|
||||
"globalExcludeError": {
|
||||
"message": "Błąd globalnego wzorca wykluczeń"
|
||||
},
|
||||
"help": {
|
||||
"message": "Pomoc"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Nazwa hosta"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "Brak nazwy hosta - portu"
|
||||
},
|
||||
"import": {
|
||||
"message": "Importuj"
|
||||
},
|
||||
"importAs": {
|
||||
"message": "Zaimportuj jako"
|
||||
},
|
||||
"include": {
|
||||
"message": "Uwzględnij"
|
||||
},
|
||||
"incognitoAccess": {
|
||||
"message": "Ustawienia proxy wymagają zezwolenia do trybu prywatnego."
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "Ogranicz WebRTC"
|
||||
},
|
||||
"location": {
|
||||
"message": "Lokalizacja"
|
||||
},
|
||||
"log": {
|
||||
"message": "Dziennik"
|
||||
},
|
||||
"method": {
|
||||
"message": "Metoda"
|
||||
},
|
||||
"ok": {
|
||||
"message": "OK"
|
||||
},
|
||||
"options": {
|
||||
"message": "Opcje"
|
||||
},
|
||||
"pacUrlError": {
|
||||
"message": "Brak adresu URl dla typu PAC"
|
||||
},
|
||||
"password": {
|
||||
"message": "Hasło"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Wzorzec"
|
||||
},
|
||||
"patternError": {
|
||||
"message": "Błąd wzorca"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "Zwykły host"
|
||||
},
|
||||
"port": {
|
||||
"message": "Port"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "Proxy"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "Proxy według wzorców"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "Proxy DNS (tylko SOCKS 5)"
|
||||
},
|
||||
"proxyList": {
|
||||
"message": "Lista Proxy"
|
||||
},
|
||||
"proxyPortError": {
|
||||
"message": "Brak portu"
|
||||
},
|
||||
"proxyTypeError": {
|
||||
"message": "Nieznany typ"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "Szybko dodaj"
|
||||
},
|
||||
"random": {
|
||||
"message": "Losowo"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "Błąd tworzenia regexp dla wzorca"
|
||||
},
|
||||
"resetWebRTC": {
|
||||
"message": "Zresetuj WebRTC"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "Przywróć ustawienia domyślne"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "Czy na pewno chcesz przywrócić wszystkie ustawienia do wartości domyślnych?"
|
||||
},
|
||||
"result": {
|
||||
"message": "Wynik"
|
||||
},
|
||||
"save": {
|
||||
"message": "Zapisz"
|
||||
},
|
||||
"selectCountry": {
|
||||
"message": "Wybierz kraj"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "Błąd synchronizacji"
|
||||
},
|
||||
"test": {
|
||||
"message": "Testuj"
|
||||
},
|
||||
"tester": {
|
||||
"message": "Tester"
|
||||
},
|
||||
"time": {
|
||||
"message": "Czas"
|
||||
},
|
||||
"title": {
|
||||
"message": "Nazwa"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Przełącz hasło"
|
||||
},
|
||||
"type": {
|
||||
"message": "Typ"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Proszę wpisać nazwę użytkownika oraz hasło."
|
||||
},
|
||||
"username": {
|
||||
"message": "Nazwa użytkownika"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "Wildcard"
|
||||
}
|
||||
}
|
||||
398
foxyproxy/_locales/pt_BR/messages.json
Normal file
@@ -0,0 +1,398 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "Sobre"
|
||||
},
|
||||
"active": {
|
||||
"message": "Ativado"
|
||||
},
|
||||
"activeNote": {
|
||||
"message": "FoxyProxy ignora tudo nesta página, a menos que definido como"
|
||||
},
|
||||
"add": {
|
||||
"message": "Adicionar"
|
||||
},
|
||||
"addBlacklist": {
|
||||
"message": "Adicione padrões pretos para evitar que esse proxy seja usado para endereços IP de host local e intranet/privados"
|
||||
},
|
||||
"addBlacklistTip": {
|
||||
"message": "Adicionar padrões de lista negra para localhost, 127.0.0.1, 192.168.*.*, 172.16.*.*, & 10.*.*.*"
|
||||
},
|
||||
"addProxy": {
|
||||
"message": "Adicionar Proxy"
|
||||
},
|
||||
"addWhitelist": {
|
||||
"message": "Adicionar padrão de lista de permissões para corresponder a todos os URLs"
|
||||
},
|
||||
"addWhitelistTip": {
|
||||
"message": "Adicionar padrão de lista de permissões *"
|
||||
},
|
||||
"authError": {
|
||||
"message": "$1 está recusando conexões\nVerifique o nome de usuário/senha do proxy"
|
||||
},
|
||||
"back": {
|
||||
"message": "Voltar"
|
||||
},
|
||||
"black": {
|
||||
"message": "Preto"
|
||||
},
|
||||
"blackPatterns": {
|
||||
"message": "Padrões pretos"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancelar"
|
||||
},
|
||||
"clear": {
|
||||
"message": "Limpar"
|
||||
},
|
||||
"clickTest": {
|
||||
"message": "Clique em 'Testar' quando estiver pronto"
|
||||
},
|
||||
"color": {
|
||||
"message": "Cor"
|
||||
},
|
||||
"complete": {
|
||||
"message": "completo"
|
||||
},
|
||||
"completeFormat": {
|
||||
"message": "Com este formato, você pode especificar todas as configurações de proxy. Mas apenas protocolo e servidor são necessários."
|
||||
},
|
||||
"confirmDelete": {
|
||||
"message": "Tem certeza de que deseja excluir?"
|
||||
},
|
||||
"confirmOverwrite": {
|
||||
"message": "Tem certeza de que deseja substituir os proxies existentes?"
|
||||
},
|
||||
"confirmTransferToLocal": {
|
||||
"message": "Deseja transferir as configurações sincronizadas para seu perfil local?\nOs dados existentes serão mesclados."
|
||||
},
|
||||
"confirmTransferToSync": {
|
||||
"message": "Deseja transferir as configurações locais para seu perfil sincronizado?\nOs dados existentes serão mesclados."
|
||||
},
|
||||
"delete": {
|
||||
"message": "Excluir"
|
||||
},
|
||||
"deleteAll": {
|
||||
"message": "Excluir tudo"
|
||||
},
|
||||
"deleteAllmessage": {
|
||||
"message": "Todos os dados excluídos"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Excluir dados do navegador"
|
||||
},
|
||||
"deleteBrowserDataDescription": {
|
||||
"message": "Cache, cookies, armazenamento indexedDB, armazenamento local DOM, dados de plug-in, dados de service worker."
|
||||
},
|
||||
"deleteBrowserDataNotDescription": {
|
||||
"message": "Senhas armazenadas, histórico de navegação e formulário, histórico de download, webSQL e certificados vinculados ao servidor."
|
||||
},
|
||||
"deleteNot": {
|
||||
"message": "Não apague"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "Desativado"
|
||||
},
|
||||
"done": {
|
||||
"message": "Pronto!"
|
||||
},
|
||||
"down": {
|
||||
"message": "Descer"
|
||||
},
|
||||
"edit": {
|
||||
"message": "Editar"
|
||||
},
|
||||
"editPatterns": {
|
||||
"message": "Editar padrões"
|
||||
},
|
||||
"editPatternsFor": {
|
||||
"message": "Editar padrões de $1"
|
||||
},
|
||||
"editProxy": {
|
||||
"message": "Editar Proxy $1"
|
||||
},
|
||||
"enterUrl": {
|
||||
"message": "Insira o URL para testar"
|
||||
},
|
||||
"enterUrlNote": {
|
||||
"message": "(caminhos e parâmetros de consulta são ignorados)"
|
||||
},
|
||||
"erroNoSettings": {
|
||||
"message": "Nenhuma configuração foi encontrada. Reinstale o FoxyProxy."
|
||||
},
|
||||
"error": {
|
||||
"message": "Falha"
|
||||
},
|
||||
"errorEmpty": {
|
||||
"message": "O campo não pode ficar vazio."
|
||||
},
|
||||
"errorFetch": {
|
||||
"message": "Ocorreu um erro com a operação"
|
||||
},
|
||||
"errorPattern": {
|
||||
"message": "Por favor, insira um padrão"
|
||||
},
|
||||
"errorProtocol": {
|
||||
"message": "O protocolo não corresponde."
|
||||
},
|
||||
"errorSlash": {
|
||||
"message": "Nenhuma barra em padrões curinga. Você não pode corresponder a caminhos de URL devido às restrições do Firefox."
|
||||
},
|
||||
"errorWas": {
|
||||
"message": "Houve um erro. Por favor, tente novamente."
|
||||
},
|
||||
"examples": {
|
||||
"message": "Exemplos"
|
||||
},
|
||||
"export": {
|
||||
"message": "Exportar configurações"
|
||||
},
|
||||
"exportPatterns": {
|
||||
"message": "Exportar padrões"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Ferramenta avançada de gerenciamento de proxy fácil de usar para todos"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Básico"
|
||||
},
|
||||
"forAll": {
|
||||
"message": "para todos os URLs"
|
||||
},
|
||||
"formats": {
|
||||
"message": "formatos"
|
||||
},
|
||||
"help": {
|
||||
"message": "Ajuda"
|
||||
},
|
||||
"import": {
|
||||
"message": "Configurações de Importação"
|
||||
},
|
||||
"importBW": {
|
||||
"message": "Padrões importados $1 branco e $2 preto."
|
||||
},
|
||||
"importEnd": {
|
||||
"message": "As configurações foram importadas com sucesso"
|
||||
},
|
||||
"importEndSlash": {
|
||||
"message": "Importação concluída. Barras em padrões não são suportadas devido a um bug do Firefox. Revise seus padrões e remova as barras, se houver."
|
||||
},
|
||||
"importPatterns": {
|
||||
"message": "Importar padrões"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "Importar lista de proxy"
|
||||
},
|
||||
"importSucceeded": {
|
||||
"message": "Ler e armazenar proxies de $1."
|
||||
},
|
||||
"imported": {
|
||||
"message": "Importado"
|
||||
},
|
||||
"importedPattern": {
|
||||
"message": "Esse padrão foi importado de uma versão mais antiga do FoxyProxy e alterado durante a importação. Aqui está o padrão original, inalterado:"
|
||||
},
|
||||
"importsSkipped": {
|
||||
"message": "$1 linhas ignoradas porque não puderam ser analisadas:\n\n$2"
|
||||
},
|
||||
"ip": {
|
||||
"message": "Endereço IP do proxy ou nome DNS"
|
||||
},
|
||||
"ipPort": {
|
||||
"message": "ip:porta"
|
||||
},
|
||||
"ipPortUsernamePassword": {
|
||||
"message": "ip:porta:usuário:senha"
|
||||
},
|
||||
"log": {
|
||||
"message": "Registro"
|
||||
},
|
||||
"logSize": {
|
||||
"message": "Tamanho do registro"
|
||||
},
|
||||
"matchPattern": {
|
||||
"message": "Padrão de correspondência"
|
||||
},
|
||||
"matchedURLs": {
|
||||
"message": "URLs que correspondem a padrões"
|
||||
},
|
||||
"modeDisabled": {
|
||||
"message": "Desativar (usar configurações do Firefox)"
|
||||
},
|
||||
"modePatterns": {
|
||||
"message": "Use proxies habilitados por padrões em ordem"
|
||||
},
|
||||
"myIP": {
|
||||
"message": "Qual é o meu IP?"
|
||||
},
|
||||
"name": {
|
||||
"message": "Nome"
|
||||
},
|
||||
"newBlack": {
|
||||
"message": "Novo Preto"
|
||||
},
|
||||
"newWhite": {
|
||||
"message": "Novo Branco"
|
||||
},
|
||||
"noLocal": {
|
||||
"message": "Não use para endereços IP de host local e intranet/privados"
|
||||
},
|
||||
"noMatch": {
|
||||
"message": "Sem correspondência"
|
||||
},
|
||||
"noProxies": {
|
||||
"message": "Você não tem nenhuma configuração de proxy."
|
||||
},
|
||||
"none": {
|
||||
"message": "Nenhum"
|
||||
},
|
||||
"notApplicable": {
|
||||
"message": "n/a"
|
||||
},
|
||||
"off": {
|
||||
"message": "Desligar"
|
||||
},
|
||||
"ok": {
|
||||
"message": "OK"
|
||||
},
|
||||
"on": {
|
||||
"message": "Ligar"
|
||||
},
|
||||
"options": {
|
||||
"message": "Opções"
|
||||
},
|
||||
"optionsPage": {
|
||||
"message": "FoxyProxy Opções"
|
||||
},
|
||||
"overwritProxiesHelp1": {
|
||||
"message": "Marque para excluir todos os proxies existentes e substituí-los por proxies desta lista."
|
||||
},
|
||||
"overwritProxiesHelp2": {
|
||||
"message": "Desmarque para anexar proxies nesta lista a proxies existentes."
|
||||
},
|
||||
"overwriteProxies": {
|
||||
"message": "Substituir proxies existentes"
|
||||
},
|
||||
"password": {
|
||||
"message": "Senha (opcional)"
|
||||
},
|
||||
"pasteList": {
|
||||
"message": "Cole uma lista de proxy abaixo."
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Padrões"
|
||||
},
|
||||
"patternCheatSheet": {
|
||||
"message": "Folha de referência de padrão"
|
||||
},
|
||||
"patternDetail": {
|
||||
"message": "Insira os detalhes do padrão."
|
||||
},
|
||||
"patternHelp": {
|
||||
"message": "Ajuda de padrão"
|
||||
},
|
||||
"patternMatch": {
|
||||
"message": "O padrão corresponde ao URL!"
|
||||
},
|
||||
"patternNotMatch": {
|
||||
"message": "O padrão não corresponde ao URL."
|
||||
},
|
||||
"patternNote": {
|
||||
"message": "(sem caminhos, sem parâmetros de consulta)"
|
||||
},
|
||||
"patternShortcuts": {
|
||||
"message": "Atalhos de padrão"
|
||||
},
|
||||
"patternTester": {
|
||||
"message": "Testador de padrões"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Padrões"
|
||||
},
|
||||
"patternsChanged": {
|
||||
"message": "Alguns padrões foram alterados porque continham barras. Barras em padrões não são mais suportadas."
|
||||
},
|
||||
"port": {
|
||||
"message": "Porta"
|
||||
},
|
||||
"protocol": {
|
||||
"message": "Protocolo"
|
||||
},
|
||||
"proxyAddress": {
|
||||
"message": "Endereço proxy"
|
||||
},
|
||||
"proxyTitle": {
|
||||
"message": "Título do proxy"
|
||||
},
|
||||
"proxyType": {
|
||||
"message": "Tipo da Proxy"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "Atualizar"
|
||||
},
|
||||
"save": {
|
||||
"message": "Salvar"
|
||||
},
|
||||
"saveAdd": {
|
||||
"message": "Salvar & Adicionar outro"
|
||||
},
|
||||
"saveEditPattern": {
|
||||
"message": "Salvar & Editar padrões"
|
||||
},
|
||||
"simple": {
|
||||
"message": "simples"
|
||||
},
|
||||
"simpleFormat": {
|
||||
"message": "Endereço IP/Servidor e Porta são necessários. Nome de Usuário:Senha são Opcionais."
|
||||
},
|
||||
"syncSettings": {
|
||||
"message": "Sincronizar configurações"
|
||||
},
|
||||
"syncSettingsHelp": {
|
||||
"message": "Ative para usar o Firefox Sync (as configurações são sincronizadas em todos os seus navegadores Firefox). Desligue para armazenar as configurações localmente. Observe que você pode ter duas instâncias diferentes de configurações, uma de sincronização e uma local."
|
||||
},
|
||||
"test": {
|
||||
"message": "Testar"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "Carimbo de data e hora"
|
||||
},
|
||||
"title": {
|
||||
"message": "Título ou Descrição (opcional)"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Alternar senha"
|
||||
},
|
||||
"type": {
|
||||
"message": "Tipo"
|
||||
},
|
||||
"unmatchedURLs": {
|
||||
"message": "URLs que não correspondem aos padrões"
|
||||
},
|
||||
"up": {
|
||||
"message": "Subir"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Por favor, digite o nome de usuário e a senha."
|
||||
},
|
||||
"username": {
|
||||
"message": "Usuário (opcional)"
|
||||
},
|
||||
"welcome": {
|
||||
"message": "Bem Vindo"
|
||||
},
|
||||
"white": {
|
||||
"message": "Branco"
|
||||
},
|
||||
"whiteBlack": {
|
||||
"message": "Branco/Preto"
|
||||
},
|
||||
"whitePatterns": {
|
||||
"message": "Padrões brancos"
|
||||
}
|
||||
}
|
||||
272
foxyproxy/_locales/ru/messages.json
Normal file
@@ -0,0 +1,272 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "О программе"
|
||||
},
|
||||
"add": {
|
||||
"message": "Добавить"
|
||||
},
|
||||
"all": {
|
||||
"message": "Все"
|
||||
},
|
||||
"autoBackup": {
|
||||
"message": "Автоматическое резервное копирование"
|
||||
},
|
||||
"autoBackupDescription": {
|
||||
"message": "Автоматическое резервное копирование настроек в папку «Загрузки» при сохранении."
|
||||
},
|
||||
"back": {
|
||||
"message": "Назад"
|
||||
},
|
||||
"city": {
|
||||
"message": "Город"
|
||||
},
|
||||
"color": {
|
||||
"message": "Цвет"
|
||||
},
|
||||
"container": {
|
||||
"message": "Контейнер"
|
||||
},
|
||||
"containerDescription": {
|
||||
"message": "Инкогнито и контейнерный прокси (только для Firefox)"
|
||||
},
|
||||
"controlledByOtherExtensions": {
|
||||
"message": "Настройки контролируются другими расширениями."
|
||||
},
|
||||
"country": {
|
||||
"message": "Страна"
|
||||
},
|
||||
"default": {
|
||||
"message": "Стандартная"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Удалить"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Удалить данные браузера"
|
||||
},
|
||||
"deleteBrowsingDataConfirm": {
|
||||
"message": "Вы уверены, что хотите удалить cookies, хранилище IndexedDB и хранилище DOM?"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "Действительно хотите удалить?"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Отключить"
|
||||
},
|
||||
"documentUrl": {
|
||||
"message": "Document URL"
|
||||
},
|
||||
"duplicate": {
|
||||
"message": "Дублировать"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "Включить синхронизацию"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "Синхронизировать исключения, список прокси и шаблоны"
|
||||
},
|
||||
"error": {
|
||||
"message": "Ошибка выполнения операции"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "Исключить"
|
||||
},
|
||||
"excludeHost": {
|
||||
"message": "Исключить хост"
|
||||
},
|
||||
"export": {
|
||||
"message": "Экспортировать настройки"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Простой инструмент управления прокси"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "Ошибка содержимого файла"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "Ошибка чтения файла"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "Неподдерживаемый формат файла"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "Узнать местонахождение"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "Глобальные исключения"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "Список хостов (доменов), которые не следует проксировать"
|
||||
},
|
||||
"help": {
|
||||
"message": "Помощь"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Прокси IP адрес или имя DNS"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "Отсутствует имя хоста - Порт"
|
||||
},
|
||||
"import": {
|
||||
"message": "Импортировать настройки"
|
||||
},
|
||||
"importFoxyProxyAccount": {
|
||||
"message": "Импортировать FoxyProxy аккаунт"
|
||||
},
|
||||
"importFromOlderVersions": {
|
||||
"message": "Импортировать из старых версий"
|
||||
},
|
||||
"importFromURL": {
|
||||
"message": "Импортировать из URL"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "Импортировать список прокси"
|
||||
},
|
||||
"include": {
|
||||
"message": "Включить"
|
||||
},
|
||||
"incognito": {
|
||||
"message": "Инкогнито"
|
||||
},
|
||||
"incognitoAccessError": {
|
||||
"message": "Firefox требует разрешения на приватный просмотр для настроек прокси."
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "Ограничить WebRTC"
|
||||
},
|
||||
"limitWebRTCDescription": {
|
||||
"message": "Переключить настройки WebRTC браузера"
|
||||
},
|
||||
"location": {
|
||||
"message": "Расположение"
|
||||
},
|
||||
"log": {
|
||||
"message": "Журнал"
|
||||
},
|
||||
"method": {
|
||||
"message": "Метод"
|
||||
},
|
||||
"more": {
|
||||
"message": "Больше"
|
||||
},
|
||||
"notAvailable": {
|
||||
"message": "Прокси не доступны в Chrome (только в Firefox)"
|
||||
},
|
||||
"options": {
|
||||
"message": "Настройки"
|
||||
},
|
||||
"password": {
|
||||
"message": "Пароль пользователя"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Шаблон"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Шаблоны"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "Обычный хост"
|
||||
},
|
||||
"port": {
|
||||
"message": "Порт"
|
||||
},
|
||||
"proxies": {
|
||||
"message": "Прокси"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "Список прокси"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "Прокси из шаблона"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "Прокси DNS"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "Быстрое добавление"
|
||||
},
|
||||
"random": {
|
||||
"message": "Случайный"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "Ошибка регулярного выражения для шаблона"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "Восстановить настройки по умолчанию"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "Вы уверены, что хотите восстановить все настройки по умолчанию?"
|
||||
},
|
||||
"result": {
|
||||
"message": "Результат"
|
||||
},
|
||||
"saveOptions": {
|
||||
"message": "Save"
|
||||
},
|
||||
"setProxy": {
|
||||
"message": "Установить прокси"
|
||||
},
|
||||
"setTabProxy": {
|
||||
"message": "Установить прокси вкладки"
|
||||
},
|
||||
"shortcut": {
|
||||
"message": "Сочетание клавиш"
|
||||
},
|
||||
"shortcutDescription": {
|
||||
"message": "Предварительный выбор прокси для ассоциации с действием сочетания клавиш"
|
||||
},
|
||||
"storeLocally": {
|
||||
"message": "Сохранить локально"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "Ошибка синхронизации"
|
||||
},
|
||||
"test": {
|
||||
"message": "Тест"
|
||||
},
|
||||
"tester": {
|
||||
"message": "Проверить"
|
||||
},
|
||||
"theme": {
|
||||
"message": "Тема"
|
||||
},
|
||||
"time": {
|
||||
"message": "Время"
|
||||
},
|
||||
"title": {
|
||||
"message": "Название"
|
||||
},
|
||||
"togglePassword": {
|
||||
"message": "Переключить видимость"
|
||||
},
|
||||
"type": {
|
||||
"message": "Тип прокси"
|
||||
},
|
||||
"unsetTabProxy": {
|
||||
"message": "Отключить прокси вкладки"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Введите имя пользователя и пароль."
|
||||
},
|
||||
"username": {
|
||||
"message": "Имя пользователя"
|
||||
},
|
||||
"view": {
|
||||
"message": "Просмотр"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "Шаблон хоста"
|
||||
}
|
||||
}
|
||||
245
foxyproxy/_locales/uk/messages.json
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "Про"
|
||||
},
|
||||
"add": {
|
||||
"message": "Додати"
|
||||
},
|
||||
"addHostTo": {
|
||||
"message": "Додати шаблон хосту до ..."
|
||||
},
|
||||
"all": {
|
||||
"message": "Усі"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Скасувати"
|
||||
},
|
||||
"city": {
|
||||
"message": "Місто"
|
||||
},
|
||||
"color": {
|
||||
"message": "Колір"
|
||||
},
|
||||
"confirmDeleteBrowserData": {
|
||||
"message": "Ви точно бажаєте видалити файли-куки, сховище індексованої БД, локальне сховище DOM?"
|
||||
},
|
||||
"country": {
|
||||
"message": "Країна"
|
||||
},
|
||||
"delete": {
|
||||
"message": "Видалити"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "Видалити дані з браузера"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"message": "Ви справді хочете це видалити?"
|
||||
},
|
||||
"disable": {
|
||||
"message": "Вимкнути"
|
||||
},
|
||||
"documentURL": {
|
||||
"message": "URL-адреса документу"
|
||||
},
|
||||
"domain": {
|
||||
"message": "Домен"
|
||||
},
|
||||
"enableSync": {
|
||||
"message": "Увімкнути синхронізацію"
|
||||
},
|
||||
"enableSyncDescription": {
|
||||
"message": "Синхронізувати глобальні винятки, проксі-сервери та шаблони"
|
||||
},
|
||||
"error": {
|
||||
"message": "Трапилася помилка із цією дією"
|
||||
},
|
||||
"exclude": {
|
||||
"message": "Виключити"
|
||||
},
|
||||
"export": {
|
||||
"message": "Експортувати"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "Простий у використанні просунутий засіб керування проксі для кожного"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"fileParseError": {
|
||||
"message": "Сталася помилка під час розбору вмісту файлу"
|
||||
},
|
||||
"fileReadError": {
|
||||
"message": "Сталася помилка читання файлу"
|
||||
},
|
||||
"fileSizeError": {
|
||||
"message": "Розмір файлу перевищує дозволені $1кб"
|
||||
},
|
||||
"fileTypeError": {
|
||||
"message": "Непідтримуваний формат файлу"
|
||||
},
|
||||
"foxyProxyAccount": {
|
||||
"message": "Обліковий запис FoxyProxy"
|
||||
},
|
||||
"fromOlderVersions": {
|
||||
"message": "Зі старіших версій"
|
||||
},
|
||||
"getLocation": {
|
||||
"message": "З’ясувати місцезнаходження"
|
||||
},
|
||||
"globalExclude": {
|
||||
"message": "Глобальні винятки"
|
||||
},
|
||||
"globalExcludeDescription": {
|
||||
"message": "Вказані тут шаблони ніколи не проксіюватимуться"
|
||||
},
|
||||
"globalExcludeError": {
|
||||
"message": "Помилка в шаблоні-винятку"
|
||||
},
|
||||
"help": {
|
||||
"message": "Допомога"
|
||||
},
|
||||
"hostname": {
|
||||
"message": "Ім’я хоста"
|
||||
},
|
||||
"hostnamePortError": {
|
||||
"message": "Бракує імені хоста чи порта"
|
||||
},
|
||||
"import": {
|
||||
"message": "Імпортувати"
|
||||
},
|
||||
"importAs": {
|
||||
"message": "Імпортувати як"
|
||||
},
|
||||
"importURL": {
|
||||
"message": "Імпортувати за URL-адресою"
|
||||
},
|
||||
"include": {
|
||||
"message": "Включити"
|
||||
},
|
||||
"incognitoAccess": {
|
||||
"message": "Для налаштування проксі-сервера необхідний дозвіл приватного перегляду."
|
||||
},
|
||||
"ip": {
|
||||
"message": "IP"
|
||||
},
|
||||
"limitWebRTC": {
|
||||
"message": "Обмежити WebRTC"
|
||||
},
|
||||
"location": {
|
||||
"message": "Місцезнаходження"
|
||||
},
|
||||
"log": {
|
||||
"message": "Журнал"
|
||||
},
|
||||
"method": {
|
||||
"message": "Метод"
|
||||
},
|
||||
"ok": {
|
||||
"message": "Гаразд"
|
||||
},
|
||||
"options": {
|
||||
"message": "Опції"
|
||||
},
|
||||
"pacUrlError": {
|
||||
"message": "Бракує URL-адреси PAC для типу PAC"
|
||||
},
|
||||
"password": {
|
||||
"message": "Пароль"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "Шаблон"
|
||||
},
|
||||
"patternError": {
|
||||
"message": "Помилка в шаблоні"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "Шаблони"
|
||||
},
|
||||
"plainHost": {
|
||||
"message": "Звичайний хост"
|
||||
},
|
||||
"port": {
|
||||
"message": "Порт"
|
||||
},
|
||||
"proxy": {
|
||||
"message": "Проксі-сервер"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"message": "Проксіювання згідно з шаблонами"
|
||||
},
|
||||
"proxyDNS": {
|
||||
"message": "Проксіювати DNS"
|
||||
},
|
||||
"proxyDnsDescription": {
|
||||
"message": "Використовувати цей проксі-сервер, щоб здійснювати певні DNS-запити (лише для SOCKS у Firefox)"
|
||||
},
|
||||
"proxyList": {
|
||||
"message": "Перелік проксі-серверів"
|
||||
},
|
||||
"proxyPortError": {
|
||||
"message": "Бракує порту"
|
||||
},
|
||||
"proxyTypeError": {
|
||||
"message": "Невідомий тип"
|
||||
},
|
||||
"quickAdd": {
|
||||
"message": "Швидке додавання"
|
||||
},
|
||||
"random": {
|
||||
"message": "Випадковий"
|
||||
},
|
||||
"regexError": {
|
||||
"message": "Помилка створення регулярного виразу для шаблону"
|
||||
},
|
||||
"resetWebRTC": {
|
||||
"message": "Скинути WebRTC"
|
||||
},
|
||||
"restoreDefaults": {
|
||||
"message": "Відновити налаштування за замовчуванням"
|
||||
},
|
||||
"restoreDefaultsConfirm": {
|
||||
"message": "Ви точно хочете відновити усі налаштування до їх типових значень?"
|
||||
},
|
||||
"result": {
|
||||
"message": "Результат"
|
||||
},
|
||||
"save": {
|
||||
"message": "Зберегти"
|
||||
},
|
||||
"selectCountry": {
|
||||
"message": "Оберіть країну"
|
||||
},
|
||||
"syncError": {
|
||||
"message": "Помилка синхронізації"
|
||||
},
|
||||
"test": {
|
||||
"message": "Тестувати"
|
||||
},
|
||||
"tester": {
|
||||
"message": "Тестувальник"
|
||||
},
|
||||
"time": {
|
||||
"message": "Час"
|
||||
},
|
||||
"title": {
|
||||
"message": "Заголовок"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "Показати/сховати пароль"
|
||||
},
|
||||
"type": {
|
||||
"message": "Тип"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "Будь ласка, введіть і ім’я користувача, і пароль."
|
||||
},
|
||||
"username": {
|
||||
"message": "Ім’я користувача"
|
||||
},
|
||||
"wildcard": {
|
||||
"message": "Шаблон"
|
||||
}
|
||||
}
|
||||
347
foxyproxy/_locales/zh_CN/messages.json
Normal file
@@ -0,0 +1,347 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "关于"
|
||||
},
|
||||
"active": {
|
||||
"message": "已启用"
|
||||
},
|
||||
"activeNote": {
|
||||
"message": "FoxyProxy 将忽略此页面的所有选项,除非设为"
|
||||
},
|
||||
"add": {
|
||||
"message": "添加"
|
||||
},
|
||||
"addBlacklist": {
|
||||
"message": "添加黑名单模式可以防止代理用在本地主机和内部网/私密 IP 地址上"
|
||||
},
|
||||
"addBlacklistTip": {
|
||||
"message": "把本地主机、127.0.0.1、192.168.*.*、172.16.*.* 和 10.*.*.* 添加到黑名单模式"
|
||||
},
|
||||
"addProxy": {
|
||||
"message": "添加代理"
|
||||
},
|
||||
"addWhitelist": {
|
||||
"message": "添加匹配全部 URLs 的白名单模式"
|
||||
},
|
||||
"addWhitelistTip": {
|
||||
"message": "添加白名单模式 *"
|
||||
},
|
||||
"authError": {
|
||||
"message": "$1 拒绝连接\n请检查代理用户名/密码"
|
||||
},
|
||||
"back": {
|
||||
"message": "返回"
|
||||
},
|
||||
"black": {
|
||||
"message": "黑名单"
|
||||
},
|
||||
"blackPatterns": {
|
||||
"message": "黑名单模式"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "取消"
|
||||
},
|
||||
"clear": {
|
||||
"message": "清除"
|
||||
},
|
||||
"clickTest": {
|
||||
"message": "准备好后点击“测试”"
|
||||
},
|
||||
"color": {
|
||||
"message": "颜色"
|
||||
},
|
||||
"confirmDelete": {
|
||||
"message": "你确定要删除吗?"
|
||||
},
|
||||
"confirmTransferToLocal": {
|
||||
"message": "是否将已同步的设置传输到你的本地配置文件?\n将合并已有的数据。"
|
||||
},
|
||||
"confirmTransferToSync": {
|
||||
"message": "是否将本地设置传输到你的同步配置文件?\n将合并已有的数据。"
|
||||
},
|
||||
"delete": {
|
||||
"message": "删除"
|
||||
},
|
||||
"deleteAll": {
|
||||
"message": "全部删除"
|
||||
},
|
||||
"deleteAllmessage": {
|
||||
"message": "已删除所有数据"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "删除浏览器数据"
|
||||
},
|
||||
"deleteBrowserDataDescription": {
|
||||
"message": "缓存、cookies、indexedDB 存储、DOM 本地存储、插件数据、service worker 数据。"
|
||||
},
|
||||
"deleteBrowserDataNotDescription": {
|
||||
"message": "已保存的密码、浏览和表单历史、下载历史、webSQL 和服务器绑定证书。"
|
||||
},
|
||||
"deleteNot": {
|
||||
"message": "不要删除"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "已禁用"
|
||||
},
|
||||
"done": {
|
||||
"message": "完成!"
|
||||
},
|
||||
"down": {
|
||||
"message": "下移"
|
||||
},
|
||||
"edit": {
|
||||
"message": "编辑"
|
||||
},
|
||||
"editPatterns": {
|
||||
"message": "编辑模式"
|
||||
},
|
||||
"editPatternsFor": {
|
||||
"message": "编辑 $1 的模式"
|
||||
},
|
||||
"editProxy": {
|
||||
"message": "编辑代理 $1"
|
||||
},
|
||||
"enterUrl": {
|
||||
"message": "输入要测试的 URL"
|
||||
},
|
||||
"enterUrlNote": {
|
||||
"message": "(已忽略路径和查询参数)"
|
||||
},
|
||||
"erroNoSettings": {
|
||||
"message": "未找到设置。请重新安装 FoxyProxy。"
|
||||
},
|
||||
"error": {
|
||||
"message": "错误"
|
||||
},
|
||||
"errorEmpty": {
|
||||
"message": "字段不能为空。"
|
||||
},
|
||||
"errorFetch": {
|
||||
"message": "操作出现错误"
|
||||
},
|
||||
"errorPattern": {
|
||||
"message": "请输入一项模式"
|
||||
},
|
||||
"errorProtocol": {
|
||||
"message": "协议不匹配。"
|
||||
},
|
||||
"errorSlash": {
|
||||
"message": "通配符模式不能有斜线 /。因为 Firefox 的限制你无法匹配 URL 路径。"
|
||||
},
|
||||
"errorWas": {
|
||||
"message": "存在错误。请重试。"
|
||||
},
|
||||
"export": {
|
||||
"message": "导出"
|
||||
},
|
||||
"exportPatterns": {
|
||||
"message": "导出模式"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "易于使用,适用于任何人的高级代理管理工具"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy 基础版"
|
||||
},
|
||||
"forAll": {
|
||||
"message": "对全部 URLs 使用"
|
||||
},
|
||||
"help": {
|
||||
"message": "帮助"
|
||||
},
|
||||
"import": {
|
||||
"message": "导入"
|
||||
},
|
||||
"importBW": {
|
||||
"message": "已导入 $1 项白名单和 $2 项黑名单模式。"
|
||||
},
|
||||
"importEnd": {
|
||||
"message": "设置已成功导入"
|
||||
},
|
||||
"importEndSlash": {
|
||||
"message": "导入已完成。由于 Firefox 缺陷,已不再支持于模式里使用斜线。请复查你的模式并移除所有斜线。"
|
||||
},
|
||||
"importPatterns": {
|
||||
"message": "导入模式"
|
||||
},
|
||||
"imported": {
|
||||
"message": "已导入"
|
||||
},
|
||||
"importedPattern": {
|
||||
"message": "此模式来自自旧版本的 FoxyProxy,且在导入过程中更改。下面为未更改的原模式:"
|
||||
},
|
||||
"ip": {
|
||||
"message": "代理 IP 地址或 DNS 名称"
|
||||
},
|
||||
"log": {
|
||||
"message": "日志"
|
||||
},
|
||||
"logSize": {
|
||||
"message": "日志大小"
|
||||
},
|
||||
"matchPattern": {
|
||||
"message": "匹配模式"
|
||||
},
|
||||
"matchedURLs": {
|
||||
"message": "与模式匹配的 URLs"
|
||||
},
|
||||
"modeDisabled": {
|
||||
"message": "关闭(使用 Firefox 设置)"
|
||||
},
|
||||
"modePatterns": {
|
||||
"message": "按模式和顺序使用启用的代理"
|
||||
},
|
||||
"myIP": {
|
||||
"message": "查询我的 IP"
|
||||
},
|
||||
"name": {
|
||||
"message": "名称"
|
||||
},
|
||||
"newBlack": {
|
||||
"message": "新增黑名单"
|
||||
},
|
||||
"newWhite": {
|
||||
"message": "新增白名单"
|
||||
},
|
||||
"noLocal": {
|
||||
"message": "不用于本地主机和内部网/私密 IP 地址"
|
||||
},
|
||||
"noMatch": {
|
||||
"message": "无匹配"
|
||||
},
|
||||
"noProxies": {
|
||||
"message": "你没有设置任何代理。"
|
||||
},
|
||||
"none": {
|
||||
"message": "无"
|
||||
},
|
||||
"notApplicable": {
|
||||
"message": "不适用"
|
||||
},
|
||||
"off": {
|
||||
"message": "关闭"
|
||||
},
|
||||
"ok": {
|
||||
"message": "确定"
|
||||
},
|
||||
"on": {
|
||||
"message": "开启"
|
||||
},
|
||||
"options": {
|
||||
"message": "选项"
|
||||
},
|
||||
"optionsPage": {
|
||||
"message": "FoxyProxy 选项"
|
||||
},
|
||||
"password": {
|
||||
"message": "密码(可选)"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "模式"
|
||||
},
|
||||
"patternCheatSheet": {
|
||||
"message": "模式速查表"
|
||||
},
|
||||
"patternDetail": {
|
||||
"message": "输入模式详细信息。"
|
||||
},
|
||||
"patternHelp": {
|
||||
"message": "模式帮助"
|
||||
},
|
||||
"patternMatch": {
|
||||
"message": "模式与 URL 匹配!"
|
||||
},
|
||||
"patternNotMatch": {
|
||||
"message": "模式与 URL 不匹配。"
|
||||
},
|
||||
"patternNote": {
|
||||
"message": "(无路径,无查询参数)"
|
||||
},
|
||||
"patternShortcuts": {
|
||||
"message": "模式快捷选项"
|
||||
},
|
||||
"patternTester": {
|
||||
"message": "模式测试器"
|
||||
},
|
||||
"patternsChanged": {
|
||||
"message": "有些模式已做更改因为它们包含斜线。不再支持于模式里使用斜线。"
|
||||
},
|
||||
"port": {
|
||||
"message": "端口"
|
||||
},
|
||||
"protocol": {
|
||||
"message": "协议"
|
||||
},
|
||||
"proxyAddress": {
|
||||
"message": "代理地址"
|
||||
},
|
||||
"proxyTitle": {
|
||||
"message": "代理标题"
|
||||
},
|
||||
"proxyType": {
|
||||
"message": "代理类型"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "刷新"
|
||||
},
|
||||
"save": {
|
||||
"message": "保存"
|
||||
},
|
||||
"saveAdd": {
|
||||
"message": "保存并添加另一个"
|
||||
},
|
||||
"saveEditPattern": {
|
||||
"message": "保存并编辑模式"
|
||||
},
|
||||
"syncSettings": {
|
||||
"message": "同步设置"
|
||||
},
|
||||
"syncSettingsHelp": {
|
||||
"message": "开启以使用 Firefox 同步(将在你所有的 Firefox 浏览器间同步设置)。关闭则只在本地保存设置。注意你可以有两套不同的设置,一套同步的和一套本地的。"
|
||||
},
|
||||
"test": {
|
||||
"message": "测试"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "时间戳"
|
||||
},
|
||||
"title": {
|
||||
"message": "标题或描述(可选)"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "显示密码"
|
||||
},
|
||||
"type": {
|
||||
"message": "类型"
|
||||
},
|
||||
"unmatchedURLs": {
|
||||
"message": "与模式不匹配的 URLs"
|
||||
},
|
||||
"up": {
|
||||
"message": "上移"
|
||||
},
|
||||
"url": {
|
||||
"message": "URL"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "请输入用户名和密码。"
|
||||
},
|
||||
"username": {
|
||||
"message": "用户名(可选)"
|
||||
},
|
||||
"welcome": {
|
||||
"message": "欢迎"
|
||||
},
|
||||
"white": {
|
||||
"message": "白名单"
|
||||
},
|
||||
"whiteBlack": {
|
||||
"message": "白/黑名单"
|
||||
},
|
||||
"whitePatterns": {
|
||||
"message": "白名单模式"
|
||||
}
|
||||
}
|
||||
398
foxyproxy/_locales/zh_TW/messages.json
Normal file
@@ -0,0 +1,398 @@
|
||||
{
|
||||
"about": {
|
||||
"message": "關於"
|
||||
},
|
||||
"active": {
|
||||
"message": "已啟用"
|
||||
},
|
||||
"activeNote": {
|
||||
"message": "FoxyProxy 將會略過此頁面上所有設定,除非設定為"
|
||||
},
|
||||
"add": {
|
||||
"message": "新建"
|
||||
},
|
||||
"addBlacklist": {
|
||||
"message": "新增黑名單規則可以防止這個代理伺服器設定被套用於 localhost 或內部 / 私有 IP 位址上。"
|
||||
},
|
||||
"addBlacklistTip": {
|
||||
"message": "將 localhost、127.0.0.1、192.168.*.*、172.16.*.* 和 10.*.*.* 新增至黑名單規則中"
|
||||
},
|
||||
"addProxy": {
|
||||
"message": "新增代理伺服器"
|
||||
},
|
||||
"addWhitelist": {
|
||||
"message": "新增白名單規則以對應到所有網址"
|
||||
},
|
||||
"addWhitelistTip": {
|
||||
"message": "將 * 新增至白名單規則中"
|
||||
},
|
||||
"authError": {
|
||||
"message": "$1 拒絕連線\n請檢查代理伺服器的使用者帳號與密碼是否正確"
|
||||
},
|
||||
"back": {
|
||||
"message": "返回"
|
||||
},
|
||||
"black": {
|
||||
"message": "黑名單"
|
||||
},
|
||||
"blackPatterns": {
|
||||
"message": "黑名單規則"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "取消"
|
||||
},
|
||||
"clear": {
|
||||
"message": "清除"
|
||||
},
|
||||
"clickTest": {
|
||||
"message": "準備就緒後按下「測試」開始進行測試。"
|
||||
},
|
||||
"color": {
|
||||
"message": "顏色"
|
||||
},
|
||||
"complete": {
|
||||
"message": "完整版"
|
||||
},
|
||||
"completeFormat": {
|
||||
"message": "使用這種格式,您可以指定所有代理伺服器設定。其中僅有協定和伺服器為必填。"
|
||||
},
|
||||
"confirmDelete": {
|
||||
"message": "您確定要移除此設定嗎?"
|
||||
},
|
||||
"confirmOverwrite": {
|
||||
"message": "您確定要覆蓋所有已存在的代理伺服器設定嗎?"
|
||||
},
|
||||
"confirmTransferToLocal": {
|
||||
"message": "是否將您的設定同步至本機設定檔中?\n已經存在的資料將會被合併。"
|
||||
},
|
||||
"confirmTransferToSync": {
|
||||
"message": "是否將您的本機設定同步至同步設定檔中?\n已經存在的資料將會被合併。"
|
||||
},
|
||||
"delete": {
|
||||
"message": "移除"
|
||||
},
|
||||
"deleteAll": {
|
||||
"message": "移除全部"
|
||||
},
|
||||
"deleteAllmessage": {
|
||||
"message": "已將所有資料移除"
|
||||
},
|
||||
"deleteBrowserData": {
|
||||
"message": "移除瀏覽器資料"
|
||||
},
|
||||
"deleteBrowserDataDescription": {
|
||||
"message": "快取、Cookies、indexedDB 儲存空間, DOM 本機儲存空間, 擴充套件資料、Service Worker 資料。"
|
||||
},
|
||||
"deleteBrowserDataNotDescription": {
|
||||
"message": "以儲存的密碼、瀏覽與表單歷史紀錄、下載紀錄、WebSQL 和 Server-Bound 認證。"
|
||||
},
|
||||
"deleteNot": {
|
||||
"message": "不要移除"
|
||||
},
|
||||
"disabled": {
|
||||
"message": "已停用"
|
||||
},
|
||||
"done": {
|
||||
"message": "完成!"
|
||||
},
|
||||
"down": {
|
||||
"message": "下移"
|
||||
},
|
||||
"edit": {
|
||||
"message": "編輯"
|
||||
},
|
||||
"editPatterns": {
|
||||
"message": "編輯規則"
|
||||
},
|
||||
"editPatternsFor": {
|
||||
"message": "編輯規則 - $1"
|
||||
},
|
||||
"editProxy": {
|
||||
"message": "編輯代理伺服器 - $1"
|
||||
},
|
||||
"enterUrl": {
|
||||
"message": "請輸入網址以進行測試"
|
||||
},
|
||||
"enterUrlNote": {
|
||||
"message": "(已略過路徑與查詢字串)"
|
||||
},
|
||||
"erroNoSettings": {
|
||||
"message": "找不到設定檔。 請重新安裝 FoxyProxy。"
|
||||
},
|
||||
"error": {
|
||||
"message": "錯誤"
|
||||
},
|
||||
"errorEmpty": {
|
||||
"message": "該欄位不可留空。"
|
||||
},
|
||||
"errorFetch": {
|
||||
"message": "該操作行為發生錯誤。"
|
||||
},
|
||||
"errorPattern": {
|
||||
"message": "請輸入規則"
|
||||
},
|
||||
"errorProtocol": {
|
||||
"message": "協定不對應。"
|
||||
},
|
||||
"errorSlash": {
|
||||
"message": "萬用字元不能包含斜線。 由於 Firefox 的限制,您無法對應到任何的網址或路徑。"
|
||||
},
|
||||
"errorWas": {
|
||||
"message": "發生錯誤。請重試。"
|
||||
},
|
||||
"examples": {
|
||||
"message": "範例"
|
||||
},
|
||||
"export": {
|
||||
"message": "匯出設定"
|
||||
},
|
||||
"exportPatterns": {
|
||||
"message": "匯出規則"
|
||||
},
|
||||
"extensionDescription": {
|
||||
"message": "任誰都能快速上手的進階代理伺服器管理工具"
|
||||
},
|
||||
"extensionName": {
|
||||
"message": "FoxyProxy"
|
||||
},
|
||||
"extensionNameBasic": {
|
||||
"message": "FoxyProxy Basic"
|
||||
},
|
||||
"forAll": {
|
||||
"message": "於所有網址上啟用"
|
||||
},
|
||||
"formats": {
|
||||
"message": "格式"
|
||||
},
|
||||
"help": {
|
||||
"message": "說明"
|
||||
},
|
||||
"import": {
|
||||
"message": "匯入設定"
|
||||
},
|
||||
"importBW": {
|
||||
"message": "已匯入 $1 個白名單與 $2 個黑名單規則。"
|
||||
},
|
||||
"importEnd": {
|
||||
"message": "設定檔匯入成功。"
|
||||
},
|
||||
"importEndSlash": {
|
||||
"message": "匯入完成。 由於 Firefox 的一隻 Bug ,已不支援斜線規則。 請重新檢查您的規則是否仍含有斜線,若有請將之移除。"
|
||||
},
|
||||
"importPatterns": {
|
||||
"message": "匯入規則"
|
||||
},
|
||||
"importProxyList": {
|
||||
"message": "匯入代理伺服器清單"
|
||||
},
|
||||
"importSucceeded": {
|
||||
"message": "讀取並匯入 $1 個代理伺服器設定"
|
||||
},
|
||||
"imported": {
|
||||
"message": "已匯入"
|
||||
},
|
||||
"importedPattern": {
|
||||
"message": "這個規則為舊版匯入的規則,且於匯入期間已被修改。 以下為其原始規則:"
|
||||
},
|
||||
"importsSkipped": {
|
||||
"message": "因為無法解析,已略過 $1 行:\n\n$2"
|
||||
},
|
||||
"ip": {
|
||||
"message": "代理伺服器 IP 位址或 DNS 域名"
|
||||
},
|
||||
"ipPort": {
|
||||
"message": "ip:port"
|
||||
},
|
||||
"ipPortUsernamePassword": {
|
||||
"message": "ip:port:username:password"
|
||||
},
|
||||
"log": {
|
||||
"message": "日誌"
|
||||
},
|
||||
"logSize": {
|
||||
"message": "日誌大小"
|
||||
},
|
||||
"matchPattern": {
|
||||
"message": "對應規則"
|
||||
},
|
||||
"matchedURLs": {
|
||||
"message": "與規則相對應的網址"
|
||||
},
|
||||
"modeDisabled": {
|
||||
"message": "停用 (使用 Firefox 內建設定)"
|
||||
},
|
||||
"modePatterns": {
|
||||
"message": "以設定的規則與順序套用代理伺服器設定"
|
||||
},
|
||||
"myIP": {
|
||||
"message": "我的 IP 為何?"
|
||||
},
|
||||
"name": {
|
||||
"message": "名稱"
|
||||
},
|
||||
"newBlack": {
|
||||
"message": "新增黑名單"
|
||||
},
|
||||
"newWhite": {
|
||||
"message": "新增白名單"
|
||||
},
|
||||
"noLocal": {
|
||||
"message": "請不要使用 localhost 或內部 / 私有 IP 位址"
|
||||
},
|
||||
"noMatch": {
|
||||
"message": "沒有對應結果"
|
||||
},
|
||||
"noProxies": {
|
||||
"message": "您尚未建立任何代理伺服器設定。"
|
||||
},
|
||||
"none": {
|
||||
"message": "無"
|
||||
},
|
||||
"notApplicable": {
|
||||
"message": "不適用"
|
||||
},
|
||||
"off": {
|
||||
"message": "停用"
|
||||
},
|
||||
"ok": {
|
||||
"message": "確定"
|
||||
},
|
||||
"on": {
|
||||
"message": "啟用"
|
||||
},
|
||||
"options": {
|
||||
"message": "選項"
|
||||
},
|
||||
"optionsPage": {
|
||||
"message": "FoxyProxy 選項"
|
||||
},
|
||||
"overwritProxiesHelp1": {
|
||||
"message": "若勾選此選項,則會將所有已存在的代理伺服器先行移除並取代為此清單上的代理伺服器設定。"
|
||||
},
|
||||
"overwritProxiesHelp2": {
|
||||
"message": "若不勾選此選項,則會將此清單內的代理伺服器新增至已經存在的代理伺服器清單之後。"
|
||||
},
|
||||
"overwriteProxies": {
|
||||
"message": "覆蓋已經存在的代理伺服器"
|
||||
},
|
||||
"password": {
|
||||
"message": "密碼(選填)"
|
||||
},
|
||||
"pasteList": {
|
||||
"message": "在下方貼上代理伺服器清單。"
|
||||
},
|
||||
"pattern": {
|
||||
"message": "規則"
|
||||
},
|
||||
"patternCheatSheet": {
|
||||
"message": "規則速查表"
|
||||
},
|
||||
"patternDetail": {
|
||||
"message": "輸入規則詳細資料。"
|
||||
},
|
||||
"patternHelp": {
|
||||
"message": "規則說明"
|
||||
},
|
||||
"patternMatch": {
|
||||
"message": "規則與網址相對應!"
|
||||
},
|
||||
"patternNotMatch": {
|
||||
"message": "規則與網址無法對應。"
|
||||
},
|
||||
"patternNote": {
|
||||
"message": "(不包含路徑及查詢字串)"
|
||||
},
|
||||
"patternShortcuts": {
|
||||
"message": "規則快捷鍵"
|
||||
},
|
||||
"patternTester": {
|
||||
"message": "規則測試器"
|
||||
},
|
||||
"patterns": {
|
||||
"message": "規則"
|
||||
},
|
||||
"patternsChanged": {
|
||||
"message": "部分規則因含有斜線而被變更。 不支援斜線規則。"
|
||||
},
|
||||
"port": {
|
||||
"message": "連接埠"
|
||||
},
|
||||
"protocol": {
|
||||
"message": "協定"
|
||||
},
|
||||
"proxyAddress": {
|
||||
"message": "代理伺服器位址"
|
||||
},
|
||||
"proxyTitle": {
|
||||
"message": "代理伺服器標題"
|
||||
},
|
||||
"proxyType": {
|
||||
"message": "代理伺服器類型"
|
||||
},
|
||||
"refresh": {
|
||||
"message": "重新整理"
|
||||
},
|
||||
"save": {
|
||||
"message": "儲存"
|
||||
},
|
||||
"saveAdd": {
|
||||
"message": "儲存並新建"
|
||||
},
|
||||
"saveEditPattern": {
|
||||
"message": "儲存並編輯規則"
|
||||
},
|
||||
"simple": {
|
||||
"message": "簡易版"
|
||||
},
|
||||
"simpleFormat": {
|
||||
"message": "IP 位址 / 伺服器及連接埠皆為必填。 username:password(使用者名稱與密碼)為選填。"
|
||||
},
|
||||
"syncSettings": {
|
||||
"message": "同步設定"
|
||||
},
|
||||
"syncSettingsHelp": {
|
||||
"message": "啟用此設定將會使用 Firefox Sync (設定將同步至所有的瀏覽器中)。 停用此設定僅將設定儲存於本機中。 小提醒:您可以儲存兩種設定,一種為本機設定,另一種為同步設定。"
|
||||
},
|
||||
"test": {
|
||||
"message": "測試"
|
||||
},
|
||||
"timestamp": {
|
||||
"message": "時間戳"
|
||||
},
|
||||
"title": {
|
||||
"message": "標題或描述(選填)"
|
||||
},
|
||||
"togglePW": {
|
||||
"message": "顯示或隱藏密碼"
|
||||
},
|
||||
"type": {
|
||||
"message": "類型"
|
||||
},
|
||||
"unmatchedURLs": {
|
||||
"message": "與規則不對應的網址"
|
||||
},
|
||||
"up": {
|
||||
"message": "上移"
|
||||
},
|
||||
"url": {
|
||||
"message": "網址"
|
||||
},
|
||||
"userPassError": {
|
||||
"message": "請輸入使用者名稱和密碼。"
|
||||
},
|
||||
"username": {
|
||||
"message": "使用者名稱(選填)"
|
||||
},
|
||||
"welcome": {
|
||||
"message": "歡迎"
|
||||
},
|
||||
"white": {
|
||||
"message": "白名單"
|
||||
},
|
||||
"whiteBlack": {
|
||||
"message": "白名單 / 黑名單"
|
||||
},
|
||||
"whitePatterns": {
|
||||
"message": "白名單規則"
|
||||
}
|
||||
}
|
||||
1
foxyproxy/_metadata/computed_hashes.json
Normal file
1
foxyproxy/_metadata/verified_contents.json
Normal file
238
foxyproxy/content/about.html
Normal file
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>About</title>
|
||||
<link href="iframe.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<a href="#changelog">Changelog</a>
|
||||
<a href="#credits">Credits</a>
|
||||
</nav>
|
||||
|
||||
<article class="about">
|
||||
|
||||
<h2 id="changelog">Changelog</h2>
|
||||
<dl>
|
||||
|
||||
<dt>9.2</dt>
|
||||
<dd>Fixed an error when saving incomplete proxies (#197)</dd>
|
||||
|
||||
<dt>9.1</dt>
|
||||
<dd>Fixed an error on Firefox for Android (#196)</dd>
|
||||
|
||||
<dt>9.0</dt>
|
||||
<dd>Added "407 Proxy Authentication Required" to the log (#145)</dd>
|
||||
<dd>Added "Get Associated Domains" feature to Log (#191)</dd>
|
||||
<dd>Added "Get Associated Domains" option to add patterns</dd>
|
||||
<dd>Added Bulk Edit user interface feature (#131, #137, #169)</dd>
|
||||
<dd>Added Context Menu feature</dd>
|
||||
<dd>Added Ctrl-Click option to Add Pattern (#169)</dd>
|
||||
<dd>Added Ctrl-Click option to Add Proxy</dd>
|
||||
<dd>Added extra options to Import FoxyProxy Account</dd>
|
||||
<dd>Added link to Open Shortcut Settings link (Firefox 137)</dd>
|
||||
<dd>Added links to Help from Options</dd>
|
||||
<dd>Added Match Pattern support (#177)</dd>
|
||||
<dd>Added Open Link in New Tab Proxy feature (#149)</dd>
|
||||
<dd>Added Ping feature</dd>
|
||||
<dd>Added Proxy by Pattern tester</dd>
|
||||
<dd>Added show/hide feature to toolbar popup elements based on Tab URL</dd>
|
||||
<dd>Added Tab Proxy by patterns feature (Firefox only) (#180)</dd>
|
||||
<dd>Added Test feature</dd>
|
||||
<dd>Changed Firefox icons to PNG for compatibility with Chrome</dd>
|
||||
<dd>Enabled sending username without password for SOCKS (Firefox only)</dd>
|
||||
<dd>Fixed a Drag and Drop issue (#183)</dd>
|
||||
<dd>Fixed an issue with resetting containers (from 8.10) (#185)</dd>
|
||||
<dd>Increased minimum version to Firefox 128 (released 2024-07-09) due to Firefox root certificate expiry on 2025-03-14</dd>
|
||||
<dd>Removed Chrome v3 to v8 data migration decryption code</dd>
|
||||
<dd>Removed default network passthrough on Firefox in favour of <code>about:config</code> setting (Firefox 137+) (#178)</dd>
|
||||
<dd>Renamed "Quick Add" to "Include Host"</dd>
|
||||
<dd>Repurposed "Exclude Host" to match "Include Host" function</dd>
|
||||
<dd>Unified Firefox and Chrome "manifest.json"</dd>
|
||||
<dd>Updated browser badge code to reflect Global Exclude</dd>
|
||||
<dd>Updated data migration for Match Pattern use (#177)</dd>
|
||||
<dd>Updated Firefox manifest to MV3</dd>
|
||||
<dd>Updated Include/Exclude Host to generate Glob patterns</dd>
|
||||
<dd>Updated Limit WebRTC options (#162)</dd>
|
||||
<dd>Updated Options page user interface</dd>
|
||||
<dd>Updated Pattern Tester</dd>
|
||||
<dd>Updated Tab Proxy for MV3</dd>
|
||||
<dd>Updated toolbar popup user interface</dd>
|
||||
|
||||
<!--
|
||||
https://blog.mozilla.org/addons/2025/03/10/root-certificate-will-expire-on-14-march-users-need-to-update-firefox-to-prevent-add-on-breakage/
|
||||
https://blog.mozilla.org/addons/2024/07/10/manifest-v3-updates-landed-in-firefox-128/
|
||||
minimum version to Firefox 128:
|
||||
-----
|
||||
FF109, Ch88: "host_permissions": Property "host_permissions" is unsupported in Manifest Version 2
|
||||
FF112, Ch92: Added Background script module, removed "content/background.html"
|
||||
FF115: Deprecate browser_style in MV3 defaults to false
|
||||
FF115, Ch102: storage.session 10mb (Ch102-Ch114 1mb)
|
||||
FF121, Ch105: :has()
|
||||
FF126, Ch108: webRequestAuthProvider
|
||||
|
||||
FF117, Ch112 (2023-04-04): CSS nesting ?
|
||||
-->
|
||||
|
||||
<dt>8.10</dt>
|
||||
<dd>Added console log for Save file errors (#144)</dd>
|
||||
<dd>Added custom container option (#33, #161)</dd>
|
||||
<dd>Added direct HTTP authentication (Firefox 125+)</dd>
|
||||
<dd>Added folder to Auto Backup (#156)</dd>
|
||||
<dd>Added new options to the proxy types</dd>
|
||||
<dd>Added QUIC (HTTP) option (Chrome only) (experimental) (#124)</dd>
|
||||
<dd>Removed a bug that caused Glob wildcard to match domains with subdomains</dd>
|
||||
<dd>Updated browser detection (firefox-extension/issues/220, #139, #141)</dd>
|
||||
<dd>Updated default network passthrough on Firefox to include <code>localhost, 127.0.0.1, ::1</code> (firefox-extension/issues/159, #20, #134)</dd>
|
||||
<dd>Updated managed storage to allow Tab Proxy (#172)</dd>
|
||||
<dd>Updated Options page user interface</dd>
|
||||
<dd>Updated Tab Proxy to include new tab (#157)</dd>
|
||||
<dd>Updated toolbar popup user interface</dd>
|
||||
|
||||
<dt>8.9</dt>
|
||||
<dd>Added "Log" to the toolbar popup buttons (#44)</dd>
|
||||
<dd>Added limited log display on Chrome (experimental)</dd>
|
||||
<dd>Added Theme feature (#71, #100)</dd>
|
||||
<dd>Added toggle more options on toolbar popup (#54)</dd>
|
||||
<dd>Fixed proxy DNS in "Import Proxy List" (#102) (from 8.7)</dd>
|
||||
<dd>Fixed settings upgrade (import older) when hostname is missing (#108)</dd>
|
||||
<dd>Fixed settings upgrade (import older) when username/password is missing (#103)</dd>
|
||||
<dd>Fixed socks in "Import Proxy List" on some Firefox (#120)</dd>
|
||||
<dd>Increased log content</dd>
|
||||
<dd>Removed "Show Pattern Proxy" option and made it default (#57) (from 8.7) (Firefox only)</dd>
|
||||
<dd>Removed Tab Proxy page-action and set it to the toolbar icon (#114) (Firefox only)</dd>
|
||||
<dd>Updated add pattern user interface (#105)</dd>
|
||||
<dd>Updated code to process duplicate hostname:port (#33, #76)</dd>
|
||||
<dd>Updated options to disable "Store Locally" on Firefox (Chrome only)</dd>
|
||||
<dd>Updated toolbar popup include/exclude host feature</dd>
|
||||
<dd>Updated toolbar popup user interface</dd>
|
||||
|
||||
<dt>8.8</dt>
|
||||
<dd>Added Show hidden feature</dd>
|
||||
<dd>Fixed an issue with sync (#99)</dd>
|
||||
<dd>Updated code to process duplicate hostname:port (#33, #76)</dd>
|
||||
<dd>Updated user interface to hide patterns on FoxyProxy Basic</dd>
|
||||
|
||||
<dt>8.7</dt>
|
||||
<dd>Added Auto Backup feature</dd>
|
||||
<dd>Added FoxyProxy Basic detection (disabled for now)</dd>
|
||||
<dd>Added Help translation form</dd>
|
||||
<dd>Added "Show Pattern Proxy" option to show proxies when in "Proxy by Patterns" mode (#57) (Firefox only)</dd>
|
||||
<dd>Added pattern matching to the Log display (#91)</dd>
|
||||
<dd>Added proxy title to the toolbar icon mouse-over title display (#74)</dd>
|
||||
<dd>Changed the global Proxy DNS to per-proxy setting (#75)</dd>
|
||||
<dd>Fixed Firefox for Android compatibility (#60)</dd>
|
||||
<dd>Increased the maximum height of the pattern section before scrolling</dd>
|
||||
<dd>Removed Help document display on install/upgrade (#86)</dd>
|
||||
<dd>Updated default Firefox proxy resetting (#59)</dd>
|
||||
<dd>Updated PAC check to allow "file:" (#49)</dd>
|
||||
<dd>Updated pattern "Add" button text due to localisation issues (#88)</dd>
|
||||
<dd>Updated schema.json</dd>
|
||||
<dd>Updated wildcard to regular expression conversion (#72)</dd>
|
||||
|
||||
<dt>8.6</dt>
|
||||
<dd>Fixed an issue with migrating database from older versions (#69)</dd>
|
||||
<dd>Updated user interface to disable inapplicable options in proxies (#52)</dd>
|
||||
<dd>Updated wildcard to regular expression conversion (#72)</dd>
|
||||
|
||||
<dt>8.5</dt>
|
||||
<dd>Skipped</dd>
|
||||
|
||||
<dt>8.4</dt>
|
||||
<dd>Added light/dark theme detection for badge background color (#61)</dd>
|
||||
<dd>Enabled to run with "controlled_by_other_extensions" on Firefox (#68)</dd>
|
||||
<dd>Removed localhost and local network passthrough in Firefox (#50, #63, #64, #66, #71)</dd>
|
||||
<dd>Updated Options save process to fill blank proxy header title display (#74)</dd>
|
||||
|
||||
<dt>8.3</dt>
|
||||
<dd>Added enterprise policy and managed storage feature (experimental) (#42)</dd>
|
||||
<dd>Added PAC "Store Locally" feature (#46) (experimental) (Chrome only)</dd>
|
||||
<dd>Added PAC view feature</dd>
|
||||
<dd>Fixed an issue with empty Global Exclude</dd>
|
||||
<dd>Fixed an issue with upgrade sync data on Firefox (#53)</dd>
|
||||
<dd>Updated hostname check to allow "file:" for Unix Domain Socket (#47)</dd>
|
||||
<dd>Updated PAC check to allow "file:" (#49)</dd>
|
||||
<dd>Updated user interface to disable inapplicable options in proxies</dd>
|
||||
|
||||
<dt>8.2</dt>
|
||||
<dd>Added option to set the country to blank</dd>
|
||||
<dd>Fixed an issue with upgrade sync data on Chrome (#45)</dd>
|
||||
<dd>Updated Incognito process on Chrome</dd>
|
||||
|
||||
<dt>8.1</dt>
|
||||
<dd>Added Drag and Drop sorting of proxies (#29)</dd>
|
||||
<dd>Added duplicate proxy feature</dd>
|
||||
<dd>Added Incognito/Container proxy (firefox-extension/issues/22, #33)</dd>
|
||||
<dd>Added Keyboard Shortcut feature (firefox-extension/issues/217)</dd>
|
||||
<dd>Added pattern import/export</dd>
|
||||
<dd>Added port search to search filter</dd>
|
||||
<dd>Added search filter to toolbar popup (#23)</dd>
|
||||
<dd>Fixed a pattern conversion issue (#28)</dd>
|
||||
<dd>Fixed an error in Sync (#36)</dd>
|
||||
<dd>Fixed Tab Proxy icon when reloading a tab (#33)</dd>
|
||||
<dd>Increased log content</dd>
|
||||
<dd>Increased log display entries to 200</dd>
|
||||
<dd>Increased log width to full screen</dd>
|
||||
<dd>Updated Global Exclude process</dd>
|
||||
|
||||
<dt>8.0</dt>
|
||||
<dd>Added complete Light/Dark Theme</dd>
|
||||
<dd>Added Exclude host feature</dd>
|
||||
<dd>Added Firefox on Android support (experimental) (#21)</dd>
|
||||
<dd>Added Get Location feature</dd>
|
||||
<dd>Added Global Exclude</dd>
|
||||
<dd>Added Host Pattern to proxy feature</dd>
|
||||
<dd>Added Import FoxyProxy Account (Chrome)</dd>
|
||||
<dd>Added Import From URL</dd>
|
||||
<dd>Added Limit WebRTC feature (Foxyproxy_Chrome/issues/4)</dd>
|
||||
<dd>Added live Log (Firefox only)</dd>
|
||||
<dd>Added Tab Proxy feature (Firefox only)</dd>
|
||||
<dd>Changed "browsingData" to optional permissions</dd>
|
||||
<dd>Dropped "browser_style" in preparation for MV3</dd>
|
||||
<dd>Increased minimum version to Firefox 93 (released 2021-10-05)</dd>
|
||||
<dd>Unified code for Firefox, Chrome, and other Chromium-based browsers</dd>
|
||||
<dd>Unified storage to share between Firefox and Chrome</dd>
|
||||
<dd>Updated code and style for manifest v3 (MV3) compatibility</dd>
|
||||
<dd>Updated Import Proxy List</dd>
|
||||
<dd>Updated User Interface</dd>
|
||||
</dl>
|
||||
|
||||
<h2 id="credits">Credits</h2>
|
||||
<dl>
|
||||
<dt>Developer</dt>
|
||||
<dd><a href="https://github.com/erosman" target="_blank">erosman</a></dd>
|
||||
|
||||
<dt>Translations</dt>
|
||||
<dd><a href="https://github.com/foxyproxy/browser-extension" target="_blank">Github Public Repository</a></dd>
|
||||
<dd>es: <a href="https://github.com/LuisAlfredo92" target="_blank">Luis Alfredo Figueroa Bracamontes</a></dd>
|
||||
<dd>fa: <a href="https://github.com/axone13" target="_blank">Matin Kargar </a></dd>
|
||||
<dd>fr: <a href="https://github.com/Hugo-C" target="_blank">Hugo-C</a></dd>
|
||||
<dd>ja: <a href="https://github.com/yutayamate" target="_blank">Yuta Yamate</a></dd>
|
||||
<dd>pl: Grzegorz Koryga</dd>
|
||||
<dd>pt_BR: </dd>
|
||||
<dd>ru: <a href="https://github.com/sosiska" target="_blank">Kirill Motkov</a>, <a href="https://github.com/krolchonok" target="_blank">krolchonok</a></dd>
|
||||
<dd>uk: <a href="https://github.com/sponsors/webknjaz" target="_blank">Sviatoslav Sydorenko</a></dd>
|
||||
<dd>zh_CN: <a href="https://github.com/wsxy162" target="_blank">FeralMeow </a></dd>
|
||||
<dd>zh_TW: <a href="https://github.com/samuikaze" target="_blank">samuikaze</a></dd>
|
||||
|
||||
<dt>Founder</dt>
|
||||
<dd>
|
||||
<img class="figure" src="../image/ericjung.png" alt=""><br>
|
||||
<a href="mailto:eric.jung@getfoxyproxy.org">Eric H. Jung</a><br>
|
||||
Denver, Colorado, USA<br>
|
||||
Developer
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<p style="margin-top: 3em;">To support this free software, please please donate (<a href="https://buy.stripe.com/00gcNd1YQ2NQbKM148" target="_blank">Stripe</a>, <a href="https://www.paypal.me/ericjung2/5.99" target="_blank">PayPal</a>) or
|
||||
<a href="https://getfoxyproxy.org/order/" target="_blank">buy dedicated VPN/Proxy Servers</a> in over 100 countries.<br>
|
||||
<i>(including such remote places like <a href="https://wikipedia.org/wiki/Réunion" target="_blank">Reunion Island</a>)</i>
|
||||
</p>
|
||||
|
||||
<p style="font: 1.5em cursive;">Thank you for using FoxyProxy!</p>
|
||||
<img src="../image/logo.svg" style="width: 5em;" alt="">
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
49
foxyproxy/content/action.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// import {Flag} from './flag.js';
|
||||
import {Location} from './location.js';
|
||||
|
||||
export class Action {
|
||||
|
||||
// https://github.com/w3c/webextensions/issues/72#issuecomment-1848874359
|
||||
// 'prefers-color-scheme' detection in Chrome background service worker
|
||||
static dark = false;
|
||||
|
||||
static set(pref) {
|
||||
// --- set action/browserAction
|
||||
let title = '';
|
||||
let text = '';
|
||||
let color = this.dark ? '#444' : '#fff';
|
||||
switch (pref.mode) {
|
||||
case 'disable':
|
||||
title = browser.i18n.getMessage('disable');
|
||||
text = '⛔';
|
||||
break;
|
||||
|
||||
case 'direct':
|
||||
title = 'DIRECT';
|
||||
text = '⮕';
|
||||
break;
|
||||
|
||||
case 'pattern':
|
||||
title = browser.i18n.getMessage('proxyByPatterns');
|
||||
text = '🌐';
|
||||
break;
|
||||
|
||||
default:
|
||||
const item = pref.data.find(i => pref.mode === (i.type === 'pac' ? i.pac : `${i.hostname}:${i.port}`));
|
||||
if (item) {
|
||||
// Chrome 113-114 started having a bug showing unicode flags
|
||||
// const flag = Flag.get(item.cc);
|
||||
// const host = flag + ' ' + [item.hostname, item.port].filter(Boolean).join(':');
|
||||
const host = [item.hostname, item.port].filter(Boolean).join(':');
|
||||
title = [item.title, host, item.city, Location.get(item.cc)].filter(Boolean).join('\n');
|
||||
// text = item.cc ? flag : item.hostname;
|
||||
text = item.title || item.hostname;
|
||||
color = item.color;
|
||||
}
|
||||
}
|
||||
|
||||
browser.action.setBadgeBackgroundColor({color});
|
||||
browser.action.setTitle({title});
|
||||
browser.action.setBadgeText({text});
|
||||
}
|
||||
}
|
||||
88
foxyproxy/content/app.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// ---------- Polyfill (Side Effect) -----------------------
|
||||
// Promise based 'browser' namespace is used to avoid conflict
|
||||
// Firefox 'chrome' API: MV2 callback | MV3 promise
|
||||
// Firefox/Edge: browser namespace | Chrome/Opera: chrome namespace
|
||||
globalThis.browser ??= chrome;
|
||||
|
||||
// ---------- Default Preferences --------------------------
|
||||
export const pref = {
|
||||
mode: 'disable',
|
||||
sync: false,
|
||||
autoBackup: false,
|
||||
passthrough: '',
|
||||
theme: '',
|
||||
container: {},
|
||||
commands: {},
|
||||
data: []
|
||||
};
|
||||
// ---------- /Default Preferences -------------------------
|
||||
|
||||
// ---------- App ------------------------------------------
|
||||
export class App {
|
||||
|
||||
// https://github.com/foxyproxy/firefox-extension/issues/220
|
||||
// navigator.userAgent identification fails in custom userAgent and browser forks
|
||||
// Chrome does not support runtime.getBrowserInfo()
|
||||
// getURL: moz-extension: | chrome-extension: | safari-web-extension:
|
||||
static firefox = browser.runtime.getURL('').startsWith('moz-extension:');
|
||||
static basic = browser.runtime.getManifest().name === browser.i18n.getMessage('extensionNameBasic');
|
||||
static android = navigator.userAgent.includes('Android');
|
||||
|
||||
// ---------- User Preferences ---------------------------
|
||||
// not syncing mode & sync (to have a choice), data (will be broken into parts)
|
||||
static syncProperties = Object.keys(pref).filter(i => !['mode', 'sync', 'data'].includes(i));
|
||||
|
||||
static defaultPref = JSON.stringify(pref);
|
||||
|
||||
static getDefaultPref() {
|
||||
return JSON.parse(this.defaultPref);
|
||||
}
|
||||
|
||||
static getPref() {
|
||||
// update pref with the saved version
|
||||
return browser.storage.local.get().then(result => {
|
||||
Object.keys(result).forEach(i => pref[i] = result[i]);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Helper functions ---------------------------
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=478654
|
||||
// Add support for SVG images in Web Notifications API -> CH107
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1353252
|
||||
// svg broken from bg service worker
|
||||
static notify(message, title = browser.i18n.getMessage('extensionName'), id = '') {
|
||||
browser.notifications.create(id, {
|
||||
type: 'basic',
|
||||
iconUrl: '/image/icon48.png',
|
||||
title,
|
||||
message
|
||||
});
|
||||
}
|
||||
|
||||
static equal(a, b) {
|
||||
return JSON.stringify(a) === JSON.stringify(b);
|
||||
}
|
||||
|
||||
static parseURL(url) {
|
||||
// rebuild file://
|
||||
url.startsWith('file://') && (url = 'http' + url.substring(4));
|
||||
|
||||
try { url = new URL(url); }
|
||||
catch (error) {
|
||||
alert(`${url} ➜ ${error.message}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
// check protocol
|
||||
if (!['http:', 'https:', 'file:'].includes(url.protocol)) {
|
||||
alert(`${url} ➜ Unsupported Protocol ${url.protocol}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
static allowedTabProxy(url) {
|
||||
return /^https?:\/\/.+|^about:(blank|newtab)$/.test(url);
|
||||
}
|
||||
}
|
||||
51
foxyproxy/content/authentication.js
Normal file
@@ -0,0 +1,51 @@
|
||||
// webRequest.onAuthRequired: Firefox HTTP/HTTPS/WS/WSS | Chrome: HTTP/HTTPS
|
||||
// 'webRequestAuthProvider' permission Chrome 108, Firefox 126
|
||||
|
||||
export class Authentication {
|
||||
|
||||
static {
|
||||
this.data = {};
|
||||
// prevent bad authentication loop
|
||||
this.pending = {};
|
||||
// webRequest.onAuthRequired is only called for HTTP and HTTPS/TLS proxy servers
|
||||
const urls = ['<all_urls>'];
|
||||
browser.webRequest.onAuthRequired.addListener(e => this.process(e), {urls}, ['blocking']);
|
||||
browser.webRequest.onCompleted.addListener(e => this.clearPending(e), {urls});
|
||||
browser.webRequest.onErrorOccurred.addListener(e => this.clearPending(e), {urls});
|
||||
}
|
||||
|
||||
static init(data) {
|
||||
// reset data
|
||||
this.data = {};
|
||||
data.forEach(i => {
|
||||
const {hostname, port, username, password} = i;
|
||||
hostname && port && username && password &&
|
||||
(this.data[`${hostname}:${port}`] = {username, password});
|
||||
});
|
||||
}
|
||||
|
||||
static process(e) {
|
||||
// true for Proxy-Authenticate, false for WWW-Authenticate
|
||||
if (!e.isProxy) { return; }
|
||||
|
||||
// sending message to log.js
|
||||
browser.runtime.sendMessage({id: 'onAuthRequired', e});
|
||||
|
||||
// already sent once and pending
|
||||
if (this.pending[e.requestId]) {
|
||||
return {cancel: true};
|
||||
}
|
||||
|
||||
const {host, port} = e.challenger;
|
||||
const authCredentials = this.data[`${host}:${port}`];
|
||||
if (authCredentials) {
|
||||
// prevent bad authentication loop
|
||||
this.pending[e.requestId] = 1;
|
||||
return {authCredentials};
|
||||
}
|
||||
}
|
||||
|
||||
static clearPending(e) {
|
||||
delete this.pending[e.requestId];
|
||||
}
|
||||
}
|
||||
35
foxyproxy/content/background.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import {Sync} from "./sync.js";
|
||||
import {Migrate} from './migrate.js';
|
||||
import {Proxy} from './proxy.js';
|
||||
import './commands.js';
|
||||
|
||||
// ---------- Process Preferences --------------------------
|
||||
class ProcessPref {
|
||||
|
||||
static {
|
||||
this.init();
|
||||
}
|
||||
|
||||
static async init() {
|
||||
const pref = await browser.storage.local.get();
|
||||
|
||||
// storage sync -> local update
|
||||
await Sync.get(pref);
|
||||
|
||||
// migrate after storage sync check
|
||||
await Migrate.init(pref);
|
||||
|
||||
// set proxy
|
||||
Proxy.set(pref);
|
||||
|
||||
// add listener after migrate
|
||||
Sync.init(pref);
|
||||
}
|
||||
}
|
||||
// ---------- /Process Preferences -------------------------
|
||||
|
||||
// ---------- Initialisation -------------------------------
|
||||
// browser.runtime.onInstalled.addListener(e => {
|
||||
// // show help
|
||||
// ['install', 'update'].includes(e.reason) && browser.tabs.create({url: '/content/help.html'});
|
||||
// });
|
||||
35
foxyproxy/content/browsing-data.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- browsingData (Side Effect) -------------------
|
||||
class BrowsingData {
|
||||
|
||||
static {
|
||||
document.querySelector('#deleteBrowsingData').addEventListener('click', () => this.process());
|
||||
this.init();
|
||||
}
|
||||
|
||||
static async init() {
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/permissions/request
|
||||
// Any permissions granted are retained by the extension, even over upgrade and disable/enable cycling.
|
||||
// check if permission is granted
|
||||
this.permission = await browser.permissions.contains({permissions: ['browsingData']});
|
||||
}
|
||||
|
||||
static async process() {
|
||||
if (!this.permission) {
|
||||
// request permission
|
||||
// Chrome appears to return true, granted silently without a popup prompt
|
||||
this.permission = await browser.permissions.request({permissions: ['browsingData']});
|
||||
if (!this.permission) { return; }
|
||||
}
|
||||
|
||||
if (!confirm(browser.i18n.getMessage('deleteBrowsingDataConfirm'))) { return; }
|
||||
|
||||
browser.browsingData.remove({}, {
|
||||
cookies: true,
|
||||
indexedDB: true,
|
||||
localStorage: true
|
||||
})
|
||||
.catch(error => App.notify(browser.i18n.getMessage('deleteBrowsingData') + '\n\n' + error.message));
|
||||
}
|
||||
}
|
||||
218
foxyproxy/content/bulk-edit.js
Normal file
@@ -0,0 +1,218 @@
|
||||
// ---------- Bulk Edit (Side Effect) ----------------------
|
||||
class BulkEdit {
|
||||
|
||||
static {
|
||||
const div = document.querySelector('.bulk-edit');
|
||||
[this.t1, this.t2, this.s1, this.s2, this.select] = div.children;
|
||||
|
||||
this.t1.addEventListener('change', () => this.toggleProxy('t1'));
|
||||
this.s1.addEventListener('change', () => this.toggleProxy('s1'));
|
||||
this.t2.addEventListener('change', () => this.togglePattern());
|
||||
this.select.addEventListener('change', () => this.process());
|
||||
}
|
||||
|
||||
static toggleProxy(i) {
|
||||
// remove previous selection
|
||||
document.querySelector(`details.proxy.${i}`)?.classList.remove(i);
|
||||
const n = this.getNumber(i);
|
||||
if (!n) { return; }
|
||||
|
||||
document.querySelector(`details.proxy:nth-of-type(${n})`)?.classList.add(i);
|
||||
|
||||
// reselect t2
|
||||
this.togglePattern();
|
||||
}
|
||||
|
||||
static togglePattern() {
|
||||
// remove previous selection
|
||||
const prev = document.querySelector('.pattern-row.t2');
|
||||
if (prev) {
|
||||
prev.classList.remove('t2');
|
||||
prev.closest('details').open = false;
|
||||
}
|
||||
|
||||
const n = this.getNumber('t2');
|
||||
if (!n) { return; }
|
||||
|
||||
const t = this.getNumber('t1') || this.getNumber('s1');
|
||||
if (!t) { return; }
|
||||
|
||||
const elem = document.querySelector(`details.proxy:nth-of-type(${t}) .pattern-row:nth-of-type(${n})`);
|
||||
if (elem) {
|
||||
elem.classList.add('t2');
|
||||
elem.closest('details').open = true;
|
||||
}
|
||||
}
|
||||
|
||||
static process() {
|
||||
if (!this.select.value) { return; }
|
||||
|
||||
const id = this.select.value;
|
||||
switch (id) {
|
||||
case 'openAll':
|
||||
case 'closeAll':
|
||||
this.getProxies().forEach(i => i.open = id === 'openAll');
|
||||
break;
|
||||
|
||||
case 'setType':
|
||||
case 'setPort':
|
||||
case 'setTitle':
|
||||
case 'setUsername':
|
||||
case 'setPassword':
|
||||
let s2 = this.s2.value.trim();
|
||||
if (!s2) { break; }
|
||||
|
||||
const ref = id.substring(3).toLowerCase();
|
||||
if (ref === 'type') {
|
||||
s2 = s2.toLowerCase();
|
||||
if (!['http', 'https', 'socks4', 'socks5', 'quic', 'pac', 'direct'].includes(s2)) { break; }
|
||||
}
|
||||
|
||||
document.querySelectorAll(`[data-id="${ref}"]`).forEach(i =>
|
||||
s2.startsWith('+') ? i.value += s2.substring(1) : i.value = s2);
|
||||
break;
|
||||
|
||||
case 'deleteProxy':
|
||||
this.deleteProxy();
|
||||
this.reset();
|
||||
break;
|
||||
|
||||
case 'moveProxy':
|
||||
this.moveProxy();
|
||||
this.reset();
|
||||
break;
|
||||
|
||||
case 'movePattern':
|
||||
this.movePattern();
|
||||
this.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
// --- reset
|
||||
this.select.selectedIndex = 0;
|
||||
}
|
||||
|
||||
static reset() {
|
||||
document.querySelectorAll('details.proxy:is(.t1, .s1), .pattern-row.t2').forEach(i =>
|
||||
i.classList.remove('t1', 't2', 's1'));
|
||||
// ['t1', 't2', 's1'].forEach(i => this[i].value = '');
|
||||
}
|
||||
|
||||
static getProxies() {
|
||||
return document.querySelectorAll('details.proxy');
|
||||
}
|
||||
|
||||
static getNumber(i) {
|
||||
return this[i].checkValidity() && this[i].value ? this[i].value : null;
|
||||
}
|
||||
|
||||
static getSourceNumbers() {
|
||||
const n = this.s2.value.match(/\d+-\d+|\d+/g);
|
||||
if (!n) { return; }
|
||||
|
||||
let arr = [];
|
||||
n.forEach(i => {
|
||||
// check if number range e.g. 5-8
|
||||
const [a, b] = i.split('-');
|
||||
b ? arr.push(...Array.from({length: b - a + 1}, (_, i) => (a * 1) + i)) : arr.push(a);
|
||||
});
|
||||
|
||||
// map to index (-1), sort, remove duplicates
|
||||
arr = [...new Set(arr.map(i => i - 1).sort((a, b) => a - b))];
|
||||
return arr.length ? arr : null;
|
||||
}
|
||||
|
||||
static deleteProxy() {
|
||||
const n = this.getSourceNumbers();
|
||||
if (!n) { return; }
|
||||
|
||||
const p = this.getProxies();
|
||||
n.forEach(i => p[i]?.remove());
|
||||
}
|
||||
|
||||
static moveProxy() {
|
||||
let n = this.getSourceNumbers();
|
||||
if (!n) { return; }
|
||||
|
||||
const t1 = this.t1.value - 1;
|
||||
const p = this.getProxies();
|
||||
|
||||
// filter target, map to elements, filter non-existing
|
||||
n = n.filter(i => i !== t1).map(i => p[i]).filter(Boolean);
|
||||
if (!n[0]) { return; }
|
||||
|
||||
// before target or after all
|
||||
p[t1] ? p[t1].before(...n) : p[0].parentElement.append(...n);
|
||||
}
|
||||
|
||||
static movePattern() {
|
||||
const t1 = this.t1.value - 1;
|
||||
const s1 = this.s1.value - 1;
|
||||
|
||||
switch (true) {
|
||||
// move withing the same proxy
|
||||
case t1 === -1 || t1 === s1:
|
||||
s1 !== -1 && this.movePatternWithin(s1);
|
||||
break;
|
||||
|
||||
// move all patterns to target
|
||||
case s1 === -1:
|
||||
this.movePatternAll(t1);
|
||||
break;
|
||||
|
||||
// move source patterns to target
|
||||
default:
|
||||
this.movePatternSome(t1, s1);
|
||||
}
|
||||
}
|
||||
|
||||
static movePatternWithin(s1) {
|
||||
let n = this.getSourceNumbers();
|
||||
if (!n) { return; }
|
||||
|
||||
const p = this.getProxies();
|
||||
if (!p[s1]) { return; }
|
||||
|
||||
const t2 = this.t2.value - 1;
|
||||
|
||||
// filter target, map to elements, filter non-existing
|
||||
const pat = p[s1].querySelectorAll('.pattern-row');
|
||||
n = n.filter(i => i !== t2).map(i => pat[i]).filter(Boolean);
|
||||
if (!n[0]) { return; }
|
||||
|
||||
pat[t2] ? pat[t2].before(...n) : pat[0].parentElement.append(...n);
|
||||
}
|
||||
|
||||
static movePatternAll(t1) {
|
||||
const n = this.getSourceNumbers();
|
||||
if (!n) { return; }
|
||||
|
||||
const p = this.getProxies();
|
||||
if (!p[t1]) { return; }
|
||||
|
||||
// filter target, map to elements
|
||||
const pat = [];
|
||||
n.filter(i => i !== t1).forEach(i => p[i] && pat.push(...p[i].querySelectorAll('.pattern-row')));
|
||||
|
||||
const target = p[t1].querySelector('.pattern-box');
|
||||
const row = target.children?.[this.t2.value - 1];
|
||||
row ? row.before(...pat) : target.append(...pat);
|
||||
}
|
||||
|
||||
static movePatternSome(t1, s1) {
|
||||
let n = this.getSourceNumbers();
|
||||
if (!n) { return; }
|
||||
|
||||
const p = this.getProxies();
|
||||
if (!p[t1] || !p[s1]) { return; }
|
||||
|
||||
// map to elements, filter non-existing
|
||||
const pat = p[s1].querySelectorAll('.pattern-row');
|
||||
n = n.map(i => pat[i]).filter(Boolean);
|
||||
if (!n[0]) { return; }
|
||||
|
||||
const target = p[t1].querySelector('.pattern-box');
|
||||
const row = target.children?.[this.t2.value - 1];
|
||||
row ? row.before(...n) : target.append(...n);
|
||||
}
|
||||
}
|
||||
21
foxyproxy/content/color.js
Normal file
@@ -0,0 +1,21 @@
|
||||
export class Color {
|
||||
|
||||
static getRandom() {
|
||||
return this.colors[Math.floor(Math.random() * this.colors.length)];
|
||||
}
|
||||
|
||||
static colors = [
|
||||
'#faebd7', '#00ffff', '#7fffd4', '#f5f5dc', '#ffe4c4', '#ffebcd', '#0000ff', '#8a2be2', '#a52a2a', '#deb887',
|
||||
'#5f9ea0', '#7fff00', '#d2691e', '#ff7f50', '#6495ed', '#fff8dc', '#dc143c', '#00008b', '#008b8b', '#b8860b',
|
||||
'#a9a9a9', '#006400', '#bdb76b', '#8b008b', '#556b2f', '#ff8c00', '#9932cc', '#8b0000', '#e9967a', '#8fbc8f',
|
||||
'#483d8b', '#2f4f4f', '#00ced1', '#9400d3', '#ff1493', '#00bfff', '#696969', '#1e90ff', '#b22222', '#228b22',
|
||||
'#ff00ff', '#ffd700', '#daa520', '#808080', '#008000', '#adff2f', '#ff69b4', '#cd5c5c', '#4b0082', '#f0e68c',
|
||||
'#7cfc00', '#fffacd', '#add8e6', '#f08080', '#e0ffff', '#fafad2', '#d3d3d3', '#90ee90', '#ffb6c1', '#ffa07a',
|
||||
'#20b2aa', '#87cefa', '#778899', '#b0c4de', '#00ff00', '#32cd32', '#800000', '#66cdaa', '#0000cd', '#ba55d3',
|
||||
'#9370db', '#3cb371', '#7b68ee', '#00fa9a', '#48d1cc', '#c71585', '#191970', '#ffe4e1', '#ffe4b5', '#ffdead',
|
||||
'#000080', '#fdf5e6', '#808000', '#6b8e23', '#ffa500', '#ff4500', '#da70d6', '#eee8aa', '#98fb98', '#afeeee',
|
||||
'#db7093', '#ffefd5', '#ffdab9', '#cd853f', '#ffc0cb', '#dda0dd', '#b0e0e6', '#800080', '#ff0000', '#bc8f8f',
|
||||
'#4169e1', '#8b4513', '#fa8072', '#f4a460', '#2e8b57', '#fff5ee', '#a0522d', '#87ceeb', '#6a5acd', '#708090',
|
||||
'#00ff7f', '#4682b4', '#d2b48c', '#008080', '#d8bfd8', '#ff6347', '#40e0d0', '#ee82ee', '#f5deb3', '#ffff00',
|
||||
'#9acd32'];
|
||||
}
|
||||
77
foxyproxy/content/commands.js
Normal file
@@ -0,0 +1,77 @@
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/commands/onCommand
|
||||
// https://developer.chrome.com/docs/extensions/reference/commands/#event-onCommand
|
||||
// Chrome commands returns command, tab
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1843866
|
||||
// Add tab parameter to commands.onCommand (fixed in Firefox 126)
|
||||
|
||||
import {App} from './app.js';
|
||||
import {Proxy} from './proxy.js';
|
||||
import {OnRequest} from './on-request.js';
|
||||
|
||||
// ---------- Commands (Side Effect) ------------------------
|
||||
class Commands {
|
||||
|
||||
static {
|
||||
// commands is not supported on Android
|
||||
browser.commands?.onCommand.addListener((...e) => this.process(...e));
|
||||
}
|
||||
|
||||
static async process(name, tab) {
|
||||
// firefox only Tab Proxy
|
||||
const tabProxy = ['setTabProxy', 'unsetTabProxy'].includes(name);
|
||||
if (!App.firefox && tabProxy) { return; }
|
||||
|
||||
const pref = await browser.storage.local.get();
|
||||
|
||||
// only Tab Proxy allowed for storage.managed
|
||||
if (pref.managed && !tabProxy) { return; }
|
||||
|
||||
const host = pref.commands[name];
|
||||
let proxy;
|
||||
|
||||
switch (name) {
|
||||
case 'proxyByPatterns':
|
||||
this.set(pref, 'pattern');
|
||||
break;
|
||||
|
||||
case 'disable':
|
||||
this.set(pref, 'disable');
|
||||
break;
|
||||
|
||||
case 'setProxy':
|
||||
host && this.set(pref, host);
|
||||
break;
|
||||
|
||||
case 'includeHost':
|
||||
case 'excludeHost':
|
||||
if (!host) { break; }
|
||||
|
||||
proxy = this.findProxy(pref, host);
|
||||
proxy && Proxy.includeHost(pref, proxy, tab, name);
|
||||
break;
|
||||
|
||||
case 'setTabProxy':
|
||||
if (!host) { break; }
|
||||
|
||||
proxy = this.findProxy(pref, host);
|
||||
proxy && OnRequest.setTabProxy(tab, proxy);
|
||||
break;
|
||||
|
||||
case 'unsetTabProxy':
|
||||
OnRequest.setTabProxy(tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static findProxy(pref, host) {
|
||||
return host && pref.data.find(i => i.active && host === `${i.hostname}:${i.port}`);
|
||||
}
|
||||
|
||||
static set(pref, mode) {
|
||||
pref.mode = mode;
|
||||
// save mode
|
||||
browser.storage.local.set({mode});
|
||||
// set proxy without menus update
|
||||
Proxy.set(pref, true);
|
||||
}
|
||||
}
|
||||
161
foxyproxy/content/default.css
Normal file
@@ -0,0 +1,161 @@
|
||||
/* ----- Light Theme ----- */
|
||||
:root {
|
||||
--color: #000;
|
||||
--bg: #fff;
|
||||
--alt-bg: #f5f5f5;
|
||||
--hover: #eaeaea;
|
||||
--highlight: #f90;
|
||||
|
||||
--body-bg: #630;
|
||||
--header: #c60;
|
||||
|
||||
--nav-bg: #420;
|
||||
--nav-hover: #851;
|
||||
--nav-color: cornsilk;
|
||||
|
||||
--btn-bg: #f90;
|
||||
--btn-hover: #e70;
|
||||
|
||||
--link: #e70;
|
||||
--border: #ddd;
|
||||
/* --shadow: #0004; */
|
||||
--dim: #777;
|
||||
--tr: #f5f5f5;
|
||||
}
|
||||
|
||||
/* ----- Dark Theme ----- */
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color: #fff;
|
||||
--bg: #420;
|
||||
--alt-bg: #666;
|
||||
--hover: #444;
|
||||
|
||||
/* --body-bg: #630; */
|
||||
--header: #e70;
|
||||
|
||||
--btn-bg: #f90;
|
||||
--btn-hover: #e70;
|
||||
|
||||
--link: #f90;
|
||||
--border: #777;
|
||||
/* --shadow: #fff8; */
|
||||
--dim: #ccc;
|
||||
--tr: #531;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- General ----- */
|
||||
body {
|
||||
color: var(--color);
|
||||
background-color: var(--body-bg);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
select,
|
||||
textarea,
|
||||
input[type="number"],
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="url"] {
|
||||
width: 100%;
|
||||
color: inherit;
|
||||
background-color: var(--alt-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
:is(select,
|
||||
input[type="number"],
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="url"]):hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
label[for],
|
||||
input[type="checkbox"],
|
||||
summary,
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
opacity: 0.5;
|
||||
color: inherit;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.invalid,
|
||||
input:invalid {
|
||||
box-shadow: 1px 1px 4px #f20, -1px -1px 4px #f20;
|
||||
}
|
||||
|
||||
/* ----- Buttons ----- */
|
||||
button,
|
||||
label.flat {
|
||||
background-color: var(--btn-bg);
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
button.flat,
|
||||
label.flat {
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
color: #fff;
|
||||
border-radius: 5px;
|
||||
padding: 0.4em 1em;
|
||||
min-width: 8em;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
label.flat:hover {
|
||||
background-color: var(--btn-hover);
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
select:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
button.plain {
|
||||
background-color: unset;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-width: 1em;
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
display: table;
|
||||
color: #fff;
|
||||
font-size:0.9em;
|
||||
border-radius: 5px;
|
||||
padding: 0.5em 5em;
|
||||
margin: 1em auto 0;
|
||||
}
|
||||
/* ----- /Buttons ----- */
|
||||
30
foxyproxy/content/drag-drop.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// ---------- Drag and Drop (Side Effect) ------------------
|
||||
class Drag {
|
||||
|
||||
static {
|
||||
this.proxyDiv = document.querySelector('div.proxy-div');
|
||||
this.proxyDiv.addEventListener("dragstart", e => this.dragstart(e));
|
||||
this.proxyDiv.addEventListener('dragover', e => this.dragover(e));
|
||||
this.proxyDiv.addEventListener('dragend', e => this.dragend(e));
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
static dragstart(e) {
|
||||
if (e.target.localName === 'input') {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
static dragover(e) {
|
||||
this.target = e.target.closest('details');
|
||||
}
|
||||
|
||||
static dragend(e) {
|
||||
if (!this.target) { return; }
|
||||
|
||||
const arr = [...this.proxyDiv.children];
|
||||
arr.indexOf(e.target) > arr.indexOf(this.target) ? this.target.before(e.target) : this.target.after(e.target);
|
||||
this.target = null;
|
||||
}
|
||||
}
|
||||
54
foxyproxy/content/flag.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// ---------- Unicode flag ---------------------------------
|
||||
export class Flag {
|
||||
|
||||
static get(cc) {
|
||||
cc = /^[A-Z]{2}$/i.test(cc) && cc.toUpperCase();
|
||||
return cc ? String.fromCodePoint(...[...cc].map(i => i.charCodeAt() + 127397)) : '🌎';
|
||||
}
|
||||
|
||||
static show(item) {
|
||||
switch (true) {
|
||||
case !!item.cc:
|
||||
return this.get(item.cc);
|
||||
|
||||
case item.type === 'direct':
|
||||
return '⮕';
|
||||
|
||||
case this.isLocal(item.hostname):
|
||||
return '🖥️';
|
||||
|
||||
default:
|
||||
return '🌎';
|
||||
}
|
||||
}
|
||||
|
||||
static isLocal(host) {
|
||||
// check local network
|
||||
const isIP = /^[\d.:]+$/.test(host);
|
||||
switch (true) {
|
||||
// --- localhost & <local>
|
||||
// case host === 'localhost':
|
||||
// plain hostname (no dots)
|
||||
case !host.includes('.'):
|
||||
// *.localhost
|
||||
case host.endsWith('.localhost'):
|
||||
|
||||
// --- IPv4
|
||||
// case host === '127.0.0.1':
|
||||
// 127.0.0.1 up to 127.255.255.254
|
||||
case isIP && host.startsWith('127.'):
|
||||
// 169.254.0.0/16 - 169.254.0.0 to 169.254.255.255
|
||||
case isIP && host.startsWith('169.254.'):
|
||||
// 192.168.0.0/16 - 192.168.0.0 to 192.168.255.255
|
||||
case isIP && host.startsWith('192.168.'):
|
||||
|
||||
// --- IPv6
|
||||
// case host === '[::1]':
|
||||
// literal IPv6 [::1]:80 with/without port
|
||||
case host.startsWith('[::1]'):
|
||||
// literal IPv6 [FE80::]/10
|
||||
case host.toUpperCase().startsWith('[FE80::]'):
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
foxyproxy/content/get-location.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import {Spinner} from './spinner.js';
|
||||
|
||||
// ---------- Get Location (Side Effect) -------------------
|
||||
class GetLocation {
|
||||
|
||||
static {
|
||||
this.proxyDiv = document.querySelector('div.proxy-div');
|
||||
document.querySelector('.proxy-top button[data-i18n="getLocation"]').addEventListener('click', () => this.process());
|
||||
}
|
||||
|
||||
static async process() {
|
||||
const ignore = ['127.0.0.1', 'localhost'];
|
||||
|
||||
let {data} = await browser.storage.local.get({data: []});
|
||||
data = data.filter(i => i.type !== 'direct' && !ignore.includes(i.hostname)).map(i => i.hostname);
|
||||
if (!data[0]) { return; }
|
||||
|
||||
// remove duplicates
|
||||
const hosts = [...new Set(data)];
|
||||
|
||||
Spinner.show();
|
||||
|
||||
fetch('https://getfoxyproxy.org/webservices/lookup.php?' + hosts.join('&'))
|
||||
.then(response => response.json())
|
||||
.then(json => this.updateLocation(json))
|
||||
.catch(error => {
|
||||
Spinner.hide();
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
|
||||
static updateLocation(json) {
|
||||
// update display
|
||||
this.proxyDiv.querySelectorAll('[data-id="cc"], [data-id="city"]').forEach(i => {
|
||||
const {hostname, id} = i.dataset;
|
||||
// cache old value to compare
|
||||
const value = i.value;
|
||||
json[hostname]?.[id] && (i.value = json[hostname][id]);
|
||||
// dispatch change event
|
||||
id === 'cc' && i.value !== value && i.dispatchEvent(new Event('change'));
|
||||
});
|
||||
|
||||
Spinner.hide();
|
||||
}
|
||||
}
|
||||
1582
foxyproxy/content/help.html
Normal file
18
foxyproxy/content/i18n.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// ---------- Internationalization (Side Effect) -----------
|
||||
class I18n {
|
||||
|
||||
static {
|
||||
document.querySelectorAll('template').forEach(i => this.set(i.content));
|
||||
this.set();
|
||||
// show after
|
||||
// document.body.style.opacity = 1;
|
||||
}
|
||||
|
||||
static set(target = document) {
|
||||
target.querySelectorAll('[data-i18n]').forEach(elem => {
|
||||
let [text, attr] = elem.dataset.i18n.split('|');
|
||||
text = browser.i18n.getMessage(text);
|
||||
attr ? elem.setAttribute(attr, text) : elem.append(text);
|
||||
});
|
||||
}
|
||||
}
|
||||
315
foxyproxy/content/iframe.css
Normal file
@@ -0,0 +1,315 @@
|
||||
@import 'default.css';
|
||||
@import 'theme.css';
|
||||
|
||||
/* ----- General ----- */
|
||||
:root {
|
||||
--nav-height: 2.5rem;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-padding-top: calc(var(--nav-height) + 0.5rem);
|
||||
}
|
||||
|
||||
body {
|
||||
/* Chrome sets font-size to 75% (16px x 75% = 12px) */
|
||||
font-size: unset;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 2em;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
|
||||
/* ----- h1-h5 ----- */
|
||||
h2 {
|
||||
color: var(--header);
|
||||
font-size: 2.5em;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h2:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
:not(h2) + h3 {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
:is(h1, h2, h3, h4, h5) span {
|
||||
color: var(--dim);
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
/* ----- /h1-h5 ----- */
|
||||
|
||||
p {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
pre {
|
||||
border-left: 3px solid #ccc;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0 0.3em;
|
||||
background-color: var(--hover);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: var(--color);
|
||||
padding: 1em 3.5em;
|
||||
font-style: italic;
|
||||
position: relative;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
blockquote::before,
|
||||
blockquote::after {
|
||||
color: #ccc;
|
||||
opacity: 0.6;
|
||||
font-size: 4em;
|
||||
position: absolute;
|
||||
content: '❝';
|
||||
top: 0;
|
||||
left: 0.1em;
|
||||
}
|
||||
|
||||
blockquote::after {
|
||||
bottom: -0.5em;
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
cite {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
cite::before {
|
||||
content: '— source: ';
|
||||
}
|
||||
|
||||
img.figure {
|
||||
border-radius: 1em;
|
||||
border: 4px solid var(--nav-hover);
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
dd + dt {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
dd > dl {
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
th span,
|
||||
dt span,
|
||||
dd span,
|
||||
li span {
|
||||
margin-left: 0.5em;
|
||||
color: var(--dim);
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
mark {
|
||||
color: var(--header);
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
.scroll {
|
||||
max-height: 25em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* ----- About ----- */
|
||||
.about dt {
|
||||
display: table;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 0.2em;
|
||||
min-width: 15vw;
|
||||
font-weight: bold;
|
||||
}
|
||||
/* ----- /About ----- */
|
||||
|
||||
/* ----- Navigation ----- */
|
||||
nav {
|
||||
background-color: var(--bg);
|
||||
height: var(--nav-height);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
box-shadow: 0 3px 6px #0004;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: var(--color);
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
/* ----- /Navigation ----- */
|
||||
|
||||
/* ----- Table ----- */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--border);
|
||||
margin-bottom: 1em;
|
||||
width: calc(100% - 2.5rem);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
tr:nth-child(2n) {
|
||||
background-color: var(--alt-bg);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--border);
|
||||
vertical-align: top;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tbody th {
|
||||
min-width: 10em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td pre,
|
||||
.code td,
|
||||
td.code {
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.slim th,
|
||||
.slim td {
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
/* ----- /Table ----- */
|
||||
|
||||
/* ----- note, footnote, warning, experimental ----- */
|
||||
.note,
|
||||
.warning {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.5em;
|
||||
border-left: 3px solid #17f;
|
||||
padding: 0.3em 0.5em 0.3em 2em;
|
||||
margin-top: 0.5em;
|
||||
position: relative;
|
||||
display: table;
|
||||
}
|
||||
|
||||
.note::before,
|
||||
.warning::before {
|
||||
content: 'ⓘ';
|
||||
color: #17f;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border-left-color: #f90;
|
||||
}
|
||||
|
||||
.warning::before {
|
||||
content: '⚠️';
|
||||
}
|
||||
|
||||
.experimental::after {
|
||||
content: '';
|
||||
background: url('../image/beaker.svg') no-repeat center / contain;
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.footnote {
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ----- span links ----- */
|
||||
.chrome-extension,
|
||||
.moz-extension {
|
||||
cursor: pointer;
|
||||
font-style: normal;
|
||||
font-size: 0.8em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ----- Translate ----- */
|
||||
.translate {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
padding: 1em 1em 0;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.translate select {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.translate input[type="submit"] {
|
||||
color: inherit;
|
||||
background-color: var(--alt-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.3em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
/* ----- /Translate ----- */
|
||||
98
foxyproxy/content/import-account.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import {App} from './app.js';
|
||||
import {Proxies} from './options-proxies.js';
|
||||
import {Spinner} from './spinner.js';
|
||||
import {Toggle} from './toggle.js';
|
||||
import {Nav} from './nav.js';
|
||||
|
||||
// ---------- Import FoxyProxy Account (Side Effect) -------
|
||||
class ImportAccount {
|
||||
|
||||
static {
|
||||
this.username = document.querySelector('.import-account #username');
|
||||
this.password = document.querySelector('.import-account #password');
|
||||
Toggle.password(this.password.nextElementSibling);
|
||||
document.querySelector('.import-account button[data-i18n="import"]').addEventListener('click', () => this.process());
|
||||
}
|
||||
|
||||
static async process() {
|
||||
// --- check username/password
|
||||
const username = this.username.value.trim();
|
||||
const password = this.password.value.trim();
|
||||
if (!username || !password) {
|
||||
alert(browser.i18n.getMessage('userPassError'));
|
||||
return;
|
||||
}
|
||||
|
||||
Spinner.show();
|
||||
|
||||
const options = [...document.querySelectorAll('.import-account .account-options select')].map(i => i.value);
|
||||
// Array(3) [ "https", "hostname", "alt" ]
|
||||
const url = options.includes('alt') ?
|
||||
'https://bilestoad.com/webservices/get-accounts.php' :
|
||||
'https://getfoxyproxy.org/webservices/get-accounts.php';
|
||||
const ip = options.includes('ip');
|
||||
const socks = options.includes('socks5');
|
||||
const https = options.includes('https');
|
||||
|
||||
const proxyDiv = document.querySelector('div.proxy-div');
|
||||
const docFrag = document.createDocumentFragment();
|
||||
|
||||
// --- fetch data
|
||||
const data = await this.getAccount(url, username, password);
|
||||
if (data) {
|
||||
data.forEach(i => {
|
||||
// proxy template
|
||||
const pxy = {
|
||||
active: true,
|
||||
title: i.hostname.split('.')[0],
|
||||
type: socks ? 'socks5' : https ? 'https' : 'http',
|
||||
hostname: ip ? i.ip : i.hostname,
|
||||
port: socks ? i.socks5_port : https ? i.ssl_port : i.port[0],
|
||||
username: i.username,
|
||||
password: i.password,
|
||||
// convert UK to ISO 3166-1 GB
|
||||
cc: i.country_code === 'UK' ? 'GB' : i.country_code,
|
||||
city: i.city,
|
||||
// random color will be set
|
||||
color: '',
|
||||
pac: '',
|
||||
pacString: '',
|
||||
proxyDNS: true,
|
||||
include: [],
|
||||
exclude: [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
docFrag.append(Proxies.addProxy(pxy));
|
||||
});
|
||||
|
||||
proxyDiv.append(docFrag);
|
||||
Nav.get('proxies');
|
||||
}
|
||||
|
||||
Spinner.hide();
|
||||
}
|
||||
|
||||
static async getAccount(url, username, password) {
|
||||
// --- fetch data
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!Array.isArray(data) || !data[0]?.hostname) {
|
||||
App.notify(browser.i18n.getMessage('error'));
|
||||
return;
|
||||
}
|
||||
|
||||
// import active accounts only
|
||||
data = data.filter(i => i.active === 'true');
|
||||
// sort by country
|
||||
data.sort((a, b) => a.country.localeCompare(b.country));
|
||||
return data;
|
||||
})
|
||||
.catch(error => App.notify(browser.i18n.getMessage('error') + '\n\n' + error.message));
|
||||
}
|
||||
}
|
||||
76
foxyproxy/content/import-export.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- Import/Export Preferences --------------------
|
||||
export class ImportExport {
|
||||
|
||||
// pref references the same object in the memory and its value gets updated
|
||||
static init(pref, callback) {
|
||||
this.callback = callback;
|
||||
document.getElementById('file').addEventListener('change', e => this.import(e, pref));
|
||||
document.getElementById('export').addEventListener('click', () => this.export(pref));
|
||||
}
|
||||
|
||||
// import preferences
|
||||
static import(e, pref) {
|
||||
const file = e.target.files[0];
|
||||
switch (true) {
|
||||
case !file: App.notify(browser.i18n.getMessage('error'));
|
||||
return;
|
||||
// check file MIME type
|
||||
case !['text/plain', 'application/json'].includes(file.type):
|
||||
App.notify(browser.i18n.getMessage('fileTypeError'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.fileReader(file, r => this.readData(r, pref));
|
||||
}
|
||||
|
||||
static readData(data, pref) {
|
||||
try { data = JSON.parse(data); }
|
||||
catch {
|
||||
// display the error
|
||||
App.notify(browser.i18n.getMessage('fileParseError'));
|
||||
return;
|
||||
}
|
||||
|
||||
// update pref with the saved version
|
||||
Object.keys(pref).forEach(i => Object.hasOwn(data, i) && (pref[i] = data[i]));
|
||||
|
||||
// successful import
|
||||
this.callback();
|
||||
}
|
||||
|
||||
// export preferences
|
||||
static export(pref, saveAs = true, folder = '') {
|
||||
const data = JSON.stringify(pref, null, 2);
|
||||
const filename = `${folder}${browser.i18n.getMessage('extensionName')}_${new Date().toISOString().substring(0, 10)}.json`;
|
||||
this.saveFile({data, filename, type: 'application/json', saveAs});
|
||||
}
|
||||
|
||||
static saveFile({data, filename, saveAs = true, type = 'text/plain'}) {
|
||||
if (!browser.downloads) {
|
||||
const a = document.createElement('a');
|
||||
a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(data);
|
||||
a.setAttribute('download', filename);
|
||||
a.dispatchEvent(new MouseEvent('click'));
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = new Blob([data], {type});
|
||||
browser.downloads.download({
|
||||
url: URL.createObjectURL(blob),
|
||||
filename,
|
||||
saveAs,
|
||||
conflictAction: 'uniquify'
|
||||
})
|
||||
// eslint-disable-next-line no-console
|
||||
.catch(console.log);
|
||||
}
|
||||
|
||||
static fileReader(file, callback) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => callback(reader.result);
|
||||
reader.onerror = () => App.notify(browser.i18n.getMessage('fileReadError'));
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
132
foxyproxy/content/import-list.js
Normal file
@@ -0,0 +1,132 @@
|
||||
import {Proxies} from './options-proxies.js';
|
||||
import {Nav} from './nav.js';
|
||||
|
||||
// ---------- Import List (Side Effect) --------------------
|
||||
class ImportList {
|
||||
|
||||
static {
|
||||
this.textarea = document.querySelector('.import-proxy-list textarea');
|
||||
document.querySelector('.import-proxy-list button').addEventListener('click', () => this.process());
|
||||
}
|
||||
|
||||
static process() {
|
||||
this.textarea.value = this.textarea.value.trim();
|
||||
if (!this.textarea.value) { return; }
|
||||
|
||||
const proxyDiv = document.querySelector('div.proxy-div');
|
||||
const docFrag = document.createDocumentFragment();
|
||||
|
||||
for (const item of this.textarea.value.split(/\n+/)) {
|
||||
// simple vs Extended format
|
||||
const pxy = item.includes('://') ? this.parseExtended(item) : this.parseSimple(item);
|
||||
// end on error
|
||||
if (!pxy) { return; }
|
||||
|
||||
docFrag.append(Proxies.addProxy(pxy));
|
||||
}
|
||||
|
||||
proxyDiv.append(docFrag);
|
||||
Nav.get('proxies');
|
||||
}
|
||||
|
||||
static parseSimple(item) {
|
||||
// example.com:3128:user:pass
|
||||
const [hostname, port, username = '', password = ''] = item.split(':');
|
||||
if (!hostname || !(port * 1)) {
|
||||
alert(`Error: ${item}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const type = port === '443' ? 'https' : 'http';
|
||||
|
||||
// proxy template
|
||||
const pxy = {
|
||||
active: true,
|
||||
title: '',
|
||||
type,
|
||||
hostname,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
cc: '',
|
||||
city: '',
|
||||
color: '',
|
||||
pac: '',
|
||||
pacString: '',
|
||||
proxyDNS: true,
|
||||
include: [],
|
||||
exclude: [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
return pxy;
|
||||
}
|
||||
|
||||
static parseExtended(item) {
|
||||
// https://user:password@78.205.12.1:21?color=ff00bc&title=work%20proxy
|
||||
// https://example.com:443?active=false&title=Work&username=abcd&password=1234&cc=US&city=Miami
|
||||
let url;
|
||||
try { url = new URL(item); }
|
||||
catch (error) {
|
||||
alert(`${error}\n\n${item}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// convert old schemes to type
|
||||
let type = url.protocol.slice(0, -1);
|
||||
const scheme = {
|
||||
proxy: 'http',
|
||||
ssl: 'https',
|
||||
socks: 'socks5',
|
||||
};
|
||||
scheme[type] && (type = scheme[type]);
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1851426
|
||||
// Reland URL: protocol setter needs to be more restrictive around file (fixed in Firefox 120)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1603699
|
||||
// Enable DefaultURI use for unknown schemes (fixed in Firefox 122)
|
||||
// missing hostname, port with socks protocol (#120)
|
||||
!url.hostname && (url = new URL('http:' + item.substring(url.protocol.length)));
|
||||
|
||||
const {hostname, port, username, password} = url;
|
||||
// set to pram, can be overridden in searchParams
|
||||
const pram = {type, hostname, port, username, password};
|
||||
|
||||
// prepare object, make parameter keys case-insensitive
|
||||
for (const [key, value] of url.searchParams) {
|
||||
pram[key.toLowerCase()] = value;
|
||||
}
|
||||
|
||||
// fix missing default port
|
||||
const defaultPort = {
|
||||
http: '80',
|
||||
https: '443',
|
||||
ws: '80',
|
||||
wss: '443'
|
||||
};
|
||||
!pram.port && defaultPort[type] && (pram.port = defaultPort[type]);
|
||||
|
||||
// proxy template
|
||||
const pxy = {
|
||||
// defaults to true
|
||||
active: pram.active !== 'false',
|
||||
title: pram.title || '',
|
||||
type: pram.type.toLowerCase(),
|
||||
hostname: pram.hostname,
|
||||
port: pram.port,
|
||||
username: decodeURIComponent(pram.username),
|
||||
password: decodeURIComponent(pram.password),
|
||||
cc: (pram.cc || pram.countrycode || '').toUpperCase(),
|
||||
city: pram.city || '',
|
||||
color: pram.color ? '#' + pram.color : '',
|
||||
pac: pram.pac || (pram.type === 'pac' && url.origin + url.pathname) || '',
|
||||
pacString: '',
|
||||
// defaults to true
|
||||
proxyDNS: pram.proxydns !== 'false',
|
||||
include: [],
|
||||
exclude: [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
return pxy;
|
||||
}
|
||||
}
|
||||
43
foxyproxy/content/import-older.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import {App} from './app.js';
|
||||
import {Migrate} from './migrate.js';
|
||||
|
||||
// ---------- Import Older Preferences ---------------------
|
||||
export class ImportOlder {
|
||||
|
||||
// pref references the same object in the memory and its value gets updated
|
||||
static init(pref, callback) {
|
||||
this.callback = callback;
|
||||
document.querySelector('.import-from-older input').addEventListener('change', e => this.process(e, pref));
|
||||
}
|
||||
|
||||
static process(e, pref) {
|
||||
const file = e.target.files[0];
|
||||
switch (true) {
|
||||
case !file: App.notify(browser.i18n.getMessage('error')); return;
|
||||
// check file MIME type
|
||||
case !['text/plain', 'application/json'].includes(file.type):
|
||||
App.notify(browser.i18n.getMessage('fileTypeError'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => this.parseJSON(reader.result, pref);
|
||||
reader.onerror = () => App.notify(browser.i18n.getMessage('fileReadError'));
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
static parseJSON(data, pref) {
|
||||
try { data = JSON.parse(data); }
|
||||
catch {
|
||||
// display the error
|
||||
App.notify(browser.i18n.getMessage('fileParseError'));
|
||||
return;
|
||||
}
|
||||
|
||||
data = Object.hasOwn(data, 'settings') ? Migrate.convert3(data) : Migrate.convert7(data);
|
||||
// update pref with the saved version
|
||||
Object.keys(pref).forEach(i => Object.hasOwn(data, i) && (pref[i] = data[i]));
|
||||
|
||||
this.callback();
|
||||
}
|
||||
}
|
||||
38
foxyproxy/content/import-url.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import {App} from './app.js';
|
||||
import {Spinner} from './spinner.js';
|
||||
|
||||
// ---------- Import from URL ------------------------------
|
||||
export class ImportUrl {
|
||||
|
||||
static {
|
||||
this.input = document.querySelector('.import-from-url input');
|
||||
}
|
||||
|
||||
// pref references the same object in the memory and its value gets updated
|
||||
static init(pref, callback) {
|
||||
this.callback = callback;
|
||||
document.querySelector('.import-from-url button').addEventListener('click', () => this.process(pref));
|
||||
}
|
||||
|
||||
static process(pref) {
|
||||
this.input.value = this.input.value.trim();
|
||||
if (!this.input.value) { return; }
|
||||
|
||||
Spinner.show();
|
||||
|
||||
// --- fetch data
|
||||
fetch(this.input.value)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// update pref with the saved version
|
||||
Object.keys(pref).forEach(i => Object.hasOwn(data, i) && (pref[i] = data[i]));
|
||||
|
||||
this.callback();
|
||||
Spinner.hide();
|
||||
})
|
||||
.catch(error => {
|
||||
App.notify(browser.i18n.getMessage('error') + '\n\n' + error.message);
|
||||
Spinner.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
16
foxyproxy/content/incognito-access.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- Incognito Access (Side Effect) ---------------
|
||||
class IncognitoAccess {
|
||||
|
||||
static {
|
||||
// https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/proxy/settings
|
||||
// Changing proxy settings requires private browsing window access because proxy settings affect private and non-private windows.
|
||||
// https://github.com/w3c/webextensions/issues/429
|
||||
// Inconsistency: incognito in proxy.settings
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1725981
|
||||
// proxy.settings is not supported on Android
|
||||
App.firefox && !App.android && browser.extension.isAllowedIncognitoAccess()
|
||||
.then(response => !response && alert(browser.i18n.getMessage('incognitoAccessError')));
|
||||
}
|
||||
}
|
||||
262
foxyproxy/content/location.js
Normal file
@@ -0,0 +1,262 @@
|
||||
export class Location {
|
||||
|
||||
static get(cc = '') {
|
||||
return this.countryCode[cc] || '';
|
||||
}
|
||||
|
||||
// ISO 3166-1 country code
|
||||
static countryCode = {
|
||||
AA: "",
|
||||
AD: "Andorra",
|
||||
AE: "United Arab Emirates",
|
||||
AF: "Afghanistan",
|
||||
AG: "Antigua and Barbuda",
|
||||
AI: "Anguilla",
|
||||
AL: "Albania",
|
||||
AM: "Armenia",
|
||||
AO: "Angola",
|
||||
AQ: "Antarctica",
|
||||
AR: "Argentina",
|
||||
AS: "American Samoa",
|
||||
AT: "Austria",
|
||||
AU: "Australia",
|
||||
AW: "Aruba",
|
||||
AX: "Åland Islands",
|
||||
AZ: "Azerbaijan",
|
||||
BA: "Bosnia and Herzegovina",
|
||||
BB: "Barbados",
|
||||
BD: "Bangladesh",
|
||||
BE: "Belgium",
|
||||
BF: "Burkina Faso",
|
||||
BG: "Bulgaria",
|
||||
BH: "Bahrain",
|
||||
BI: "Burundi",
|
||||
BJ: "Benin",
|
||||
BL: "Saint Barthélemy",
|
||||
BM: "Bermuda",
|
||||
BN: "Brunei Darussalam",
|
||||
BO: "Bolivia, Plurinational State of",
|
||||
BQ: "Bonaire, Sint Eustatius and Saba",
|
||||
BR: "Brazil",
|
||||
BS: "Bahamas",
|
||||
BT: "Bhutan",
|
||||
BV: "Bouvet Island",
|
||||
BW: "Botswana",
|
||||
BY: "Belarus",
|
||||
BZ: "Belize",
|
||||
CA: "Canada",
|
||||
CC: "Cocos [Keeling] Islands",
|
||||
CD: "Congo, the Democratic Republic of the",
|
||||
CF: "Central African Republic",
|
||||
CG: "Congo, Republic of the",
|
||||
CH: "Switzerland",
|
||||
CI: "Ivory Coast (Côte d'Ivoire)",
|
||||
CK: "Cook Islands",
|
||||
CL: "Chile",
|
||||
CM: "Cameroon",
|
||||
CN: "China",
|
||||
CO: "Colombia",
|
||||
CR: "Costa Rica",
|
||||
CU: "Cuba",
|
||||
CV: "Cabo Verde",
|
||||
CW: "Curaçao",
|
||||
CX: "Christmas Island",
|
||||
CY: "Cyprus",
|
||||
CZ: "Czechia",
|
||||
DE: "Germany",
|
||||
DJ: "Djibouti",
|
||||
DK: "Denmark",
|
||||
DM: "Dominica",
|
||||
DO: "Dominican Republic",
|
||||
DZ: "Algeria",
|
||||
EC: "Ecuador",
|
||||
EE: "Estonia",
|
||||
EG: "Egypt",
|
||||
EH: "Western Sahara",
|
||||
ER: "Eritrea",
|
||||
ES: "Spain",
|
||||
ET: "Ethiopia",
|
||||
EU: "European Union",
|
||||
FI: "Finland",
|
||||
FJ: "Fiji",
|
||||
FK: "Falkland Islands (Malvinas)",
|
||||
FM: "Micronesia, Federated States of",
|
||||
FO: "Faroe Islands",
|
||||
FR: "France",
|
||||
GA: "Gabon",
|
||||
GB: "United Kingdom",
|
||||
GD: "Grenada",
|
||||
GE: "Georgia",
|
||||
GF: "French Guiana",
|
||||
GG: "Guernsey",
|
||||
GH: "Ghana",
|
||||
GI: "Gibraltar",
|
||||
GL: "Greenland",
|
||||
GM: "Gambia",
|
||||
GN: "Guinea",
|
||||
GP: "Guadeloupe",
|
||||
GQ: "Equatorial Guinea",
|
||||
GR: "Greece",
|
||||
GS: "South Georgia and the South Sandwich Islands",
|
||||
GT: "Guatemala",
|
||||
GU: "Guam",
|
||||
GW: "Guinea-Bissau",
|
||||
GY: "Guyana",
|
||||
HK: "Hong Kong",
|
||||
HM: "Heard Island and McDonald Islands",
|
||||
HN: "Honduras",
|
||||
HR: "Croatia",
|
||||
HT: "Haiti",
|
||||
HU: "Hungary",
|
||||
ID: "Indonesia",
|
||||
IE: "Ireland",
|
||||
IL: "Israel",
|
||||
IM: "Isle of Man",
|
||||
IN: "India",
|
||||
IO: "British Indian Ocean Territory",
|
||||
IQ: "Iraq",
|
||||
IR: "Iran, Islamic Republic Of",
|
||||
IS: "Iceland",
|
||||
IT: "Italy",
|
||||
JE: "Jersey",
|
||||
JM: "Jamaica",
|
||||
JO: "Jordan (Hashemite Kingdom of Jordan)",
|
||||
JP: "Japan",
|
||||
KE: "Kenya",
|
||||
KG: "Kyrgyzstan",
|
||||
KH: "Cambodia",
|
||||
KI: "Kiribati",
|
||||
KM: "Comoros",
|
||||
KN: "St Kitts and Nevis",
|
||||
KP: "North Korea",
|
||||
KR: "South Korea",
|
||||
KW: "Kuwait",
|
||||
KY: "Cayman Islands",
|
||||
KZ: "Kazakhstan",
|
||||
LA: "Laos (Lao People's Democratic Republic)",
|
||||
LB: "Lebanon",
|
||||
LC: "Saint Lucia",
|
||||
LI: "Liechtenstein",
|
||||
LK: "Sri Lanka",
|
||||
LR: "Liberia",
|
||||
LS: "Lesotho",
|
||||
LT: "Republic of Lithuania",
|
||||
LU: "Luxembourg",
|
||||
LV: "Latvia",
|
||||
LY: "Libya",
|
||||
MA: "Morocco",
|
||||
MC: "Monaco",
|
||||
MD: "Moldova, Republic of",
|
||||
ME: "Montenegro",
|
||||
MF: "Saint Martin (French part)",
|
||||
MG: "Madagascar",
|
||||
MH: "Marshall Islands",
|
||||
MK: "North Macedonia",
|
||||
ML: "Mali",
|
||||
MM: "Myanmar",
|
||||
MN: "Mongolia",
|
||||
MO: "Macao",
|
||||
MP: "Northern Mariana Islands",
|
||||
MQ: "Martinique",
|
||||
MR: "Mauritania",
|
||||
MS: "Montserrat",
|
||||
MT: "Malta",
|
||||
MU: "Mauritius",
|
||||
MV: "Maldives",
|
||||
MW: "Malawi",
|
||||
MX: "Mexico",
|
||||
MY: "Malaysia",
|
||||
MZ: "Mozambique",
|
||||
NA: "Namibia",
|
||||
NC: "New Caledonia",
|
||||
NE: "Niger",
|
||||
NF: "Norfolk Island",
|
||||
NG: "Nigeria",
|
||||
NI: "Nicaragua",
|
||||
NL: "Netherlands",
|
||||
NO: "Norway",
|
||||
NP: "Nepal",
|
||||
NR: "Nauru",
|
||||
NU: "Niue",
|
||||
NZ: "New Zealand",
|
||||
OM: "Oman",
|
||||
PA: "Panama",
|
||||
PE: "Peru",
|
||||
PF: "French Polynesia",
|
||||
PG: "Papua New Guinea",
|
||||
PH: "Philippines",
|
||||
PK: "Pakistan",
|
||||
PL: "Poland",
|
||||
PM: "Saint Pierre and Miquelon",
|
||||
PN: "Pitcairn Islands",
|
||||
PR: "Puerto Rico",
|
||||
PS: "Palestine",
|
||||
PT: "Portugal",
|
||||
PW: "Palau",
|
||||
PY: "Paraguay",
|
||||
QA: "Qatar",
|
||||
RE: "Réunion",
|
||||
RO: "Romania",
|
||||
RS: "Serbia",
|
||||
RU: "Russia (Russian Federation)",
|
||||
RW: "Rwanda",
|
||||
SA: "Saudi Arabia",
|
||||
SB: "Solomon Islands",
|
||||
SC: "Seychelles",
|
||||
SD: "Sudan",
|
||||
SE: "Sweden",
|
||||
SG: "Singapore",
|
||||
SH: "Saint Helena",
|
||||
SI: "Slovenia",
|
||||
SJ: "Svalbard and Jan Mayen",
|
||||
SK: "Slovakia",
|
||||
SL: "Sierra Leone",
|
||||
SM: "San Marino",
|
||||
SN: "Senegal",
|
||||
SO: "Somalia",
|
||||
SR: "Suriname",
|
||||
SS: "South Sudan",
|
||||
ST: "São Tomé and Príncipe",
|
||||
SV: "El Salvador",
|
||||
SX: "Sint Maarten (Dutch part)",
|
||||
SY: "Syria",
|
||||
SZ: "Eswatini",
|
||||
TC: "Turks and Caicos Islands",
|
||||
TD: "Chad",
|
||||
TF: "French Southern Territories",
|
||||
TG: "Togo",
|
||||
TH: "Thailand",
|
||||
TJ: "Tajikistan",
|
||||
TK: "Tokelau",
|
||||
TL: "Democratic Republic of Timor-Leste",
|
||||
TM: "Turkmenistan",
|
||||
TN: "Tunisia",
|
||||
TO: "Tonga",
|
||||
TR: "Türkiye",
|
||||
TT: "Trinidad and Tobago",
|
||||
TV: "Tuvalu",
|
||||
TW: "Taiwan",
|
||||
TZ: "Tanzania",
|
||||
UA: "Ukraine",
|
||||
UG: "Uganda",
|
||||
UM: "U.S. Minor Outlying Islands",
|
||||
US: "United States of America",
|
||||
UY: "Uruguay",
|
||||
UZ: "Uzbekistan",
|
||||
VA: "Vatican City",
|
||||
VC: "Saint Vincent and the Grenadines",
|
||||
VE: "Venezuela",
|
||||
VG: "British Virgin Islands",
|
||||
VI: "U.S. Virgin Islands",
|
||||
VN: "Vietnam",
|
||||
VU: "Vanuatu",
|
||||
WF: "Wallis and Futuna",
|
||||
WS: "Samoa",
|
||||
XK: "Kosovo",
|
||||
YE: "Yemen",
|
||||
YT: "Mayotte",
|
||||
ZA: "South Africa",
|
||||
ZM: "Zambia",
|
||||
ZW: "Zimbabwe",
|
||||
};
|
||||
}
|
||||
114
foxyproxy/content/log.css
Normal file
@@ -0,0 +1,114 @@
|
||||
/* ----- Log ----- */
|
||||
/* ----- Light Theme ----- */
|
||||
:root {
|
||||
--mark: #080;
|
||||
}
|
||||
|
||||
/* ----- Dark Theme ----- */
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--mark: #0c0;
|
||||
}
|
||||
}
|
||||
|
||||
section.log {
|
||||
padding: 0 0.5em;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.log .domain {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
gap: 0.5em;
|
||||
place-content: end;
|
||||
}
|
||||
|
||||
.log table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
background-color: var(--bg);
|
||||
}
|
||||
|
||||
.log thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.log thead th {
|
||||
color: #fff;
|
||||
background-color: #999;
|
||||
padding: 0.5em 0.2em;
|
||||
font-size: 0.9em;
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.log thead th img {
|
||||
width: 1em;
|
||||
vertical-align: unset;
|
||||
}
|
||||
|
||||
.log tr:hover {
|
||||
background-color: var(--hover) !important;
|
||||
}
|
||||
|
||||
.log tr:has(td[title*="407 Proxy"]) {
|
||||
color: var(--mark);
|
||||
}
|
||||
|
||||
.log tbody tr:nth-of-type(even) {
|
||||
background-color: var(--tr);
|
||||
}
|
||||
|
||||
.log td {
|
||||
font-size: 0.8em;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.log td:nth-of-type(6),
|
||||
.log td:nth-of-type(7),
|
||||
.log td:nth-of-type(12) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
.log td:nth-of-type(7) {
|
||||
max-width: 40em;
|
||||
}
|
||||
|
||||
.log td:nth-of-type(8) {
|
||||
border-left: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.log td:nth-of-type(12) {
|
||||
max-width: 5em;
|
||||
}
|
||||
|
||||
.log td.incognito::before {
|
||||
content: '';
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background: url('../image/privateBrowsing.svg') no-repeat center / contain;
|
||||
}
|
||||
|
||||
.log tbody {
|
||||
counter-reset: n;
|
||||
}
|
||||
|
||||
.log tbody tr td:first-child::before {
|
||||
display: inline-block;
|
||||
color: #aaa;
|
||||
min-width: 1.5em;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.4em;
|
||||
pointer-events: none;
|
||||
counter-increment: n;
|
||||
content: counter(n);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
146
foxyproxy/content/log.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import {Flag} from './flag.js';
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Popup} from './options-popup.js';
|
||||
import {Nav} from './nav.js';
|
||||
|
||||
export class Log {
|
||||
|
||||
static {
|
||||
this.trTemplate = document.querySelector('.log template').content.firstElementChild;
|
||||
this.tbody = document.querySelector('.log tbody');
|
||||
// used to find proxy
|
||||
this.proxyCache = {};
|
||||
this.mode = 'disable';
|
||||
|
||||
browser.webRequest.onBeforeRequest.addListener(e => this.process(e), {urls: ['<all_urls>']});
|
||||
|
||||
// onAuthRequired message from authentication.js
|
||||
browser.runtime.onMessage.addListener((...e) => this.onMessage(...e));
|
||||
|
||||
// Get Associated Domains
|
||||
this.input = document.querySelector('.log input');
|
||||
document.querySelector('.log button').addEventListener('click', () => this.getDomains());
|
||||
this.select = document.querySelector('.popup select.popup-log-proxy');
|
||||
this.select.addEventListener('change', () => this.addPatterns());
|
||||
}
|
||||
|
||||
static onMessage(message) {
|
||||
const {id, e} = message;
|
||||
if (id !== 'onAuthRequired') { return; }
|
||||
|
||||
const tr = this.tbody.children[199] || this.trTemplate.cloneNode(true);
|
||||
const [, time, container, method, reqType, doc, url, title, type, host, port, pattern] = tr.children;
|
||||
|
||||
time.textContent = new Date(e.timeStamp).toLocaleTimeString();
|
||||
container.classList.toggle('incognito', !!e.incognito);
|
||||
container.textContent = e.cookieStoreId?.startsWith('firefox-container-') ? 'C' + e.cookieStoreId.substring(18) : '';
|
||||
method.textContent = e.method;
|
||||
reqType.textContent = e.statusCode;
|
||||
this.prepareOverflow(doc, e.statusLine || '');
|
||||
this.prepareOverflow(url, decodeURIComponent(e.url));
|
||||
|
||||
const info = e.challenger || {host: '', port: ''};
|
||||
const item = this.proxyCache[`${info.host}:${info.port}`];
|
||||
const flag = item?.cc ? Flag.get(item.cc) + ' ' : '';
|
||||
title.textContent = flag + (item?.title || '');
|
||||
title.style.borderLeftColor = item?.color || 'var(--border)';
|
||||
type.textContent = item?.type || '';
|
||||
host.textContent = info.host;
|
||||
port.textContent = info.port;
|
||||
pattern.textContent = '';
|
||||
|
||||
// in reverse order, new on top
|
||||
this.tbody.prepend(tr);
|
||||
}
|
||||
|
||||
static process(e) {
|
||||
const tr = this.tbody.children[199] || this.trTemplate.cloneNode(true);
|
||||
const [, time, container, method, reqType, doc, url, title, type, host, port, pattern] = tr.children;
|
||||
|
||||
// shortened forms similar to Developer Tools
|
||||
const shortType = {
|
||||
'main_frame': 'html',
|
||||
'sub_frame': 'iframe',
|
||||
image: 'img',
|
||||
script: 'js',
|
||||
stylesheet: 'css',
|
||||
websocket: 'ws',
|
||||
xmlhttprequest: 'xhr',
|
||||
};
|
||||
|
||||
time.textContent = new Date(e.timeStamp).toLocaleTimeString();
|
||||
container.classList.toggle('incognito', !!e.incognito);
|
||||
container.textContent = e.cookieStoreId?.startsWith('firefox-container-') ? 'C' + e.cookieStoreId.substring(18) : '';
|
||||
method.textContent = e.method;
|
||||
reqType.textContent = shortType[e.type] || e.type;
|
||||
// For a top-level document, documentUrl is undefined, chrome uses e.initiator
|
||||
this.prepareOverflow(doc, e.documentUrl || e.initiator || '');
|
||||
this.prepareOverflow(url, decodeURIComponent(e.url));
|
||||
|
||||
const info = e.proxyInfo || {host: '', port: '', type: ''};
|
||||
const item = this.proxyCache[`${info.host}:${info.port}`];
|
||||
const flag = item?.cc ? Flag.get(item.cc) + ' ' : '';
|
||||
title.textContent = flag + (item?.title || '');
|
||||
title.style.borderLeftColor = item?.color || 'var(--border)';
|
||||
type.textContent = info.type;
|
||||
host.textContent = info.host;
|
||||
port.textContent = info.port;
|
||||
|
||||
// show matching pattern in pattern mode
|
||||
const pat = this.mode === 'pattern' && item?.include.find(i => new RegExp(Pattern.get(i.pattern, i.type), 'i').test(e.url));
|
||||
const text = pat?.title || pat?.pattern || '';
|
||||
this.prepareOverflow(pattern, text);
|
||||
|
||||
// in reverse order, new on top
|
||||
this.tbody.prepend(tr);
|
||||
}
|
||||
|
||||
// set title, in case text overflows
|
||||
static prepareOverflow(elem, value) {
|
||||
elem.textContent = value;
|
||||
elem.title = value;
|
||||
}
|
||||
|
||||
static getDomains() {
|
||||
this.select.classList.add('on');
|
||||
const input = this.input.value.trim();
|
||||
if (!input) {
|
||||
// allow showing empty popup
|
||||
Popup.show('');
|
||||
return;
|
||||
}
|
||||
|
||||
// search Document URL column
|
||||
let list = document.querySelectorAll(`.log table td:nth-child(6)[title*="${input}" i]`);
|
||||
list = [...list].map(i => i.nextElementSibling.title.split(/\/+/)[1]);
|
||||
list = [...new Set(list)].sort();
|
||||
|
||||
// true -> show select
|
||||
Popup.show(list.join('\n'));
|
||||
}
|
||||
|
||||
static addPatterns() {
|
||||
const host = this.select.value;
|
||||
if (!host) { return; }
|
||||
|
||||
const text = this.select.previousElementSibling.value.trim();
|
||||
if (!text) { return; }
|
||||
|
||||
const title = this.input.value.trim();
|
||||
const data = text.split(/\s+/).map(i => ({
|
||||
include: 'include',
|
||||
type: 'wildcard',
|
||||
title,
|
||||
pattern: `://${i}/`,
|
||||
active: true
|
||||
}));
|
||||
|
||||
Popup.hide();
|
||||
Nav.get('proxies');
|
||||
|
||||
const ev = new CustomEvent('importPatternCustom', {
|
||||
detail: {host, data}
|
||||
});
|
||||
window.dispatchEvent(ev);
|
||||
}
|
||||
}
|
||||
103
foxyproxy/content/menus.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// Using contextMenus namespace for compatibility with Chrome
|
||||
// It's not possible to create tools menu items (contexts: ["tools_menu"]) using the contextMenus namespace.
|
||||
|
||||
import {App} from './app.js';
|
||||
import {Proxy} from './proxy.js';
|
||||
import {OnRequest} from './on-request.js';
|
||||
import {Flag} from './flag.js';
|
||||
|
||||
// ---------- Context Menu ---------------------------------
|
||||
export class Menus {
|
||||
|
||||
static {
|
||||
// contextMenus is not supported on Android
|
||||
browser.contextMenus?.onClicked.addListener((...e) => this.process(...e));
|
||||
this.data = [];
|
||||
}
|
||||
|
||||
static init(pref) {
|
||||
// not available on Android
|
||||
if (!browser.contextMenus) { return; }
|
||||
|
||||
this.pref = pref;
|
||||
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/common/extensions/api/context_menus.json
|
||||
// chrome.contextMenus not promise yet -> Uncaught TypeError: Cannot read properties of undefined (reading 'then')
|
||||
browser.contextMenus.removeAll(() => this.addMenus(pref.data));
|
||||
}
|
||||
|
||||
static addMenus(data) {
|
||||
// not for PAC, limit to 10
|
||||
this.data = data.filter(i => i.active && i.type !== 'pac').slice(0, 10);
|
||||
if (!this.data[0]) { return; }
|
||||
|
||||
// --- create contextMenus
|
||||
// https://searchfox.org/mozilla-central/source/browser/components/extensions/parent/ext-menus.js#756
|
||||
// https://searchfox.org/mozilla-central/source/browser/components/extensions/parent/ext-menus.js#625-636
|
||||
// contexts defaults to ['page'], 'all' is also added in Firefox but not in Chrome
|
||||
// https://github.com/w3c/webextensions/issues/774
|
||||
// Inconsistency: contextMenus/Menus
|
||||
// child menu inherits parent's contexts but chrome has a problem with inheriting in "action" contextMenus
|
||||
const {basic, firefox} = App;
|
||||
const allowedPattern = !basic && !this.pref.managed;
|
||||
const documentUrlPatterns = ['http://*/*', 'https://*/*'];
|
||||
// menus.create requires an id for non-persistent background scripts.
|
||||
this.contextMenus = [
|
||||
...(allowedPattern ? [{id: 'includeHost', documentUrlPatterns}] : []),
|
||||
...(allowedPattern ? [{id: 'excludeHost', documentUrlPatterns}] : []),
|
||||
...(allowedPattern && firefox ? [{id: 'sep', type: 'separator', documentUrlPatterns}] : []),
|
||||
...(firefox ? [{id: 'tabProxy'}] : []),
|
||||
...(firefox ? [{parentId: 'tabProxy', id: 'tabProxy' + this.data.length, title: '\u00A0'}] : []),
|
||||
...(firefox ? [{id: 'openLinkTabProxy', contexts: ['link']}] : []),
|
||||
];
|
||||
|
||||
allowedPattern && this.addProxies('includeHost');
|
||||
allowedPattern && this.addProxies('excludeHost');
|
||||
firefox && this.addProxies('tabProxy');
|
||||
firefox && this.addProxies('openLinkTabProxy');
|
||||
|
||||
this.contextMenus.forEach(i => {
|
||||
// always use the same ID for i18n
|
||||
i.type !== 'separator' && (i.title ||= browser.i18n.getMessage(i.id));
|
||||
// add contexts
|
||||
// !i.parentId && (i.contexts ||= ['all']);
|
||||
i.contexts ||= ['all'];
|
||||
|
||||
browser.contextMenus.create(i);
|
||||
});
|
||||
}
|
||||
|
||||
static addProxies(parentId) {
|
||||
this.data.forEach((i, index) =>
|
||||
this.contextMenus.push({
|
||||
parentId,
|
||||
id: parentId + index,
|
||||
title: Flag.get(i.cc) + ' ' + (i.title || `${i.hostname}:${i.port}`)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
static async process(info, tab) {
|
||||
const pref = this.pref;
|
||||
const id = info.parentMenuItemId;
|
||||
const index = info.menuItemId.substring(id.length);
|
||||
const proxy = this.data[index];
|
||||
switch (id) {
|
||||
case 'includeHost':
|
||||
case 'excludeHost':
|
||||
Proxy.includeHost(pref, proxy, tab, id);
|
||||
break;
|
||||
|
||||
// --- firefox only
|
||||
case 'setTabProxy':
|
||||
OnRequest.setTabProxy(tab, proxy);
|
||||
break;
|
||||
|
||||
case 'openLinkTabProxy':
|
||||
tab = await browser.tabs.create({});
|
||||
OnRequest.setTabProxy(tab, proxy);
|
||||
browser.tabs.update(tab.id, {url: info.linkUrl});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
303
foxyproxy/content/migrate.js
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
----- Patterns -----
|
||||
Chrome FoxyProxy v3 (old) featured full/partial url match pattern
|
||||
Firefox FoxyProxy v4 - v7 (old) featured host only match pattern
|
||||
- Migrating v4 - v7 storage data to full/partial url match pattern
|
||||
- Dropping select for http|https|all
|
||||
|
||||
----- SOCKS keyword -----
|
||||
https://developer.chrome.com/docs/extensions/reference/proxy/#proxy-rules
|
||||
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
|
||||
https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md#http-proxy-scheme
|
||||
Chrome PAC schemes PROXY | HTTPS | SOCKS4/SOCKS | SOCKS5
|
||||
Chrome API schemes http | https | socks4 | socks5 | quic
|
||||
|
||||
Firefox PAC schemes PROXY/HTTP | HTTPS | SOCKS4/SOCKS | SOCKS5
|
||||
Firefox API types http | https | socks4 | socks (means socks5) | direct
|
||||
|
||||
Firefox/Chrome PAC 'SOCKS' means SOCKS4
|
||||
Firefox API 'SOCKS' means SOCKS5
|
||||
Code uses PAC 'SOCKS4/5' but converts to socks in Firefox API
|
||||
|
||||
----- host/hostname keywords -----
|
||||
Firefox/Chrome API/PAC use 'host' for domain/ip
|
||||
JavaScript uses 'hostname' for for domain/ip & 'host' for domain:port/ip:port
|
||||
Code uses JavaScript 'hostname' domain/ip
|
||||
*/
|
||||
|
||||
import {App} from './app.js';
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Color} from './color.js';
|
||||
|
||||
/*
|
||||
Chrome v3 (old) encrypts username/passwords using CryptoJS 3.1.2
|
||||
CryptoJS library is used to migrate preferences to v8.0 but will be removed in future upgrades
|
||||
Original CryptoJS 3.1.2 aes.js https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js
|
||||
`export {CryptoJS};` was added to be able to import as ES6 module
|
||||
*/
|
||||
// removed in v9.0
|
||||
// import {CryptoJS} from '../lib/aes.3.1.2.js';
|
||||
|
||||
export class Migrate {
|
||||
|
||||
static async init(pref) {
|
||||
// --- 8.9
|
||||
// 8.9 remove showPatternProxy (from 8.7)
|
||||
// 8.8 tidy up left-over obj Sync typo mistake (from 8.0)
|
||||
// 8.7 change global proxyDNS to per-proxy (from 8.0)
|
||||
if (Object.hasOwn(pref, 'proxyDNS') && pref.data) {
|
||||
pref.data.forEach(i => i.proxyDNS = !!pref.proxyDNS);
|
||||
await browser.storage.local.set(pref);
|
||||
}
|
||||
// 8.1 remove globalExcludeWildcard, globalExcludeRegex (from 8.0)
|
||||
const keys = ['showPatternProxy', 'obj', 'proxyDNS', 'globalExcludeWildcard', 'globalExcludeRegex'];
|
||||
keys.forEach(i => delete pref[i]);
|
||||
await browser.storage.local.remove(keys);
|
||||
await browser.storage.sync.remove(keys);
|
||||
|
||||
// --- 8.0
|
||||
if (pref.data) { return; }
|
||||
|
||||
let db = {};
|
||||
switch (true) {
|
||||
case !Object.keys(pref)[0]:
|
||||
db = App.getDefaultPref();
|
||||
break;
|
||||
|
||||
case Object.hasOwn(pref, 'settings'):
|
||||
db = this.convert3(pref);
|
||||
break;
|
||||
|
||||
default:
|
||||
db = this.convert7(pref);
|
||||
}
|
||||
|
||||
if (Object.keys(pref)[0]) {
|
||||
// clear pref
|
||||
Object.keys(pref).forEach(i => delete pref[i]);
|
||||
await browser.storage.local.clear();
|
||||
}
|
||||
|
||||
// populate pref
|
||||
Object.keys(db).forEach(i => pref[i] = db[i]);
|
||||
|
||||
// --- update database
|
||||
await browser.storage.local.set(pref);
|
||||
// return pref;
|
||||
}
|
||||
|
||||
// static decrypt(str, key) {
|
||||
// return CryptoJS.AES.decrypt(str, key).toString(CryptoJS.enc.Utf8).split(/(?<!\\):/).map(i => i.replace(/\\:/g, ':'));
|
||||
// }
|
||||
|
||||
// --- Chrome v3
|
||||
static convert3(pref) {
|
||||
// https://groups.google.com/a/chromium.org/g/chromium-extensions/c/6qiMo0P-XS4
|
||||
// mode in v3 was saved to localStorage and not accessible in MV3 service worker
|
||||
|
||||
// new database format
|
||||
const db = App.getDefaultPref();
|
||||
db.sync = !!pref?.settings?.useSyncStorage;
|
||||
|
||||
// CryptoJS key
|
||||
// const sk = pref.settings.sk;
|
||||
pref.proxyList?.forEach(key => {
|
||||
const item = pref[key].data;
|
||||
// skip
|
||||
if (key === 'default' || !item?.patterns) { return; }
|
||||
// convert to actual type: http | https | socks4 | socks5 | + PAC
|
||||
// default HTTP (no HTTPS option in FP Chrome v3)
|
||||
const type = item.isSocks ? (item.socks === '5' ? 'socks5' : 'socks4') :
|
||||
(item.type === 'auto' ? 'pac' : 'http');
|
||||
|
||||
// removed in v9.0
|
||||
// decrypt username, password
|
||||
// const [username = '', password = ''] = sk ? this.decrypt(item.credentials, sk) : [];
|
||||
|
||||
// proxy template
|
||||
const pxy = {
|
||||
active: item.enabled,
|
||||
title: item.name || '',
|
||||
type,
|
||||
// rename to hostname
|
||||
hostname: item.host,
|
||||
port: item.port,
|
||||
username: '',
|
||||
password: '',
|
||||
// remove country, use CC in location.js
|
||||
cc: '',
|
||||
city: '',
|
||||
color: item.color || Color.getRandom(),
|
||||
pac: type === 'pac' ? item.configUrl : '',
|
||||
pacString: '',
|
||||
proxyDNS: true,
|
||||
include: [],
|
||||
exclude: [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
// process include/exclude
|
||||
item.patterns.forEach(elem => {
|
||||
const p = elem.data;
|
||||
// skip
|
||||
if (!p?.type) { return; }
|
||||
|
||||
const pat = {
|
||||
active: true,
|
||||
// v3 keep patterns as they are
|
||||
pattern: p.url,
|
||||
title: p.name || '',
|
||||
// uses wildcard | regexp -> change to regex
|
||||
type: p.type === 'wildcard' ? 'wildcard' : 'regex'
|
||||
};
|
||||
|
||||
// Validate RegExp, deactivate on error
|
||||
!Pattern.validate(pat.pattern, pat.type) && (pat.active = false);
|
||||
|
||||
// whitelist: Inclusive/Exclusive
|
||||
p.whitelist === 'Inclusive' ? pxy.include.push(pat) : pxy.exclude.push(pat);
|
||||
});
|
||||
|
||||
db.data.push(pxy);
|
||||
});
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
// --- Firefox v6-7 (also used in Options -> Import Older Export)
|
||||
static convert7(pref) {
|
||||
const typeSet = {
|
||||
1: 'http', // PROXY_TYPE_HTTP
|
||||
2: 'https', // PROXY_TYPE_HTTPS
|
||||
3: 'socks5', // PROXY_TYPE_SOCKS5
|
||||
4: 'socks4', // PROXY_TYPE_SOCKS4
|
||||
5: 'direct' // PROXY_TYPE_NONE
|
||||
};
|
||||
|
||||
// new database format
|
||||
pref.mode ||= 'disable';
|
||||
// rename disabled -> disable
|
||||
pref.mode === 'disabled' && (pref.mode = 'disable');
|
||||
// rename patterns -> pattern
|
||||
pref.mode === 'patterns' && (pref.mode = 'pattern');
|
||||
// convert old mode
|
||||
if (pref[pref.mode]) {
|
||||
const i = pref[pref.mode];
|
||||
pref.mode = `${i.address}:${i.port}`;
|
||||
}
|
||||
|
||||
const db = App.getDefaultPref();
|
||||
db.sync = !!pref.sync;
|
||||
|
||||
// null value causes an error in hasOwn, direct proxies don't have 'address'
|
||||
const data = Object.values(pref).filter(i => i && ['address', 'type'].some(p => Object.hasOwn(i, p)));
|
||||
|
||||
// sort by index
|
||||
data.sort((a, b) => a.index - b.index);
|
||||
|
||||
data.forEach(i => {
|
||||
// proxy template
|
||||
const pxy = {
|
||||
// convert to boolean, some old databases have mixed types
|
||||
active: i.active === 'true' || i.active === true,
|
||||
title: i.title || '',
|
||||
// convert to actual type: http | https | socks4 | socks5 | direct | + add PAC
|
||||
type: typeSet[i.type],
|
||||
// rename to hostname
|
||||
hostname: i.address || '',
|
||||
port: i.port || '',
|
||||
username: i.username || '',
|
||||
password: i.password || '',
|
||||
// remove country, use CC in location.js
|
||||
cc: i.cc || '',
|
||||
city: '',
|
||||
color: i.color || Color.getRandom(),
|
||||
// add PAC option
|
||||
pac: '',
|
||||
pacString: '',
|
||||
proxyDNS: !!i.proxyDNS,
|
||||
// rename to include
|
||||
include: i.whitePatterns || [],
|
||||
// rename to exclude
|
||||
exclude: i.blackPatterns || [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
// convert UK to ISO 3166-1 GB
|
||||
pxy.cc === 'UK' && (pxy.cc = 'GB');
|
||||
// type 'direct'
|
||||
pxy.type === 'direct' && (pxy.hostname = 'DIRECT');
|
||||
|
||||
/*
|
||||
{
|
||||
"active": true,
|
||||
"pattern": "*.example.com",
|
||||
"title": "example",
|
||||
"type": 1,
|
||||
// "protocols": 1,
|
||||
},
|
||||
*/
|
||||
|
||||
const patternSet = {
|
||||
1: 'wildcard',
|
||||
2: 'regex'
|
||||
};
|
||||
// process include/exclude
|
||||
[...pxy.include, ...pxy.exclude].forEach(i => {
|
||||
// convert to actual type: wildcard | regex
|
||||
i.type = patternSet[i.type];
|
||||
|
||||
// convert wildcard all | http | https to v3 patterns
|
||||
i.pattern = i.type === 'wildcard' ?
|
||||
this.convertWildcard(i.pattern, i.protocols) : this.convertRegEx(i.pattern, i.protocols);
|
||||
// no longer needed
|
||||
delete i.protocols;
|
||||
|
||||
// Validate RegExp, deactivate on error
|
||||
!Pattern.validate(i.pattern, i.type) && (i.active = false);
|
||||
|
||||
// convert v6-7 patterns to match pattern (v9.0)
|
||||
if (i.type === 'wildcard' && Pattern.validMatchPattern(i.pattern + '*')) {
|
||||
i.type === 'match';
|
||||
i.pattern += '*';
|
||||
}
|
||||
});
|
||||
|
||||
db.data.push(pxy);
|
||||
});
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
/*
|
||||
.bbc.co.uk exact domain and all subdomains
|
||||
*.bbc.co.uk exact domain and all subdomains
|
||||
**.bbc.co.uk subdomains only (not bbc.co.uk)
|
||||
*/
|
||||
static convertWildcard(pat, protocol) {
|
||||
const protocolSet = {
|
||||
// all | http | https
|
||||
1: '*://',
|
||||
2: 'http://',
|
||||
4: 'https://'
|
||||
};
|
||||
|
||||
return protocolSet[protocol] + (pat.startsWith('.') ? '*' : '') + pat + '/';
|
||||
}
|
||||
|
||||
static convertRegEx(pat, protocol) {
|
||||
const protocolSet = {
|
||||
// all | http | https
|
||||
1: '.+://',
|
||||
2: 'http://',
|
||||
4: 'https://'
|
||||
};
|
||||
|
||||
// remove start assertion
|
||||
pat.startsWith('^') && (pat = pat.substring(1));
|
||||
// remove end assertion
|
||||
pat.endsWith('$') && (pat = pat.slice(0, -1));
|
||||
|
||||
return protocolSet[protocol] + pat + '/';
|
||||
}
|
||||
}
|
||||
75
foxyproxy/content/nav.css
Normal file
@@ -0,0 +1,75 @@
|
||||
/* ----- Navigation ----- */
|
||||
input[name="nav"],
|
||||
input[type="checkbox"].control {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav {
|
||||
background-color: var(--nav-bg);
|
||||
}
|
||||
|
||||
nav {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
justify-content: start;
|
||||
align-items: end;
|
||||
color: var(--nav-color);
|
||||
height: var(--nav-height);
|
||||
max-width: var(--max-width);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
nav img {
|
||||
width: 2em;
|
||||
margin: 0.2em 0.5em;
|
||||
}
|
||||
|
||||
nav > label {
|
||||
padding: 0.5em 1em;
|
||||
/* transition: 0.5s; */
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
}
|
||||
|
||||
nav > label:hover {
|
||||
background-color: var(--nav-hover);
|
||||
}
|
||||
|
||||
/* nav > label img {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
nav label a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
nav label a:hover {
|
||||
text-decoration: none;
|
||||
} */
|
||||
|
||||
#nav1:checked ~ article section:nth-of-type(1),
|
||||
#nav2:checked ~ article section:nth-of-type(2),
|
||||
#nav3:checked ~ article section:nth-of-type(3),
|
||||
#nav4:checked ~ article section:nth-of-type(4),
|
||||
#nav5:checked ~ article section:nth-of-type(5),
|
||||
#nav6:checked ~ article section:nth-of-type(6),
|
||||
#nav7:checked ~ article section:nth-of-type(7),
|
||||
#nav8:checked ~ article section:nth-of-type(8) {
|
||||
display: block;
|
||||
/* animation: sect 0.5s ease-in-out; */
|
||||
}
|
||||
|
||||
#nav1:checked ~ .nav label[for="nav1"],
|
||||
#nav2:checked ~ .nav label[for="nav2"],
|
||||
#nav3:checked ~ .nav label[for="nav3"],
|
||||
#nav4:checked ~ .nav label[for="nav4"],
|
||||
#nav5:checked ~ .nav label[for="nav5"],
|
||||
#nav6:checked ~ .nav label[for="nav6"],
|
||||
#nav7:checked ~ .nav label[for="nav7"],
|
||||
#nav8:checked ~ .nav label[for="nav8"] {
|
||||
background-color: var(--body-bg);
|
||||
}
|
||||
|
||||
/* @keyframes sect {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
} */
|
||||
52
foxyproxy/content/nav.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
export class Nav {
|
||||
|
||||
static {
|
||||
document.querySelectorAll('label[for^="nav"]').forEach(i =>
|
||||
this[i.dataset.i18n] = i.control);
|
||||
}
|
||||
|
||||
static get(pram = location.search.substring(1)) {
|
||||
pram && this[pram] && (this[pram].checked = true);
|
||||
}
|
||||
|
||||
static {
|
||||
// --- openShortcutSettings FF137
|
||||
const shortcut = document.querySelector('.shortcut-link');
|
||||
// commands is not supported on Android
|
||||
if (!App.firefox || browser.commands?.openShortcutSettings) {
|
||||
shortcut.style.display = 'unset';
|
||||
shortcut.addEventListener('click', () =>
|
||||
App.firefox ? browser.commands?.openShortcutSettings() :
|
||||
browser.tabs.create({url: 'chrome://extensions/shortcuts'})
|
||||
);
|
||||
}
|
||||
|
||||
// help document
|
||||
const help = document.querySelector('iframe[src="help.html"]').contentDocument;
|
||||
|
||||
// --- data-link
|
||||
const helpLink = help.querySelector('.nav-link');
|
||||
document.querySelectorAll('[data-link]').forEach(i => i.addEventListener('click', e => {
|
||||
const {link} = e.target.dataset;
|
||||
if (!link) { return; }
|
||||
|
||||
Nav.get('help');
|
||||
helpLink.href = link;
|
||||
helpLink.click();
|
||||
}));
|
||||
|
||||
// --- Extension link in the Help
|
||||
// not for Firefox
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1956860
|
||||
|
||||
// chrome
|
||||
if (!App.firefox) {
|
||||
const link = help.querySelector('.chrome-extension');
|
||||
link.style.display = 'unset';
|
||||
link.addEventListener('click', () =>
|
||||
browser.tabs.create({url: 'chrome://extensions/?id=' + location.hostname}));
|
||||
}
|
||||
}
|
||||
}
|
||||
305
foxyproxy/content/on-request.js
Normal file
@@ -0,0 +1,305 @@
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1198822
|
||||
// Dynamic import is not available yet in MV3 service worker
|
||||
// Once implemented, module will be dynamically imported for Firefox only
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1853203
|
||||
// Support non-ASCII username/password for socks proxy (fixed in Firefox 119)
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1741375
|
||||
// Proxy DNS by default when using SOCKS v5 (fixed in Firefox 128, defaults to true for SOCKS5 & false for SOCKS4)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1893670
|
||||
// Proxy DNS by default for SOCK4 proxies. Defaulting to SOCKS4a
|
||||
|
||||
// proxyAuthorizationHeader on Firefox only applied to HTTPS (HTTP broke the API and sent DIRECT)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1794464
|
||||
// Allow HTTP authentication in proxy.onRequest (fixed in Firefox 125)
|
||||
|
||||
// proxy.onRequest only applies to http/https/ws/wss
|
||||
// it can not catch domains set by user to 127.0.0.1 in the hosts file
|
||||
|
||||
import {App} from './app.js';
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Location} from './location.js';
|
||||
|
||||
// ---------- Firefox proxy.onRequest API ------------------
|
||||
export class OnRequest {
|
||||
|
||||
static {
|
||||
// --- default values
|
||||
this.mode = 'disable';
|
||||
// used for Single Proxy
|
||||
this.proxy = {};
|
||||
// used for Proxy by Pattern
|
||||
this.data = [];
|
||||
// RegExp string
|
||||
this.passthrough = [];
|
||||
// [start, end] strings
|
||||
this.net = [];
|
||||
// tab proxy, will be lost in MV3 background unloading
|
||||
this.tabProxy = {};
|
||||
// incognito/container proxy
|
||||
this.container = {};
|
||||
|
||||
// --- Firefox only
|
||||
if (browser.proxy.onRequest) {
|
||||
browser.proxy.onRequest.addListener(e => this.process(e), {urls: ['<all_urls>']});
|
||||
// check Tab for tab proxy
|
||||
browser.tabs.onUpdated.addListener((...e) => this.onUpdated(...e));
|
||||
// remove redundant data from this.tabProxy cache
|
||||
browser.tabs.onRemoved.addListener(tabId => delete this.tabProxy[tabId]);
|
||||
// mark incognito/container
|
||||
browser.tabs.onCreated.addListener(e => this.checkPageAction(e));
|
||||
|
||||
// prevent proxy.onRequest.addListener unloading in MV3 (default 30s)
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1771203
|
||||
// it can trigger DNS leak on reloading under limited circumstances
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1882276
|
||||
this.persist();
|
||||
}
|
||||
}
|
||||
|
||||
static persist() {
|
||||
// clear the previous interval & set a new one
|
||||
clearInterval(this.interval);
|
||||
this.interval = setInterval(() => browser.runtime.getPlatformInfo(), 25_000);
|
||||
}
|
||||
|
||||
static init(pref) {
|
||||
this.mode = pref.mode;
|
||||
[this.passthrough, , this.net] = Pattern.getPassthrough(pref.passthrough);
|
||||
|
||||
// filter data
|
||||
const data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname);
|
||||
|
||||
// --- single proxy (false|undefined|proxy object)
|
||||
this.proxy = /:\d+[^/]*$/.test(pref.mode) && data.find(i => pref.mode === `${i.hostname}:${i.port}`);
|
||||
|
||||
// --- proxy by pattern
|
||||
this.data = data.filter(i => i.include[0] || i.exclude[0] || i.tabProxy?.[0]).map(item => {
|
||||
item.tabProxy ||= [];
|
||||
return {
|
||||
type: item.type,
|
||||
hostname: item.hostname,
|
||||
port: item.port,
|
||||
username: item.username,
|
||||
password: item.password,
|
||||
// proxyDNS used in mode pattern or single proxy
|
||||
proxyDNS: item.proxyDNS,
|
||||
include: item.include.filter(i => i.active).map(i => Pattern.get(i.pattern, i.type)),
|
||||
exclude: item.exclude.filter(i => i.active).map(i => Pattern.get(i.pattern, i.type)),
|
||||
tabProxy: item.tabProxy.filter(i => i.active).map(i => Pattern.get(i.pattern, i.type)),
|
||||
|
||||
// used for showPatternProxy
|
||||
title: item.title,
|
||||
cc: item.cc,
|
||||
city: item.city,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
|
||||
// --- incognito/container proxy
|
||||
// reset container
|
||||
this.container = {};
|
||||
pref.container && Object.entries(pref.container).forEach(([key, val]) => {
|
||||
// prefix key
|
||||
key.startsWith('container-') && (key = 'firefox-' + key);
|
||||
this.container[key] = val && data.find(i => val === `${i.hostname}:${i.port}`);
|
||||
});
|
||||
|
||||
// mirror as this.tabProxy is lost in MV3 background unloading
|
||||
browser.storage.session.get('tabProxy')
|
||||
.then(i => this.tabProxy = i.tabProxy || {});
|
||||
}
|
||||
|
||||
static process(e) {
|
||||
// reset interval
|
||||
this.persist();
|
||||
|
||||
const tabId = e.tabId;
|
||||
const fromTab = tabId !== -1;
|
||||
|
||||
// --- check Tab Proxy Pattern
|
||||
this.processTabProxy(tabId, e.url, e);
|
||||
|
||||
switch (true) {
|
||||
// --- check local & global passthrough
|
||||
case this.bypass(e.url):
|
||||
this.setAction(tabId);
|
||||
return {type: 'direct'};
|
||||
|
||||
// --- tab proxy
|
||||
case fromTab && !!this.tabProxy[tabId]:
|
||||
return this.processProxy(tabId, this.tabProxy[tabId]);
|
||||
|
||||
// --- incognito proxy
|
||||
case fromTab && e.incognito && !!this.container.incognito:
|
||||
return this.processProxy(tabId, this.container.incognito);
|
||||
|
||||
// --- container proxy
|
||||
case fromTab && e.cookieStoreId && !!this.container[e.cookieStoreId]:
|
||||
return this.processProxy(tabId, this.container[e.cookieStoreId]);
|
||||
|
||||
// --- standard operation
|
||||
// pass direct
|
||||
case this.mode === 'disable':
|
||||
case this.mode === 'direct':
|
||||
// PAC URL is set
|
||||
case this.mode.includes('://') && !/:\d+$/.test(this.mode):
|
||||
this.setAction(tabId);
|
||||
return {type: 'direct'};
|
||||
|
||||
// check if url matches patterns
|
||||
case this.mode === 'pattern':
|
||||
return this.processPattern(tabId, e.url);
|
||||
|
||||
// get the proxy for all
|
||||
default:
|
||||
return this.processProxy(tabId, this.proxy);
|
||||
}
|
||||
}
|
||||
|
||||
static processTabProxy(tabId, url, e) {
|
||||
if (this.mode !== 'pattern' || e.type !== 'main_frame' || this.tabProxy[tabId]) { return; }
|
||||
|
||||
const match = arr => arr.some(i => new RegExp(i, 'i').test(url));
|
||||
const proxy = this.data.find(i => match(i.tabProxy));
|
||||
proxy && (this.tabProxy[tabId] = proxy);
|
||||
}
|
||||
|
||||
static processPattern(tabId, url) {
|
||||
const match = arr => arr.some(i => new RegExp(i, 'i').test(url));
|
||||
const proxy = this.data.find(i => !match(i.exclude) && match(i.include));
|
||||
if (proxy) {
|
||||
return this.processProxy(tabId, proxy);
|
||||
}
|
||||
|
||||
// no match
|
||||
this.setAction(tabId);
|
||||
return {type: 'direct'};
|
||||
}
|
||||
|
||||
static processProxy(tabId, proxy) {
|
||||
this.setAction(tabId, proxy);
|
||||
const {type, hostname: host, port, username, password, proxyDNS} = proxy || {};
|
||||
if (!type || type === 'direct') { return {type: 'direct'}; }
|
||||
|
||||
const auth = username && password && type.startsWith('http');
|
||||
|
||||
const response = {
|
||||
host,
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#102
|
||||
// Although API converts to number -> let port = Number.parseInt(proxyData.port, 10);
|
||||
// port 'number', prepare for augmented port
|
||||
port: parseInt(port),
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#43
|
||||
// API uses socks for socks5
|
||||
type: type === 'socks5' ? 'socks' : type,
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#135
|
||||
...(type.startsWith('socks') && {proxyDNS: !!proxyDNS}),
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#117
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#126
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#231
|
||||
// only used for SOCKS 4/5, must be string and not undefined
|
||||
// allow sending username without password
|
||||
username,
|
||||
password,
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#167
|
||||
// only for HTTP/HTTPS
|
||||
// use proxyAuthorizationHeader to reduce requests in webRequest.onAuthRequired
|
||||
...(auth && {proxyAuthorizationHeader: 'Basic ' + btoa(username + ':' + password)}),
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// browser.action here only relates to showPatternProxy from proxy.onRequest
|
||||
static setAction(tabId, item) {
|
||||
// Set to -1 if the request isn't related to a tab
|
||||
if (tabId === -1) { return; }
|
||||
|
||||
// --- reset values
|
||||
let title = '';
|
||||
let text = '';
|
||||
let color = '';
|
||||
|
||||
// --- set proxy details
|
||||
if (item) {
|
||||
const host = [item.hostname, item.port].filter(Boolean).join(':');
|
||||
title = [item.title, host, item.city, Location.get(item.cc)].filter(Boolean).join('\n');
|
||||
text = item.title || item.hostname;
|
||||
color = item.color;
|
||||
}
|
||||
|
||||
color && browser.action.setBadgeBackgroundColor({color, tabId});
|
||||
browser.action.setTitle({title, tabId});
|
||||
browser.action.setBadgeText({text, tabId});
|
||||
}
|
||||
|
||||
// ---------- passthrough --------------------------------
|
||||
// Firefox & Chrome have a default localhost bypass
|
||||
// Connections to localhost, 127.0.0.1/8, and ::1 are never proxied.
|
||||
// Firefox: "network.proxy.allow_hijacking_localhost"
|
||||
// Chrome: --proxy-bypass-list="<-loopback>"
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1854324
|
||||
// proxy.onRequest failure to bypass proxy for localhost (fixed in Firefox 137)
|
||||
static bypass(url) {
|
||||
switch (true) {
|
||||
// global passthrough
|
||||
case this.passthrough.some(i => new RegExp(i, 'i').test(url)):
|
||||
// global passthrough CIDR
|
||||
case this.net[0] && this.isInNet(url):
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static isInNet(url) {
|
||||
// check if IP address
|
||||
if (!/^[a-z]+:\/\/\d+(\.\d+){3}(:\d+)?\//.test(url)) { return; }
|
||||
|
||||
// IP array
|
||||
const ipa = url.split(/[:/.]+/, 5).slice(1);
|
||||
// convert to padded string
|
||||
const ip = ipa.map(i => i.padStart(3, '0')).join('');
|
||||
return this.net.some(([st, end]) => ip >= st && ip <= end);
|
||||
}
|
||||
|
||||
// ---------- Tab Proxy ----------------------------------
|
||||
static setTabProxy(tab, pxy) {
|
||||
switch (true) {
|
||||
// unacceptable URLs
|
||||
case !App.allowedTabProxy(tab.url):
|
||||
// check global passthrough
|
||||
case this.bypass(tab.url):
|
||||
return;
|
||||
}
|
||||
|
||||
// set or unset
|
||||
pxy ? this.tabProxy[tab.id] = pxy : delete this.tabProxy[tab.id];
|
||||
this.setAction(tab.id, pxy);
|
||||
|
||||
// mirror as this.tabProxy is lost in MV3 background unloading
|
||||
browser.storage.session.set({'tabProxy': this.tabProxy});
|
||||
}
|
||||
|
||||
// ---------- Update Page Action -------------------------
|
||||
static onUpdated(tabId, changeInfo, tab) {
|
||||
if (changeInfo.status !== 'complete') { return; }
|
||||
|
||||
const pxy = this.tabProxy[tabId];
|
||||
pxy ? this.setAction(tab.id, pxy) : this.checkPageAction(tab);
|
||||
}
|
||||
|
||||
// ---------- Incognito/Container ------------------------
|
||||
static checkPageAction(tab) {
|
||||
// not if tab proxy is set
|
||||
if (tab.id === -1 || this.tabProxy[tab.id]) { return; }
|
||||
|
||||
const pxy = tab.incognito ? this.container.incognito : this.container[tab.cookieStoreId];
|
||||
pxy && this.setAction(tab.id, pxy);
|
||||
}
|
||||
}
|
||||
26
foxyproxy/content/options-filter.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// ---------- Filter Proxy (Side Effect) -------------------
|
||||
class Filter {
|
||||
|
||||
static {
|
||||
this.list = document.querySelector('div.proxy-div');
|
||||
const filter = document.querySelector('.filter');
|
||||
filter.addEventListener('input', e => this.process(e));
|
||||
}
|
||||
|
||||
static process(e) {
|
||||
const str = e.target.value.toLowerCase().trim();
|
||||
const elem = [...this.list.children];
|
||||
if (!str) {
|
||||
elem.forEach(i => i.classList.remove('off'));
|
||||
return;
|
||||
}
|
||||
|
||||
elem.forEach(item => {
|
||||
const proxyBox = item.children[1].children[0];
|
||||
const title = proxyBox.children[1].value;
|
||||
const hostname = proxyBox.children[3].value;
|
||||
const port = ':' + proxyBox.children[7].value;
|
||||
item.classList.toggle('off', ![title, hostname, port].some(i => i.toLowerCase().includes(str)));
|
||||
});
|
||||
}
|
||||
}
|
||||
55
foxyproxy/content/options-popup.css
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ----- Popup ----- */
|
||||
.popup {
|
||||
display: none;
|
||||
background-color: #0003;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.popup.on {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
animation: fade-in 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.popup span {
|
||||
background-color: var(--alt-bg);
|
||||
display: grid;
|
||||
place-content: center end;
|
||||
padding: 0 0.8em;
|
||||
cursor: pointer;
|
||||
font-size: 0.9em;
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
/* set height to maintain the same result in chrome/firefox */
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.popup span:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
.popup textarea {
|
||||
width: 50vw;
|
||||
height: 50vh;
|
||||
resize: both;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.popup select {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.popup select.on {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
24
foxyproxy/content/options-popup.js
Normal file
@@ -0,0 +1,24 @@
|
||||
export class Popup {
|
||||
|
||||
static {
|
||||
this.popup = document.querySelector('.popup');
|
||||
[this.close, this.textarea] = this.popup.children;
|
||||
this.close.addEventListener('click', () => this.hide());
|
||||
}
|
||||
|
||||
static show(text) {
|
||||
this.textarea.value += text + '\n';
|
||||
this.popup.classList.add('on');
|
||||
}
|
||||
|
||||
static hide() {
|
||||
this.popup.classList.remove('on');
|
||||
this.textarea.value = '';
|
||||
[...this.popup.children].forEach(i => {
|
||||
if (i.nodeName === 'SELECT') {
|
||||
i.selectedIndex = 0;
|
||||
i.classList.remove('on');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
413
foxyproxy/content/options-proxies.js
Normal file
@@ -0,0 +1,413 @@
|
||||
import {App} from './app.js';
|
||||
import {ImportExport} from './import-export.js';
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Flag} from './flag.js';
|
||||
import {Color} from './color.js';
|
||||
import {PAC} from './pac.js';
|
||||
import {Toggle} from './toggle.js';
|
||||
import {Tester} from './tester.js';
|
||||
import {Log} from './log.js';
|
||||
import {Nav} from './nav.js';
|
||||
|
||||
export class Proxies {
|
||||
|
||||
static {
|
||||
this.proxyDiv = document.querySelector('div.proxy-div');
|
||||
[this.proxyTemplate, this.patternTemplate] =
|
||||
document.querySelector('.proxy-section template').content.children;
|
||||
|
||||
// firefox only, disabling Tab Proxy in the template for chrome
|
||||
!App.firefox && (this.patternTemplate.children[1].lastElementChild.disabled = true);
|
||||
|
||||
// --- buttons
|
||||
document.querySelector('.proxy-top button[data-i18n="add"]').addEventListener('click', e => {
|
||||
// this.addProxy(null, e.ctrlKey)
|
||||
const [pxy, title] = this.addProxy();
|
||||
e.ctrlKey ? this.proxyDiv.prepend(pxy) : this.proxyDiv.append(pxy);
|
||||
pxy.open = true;
|
||||
pxy.draggable = false;
|
||||
title.focus();
|
||||
});
|
||||
|
||||
// used to find proxy
|
||||
this.proxyCache = {};
|
||||
// used to get the details for the log
|
||||
Log.proxyCache = this.proxyCache;
|
||||
// this.process();
|
||||
|
||||
// import pattern from log.js
|
||||
window.addEventListener('importPatternCustom', (e) => this.importPatternCustom(e));
|
||||
}
|
||||
|
||||
// pref references the same object in the memory and its value gets updated
|
||||
static process(pref) {
|
||||
Log.mode = pref.mode;
|
||||
// reset
|
||||
this.proxyDiv.textContent = '';
|
||||
const docFrag = document.createDocumentFragment();
|
||||
pref.data.forEach(i => docFrag.append(this.addProxy(i)));
|
||||
this.proxyDiv.append(docFrag);
|
||||
}
|
||||
|
||||
static addProxy(item, modifier) {
|
||||
// --- details: make a blank proxy with all event listeners
|
||||
const pxy = this.proxyTemplate.cloneNode(true);
|
||||
const summary = pxy.children[0];
|
||||
const proxyBox = pxy.children[1].children[0];
|
||||
const patternBox = pxy.children[1].children[2];
|
||||
|
||||
// disable draggable when details is open
|
||||
summary.addEventListener('click', () => pxy.draggable = pxy.open);
|
||||
|
||||
// --- summary
|
||||
const [flag, sumTitle, active, dup, del, up, down] = summary.children;
|
||||
dup.addEventListener('click', () => this.duplicateProxy(pxy));
|
||||
del.addEventListener('click', () => confirm(browser.i18n.getMessage('deleteConfirm')) && pxy.remove());
|
||||
up.addEventListener('click', () => pxy.previousElementSibling?.before(pxy));
|
||||
down.addEventListener('click', () => pxy.nextElementSibling?.after(pxy));
|
||||
|
||||
// proxy data
|
||||
const [title, hostname, type, port, cc, username, city, passwordSpan,
|
||||
colorSpan, pacSpan, proxyDNS] = [...proxyBox.children].filter((e, i) => i % 2);
|
||||
title.addEventListener('change', e => sumTitle.textContent = e.target.value);
|
||||
|
||||
const [pac, storeLocallyLabel, view] = pacSpan.children;
|
||||
|
||||
type.addEventListener('change', e => {
|
||||
// use for show/hide elements
|
||||
pxy.dataset.type = e.target.value;
|
||||
|
||||
const elem = e.target.selectedOptions[0];
|
||||
const data = elem.dataset;
|
||||
// --- server auto-fill helpers
|
||||
switch (true) {
|
||||
case ['flag', 'hostname', 'port'].some(i => data[i]):
|
||||
sumTitle.textContent = elem.textContent;
|
||||
title.value = elem.textContent;
|
||||
data.flag && (flag.textContent = data.flag);
|
||||
data.hostname && (hostname.value = data.hostname);
|
||||
data.port && (port.value = data.port);
|
||||
break;
|
||||
|
||||
default:
|
||||
flag.textContent = Flag.get(cc.value);
|
||||
}
|
||||
});
|
||||
|
||||
cc.addEventListener('change', () => flag.textContent = Flag.get(cc.value));
|
||||
Toggle.password(passwordSpan.children[1]);
|
||||
|
||||
// random color
|
||||
const color = item?.color || Color.getRandom();
|
||||
summary.style.borderLeftColor = color;
|
||||
const [colorInput, colorButton] = colorSpan.children;
|
||||
colorInput.value = color;
|
||||
colorInput.addEventListener('change', e => summary.style.borderLeftColor = e.target.value);
|
||||
|
||||
colorButton.addEventListener('click', e => {
|
||||
e.target.previousElementSibling.value = Color.getRandom();
|
||||
summary.style.borderLeftColor = e.target.previousElementSibling.value;
|
||||
});
|
||||
|
||||
pac.addEventListener('change', e => {
|
||||
const {hostname: h, port: p} = App.parseURL(e.target.value);
|
||||
if (!h) {
|
||||
e.target.classList.add('invalid');
|
||||
return;
|
||||
}
|
||||
hostname.value = h;
|
||||
port.value = p;
|
||||
type.value = 'pac';
|
||||
title.value ||= 'PAC';
|
||||
sumTitle.textContent ||= 'PAC';
|
||||
});
|
||||
|
||||
// ---patterns
|
||||
pxy.querySelector('button[data-i18n="add|title"]').addEventListener('click', e => {
|
||||
const elem = this.addPattern();
|
||||
e.ctrlKey ? patternBox.prepend(elem) : patternBox.append(elem);
|
||||
elem.children[4].focus();
|
||||
});
|
||||
pxy.querySelector('input[type="file"]').addEventListener('change', e => this.importPattern(e, patternBox));
|
||||
pxy.querySelector('button[data-i18n^="export"]').addEventListener('click', () =>
|
||||
this.exportPattern(patternBox, title.value.trim() || hostname.value.trim()));
|
||||
|
||||
// --- from add button
|
||||
if (!item) {
|
||||
return [pxy, title];
|
||||
}
|
||||
|
||||
// show/hide elements
|
||||
pxy.dataset.type = item.type;
|
||||
|
||||
const id = item.type === 'pac' ? item.pac : `${item.hostname}:${item.port}`;
|
||||
// cache to find later
|
||||
this.proxyCache[id] = item;
|
||||
|
||||
// --- populate with data
|
||||
const pxyTitle = item.title || id;
|
||||
|
||||
// --- summary
|
||||
flag.textContent = Flag.show(item);
|
||||
sumTitle.textContent = pxyTitle;
|
||||
active.checked = item.active;
|
||||
|
||||
// proxy details
|
||||
title.value = item.title;
|
||||
hostname.value = item.hostname;
|
||||
type.value = item.type;
|
||||
port.value = item.port;
|
||||
cc.value = item.cc;
|
||||
// used in "Get Location"
|
||||
cc.dataset.hostname = item.hostname;
|
||||
username.value = item.username;
|
||||
city.value = item.city;
|
||||
// used in "Get Location"
|
||||
city.dataset.hostname = item.hostname;
|
||||
passwordSpan.children[0].value = item.password;
|
||||
|
||||
pac.value = item.pac;
|
||||
storeLocallyLabel.children[0].checked = !!item.pacString;
|
||||
view.addEventListener('click', () => PAC.view(pac.value.trim()));
|
||||
|
||||
proxyDNS.checked = item.proxyDNS;
|
||||
|
||||
// --- patterns
|
||||
patternBox.dataset.host = `${item.hostname}:${item.port}`;
|
||||
item.tabProxy ||= [];
|
||||
item.include.forEach(i => patternBox.append(this.addPattern(i, 'include')));
|
||||
item.exclude.forEach(i => patternBox.append(this.addPattern(i, 'exclude')));
|
||||
item.tabProxy.forEach(i => patternBox.append(this.addPattern(i, 'tabProxy')));
|
||||
return pxy;
|
||||
}
|
||||
|
||||
static addPattern(item, inc) {
|
||||
// --- make a blank pattern with all event listeners
|
||||
const div = this.patternTemplate.cloneNode(true);
|
||||
const [quickAdd, include, type, title, pattern, active, test, del] = div.children;
|
||||
|
||||
quickAdd.addEventListener('change', e => {
|
||||
const opt = e.target.selectedOptions[0];
|
||||
type.value = opt.dataset.type;
|
||||
title.value = opt.textContent;
|
||||
pattern.value = opt.value;
|
||||
// reset select option
|
||||
e.target.selectedIndex = 0;
|
||||
});
|
||||
|
||||
test.addEventListener('click', () => {
|
||||
Tester.select.value = type.value;
|
||||
Tester.pattern.value = pattern.value;
|
||||
Tester.target = pattern;
|
||||
// reset pre DOM
|
||||
Tester.pre.textContent = Tester.pre.textContent.trim();
|
||||
// navigate to Tester tab
|
||||
Nav.get('tester');
|
||||
});
|
||||
|
||||
// del.addEventListener('click', () => confirm(browser.i18n.getMessage('deleteConfirm')) && div.remove());
|
||||
del.addEventListener('click', () => div.remove());
|
||||
|
||||
if (item) {
|
||||
include.value = inc;
|
||||
type.value = item.type;
|
||||
title.value = item.title;
|
||||
pattern.value = item.pattern;
|
||||
active.checked = item.active;
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
static async duplicateProxy(item) {
|
||||
// generating a new proxy as cloneNode() removes event listeners
|
||||
const pxy = await this.getProxyDetails(item);
|
||||
if (!pxy) { return; }
|
||||
|
||||
item.after(this.addProxy(pxy));
|
||||
// close orig proxy
|
||||
item.open = false;
|
||||
// open duplicated proxy
|
||||
item.nextElementSibling.open = true;
|
||||
}
|
||||
|
||||
// import pattern from log.js
|
||||
static importPatternCustom(e) {
|
||||
const {host, data} = e.detail;
|
||||
if (!host || !data) { return; }
|
||||
|
||||
const patternBox = document.querySelector(`.pattern-box[data-host="${host}"]`);
|
||||
if (!patternBox) { return; }
|
||||
|
||||
data.forEach(i => patternBox.append(this.addPattern(i, i.include)));
|
||||
patternBox.closest('details').open = true;
|
||||
patternBox.lastElementChild.children[4].focus();
|
||||
}
|
||||
|
||||
static importPattern(e, patternBox) {
|
||||
const file = e.target.files[0];
|
||||
switch (true) {
|
||||
case !file: App.notify(browser.i18n.getMessage('error'));
|
||||
return;
|
||||
// check file MIME type
|
||||
case !['text/plain', 'application/json'].includes(file.type):
|
||||
App.notify(browser.i18n.getMessage('fileTypeError'));
|
||||
return;
|
||||
}
|
||||
|
||||
ImportExport.fileReader(file, data => {
|
||||
try { data = JSON.parse(data); }
|
||||
catch {
|
||||
// display the error
|
||||
App.notify(browser.i18n.getMessage('fileParseError'));
|
||||
return;
|
||||
}
|
||||
|
||||
Array.isArray(data) && data.forEach(i => patternBox.append(this.addPattern(i, i.include)));
|
||||
});
|
||||
}
|
||||
|
||||
static exportPattern(patternBox, title = '') {
|
||||
const arr = [...patternBox.children].map(item => {
|
||||
const elem = item.children;
|
||||
return {
|
||||
include: elem[1].value,
|
||||
type: elem[2].value,
|
||||
title: elem[3].value.trim(),
|
||||
pattern: elem[4].value.trim(),
|
||||
active: elem[5].checked,
|
||||
};
|
||||
});
|
||||
|
||||
// no patterns to export
|
||||
if (!arr[0]) { return; }
|
||||
|
||||
title &&= '_' + title;
|
||||
const data = JSON.stringify(arr, null, 2);
|
||||
const filename = `${browser.i18n.getMessage('pattern')}${title}_${new Date().toISOString().substring(0, 10)}.json`;
|
||||
ImportExport.saveFile({data, filename, type: 'application/json'});
|
||||
}
|
||||
|
||||
static async getProxyDetails(elem) {
|
||||
// proxy template
|
||||
const obj = {
|
||||
active: true,
|
||||
title: '',
|
||||
type: 'http',
|
||||
hostname: '',
|
||||
port: '',
|
||||
username: '',
|
||||
password: '',
|
||||
cc: '',
|
||||
city: '',
|
||||
color: '',
|
||||
pac: '',
|
||||
pacString: '',
|
||||
proxyDNS: true,
|
||||
include: [],
|
||||
exclude: [],
|
||||
tabProxy: [],
|
||||
};
|
||||
|
||||
// --- populate values
|
||||
elem.querySelectorAll('[data-id]').forEach(i => {
|
||||
// reset
|
||||
i.classList.remove('invalid');
|
||||
Object.hasOwn(obj, i.dataset.id) && (obj[i.dataset.id] = i.type === 'checkbox' ? i.checked : i.value.trim());
|
||||
});
|
||||
|
||||
// --- check type: http | https | socks4 | socks5 | quic | pac | direct
|
||||
switch (true) {
|
||||
// DIRECT
|
||||
case obj.type === 'direct':
|
||||
obj.hostname = 'DIRECT';
|
||||
break;
|
||||
|
||||
// PAC
|
||||
case obj.type === 'pac':
|
||||
const {hostname, port} = App.parseURL(obj.pac);
|
||||
if (!hostname) {
|
||||
this.setInvalid(elem, 'pac');
|
||||
// alert(browser.i18n.getMessage('pacUrlError'));
|
||||
return;
|
||||
}
|
||||
obj.hostname = hostname;
|
||||
obj.port = port;
|
||||
break;
|
||||
|
||||
// http | https | socks4 | socks5 | quic
|
||||
case !obj.hostname:
|
||||
this.setInvalid(elem, 'hostname');
|
||||
alert(browser.i18n.getMessage('hostnamePortError'));
|
||||
return;
|
||||
|
||||
case !obj.port:
|
||||
this.setInvalid(elem, 'port');
|
||||
alert(browser.i18n.getMessage('hostnamePortError'));
|
||||
return;
|
||||
}
|
||||
|
||||
// --- title check
|
||||
if (!obj.title) {
|
||||
const id = obj.type === 'pac' ? obj.pac : `${obj.hostname}:${obj.port}`;
|
||||
elem.children[0].children[1].textContent = id;
|
||||
}
|
||||
|
||||
// --- check store locally for active PAC
|
||||
if (obj.active && obj.pac) {
|
||||
const storeLocally = elem.querySelector('.pac input[type="checkbox"]');
|
||||
if (storeLocally.checked) {
|
||||
const str = await PAC.get(obj.pac);
|
||||
/function\s+FindProxyForURL\s*\(/.test(str) && (obj.pacString = str.trim());
|
||||
}
|
||||
}
|
||||
|
||||
// --- check & build patterns
|
||||
const cache = [];
|
||||
for (const item of elem.querySelectorAll('.pattern-box .pattern-row')) {
|
||||
const [, inc, type, title, pattern, active] = item.children;
|
||||
// reset
|
||||
pattern.classList.remove('invalid');
|
||||
const pat = {
|
||||
type: type.value,
|
||||
title: title.value.trim(),
|
||||
pattern: pattern.value.trim(),
|
||||
active: active.checked,
|
||||
};
|
||||
|
||||
// --- test pattern
|
||||
// blank pattern
|
||||
if (!pat.pattern) { continue; }
|
||||
|
||||
if (!Pattern.validate(pat.pattern, pat.type, true)) {
|
||||
// show Proxy tab
|
||||
Nav.get('proxies');
|
||||
const details = item.closest('details');
|
||||
// open proxy
|
||||
details.open = true;
|
||||
pattern.classList.add('invalid');
|
||||
pattern.scrollIntoView({behavior: 'smooth'});
|
||||
return;
|
||||
}
|
||||
|
||||
// check for duplicate
|
||||
if (cache.includes(pat.pattern)) {
|
||||
item.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// cache to check for duplicates
|
||||
cache.push(pat.pattern);
|
||||
obj[inc.value].push(pat);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static setInvalid(parent, id) {
|
||||
parent.open = true;
|
||||
const elem = parent.querySelector(`[data-id="${id}"]`);
|
||||
elem.classList.add('invalid');
|
||||
parent.scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
}
|
||||
71
foxyproxy/content/options-show.css
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ----- show/hide elements ----- */
|
||||
details.proxy[data-type="direct"] :is(
|
||||
[data-i18n="port"], [data-id="port"],
|
||||
[data-i18n="username"], [data-id="username"],
|
||||
[data-i18n="password"], .password,
|
||||
[data-i18n="country"], [data-id="cc"],
|
||||
[data-i18n="city"], [data-id="city"],
|
||||
[data-type="pac"], .pac) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
details.proxy[data-type="pac"] :is(
|
||||
[data-i18n="port"], [data-id="port"],
|
||||
[data-i18n="username"], [data-id="username"],
|
||||
[data-i18n="password"], .password) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
details.proxy[data-type="pac"] :is(.pattern-head, .pattern-box) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details.proxy:not([data-type="pac"]) :is([data-type="pac"], .pac) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
details.proxy[data-type="socks4"] :is(
|
||||
[data-i18n="username"], [data-id="username"],
|
||||
[data-i18n="password"], .password) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
details.proxy:not([data-type="socks5"]) :is([data-i18n="proxyDNS"], [data-id="proxyDNS"]) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* --- Chrome/Firefox --- */
|
||||
body.chrome .firefox,
|
||||
body:not(.chrome) .chrome,
|
||||
body.chrome details.proxy[data-type="socks5"] :is(
|
||||
[data-i18n="username"], [data-id="username"],
|
||||
[data-i18n="password"], .password) {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
caption.firefox {
|
||||
color: var(--nav-color);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
body.chrome caption.firefox {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* --- Basic --- */
|
||||
.basic :is(.pattern-head, .pattern-box) {
|
||||
display: none;
|
||||
}
|
||||
549
foxyproxy/content/options.css
Normal file
@@ -0,0 +1,549 @@
|
||||
@import 'default.css';
|
||||
@import 'progress-bar.css';
|
||||
@import 'spinner.css';
|
||||
@import 'nav.css';
|
||||
@import 'theme.css';
|
||||
@import 'options-show.css';
|
||||
@import 'options-popup.css';
|
||||
@import 'toggle-switch.css';
|
||||
@import 'tester.css';
|
||||
@import 'log.css';
|
||||
|
||||
/* ----- Light Theme ----- */
|
||||
:root {
|
||||
--nav-height: 3rem;
|
||||
--max-width: 75rem;
|
||||
--pass: #080;
|
||||
--fail: #f00;
|
||||
}
|
||||
|
||||
/* ----- Dark Theme ----- */
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--pass: #0c0;
|
||||
--fail: #f90;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- General ----- */
|
||||
html {
|
||||
scroll-padding-top: var(--nav-height);
|
||||
}
|
||||
|
||||
body {
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 0.5s;
|
||||
font-size: initial;
|
||||
}
|
||||
|
||||
body::after {
|
||||
content: '';
|
||||
background-image: url('../image/logo.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
position: absolute;
|
||||
bottom: 0.5em;
|
||||
right: 0;
|
||||
display: inline-block;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
opacity: 0.3;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
section {
|
||||
display: none;
|
||||
margin: 0 auto;
|
||||
max-width: var(--max-width);
|
||||
height: calc(100vh - var(--nav-height) - 2rem);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 99%;
|
||||
}
|
||||
|
||||
/* img {
|
||||
vertical-align: text-bottom;
|
||||
} */
|
||||
|
||||
label:has(input[type="checkbox"]) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label > input[type="checkbox"] {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
input[type="number"] {
|
||||
width: 4em;
|
||||
}
|
||||
*/
|
||||
|
||||
textarea {
|
||||
min-height: 6em;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
background-color: var(--bg);
|
||||
border-radius: 0.5em;
|
||||
border: 0;
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
/* fieldset label img {
|
||||
width: 1em;
|
||||
} */
|
||||
|
||||
.description {
|
||||
border-left: 2px solid #abc;
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
margin-left: 0.5em;
|
||||
margin-top: 0;
|
||||
padding-left: 0.3em;
|
||||
}
|
||||
|
||||
details {
|
||||
padding: 0;
|
||||
background-color: var(--alt-bg);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
details > summary {
|
||||
border-radius: 5px;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
details > summary:hover,
|
||||
details[open] > summary {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
summary ~ * {
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
details[open] summary ~ * {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ----- Import/Export ----- */
|
||||
input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
/* ----- /Import/Export ----- */
|
||||
|
||||
/* ----- Submit Button ----- */
|
||||
/* button[type="submit"] {
|
||||
display: table;
|
||||
color: #fff;
|
||||
background-color: var(--btn-bg);
|
||||
font-size:0.9em;
|
||||
margin: 1em auto 0;
|
||||
padding: 0.5em 5em;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
button[type="submit"]:hover {
|
||||
background-color: var(--btn-hover);
|
||||
} */
|
||||
/* ----- /Submit Button ----- */
|
||||
|
||||
/* ----- Button ----- */
|
||||
button.bin,
|
||||
button.test,
|
||||
button.close {
|
||||
width: 1.5em;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
button.bin::before,
|
||||
button.test::before {
|
||||
content: '';
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
background: url('../image/bin.svg') no-repeat center / contain;
|
||||
}
|
||||
|
||||
button.test::before {
|
||||
background: url('../image/beaker.svg') no-repeat center / contain;
|
||||
}
|
||||
|
||||
button.close::before {
|
||||
content: '❌';
|
||||
}
|
||||
|
||||
.up:hover, .down:hover {
|
||||
color: #080;
|
||||
}
|
||||
|
||||
button.random {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
button.slim {
|
||||
padding: 0.2em 1em;
|
||||
min-width: unset;
|
||||
}
|
||||
/* ----- /Button ----- */
|
||||
|
||||
/* ----- data-link ----- */
|
||||
[data-link] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-link]:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
[data-link]::before {
|
||||
content: '#';
|
||||
display: inline-block;
|
||||
width: 0.8em;
|
||||
margin-left: -0.8em;
|
||||
opacity: 0.5;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
[data-link]:hover::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
footer span[data-link] {
|
||||
font-size: 0.9em;
|
||||
place-self: end;
|
||||
}
|
||||
/* ----- /data-link ----- */
|
||||
|
||||
/* ----- Options ----- */
|
||||
/*
|
||||
input[type="text"],
|
||||
input[type="password"],
|
||||
input[type="url"],
|
||||
select,
|
||||
textarea {
|
||||
font-size: 1em;
|
||||
} */
|
||||
|
||||
section.options fieldset * {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
border: 0;
|
||||
width: 4em;
|
||||
height: 1.7em;
|
||||
}
|
||||
|
||||
.options .buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
justify-content: end;
|
||||
column-gap: 0.2em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.options .theme,
|
||||
.options .container,
|
||||
.options .commands {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(10em, max-content) max-content;
|
||||
gap: 0.2em 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.options .container label,
|
||||
.options .commands label {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.container button {
|
||||
grid-column: span 2;
|
||||
justify-self: right;
|
||||
/* padding: 0.2em;
|
||||
min-width: 6em; */
|
||||
}
|
||||
|
||||
/* button strip */
|
||||
/* .options .buttons {
|
||||
padding: 0 1.5em;
|
||||
margin: 0 -1.5em 1em;
|
||||
background-color: var(--btn-bg);
|
||||
}
|
||||
|
||||
.options .buttons > * {
|
||||
border-left: 2px solid var(--bg);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.options .buttons > *:last-child {
|
||||
border-right: 2px solid var(--bg);
|
||||
} */
|
||||
|
||||
|
||||
|
||||
/* ----- /Options ----- */
|
||||
details .content {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
/* ----- Proxy ----- */
|
||||
.proxy-top {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto auto auto;
|
||||
gap: 0.2em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.proxy-top input.filter {
|
||||
background: url('../image/filter.svg') no-repeat left 0.5em center / 1em;
|
||||
padding-left: 2em;
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
details.proxy .content * {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
details.proxy.off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details.proxy > summary {
|
||||
list-style: none;
|
||||
border-left: 1.5em solid var(--btn-bg);
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr repeat(5, auto);
|
||||
gap: 0.2em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
details.proxy > summary button.plain {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
details.proxy > summary span:first-of-type {
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
details.proxy > summary span:nth-of-type(2):empty::before {
|
||||
content: 'title ...';
|
||||
font-style: italic;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.proxy-box {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 2fr;
|
||||
/* grid-auto-rows: min-content; */
|
||||
gap: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.password {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.proxy-box button.random {
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.pac {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.2em 0.5em;
|
||||
font-size: 0.9em;
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
.pac [type="url"] {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.pac button {
|
||||
/* padding: 0.2em 1em;
|
||||
min-width: unset; */
|
||||
place-self: center end;
|
||||
}
|
||||
|
||||
|
||||
/* ----- Pattern ----- */
|
||||
.pattern-box {
|
||||
padding: 0.2em;
|
||||
max-height: 20em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pattern-head,
|
||||
.pattern-row {
|
||||
display: grid;
|
||||
grid-template-columns: 3em 6em 6em 1fr 2fr 2em 1em 1em;
|
||||
gap: 0.2em;
|
||||
place-items: center;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.pattern-head {
|
||||
background-color: var(--bg);
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.pattern-head button {
|
||||
/* padding: 0.2em 1em;
|
||||
min-width: unset; */
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* .pattern-head button[data-i18n="add|title"] {
|
||||
grid-column: span 3;
|
||||
} */
|
||||
|
||||
.pattern-head button span.plus {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.pattern-head :nth-child(2) {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
|
||||
/* import & export (label & button) */
|
||||
.pattern-head .plain {
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pattern-head :last-child {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
/* hide when there are no patterns */
|
||||
.pattern-head:has(+ .pattern-box:empty) :nth-child(n+3) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pattern-row {
|
||||
margin-top: 0.2em;
|
||||
padding: 0 0.4em;
|
||||
}
|
||||
|
||||
/* .pattern-row input:nth-of-type(2) {
|
||||
grid-column: span 3;
|
||||
} */
|
||||
|
||||
.proxy summary button,
|
||||
.pattern-box button.test,
|
||||
.pattern-box button.bin {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
/* border: none; */
|
||||
font-size: 1em;
|
||||
transition: 0.5s;
|
||||
color: #ccc;
|
||||
font-weight: 900;
|
||||
text-shadow: 0 -1px 1px #555, 0 1px 0 #fff;
|
||||
}
|
||||
/* ----- /Pattern ----- */
|
||||
|
||||
/* ----- bulk edit ----- */
|
||||
.bulk-edit {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 3.5em) 10em auto;
|
||||
justify-content: end;
|
||||
gap: 0.2em;
|
||||
}
|
||||
|
||||
details.proxy.t1 {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
|
||||
details.proxy:is(.t1, .s1) .pattern-row.t2 {
|
||||
outline: 1px solid blue;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
details.proxy.s1 {
|
||||
outline: 1px solid green;
|
||||
}
|
||||
|
||||
.proxy-div:empty ~ .bulk-edit {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
/* ----- /bulk-edit ----- */
|
||||
|
||||
/* ----- Import ----- */
|
||||
.import textarea {
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
.import summary::marker {
|
||||
color: var(--header);
|
||||
}
|
||||
|
||||
.import details.import-account .content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 3fr 1em;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.import details.import-account input#username {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.import .import-account .account-options {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-column: span 2;
|
||||
justify-content: start;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.import details button.flat {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.import details.import-account button.flat {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
|
||||
/* ----- /import ----- */
|
||||
|
||||
/* ----- Counter ----- */
|
||||
/* .proxy-section {
|
||||
counter-reset: n;
|
||||
}
|
||||
|
||||
.proxy-section details summary :first-child::before {
|
||||
counter-increment: n;
|
||||
content: counter(n);
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
color: var(--dim);
|
||||
} */
|
||||
/* ----- /Counter ----- */
|
||||
781
foxyproxy/content/options.html
Normal file
@@ -0,0 +1,781 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title data-i18n="options">FoxyProxy </title>
|
||||
<link href="../image/icon.svg" rel="icon" type="image/png">
|
||||
<link href="options.css" rel="stylesheet">
|
||||
<script type="module" src="options.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- Navigation -->
|
||||
<input type="radio" name="nav" id="nav1">
|
||||
<input type="radio" name="nav" id="nav2">
|
||||
<input type="radio" name="nav" id="nav3" checked>
|
||||
<input type="radio" name="nav" id="nav4">
|
||||
<input type="radio" name="nav" id="nav5">
|
||||
<input type="radio" name="nav" id="nav6">
|
||||
<input type="radio" name="nav" id="nav7">
|
||||
|
||||
<div class="nav">
|
||||
<nav>
|
||||
<img src="../image/icon.svg" alt="">
|
||||
<label for="nav3" data-i18n="options"></label>
|
||||
<label for="nav4" data-i18n="proxies"></label>
|
||||
<label for="nav5" data-i18n="import"></label>
|
||||
<label for="nav6" data-i18n="tester"></label>
|
||||
<label for="nav7" data-i18n="log"></label>
|
||||
<label for="nav1" data-i18n="help"></label>
|
||||
<label for="nav2" data-i18n="about"></label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar"></div>
|
||||
|
||||
<article>
|
||||
<!-- spinner -->
|
||||
<div class="spinner"></div>
|
||||
|
||||
<!-- popup -->
|
||||
<div class="popup">
|
||||
<span>✕</span>
|
||||
<textarea autocomplete="off" spellcheck="false"></textarea>
|
||||
<select class="popup-log-proxy" autocomplete="off">
|
||||
<option value="" data-i18n="proxy" disabled selected></option>
|
||||
</select>
|
||||
<select class="popup-test-proxy" autocomplete="off">
|
||||
<option value="" data-i18n="proxy" disabled selected></option>
|
||||
</select>
|
||||
<select class="popup-server" autocomplete="off">
|
||||
<option value="" data-i18n="server" disabled selected></option>
|
||||
<option value="https://api.ipify.org/">ipify.org</option>
|
||||
<option value="https://checkip.amazonaws.com/">amazonaws.com (Amazon)</option>
|
||||
<option value="https://icanhazip.com/">icanhazip.com (Cloudflare)</option>
|
||||
<option value="https://ident.me/">ident.me</option>
|
||||
<option value="https://www.l2.io/ip">l2.io</option>
|
||||
<option value="https://www.trackip.net/ip">trackip.net</option>
|
||||
<option value="https://myip.dnsomatic.com/">dnsomatic.com</option>
|
||||
<hr>
|
||||
<option value="https://getfoxyproxy.org/geoip/?raw=1">getfoxyproxy.org</option>
|
||||
<option value="https://ipinfo.io/ip">ipinfo.io</option>
|
||||
<!-- <option value="https://ifconfig.co/">ifconfig.co</option> -->
|
||||
<option value="https://ipecho.net/plain">ipecho.net</option>
|
||||
<option value="https://whatismyip.akamai.com/">akamai.com</option>
|
||||
<option value="https://myexternalip.com/raw">myexternalip.com</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Help/About -->
|
||||
<section><iframe src="help.html"></iframe></section>
|
||||
<section><iframe src="about.html"></iframe></section>
|
||||
|
||||
<!-- user options -->
|
||||
<section class="options">
|
||||
<fieldset>
|
||||
<div class="buttons">
|
||||
<label class="flat" data-i18n="import"><input type="file" id="file" accept=".json"></label>
|
||||
<button type="button" id="export" class="flat" data-i18n="export"></button>
|
||||
<button type="button" class="flat" data-i18n="deleteBrowsingData" id="deleteBrowsingData"></button>
|
||||
<button type="button" class="flat" data-i18n="restoreDefaults"></button>
|
||||
</div>
|
||||
|
||||
<label data-i18n="enableSync"><input type="checkbox" id="sync"></label>
|
||||
<p class="description" data-i18n="enableSyncDescription"></p>
|
||||
|
||||
<label data-i18n="autoBackup"><input type="checkbox" id="autoBackup"></label>
|
||||
<p class="description" data-i18n="autoBackupDescription"></p>
|
||||
|
||||
<div class="theme" style="margin-bottom: 0;">
|
||||
<label data-i18n="limitWebRTC" data-link="#limit-webrtc"></label>
|
||||
<select id="limitWebRTC">
|
||||
<option value="default" data-i18n="default"></option>
|
||||
<option value="default_public_interface_only" data-i18n="publicInterfaceOnly"></option>
|
||||
<option value="default_public_and_private_interfaces" data-i18n="publicPrivateInterfaces"></option>
|
||||
<option value="disable_non_proxied_udp" data-i18n="disableNonProxied"></option>
|
||||
<option value="proxy_only" data-i18n="proxyOnly"></option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="description" data-i18n="limitWebRTCDescription"></p>
|
||||
|
||||
<!-- <label class="firefox" data-i18n="showPatternProxy"><input type="checkbox" id="showPatternProxy"></label>
|
||||
<p class="description firefox" data-i18n="showPatternProxyDescription"></p> -->
|
||||
|
||||
<div class="theme">
|
||||
<label data-i18n="theme" data-link="#theme"></label>
|
||||
<select id="theme">
|
||||
<option value="" data-i18n="default"></option>
|
||||
<option>moonlight</option>
|
||||
<option>moonlight alt</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<label data-i18n="container" data-link="#container"></label>
|
||||
<p class="description" data-i18n="containerDescription"></p>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<label data-i18n="incognito"></label>
|
||||
<select name="incognito">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<label class="firefox"><span data-i18n="container"></span> 1</label>
|
||||
<select class="firefox" name="container-1">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<label class="firefox"><span data-i18n="container"></span> 2</label>
|
||||
<select class="firefox" name="container-2">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<button type="button" class="flat slim firefox" data-i18n="add"></button>
|
||||
|
||||
<!-- container template -->
|
||||
<template>
|
||||
<label class="firefox"><span data-i18n="container"></span> </label>
|
||||
<select class="firefox" name="container-">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- openShortcutSettings Firefox 137 -->
|
||||
<label data-i18n="shortcut" data-link="#keyboard-shortcut"></label> <button type="button" class="plain shortcut-link" style="margin-left: 0.5em; display: none;">🔗</button>
|
||||
<p class="description" data-i18n="shortcutDescription"></p>
|
||||
|
||||
<div class="commands">
|
||||
|
||||
<label data-i18n="setProxy"></label>
|
||||
<select name="setProxy">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<label data-i18n="setTabProxy"></label>
|
||||
<select name="setTabProxy">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<label data-i18n="includeHost"></label>
|
||||
<select name="includeHost">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<label data-i18n="excludeHost"></label>
|
||||
<select name="excludeHost">
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<label data-i18n="globalExclude" data-link="#global-exclude"></label>
|
||||
<p class="description" data-i18n="globalExcludeDescription"></p>
|
||||
<textarea id="passthrough"></textarea>
|
||||
|
||||
|
||||
<button type="submit" data-i18n="saveOptions"></button>
|
||||
</fieldset>
|
||||
|
||||
|
||||
</section>
|
||||
<!-- /user options -->
|
||||
|
||||
<!-- proxy -->
|
||||
<section class="proxy-section">
|
||||
<fieldset>
|
||||
<div class="proxy-top">
|
||||
<button type="button" class="flat" data-i18n="add"></button>
|
||||
<input type="text" class="filter" autocomplete="off" spellcheck="false" placeholder="filter">
|
||||
|
||||
<button type="button" class="flat" data-i18n="ping">🛜 </button>
|
||||
<button type="button" class="flat" data-i18n="test"></button>
|
||||
<button type="button" class="flat" data-i18n="getLocation"></button>
|
||||
</div>
|
||||
|
||||
<div class="proxy-div"></div>
|
||||
|
||||
<div class="bulk-edit">
|
||||
<input type="number" autocomplete="off" min="1" step="1">
|
||||
<input type="number" autocomplete="off" min="1" step="1">
|
||||
<input type="number" autocomplete="off" min="1" step="1">
|
||||
<input type="text" autocomplete="off" spellcheck="false">
|
||||
|
||||
<select autocomplete="off">
|
||||
<option value="" disabled selected data-i18n="bulkEdit"></option>
|
||||
<option value="openAll" data-i18n="openAll"></option>
|
||||
<option value="closeAll" data-i18n="closeAll"></option>
|
||||
<hr>
|
||||
<option value="deleteProxy" data-i18n="deleteProxy"></option>
|
||||
<option value="moveProxy" data-i18n="moveProxy"></option>
|
||||
<option value="movePattern" data-i18n="movePattern"></option>
|
||||
<hr>
|
||||
<option value="setTitle" data-i18n="setTitle"></option>
|
||||
<option value="setType" data-i18n="setType"></option>
|
||||
<option value="setPort" data-i18n="setPort"></option>
|
||||
<option value="setUsername" data-i18n="setUsername"></option>
|
||||
<option value="setPassword" data-i18n="setPassword"></option>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" data-i18n="saveOptions"></button>
|
||||
|
||||
<footer>
|
||||
<span data-i18n="help" data-link="#proxies"></span>
|
||||
</footer>
|
||||
</fieldset>
|
||||
|
||||
<!-- template -->
|
||||
<template>
|
||||
<details class="proxy" draggable="true">
|
||||
<summary>
|
||||
<span>🌎</span>
|
||||
<span></span>
|
||||
<input type="checkbox" class="toggle" data-id="active" checked>
|
||||
<button type="button" class="plain" data-i18n="duplicate|title">⎘</button>
|
||||
<button type="button" class="bin" data-i18n="delete|title"></button>
|
||||
<button type="button" class="up">▲</button>
|
||||
<button type="button" class="down">▼</button>
|
||||
</summary>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="proxy-box">
|
||||
<label data-i18n="title"></label>
|
||||
<input type="text" data-id="title" spellcheck="false" placeholder="title">
|
||||
|
||||
<label data-i18n="hostname"></label>
|
||||
<input type="text" data-id="hostname" spellcheck="false" placeholder="1.2.3.4, www.example.com">
|
||||
|
||||
<label data-i18n="type"></label>
|
||||
<select data-id="type">
|
||||
<optgroup data-i18n="type|label">
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
<option value="socks4">SOCKS4</option>
|
||||
<option value="socks5">SOCKS5</option>
|
||||
<option value="quic">QUIC</option>
|
||||
<option value="pac" data-flag="🌎">PAC</option>
|
||||
<option value="direct" data-hostname="DIRECT" data-flag="⮕">DIRECT</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup data-i18n="server|label">
|
||||
<!-- By default v2rayA will open 20170 (socks5), 20171 (http), 20172 (http with shunt rules) ports through the core -->
|
||||
<option value="http" data-hostname="127.0.0.1" data-port="8080" data-flag="🖥️">Burp</option>
|
||||
<option value="http" data-hostname="127.0.0.1" data-port="8118" data-flag="🖥️">Privoxy</option>
|
||||
<option value="socks5" data-hostname="127.0.0.1" data-port="60351" data-flag="🖥️">Psiphon</option>
|
||||
<option value="socks5" data-hostname="127.0.0.1" data-port="1080" data-flag="🖥️">Shadowsocks</option>
|
||||
<option value="socks5" data-hostname="127.0.0.1" data-port="9050" data-flag="🖥️">TOR</option>
|
||||
|
||||
<option value="http" data-hostname="127.0.0.1" data-port="2081" data-flag="🖥️">NekoRay HTTP</option>
|
||||
<option value="socks5" data-hostname="127.0.0.1" data-port="2080" data-flag="🖥️">NekoRay SOCKS5</option>
|
||||
|
||||
<option value="http" data-hostname="127.0.0.1" data-port="20171" data-flag="🖥️">V2RayA HTTP</option>
|
||||
<option value="http" data-hostname="127.0.0.1" data-port="20172" data-flag="🖥️">V2RayA HTTP (with Rule)</option>
|
||||
<option value="socks5" data-hostname="127.0.0.1" data-port="20170" data-flag="🖥️">V2RayA SOCKS5</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
<label data-i18n="port"></label>
|
||||
<input type="text" data-id="port" placeholder="3128">
|
||||
|
||||
<label data-i18n="country"></label>
|
||||
<select data-id="cc">
|
||||
<option value=""> </option>
|
||||
<option value="AF">🇦🇫 Afghanistan</option>
|
||||
<option value="AL">🇦🇱 Albania</option>
|
||||
<option value="DZ">🇩🇿 Algeria</option>
|
||||
<option value="AS">🇦🇸 American Samoa</option>
|
||||
<option value="AD">🇦🇩 Andorra</option>
|
||||
<option value="AO">🇦🇴 Angola</option>
|
||||
<option value="AI">🇦🇮 Anguilla</option>
|
||||
<option value="AQ">🇦🇶 Antarctica</option>
|
||||
<option value="AG">🇦🇬 Antigua and Barbuda</option>
|
||||
<option value="AR">🇦🇷 Argentina</option>
|
||||
<option value="AM">🇦🇲 Armenia</option>
|
||||
<option value="AW">🇦🇼 Aruba</option>
|
||||
<option value="AU">🇦🇺 Australia</option>
|
||||
<option value="AT">🇦🇹 Austria</option>
|
||||
<option value="AZ">🇦🇿 Azerbaijan</option>
|
||||
<option value="AX">🇦🇽 Åland Islands</option>
|
||||
<option value="BS">🇧🇸 Bahamas</option>
|
||||
<option value="BH">🇧🇭 Bahrain</option>
|
||||
<option value="BD">🇧🇩 Bangladesh</option>
|
||||
<option value="BB">🇧🇧 Barbados</option>
|
||||
<option value="BY">🇧🇾 Belarus</option>
|
||||
<option value="BE">🇧🇪 Belgium</option>
|
||||
<option value="BZ">🇧🇿 Belize</option>
|
||||
<option value="BJ">🇧🇯 Benin</option>
|
||||
<option value="BM">🇧🇲 Bermuda</option>
|
||||
<option value="BT">🇧🇹 Bhutan</option>
|
||||
<option value="BO">🇧🇴 Bolivia, Plurinational State of</option>
|
||||
<option value="BQ">🇧🇶 Bonaire, Sint Eustatius and Saba</option>
|
||||
<option value="BA">🇧🇦 Bosnia and Herzegovina</option>
|
||||
<option value="BW">🇧🇼 Botswana</option>
|
||||
<option value="BV">🇧🇻 Bouvet Island</option>
|
||||
<option value="BR">🇧🇷 Brazil</option>
|
||||
<option value="IO">🇮🇴 British Indian Ocean Territory</option>
|
||||
<option value="VG">🇻🇬 British Virgin Islands</option>
|
||||
<option value="BN">🇧🇳 Brunei Darussalam</option>
|
||||
<option value="BG">🇧🇬 Bulgaria</option>
|
||||
<option value="BF">🇧🇫 Burkina Faso</option>
|
||||
<option value="BI">🇧🇮 Burundi</option>
|
||||
<option value="CV">🇨🇻 Cabo Verde</option>
|
||||
<option value="KH">🇰🇭 Cambodia</option>
|
||||
<option value="CM">🇨🇲 Cameroon</option>
|
||||
<option value="CA">🇨🇦 Canada</option>
|
||||
<option value="KY">🇰🇾 Cayman Islands</option>
|
||||
<option value="CF">🇨🇫 Central African Republic</option>
|
||||
<option value="TD">🇹🇩 Chad</option>
|
||||
<option value="CL">🇨🇱 Chile</option>
|
||||
<option value="CN">🇨🇳 China</option>
|
||||
<option value="CX">🇨🇽 Christmas Island</option>
|
||||
<option value="CC">🇨🇨 Cocos [Keeling] Islands</option>
|
||||
<option value="CO">🇨🇴 Colombia</option>
|
||||
<option value="KM">🇰🇲 Comoros</option>
|
||||
<option value="CG">🇨🇬 Congo, Republic of the</option>
|
||||
<option value="CD">🇨🇩 Congo, the Democratic Republic of the</option>
|
||||
<option value="CK">🇨🇰 Cook Islands</option>
|
||||
<option value="CR">🇨🇷 Costa Rica</option>
|
||||
<option value="HR">🇭🇷 Croatia</option>
|
||||
<option value="CU">🇨🇺 Cuba</option>
|
||||
<option value="CW">🇨🇼 Curaçao</option>
|
||||
<option value="CY">🇨🇾 Cyprus</option>
|
||||
<option value="CZ">🇨🇿 Czechia</option>
|
||||
<option value="TL">🇹🇱 Democratic Republic of Timor-Leste</option>
|
||||
<option value="DK">🇩🇰 Denmark</option>
|
||||
<option value="DJ">🇩🇯 Djibouti</option>
|
||||
<option value="DM">🇩🇲 Dominica</option>
|
||||
<option value="DO">🇩🇴 Dominican Republic</option>
|
||||
<option value="EC">🇪🇨 Ecuador</option>
|
||||
<option value="EG">🇪🇬 Egypt</option>
|
||||
<option value="SV">🇸🇻 El Salvador</option>
|
||||
<option value="GQ">🇬🇶 Equatorial Guinea</option>
|
||||
<option value="ER">🇪🇷 Eritrea</option>
|
||||
<option value="EE">🇪🇪 Estonia</option>
|
||||
<option value="SZ">🇸🇿 Eswatini</option>
|
||||
<option value="ET">🇪🇹 Ethiopia</option>
|
||||
<option value="EU">🇪🇺 European Union</option>
|
||||
<option value="FK">🇫🇰 Falkland Islands (Malvinas)</option>
|
||||
<option value="FO">🇫🇴 Faroe Islands</option>
|
||||
<option value="FJ">🇫🇯 Fiji</option>
|
||||
<option value="FI">🇫🇮 Finland</option>
|
||||
<option value="FR">🇫🇷 France</option>
|
||||
<option value="GF">🇬🇫 French Guiana</option>
|
||||
<option value="PF">🇵🇫 French Polynesia</option>
|
||||
<option value="TF">🇹🇫 French Southern Territories</option>
|
||||
<option value="GA">🇬🇦 Gabon</option>
|
||||
<option value="GM">🇬🇲 Gambia</option>
|
||||
<option value="GE">🇬🇪 Georgia</option>
|
||||
<option value="DE">🇩🇪 Germany</option>
|
||||
<option value="GH">🇬🇭 Ghana</option>
|
||||
<option value="GI">🇬🇮 Gibraltar</option>
|
||||
<option value="GR">🇬🇷 Greece</option>
|
||||
<option value="GL">🇬🇱 Greenland</option>
|
||||
<option value="GD">🇬🇩 Grenada</option>
|
||||
<option value="GP">🇬🇵 Guadeloupe</option>
|
||||
<option value="GU">🇬🇺 Guam</option>
|
||||
<option value="GT">🇬🇹 Guatemala</option>
|
||||
<option value="GG">🇬🇬 Guernsey</option>
|
||||
<option value="GN">🇬🇳 Guinea</option>
|
||||
<option value="GW">🇬🇼 Guinea-Bissau</option>
|
||||
<option value="GY">🇬🇾 Guyana</option>
|
||||
<option value="HT">🇭🇹 Haiti</option>
|
||||
<option value="HM">🇭🇲 Heard Island and McDonald Islands</option>
|
||||
<option value="HN">🇭🇳 Honduras</option>
|
||||
<option value="HK">🇭🇰 Hong Kong</option>
|
||||
<option value="HU">🇭🇺 Hungary</option>
|
||||
<option value="IS">🇮🇸 Iceland</option>
|
||||
<option value="IN">🇮🇳 India</option>
|
||||
<option value="ID">🇮🇩 Indonesia</option>
|
||||
<option value="IR">🇮🇷 Iran, Islamic Republic Of</option>
|
||||
<option value="IQ">🇮🇶 Iraq</option>
|
||||
<option value="IE">🇮🇪 Ireland</option>
|
||||
<option value="IM">🇮🇲 Isle of Man</option>
|
||||
<option value="IL">🇮🇱 Israel</option>
|
||||
<option value="IT">🇮🇹 Italy</option>
|
||||
<option value="CI">🇨🇮 Ivory Coast (Côte d'Ivoire)</option>
|
||||
<option value="JM">🇯🇲 Jamaica</option>
|
||||
<option value="JP">🇯🇵 Japan</option>
|
||||
<option value="JE">🇯🇪 Jersey</option>
|
||||
<option value="JO">🇯🇴 Jordan (Hashemite Kingdom of Jordan)</option>
|
||||
<option value="KZ">🇰🇿 Kazakhstan</option>
|
||||
<option value="KE">🇰🇪 Kenya</option>
|
||||
<option value="KI">🇰🇮 Kiribati</option>
|
||||
<option value="XK">🇽🇰 Kosovo</option>
|
||||
<option value="KW">🇰🇼 Kuwait</option>
|
||||
<option value="KG">🇰🇬 Kyrgyzstan</option>
|
||||
<option value="LA">🇱🇦 Laos (Lao People's Democratic Republic)</option>
|
||||
<option value="LV">🇱🇻 Latvia</option>
|
||||
<option value="LB">🇱🇧 Lebanon</option>
|
||||
<option value="LS">🇱🇸 Lesotho</option>
|
||||
<option value="LR">🇱🇷 Liberia</option>
|
||||
<option value="LY">🇱🇾 Libya</option>
|
||||
<option value="LI">🇱🇮 Liechtenstein</option>
|
||||
<option value="LT">🇱🇹 Lithuania</option>
|
||||
<option value="LU">🇱🇺 Luxembourg</option>
|
||||
<option value="MO">🇲🇴 Macao</option>
|
||||
<option value="MG">🇲🇬 Madagascar</option>
|
||||
<option value="MW">🇲🇼 Malawi</option>
|
||||
<option value="MY">🇲🇾 Malaysia</option>
|
||||
<option value="MV">🇲🇻 Maldives</option>
|
||||
<option value="ML">🇲🇱 Mali</option>
|
||||
<option value="MT">🇲🇹 Malta</option>
|
||||
<option value="MH">🇲🇭 Marshall Islands</option>
|
||||
<option value="MQ">🇲🇶 Martinique</option>
|
||||
<option value="MR">🇲🇷 Mauritania</option>
|
||||
<option value="MU">🇲🇺 Mauritius</option>
|
||||
<option value="YT">🇾🇹 Mayotte</option>
|
||||
<option value="MX">🇲🇽 Mexico</option>
|
||||
<option value="FM">🇫🇲 Micronesia, Federated States of</option>
|
||||
<option value="MD">🇲🇩 Moldova, Republic of</option>
|
||||
<option value="MC">🇲🇨 Monaco</option>
|
||||
<option value="MN">🇲🇳 Mongolia</option>
|
||||
<option value="ME">🇲🇪 Montenegro</option>
|
||||
<option value="MS">🇲🇸 Montserrat</option>
|
||||
<option value="MA">🇲🇦 Morocco</option>
|
||||
<option value="MZ">🇲🇿 Mozambique</option>
|
||||
<option value="MM">🇲🇲 Myanmar</option>
|
||||
<option value="NA">🇳🇦 Namibia</option>
|
||||
<option value="NR">🇳🇷 Nauru</option>
|
||||
<option value="NP">🇳🇵 Nepal</option>
|
||||
<option value="NL">🇳🇱 Netherlands</option>
|
||||
<option value="NC">🇳🇨 New Caledonia</option>
|
||||
<option value="NZ">🇳🇿 New Zealand</option>
|
||||
<option value="NI">🇳🇮 Nicaragua</option>
|
||||
<option value="NE">🇳🇪 Niger</option>
|
||||
<option value="NG">🇳🇬 Nigeria</option>
|
||||
<option value="NU">🇳🇺 Niue</option>
|
||||
<option value="NF">🇳🇫 Norfolk Island</option>
|
||||
<option value="KP">🇰🇵 North Korea</option>
|
||||
<option value="MK">🇲🇰 North Macedonia</option>
|
||||
<option value="MP">🇲🇵 Northern Mariana Islands</option>
|
||||
<option value="NO">🇳🇴 Norway</option>
|
||||
<option value="OM">🇴🇲 Oman</option>
|
||||
<option value="PK">🇵🇰 Pakistan</option>
|
||||
<option value="PW">🇵🇼 Palau</option>
|
||||
<option value="PS">🇵🇸 Palestine</option>
|
||||
<option value="PA">🇵🇦 Panama</option>
|
||||
<option value="PG">🇵🇬 Papua New Guinea</option>
|
||||
<option value="PY">🇵🇾 Paraguay</option>
|
||||
<option value="PE">🇵🇪 Peru</option>
|
||||
<option value="PH">🇵🇭 Philippines</option>
|
||||
<option value="PN">🇵🇳 Pitcairn Islands</option>
|
||||
<option value="PL">🇵🇱 Poland</option>
|
||||
<option value="PT">🇵🇹 Portugal</option>
|
||||
<option value="PR">🇵🇷 Puerto Rico</option>
|
||||
<option value="QA">🇶🇦 Qatar</option>
|
||||
<option value="LT">🇱🇹 Republic of Lithuania</option>
|
||||
<option value="RO">🇷🇴 Romania</option>
|
||||
<option value="RU">🇷🇺 Russia (Russian Federation)</option>
|
||||
<option value="RW">🇷🇼 Rwanda</option>
|
||||
<option value="RE">🇷🇪 Réunion</option>
|
||||
<option value="BL">🇧🇱 Saint Barthélemy</option>
|
||||
<option value="SH">🇸🇭 Saint Helena</option>
|
||||
<option value="LC">🇱🇨 Saint Lucia</option>
|
||||
<option value="MF">🇲🇫 Saint Martin (French part)</option>
|
||||
<option value="PM">🇵🇲 Saint Pierre and Miquelon</option>
|
||||
<option value="VC">🇻🇨 Saint Vincent and the Grenadines</option>
|
||||
<option value="WS">🇼🇸 Samoa</option>
|
||||
<option value="SM">🇸🇲 San Marino</option>
|
||||
<option value="SA">🇸🇦 Saudi Arabia</option>
|
||||
<option value="SN">🇸🇳 Senegal</option>
|
||||
<option value="RS">🇷🇸 Serbia</option>
|
||||
<option value="SC">🇸🇨 Seychelles</option>
|
||||
<option value="SL">🇸🇱 Sierra Leone</option>
|
||||
<option value="SG">🇸🇬 Singapore</option>
|
||||
<option value="SX">🇸🇽 Sint Maarten (Dutch part)</option>
|
||||
<option value="SK">🇸🇰 Slovakia</option>
|
||||
<option value="SI">🇸🇮 Slovenia</option>
|
||||
<option value="SB">🇸🇧 Solomon Islands</option>
|
||||
<option value="SO">🇸🇴 Somalia</option>
|
||||
<option value="ZA">🇿🇦 South Africa</option>
|
||||
<option value="GS">🇬🇸 South Georgia and the South Sandwich Islands</option>
|
||||
<option value="KR">🇰🇷 South Korea</option>
|
||||
<option value="SS">🇸🇸 South Sudan</option>
|
||||
<option value="ES">🇪🇸 Spain</option>
|
||||
<option value="LK">🇱🇰 Sri Lanka</option>
|
||||
<option value="KN">🇰🇳 St Kitts and Nevis</option>
|
||||
<option value="SD">🇸🇩 Sudan</option>
|
||||
<option value="SR">🇸🇷 Suriname</option>
|
||||
<option value="SJ">🇸🇯 Svalbard and Jan Mayen</option>
|
||||
<option value="SE">🇸🇪 Sweden</option>
|
||||
<option value="CH">🇨🇭 Switzerland</option>
|
||||
<option value="SY">🇸🇾 Syria</option>
|
||||
<option value="ST">🇸🇹 São Tomé and Príncipe</option>
|
||||
<option value="TW">🇹🇼 Taiwan</option>
|
||||
<option value="TJ">🇹🇯 Tajikistan</option>
|
||||
<option value="TZ">🇹🇿 Tanzania</option>
|
||||
<option value="TH">🇹🇭 Thailand</option>
|
||||
<option value="TG">🇹🇬 Togo</option>
|
||||
<option value="TK">🇹🇰 Tokelau</option>
|
||||
<option value="TO">🇹🇴 Tonga</option>
|
||||
<option value="TT">🇹🇹 Trinidad and Tobago</option>
|
||||
<option value="TN">🇹🇳 Tunisia</option>
|
||||
<option value="TM">🇹🇲 Turkmenistan</option>
|
||||
<option value="TC">🇹🇨 Turks and Caicos Islands</option>
|
||||
<option value="TV">🇹🇻 Tuvalu</option>
|
||||
<option value="TR">🇹🇷 Türkiye</option>
|
||||
<option value="UM">🇺🇲 U.S. Minor Outlying Islands</option>
|
||||
<option value="VI">🇻🇮 U.S. Virgin Islands</option>
|
||||
<option value="UG">🇺🇬 Uganda</option>
|
||||
<option value="UA">🇺🇦 Ukraine</option>
|
||||
<option value="AE">🇦🇪 United Arab Emirates</option>
|
||||
<option value="GB">🇬🇧 United Kingdom</option>
|
||||
<option value="US">🇺🇸 United States of America</option>
|
||||
<option value="UY">🇺🇾 Uruguay</option>
|
||||
<option value="UZ">🇺🇿 Uzbekistan</option>
|
||||
<option value="VU">🇻🇺 Vanuatu</option>
|
||||
<option value="VA">🇻🇦 Vatican City</option>
|
||||
<option value="VE">🇻🇪 Venezuela</option>
|
||||
<option value="VN">🇻🇳 Vietnam</option>
|
||||
<option value="WF">🇼🇫 Wallis and Futuna</option>
|
||||
<option value="EH">🇪🇭 Western Sahara</option>
|
||||
<option value="YE">🇾🇪 Yemen</option>
|
||||
<option value="ZM">🇿🇲 Zambia</option>
|
||||
<option value="ZW">🇿🇼 Zimbabwe</option>
|
||||
</select>
|
||||
|
||||
<label data-i18n="username"></label>
|
||||
<input type="text" data-id="username" spellcheck="false" placeholder="username">
|
||||
|
||||
<label data-i18n="city"></label>
|
||||
<input type="text" data-id="city" spellcheck="false" placeholder="city">
|
||||
|
||||
<label data-i18n="password"></label>
|
||||
<span class="password">
|
||||
<input type="password" data-id="password" spellcheck="false" placeholder="****">
|
||||
<button type="button" class="plain" data-i18n="togglePassword|title">👁</button>
|
||||
</span>
|
||||
|
||||
<label data-i18n="color"></label>
|
||||
<span>
|
||||
<input type="color" data-id="color" value="#ff9900">
|
||||
<button type="button" class="plain random" data-i18n="random|title">⟳</button>
|
||||
</span>
|
||||
|
||||
<label data-type="pac">PAC URL</label>
|
||||
<span class="pac">
|
||||
<input type="url" data-id="pac" data-type="pac" placeholder="PAC URL">
|
||||
<label class="pointer chrome" data-i18n="storeLocally"><input type="checkbox"></label>
|
||||
<button type="button" class="flat slim" data-i18n="view"></button>
|
||||
</span>
|
||||
|
||||
<label class="firefox" data-i18n="proxyDNS"></label>
|
||||
<input type="checkbox" class="toggle firefox" data-id="proxyDNS" checked>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pattern-head">
|
||||
<button type="button" class="flat slim" data-i18n="add|title"><span class="plus">➕</span></button>
|
||||
<span data-i18n="proxyByPatterns"></span>
|
||||
<span data-i18n="title"></span>
|
||||
<span data-i18n="pattern"></span>
|
||||
<label class="plain" data-i18n="import|title"><input type="file" accept=".json">📥</label>
|
||||
<button type="button" class="plain" data-i18n="export|title">📤</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-box"></div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="pattern-row">
|
||||
<select>
|
||||
<option value="" disabled selected> </option>
|
||||
<option data-type="wildcard" value="*" data-i18n="all"></option>
|
||||
<option data-type="wildcard" value="http://">HTTP</option>
|
||||
<option data-type="wildcard" value="https://">HTTPS</option>
|
||||
<option data-type="regex" value="^(http|ws)s?://[^.]+/" data-i18n="plainHost"></option>
|
||||
<option data-type="regex" value="^(http|ws)s?://10(\.\d+){3}/">10.*.*.*</option>
|
||||
<option data-type="regex" value="^(http|ws)s?://127(\.\d+){3}/">127.*.*.*</option>
|
||||
<option data-type="regex" value="^(http|ws)s?://172\.16(\.\d+){2}/">172.16.*.*</option>
|
||||
<option data-type="regex" value="^(http|ws)s?://192\.168(\.\d+){2}/">192.168.*.*</option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="include" data-i18n="include"></option>
|
||||
<option value="exclude" data-i18n="exclude"></option>
|
||||
<option value="tabProxy" data-i18n="tabProxy"></option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="wildcard" data-i18n="wildcard"></option>
|
||||
<option value="match">Match</option>
|
||||
<option value="regex">Reg Exp</option>
|
||||
</select>
|
||||
<input type="text" spellcheck="false" placeholder="title">
|
||||
<input type="text" spellcheck="false" placeholder="://example.com/">
|
||||
<input type="checkbox" class="toggle" checked>
|
||||
<button type="button" class="test" data-i18n="test|title"></button>
|
||||
<button type="button" class="bin" data-i18n="delete|title"></button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- /template -->
|
||||
</section>
|
||||
<!-- /proxy -->
|
||||
|
||||
<!-- import -->
|
||||
<section class="import">
|
||||
<fieldset>
|
||||
<details class="import-account">
|
||||
<summary data-i18n="importFoxyProxyAccount"></summary>
|
||||
<div class="content">
|
||||
<label data-i18n="username"></label>
|
||||
<input id="username" type="text" spellcheck="false" placeholder="username">
|
||||
|
||||
<label data-i18n="password"></label>
|
||||
<input id="password" type="password" spellcheck="false" placeholder="*****">
|
||||
<button type="button" class="plain" data-i18n="togglePassword|title">👁</button>
|
||||
|
||||
<label data-i18n="options"></label>
|
||||
<div class="account-options">
|
||||
<select>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
<option value="socks5">SOCKS5</option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="hostname" data-i18n="hostname"></option>
|
||||
<option value="ip">IP</option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="main" data-i18n="mainServer"></option>
|
||||
<option value="alt" data-i18n="altServer"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<button type="button" class="flat" data-i18n="import"></button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="import-from-url">
|
||||
<summary data-i18n="importFromURL"></summary>
|
||||
<div class="content">
|
||||
<input type="url" placeholder="https://example.com/settings.json">
|
||||
<button type="button" class="flat" data-i18n="import"></button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="import-proxy-list">
|
||||
<summary data-i18n="importProxyList"></summary>
|
||||
<div class="content">
|
||||
<textarea></textarea>
|
||||
<button type="button" class="flat" data-i18n="import"></button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="import-from-older">
|
||||
<summary data-i18n="importFromOlderVersions"></summary>
|
||||
<div class="content">
|
||||
<label class="flat" data-i18n="import"><input type="file" accept=".json, .xml"></label>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<footer>
|
||||
<span data-i18n="help" data-link="#import"></span>
|
||||
</footer>
|
||||
|
||||
|
||||
<!-- no need for submit SAVE button as all imports forward to Proxies Tab -->
|
||||
<!-- <button type="submit" data-i18n="saveOptions"></button> -->
|
||||
</fieldset>
|
||||
</section>
|
||||
<!-- /import -->
|
||||
|
||||
<!-- tester -->
|
||||
<section class="tester">
|
||||
<fieldset>
|
||||
|
||||
<h3 data-i18n="pattern" data-link="#pattern-guide"></h3>
|
||||
<p class="description" data-i18n="testerDescription"></p>
|
||||
<div class="tester-pattern">
|
||||
<select>
|
||||
<option value="wildcard" data-i18n="wildcard"></option>
|
||||
<option value="match">Match</option>
|
||||
<option value="regex">Reg Exp</option>
|
||||
</select>
|
||||
<input type="text" spellcheck="false" value="*://*.example.com/">
|
||||
</div>
|
||||
<pre contenteditable="true">https://example.com/
|
||||
https://example.org/
|
||||
http://help.example.com/
|
||||
https://help.example.com/abc
|
||||
https://google.com/ref=help.example.com</pre>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="button" class="flat" data-i18n="test"></button>
|
||||
<button type="button" class="flat" data-i18n="back">◁ </button>
|
||||
</div>
|
||||
|
||||
|
||||
<h3 data-i18n="proxyByPatterns"></h3>
|
||||
<p class="description" data-i18n="proxyByPatternsDescription"></p>
|
||||
<input type="url" spellcheck="false" pattern="https?://.*" placeholder="https://example.com/abc">
|
||||
<pre></pre>
|
||||
<button type="button" class="flat proxyByPatterns" data-i18n="test"></button>
|
||||
|
||||
<footer>
|
||||
<span data-i18n="help" data-link="#pattern-tester"></span>
|
||||
</footer>
|
||||
|
||||
</fieldset>
|
||||
</section>
|
||||
<!-- /tester -->
|
||||
|
||||
|
||||
<!-- log -->
|
||||
<section class="log">
|
||||
<div class="domain">
|
||||
<input type="text" autocomplete="off" spellcheck="false" placeholder="example.com">
|
||||
<button type="button" class="flat slim" data-i18n="getAssociatedDomains"></button>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<caption class="firefox" data-i18n="notAvailable"></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 1.5em;"></th>
|
||||
<th style="width: 5em;" data-i18n="time"></th>
|
||||
<th style="width: 1em;"><img src="../image/container.svg" alt=""></th>
|
||||
<th style="width: 3em;" data-i18n="method"></th>
|
||||
<th style="width: 3em;" data-i18n="type"></th>
|
||||
<th data-i18n="documentURL" data-link="#log"></th>
|
||||
<th data-i18n="url" data-link="#log"></th>
|
||||
<th><span data-i18n="proxy"></span> <span data-i18n="title"></span></th>
|
||||
<th style="width: 3em;" data-i18n="type"></th>
|
||||
<th data-i18n="hostname"></th>
|
||||
<th style="width: 3em;" data-i18n="port"></th>
|
||||
<th data-i18n="pattern"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- template -->
|
||||
<template>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</template>
|
||||
</section>
|
||||
<!-- /log -->
|
||||
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
302
foxyproxy/content/options.js
Normal file
@@ -0,0 +1,302 @@
|
||||
import {pref, App} from './app.js';
|
||||
import {Proxies} from './options-proxies.js';
|
||||
import {ImportExport} from './import-export.js';
|
||||
import {ImportUrl} from './import-url.js';
|
||||
import {ImportOlder} from './import-older.js';
|
||||
import {Flag} from './flag.js';
|
||||
import {ProgressBar} from './progress-bar.js';
|
||||
import {Nav} from './nav.js';
|
||||
import './get-location.js';
|
||||
import './incognito-access.js';
|
||||
import './browsing-data.js';
|
||||
import './webrtc.js';
|
||||
import './options-filter.js';
|
||||
import './bulk-edit.js';
|
||||
import './drag-drop.js';
|
||||
import './ping.js';
|
||||
import './test.js';
|
||||
import './import-account.js';
|
||||
import './import-list.js';
|
||||
import './show.js';
|
||||
import './i18n.js';
|
||||
import './theme.js';
|
||||
|
||||
// ---------- User Preferences -----------------------------
|
||||
await App.getPref();
|
||||
|
||||
// ---------- Options --------------------------------------
|
||||
class Options {
|
||||
|
||||
static {
|
||||
// --- keyboard Shortcut
|
||||
this.commands = document.querySelectorAll('.options .commands select');
|
||||
|
||||
// --- global passthrough
|
||||
this.passthrough = document.getElementById('passthrough');
|
||||
|
||||
// --- buttons
|
||||
document.querySelector('.options button[data-i18n="restoreDefaults"]').addEventListener('click', () => this.restoreDefaults());
|
||||
|
||||
this.init(['sync', 'autoBackup', 'theme', 'showPatternProxy', 'passthrough']);
|
||||
}
|
||||
|
||||
static init(keys = Object.keys(pref)) {
|
||||
// defaults to pref keys
|
||||
this.prefNode = document.querySelectorAll('#' + keys.join(',#'));
|
||||
// submit button
|
||||
document.querySelectorAll('button[type="submit"]').forEach(i => i.addEventListener('click', () => this.check()));
|
||||
|
||||
this.process();
|
||||
}
|
||||
|
||||
static process(save) {
|
||||
// 'save' is only set when clicking the button to save options
|
||||
this.prefNode.forEach(node => {
|
||||
// value: 'select-one', 'textarea', 'text', 'number'
|
||||
const attr = node.type === 'checkbox' ? 'checked' : 'value';
|
||||
save ? pref[node.id] = node[attr] : node[attr] = pref[node.id];
|
||||
});
|
||||
|
||||
// update saved pref
|
||||
save && !ProgressBar.show() && browser.storage.local.set(pref);
|
||||
this.fillContainerCommands(save);
|
||||
}
|
||||
|
||||
static async check() {
|
||||
// not for storage.managed
|
||||
if (pref.managed) { return; }
|
||||
|
||||
// --- global exclude, clean up, remove path, remove duplicates
|
||||
const passthrough = this.passthrough.value.trim();
|
||||
const [separator] = passthrough.match(/[\s,;]+/) || ['\n'];
|
||||
const arr = passthrough.split(/[\s,;]+/).filter(Boolean)
|
||||
.map(i => /[\d.]+\/\d+/.test(i) ? i : i.replace(/(?<=[a-z\d])\/[^\s,;]*/gi, ''));
|
||||
this.passthrough.value = [...new Set(arr)].join(separator);
|
||||
pref.passthrough = this.passthrough.value;
|
||||
|
||||
// --- check and build proxies & patterns
|
||||
const data = [];
|
||||
const cache = {};
|
||||
// using for loop to be able to break early
|
||||
for (const item of document.querySelectorAll('div.proxy-div details')) {
|
||||
const pxy = await Proxies.getProxyDetails(item);
|
||||
if (!pxy) { return; }
|
||||
|
||||
data.push(pxy);
|
||||
|
||||
// cache to update Proxies cache
|
||||
const id = pxy.type === 'pac' ? pxy.pac : `${pxy.hostname}:${pxy.port}`;
|
||||
cache[id] = pxy;
|
||||
}
|
||||
|
||||
// no errors, update pref.data
|
||||
pref.data = data;
|
||||
|
||||
// helper: remove if proxy is deleted or disabled
|
||||
const checkSelect = i => i.value && !cache[i.value]?.active && (i.value = '');
|
||||
|
||||
// --- container proxy
|
||||
const containerList = document.querySelectorAll('.options .container select');
|
||||
const container = {};
|
||||
containerList.forEach(i => {
|
||||
checkSelect(i);
|
||||
i.value && (container[i.name] = i.value);
|
||||
});
|
||||
// set to pref
|
||||
pref.container = container;
|
||||
|
||||
// --- keyboard shortcut proxy
|
||||
const commands = {};
|
||||
this.commands.forEach(i => {
|
||||
checkSelect(i);
|
||||
commands[i.name] = i.value;
|
||||
});
|
||||
// set to pref
|
||||
pref.commands = commands;
|
||||
|
||||
// --- check mode
|
||||
// get from storage in case it was changed while options page has been open
|
||||
let {mode} = await browser.storage.local.get({mode: 'disable'});
|
||||
switch (true) {
|
||||
case pref.mode.includes('://') && !/:\d+$/.test(pref.mode) && !pref.data.some(i => i.active && i.type === 'pac' && mode === i.pac):
|
||||
case pref.mode.includes(':') && !pref.data.some(i => i.active && i.type !== 'pac' && mode === `${i.hostname}:${i.port}`):
|
||||
case pref.mode === 'pattern' && !pref.data.some(i => i.active && i.include[0]):
|
||||
mode = 'disable';
|
||||
break;
|
||||
}
|
||||
pref.mode = mode;
|
||||
|
||||
// --- save options
|
||||
this.process(true);
|
||||
|
||||
// --- update Proxy
|
||||
// check 'prefers-color-scheme' since it is not available in background service worker
|
||||
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
browser.runtime.sendMessage({id: 'setProxy', pref, dark});
|
||||
|
||||
// --- Auto Backup
|
||||
pref.autoBackup && ImportExport.export(pref, false, `${browser.i18n.getMessage('extensionName')}/`);
|
||||
|
||||
// --- Sync
|
||||
this.sync(pref);
|
||||
}
|
||||
|
||||
// https://github.com/w3c/webextensions/issues/510
|
||||
// Proposal: Increase maximum item size in Storage sync quotas
|
||||
static sync(pref) {
|
||||
if (!pref.sync) { return; }
|
||||
|
||||
// convert array to object {...data} to avoid sync maximum item size limit
|
||||
const obj = {...pref.data};
|
||||
|
||||
// add other sync properties
|
||||
App.syncProperties.forEach(i => obj[i] = pref[i]);
|
||||
|
||||
// save changes to sync
|
||||
browser.storage.sync.set(obj)
|
||||
.then(() => {
|
||||
// delete left-over proxies
|
||||
browser.storage.sync.get()
|
||||
.then(syncObj => {
|
||||
// get & delete numerical keys that are equal or larger than data length, the rest are overwritten
|
||||
const del = Object.keys(syncObj).filter(i => /^\d+$/.test(i) && i * 1 >= pref.data.length);
|
||||
del[0] && browser.storage.sync.remove(del);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
App.notify(browser.i18n.getMessage('syncError') + '\n\n' + error.message);
|
||||
// disabling sync option to avoid repeated errors
|
||||
document.getElementById('sync').checked = false;
|
||||
browser.storage.local.set({sync: false});
|
||||
});
|
||||
}
|
||||
|
||||
static restoreDefaults() {
|
||||
if (!confirm(browser.i18n.getMessage('restoreDefaultsConfirm'))) { return; }
|
||||
|
||||
const db = App.getDefaultPref();
|
||||
Object.keys(db).forEach(i => pref[i] = db[i]);
|
||||
this.process();
|
||||
Proxies.process();
|
||||
}
|
||||
|
||||
static makeProxyOption() {
|
||||
// create proxy option
|
||||
const docFrag = document.createDocumentFragment();
|
||||
// filter out PAC, limit to 50
|
||||
pref.data.filter(i => i.active && i.type !== 'pac').slice(0, 50).forEach(i => {
|
||||
const flag = Flag.get(i.cc);
|
||||
const value = `${i.hostname}:${i.port}`;
|
||||
const opt = new Option(flag + ' ' + (i.title || value), value);
|
||||
// supported on Chrome, not on Firefox
|
||||
// opt.style.color = item.color;
|
||||
|
||||
docFrag.append(opt.cloneNode(true));
|
||||
});
|
||||
|
||||
return docFrag;
|
||||
}
|
||||
|
||||
// --- container & commands
|
||||
static fillContainerCommands(save) {
|
||||
// create proxy option
|
||||
const docFrag = this.makeProxyOption();
|
||||
|
||||
// not when clicking save
|
||||
if (!save) {
|
||||
this.addCustomContainer();
|
||||
|
||||
// populate the template select
|
||||
this.containerSelect.append(docFrag.cloneNode(true));
|
||||
|
||||
// add custom containers, sort by number
|
||||
const list = [...document.querySelectorAll('.options .container select')].map(i => i.name);
|
||||
Object.keys(pref.container).filter(i => !list.includes(i)).sort()
|
||||
.forEach(i => this.addContainer(i.substring(10)));
|
||||
}
|
||||
|
||||
const containerList = document.querySelectorAll('.options .container select');
|
||||
|
||||
// reset
|
||||
this.clearSelect(containerList);
|
||||
this.clearSelect(this.commands);
|
||||
|
||||
containerList.forEach(i => {
|
||||
i.append(docFrag.cloneNode(true));
|
||||
pref.container[i.name] && (i.value = pref.container[i.name]);
|
||||
});
|
||||
|
||||
this.commands.forEach(i => {
|
||||
i.append(docFrag.cloneNode(true));
|
||||
pref.commands[i.name] && (i.value = pref.commands[i.name]);
|
||||
});
|
||||
|
||||
// help fill log select elements
|
||||
document.querySelectorAll('.popup select:not(.popup-server)').forEach(i => i.append(docFrag.cloneNode(true)));
|
||||
}
|
||||
|
||||
static clearSelect(elem) {
|
||||
// remove children except the first one
|
||||
elem.forEach(i => i.replaceChildren(i.firstElementChild));
|
||||
}
|
||||
|
||||
static addCustomContainer() {
|
||||
// using generic names
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1386673
|
||||
// Make Contextual Identity extensions be an optional permission
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1947602
|
||||
// Allow limited read-only access to contextualIdentities.query without permission
|
||||
|
||||
[this.containerLabel, this.containerSelect] =
|
||||
document.querySelector('.options .container template').content.children;
|
||||
|
||||
this.containerButton = document.querySelector('.options .container button');
|
||||
this.containerButton.addEventListener('click', () => this.addContainer(prompt(browser.i18n.getMessage('addContainerPrompt'))));
|
||||
}
|
||||
|
||||
static addContainer(n) {
|
||||
n *= 1;
|
||||
if (!n || this.hasContainer(n)) { return; }
|
||||
|
||||
const label = this.containerLabel.cloneNode(true);
|
||||
const select = this.containerSelect.cloneNode(true);
|
||||
|
||||
label.append(n);
|
||||
select.name = `container-${n}`;
|
||||
this.containerButton.before(label, select);
|
||||
}
|
||||
|
||||
static hasContainer(n) {
|
||||
return document.querySelector(`.options .container select[name="container-${n}"]`);
|
||||
}
|
||||
}
|
||||
// ---------- /Options -------------------------------------
|
||||
|
||||
// ---------- Proxies --------------------------------------
|
||||
Proxies.process(pref);
|
||||
|
||||
// ---------- Import From URL ------------------------------
|
||||
ImportUrl.init(pref, () => {
|
||||
// set options after the pref update, update page display, show Proxy tab
|
||||
Options.process();
|
||||
Proxies.process(pref);
|
||||
Nav.get('proxies');
|
||||
});
|
||||
|
||||
// ---------- Import Older Preferences ---------------------
|
||||
ImportOlder.init(pref, () => {
|
||||
// set options after the pref update, update page display, show Proxy tab
|
||||
Options.process();
|
||||
Proxies.process(pref);
|
||||
Nav.get('proxies');
|
||||
});
|
||||
|
||||
// ---------- Import/Export Preferences --------------------
|
||||
ImportExport.init(pref, () => {
|
||||
// set options after the pref update, update page display
|
||||
Options.process();
|
||||
Proxies.process(pref);
|
||||
});
|
||||
|
||||
// ---------- Navigation -----------------------------------
|
||||
Nav.get();
|
||||
21
foxyproxy/content/pac.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import {Spinner} from './spinner.js';
|
||||
import {Popup} from './options-popup.js';
|
||||
|
||||
export class PAC {
|
||||
|
||||
static async view(url) {
|
||||
if (!url) { return; }
|
||||
|
||||
const text = await this.get(url);
|
||||
Popup.show(text);
|
||||
}
|
||||
|
||||
static async get(url) {
|
||||
Spinner.show();
|
||||
const text = await fetch(url)
|
||||
.then(response => response.text())
|
||||
.catch(error => error);
|
||||
Spinner.hide();
|
||||
return text;
|
||||
}
|
||||
}
|
||||
165
foxyproxy/content/pattern.js
Normal file
@@ -0,0 +1,165 @@
|
||||
export class Pattern {
|
||||
|
||||
// showError from options.js, not from migrate.js
|
||||
static validate(str, type, showError) {
|
||||
// --- match pattern
|
||||
if (type === 'match') {
|
||||
if (this.validMatchPattern(str)) { return true; }
|
||||
|
||||
// not valid
|
||||
if (showError) {
|
||||
const error = this.checkMatchPattern(str);
|
||||
error && alert([browser.i18n.getMessage('regexError'), str, error].join('\n'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// --- wildcard & regex
|
||||
const pat = this.get(str, type);
|
||||
try {
|
||||
new RegExp(pat);
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
showError && alert([browser.i18n.getMessage('regexError'), str, error].join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
static get(str, type) {
|
||||
return type === 'wildcard' ? this.convertWildcard(str) :
|
||||
type === 'match' ? this.convertMatchPattern(str) : str;
|
||||
}
|
||||
|
||||
// convert wildcard to regex string
|
||||
static convertWildcard(str) {
|
||||
// catch all
|
||||
if (str === '*') { return '\\w+'; }
|
||||
|
||||
// no need to add scheme as search parameters are encoded url=https%3A%2F%2F
|
||||
// escape regular expression special characters, minus * ?
|
||||
return str.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
||||
.replace(/^\*|\*$/g, '') // trim start/end *
|
||||
.replaceAll('*', '.*')
|
||||
.replaceAll('?', '.');
|
||||
}
|
||||
|
||||
// convert match pattern to regex string
|
||||
static convertMatchPattern(str) {
|
||||
// catch all
|
||||
if (str === '<all_urls>') { return '\\w+'; }
|
||||
|
||||
// escape regular expression special characters, minus *
|
||||
str = str.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
||||
.replace('*://', '.+://') // convert * scheme
|
||||
.replace('://*\\.', '://(.+\\.)?') // match domains & subdomains
|
||||
.replaceAll('*', '.*');
|
||||
|
||||
// match pattern matches the whole URL
|
||||
return '^' + str + '$';
|
||||
}
|
||||
|
||||
static checkMatchPattern(str) {
|
||||
// catch all
|
||||
if (str === '<all_urls>') { return; }
|
||||
|
||||
const [, scheme, host] = str.match(/^(.+):\/\/([^/]+)\/(.*)$/) || [];
|
||||
|
||||
switch (true) {
|
||||
case !scheme || !host:
|
||||
// Invalid Pattern
|
||||
return browser.i18n.getMessage('invalidPatternError');
|
||||
|
||||
case !['*', 'http', 'https', 'ws', 'wss'].includes(scheme):
|
||||
// "*" in scheme must be the only character | Unsupported scheme
|
||||
const msg = scheme.includes('*') ? 'schemeError' : 'unsupportedSchemeError';
|
||||
return browser.i18n.getMessage(msg);
|
||||
|
||||
case host.substring(1).includes('*'):
|
||||
// "*" in host must be at the start
|
||||
return browser.i18n.getMessage('hostError');
|
||||
|
||||
case host.startsWith('*') && !host.startsWith('*.'):
|
||||
// "*" in host must be the only character or be followed by "."
|
||||
return browser.i18n.getMessage('hostDotError');
|
||||
|
||||
case host.includes(':'):
|
||||
// Host must not include a port number
|
||||
return browser.i18n.getMessage('portError');
|
||||
}
|
||||
}
|
||||
|
||||
// --- test match pattern validity
|
||||
static validMatchPattern(p) {
|
||||
// file: is not valid for proxying purpose
|
||||
return p === '<all_urls>' ||
|
||||
/^(https?|\*):\/\/(\*|\*\.[^*:/]+|[^*:/]+)\/.*$/i.test(p);
|
||||
}
|
||||
|
||||
static getPassthrough(str) {
|
||||
if (!str) { return [[], [], []]; }
|
||||
|
||||
// RegExp string
|
||||
const regex = [];
|
||||
// 10.0.0.0/24 -> [ip, mask] e.g ['10.0.0.0', '255.255.255.0']
|
||||
const ipMask = [];
|
||||
// 10.0.0.0/24 -> [start, end] e.g. ['010000000000', '010000000255']
|
||||
const stEnd = [];
|
||||
|
||||
str.split(/[\s,;]+/).forEach(i => {
|
||||
// The literal string <local> matches simple hostnames (no dots)
|
||||
if (i === '<local>') {
|
||||
regex.push('.+://[^.]+/');
|
||||
return;
|
||||
}
|
||||
|
||||
// --- CIDR
|
||||
const [, ip, , mask] = i.match(/^(\d+(\.\d+){3})\/(\d+)$/) || [];
|
||||
if (ip && mask) {
|
||||
const netmask = this.getNetmask(mask);
|
||||
ipMask.push(ip, netmask);
|
||||
stEnd.push(this.getRange(ip, netmask));
|
||||
return;
|
||||
}
|
||||
|
||||
// --- pattern
|
||||
i = i.replaceAll('.', '\\.') // literal '.'
|
||||
.replaceAll('*', '.*'); // wildcard
|
||||
|
||||
// starting with '.'
|
||||
i.startsWith('\\.') && (i = '.+://.+' + i);
|
||||
// add scheme
|
||||
!i.includes('://') && (i = '.+://' + i);
|
||||
// add start assertion
|
||||
// !i.startsWith('^') && (i = '^' + i);
|
||||
// add pathname
|
||||
i += '/';
|
||||
regex.push(i);
|
||||
});
|
||||
|
||||
return [regex, ipMask, stEnd];
|
||||
}
|
||||
|
||||
// ---------- CIDR ---------------------------------------
|
||||
// convert mask to netmask
|
||||
static getNetmask(mask) {
|
||||
return [...Array(4)].map(() => {
|
||||
const n = Math.min(mask, 8);
|
||||
mask -= n;
|
||||
return 256 - Math.pow(2, 8 - n);
|
||||
}).join('.');
|
||||
}
|
||||
|
||||
// convert to padded start & end
|
||||
static getRange(ip, mask) {
|
||||
// ip array
|
||||
let st = ip.split('.');
|
||||
// mask array
|
||||
const ma = mask.split('.');
|
||||
// netmask wildcard array
|
||||
let end = st.map((v, i) => Math.min(v - ma[i] + 255, 255) + '');
|
||||
st = st.map(i => i.padStart(3, '0')).join('');
|
||||
end = end.map(i => i.padStart(3, '0')).join('');
|
||||
|
||||
return [st, end];
|
||||
}
|
||||
}
|
||||
45
foxyproxy/content/ping.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import {Popup} from './options-popup.js';
|
||||
|
||||
// ---------- Ping (Side Effect) ---------------------------
|
||||
class Ping {
|
||||
|
||||
static {
|
||||
document.querySelector('.proxy-top button[data-i18n="ping"]').addEventListener('click', () => this.process());
|
||||
}
|
||||
|
||||
static async process() {
|
||||
let {data} = await browser.storage.local.get({data: []});
|
||||
data = data.filter(i => i.active);
|
||||
if (!data[0]) { return; }
|
||||
|
||||
// --- text formatting
|
||||
const n = 4;
|
||||
const pType = Math.max(...data.map(i => i.type.length)) + n;
|
||||
const pHost = Math.max(...data.map(i =>
|
||||
(i.title || i.pac || `${i.hostname}:${parseInt(i.port)}`).length)) + n;
|
||||
// performance.now() Firefox 280160 | Chrome 447156.4000000004
|
||||
const format = n => new Intl.NumberFormat().format(n.toFixed()).padStart(8, ' ');
|
||||
const dash = '--- --'.padStart(11, ' ');
|
||||
|
||||
data.forEach(i => {
|
||||
const t = performance.now();
|
||||
const host = `${i.hostname}:${parseInt(i.port)}`;
|
||||
const url = i.pac || (i.type.startsWith('http') ? `${i.type}://${host}/` : `http://${host}/`);
|
||||
const target = i.type.padEnd(pType, ' ') + (i.title || i.pac || host).padEnd(pHost, ' ');
|
||||
|
||||
if (['direct'].includes(i.type)) {
|
||||
Popup.show(`${target}${dash} ${i.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Chrome a network request timeouts at 300 seconds, while in Firefox at 90 seconds.
|
||||
// AbortSignal.timeout FF100, Ch124
|
||||
fetch(url, {method: 'HEAD', cache: 'no-store', signal: AbortSignal.timeout(5000)})
|
||||
.then(r => {
|
||||
const st = ![200, 400].includes(r.status) ? ` ${r.status} ${r.statusText}` : '';
|
||||
Popup.show(`${target}${format(performance.now() - t)} ms${st}`);
|
||||
})
|
||||
.catch(e => Popup.show(`${target}${dash} ${e.message}`));
|
||||
});
|
||||
}
|
||||
}
|
||||
24
foxyproxy/content/popup-filter.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// ---------- Filter Proxy (Side Effect) -------------------
|
||||
class Filter {
|
||||
|
||||
static {
|
||||
this.list = document.querySelector('div.list');
|
||||
const filter = document.querySelector('.filter');
|
||||
filter.addEventListener('input', e => this.filterProxy(e));
|
||||
}
|
||||
|
||||
static filterProxy(e) {
|
||||
const str = e.target.value.toLowerCase().trim();
|
||||
const elem = [...this.list.children].slice(2); // not the first 2
|
||||
if (!str) {
|
||||
elem.forEach(i => i.classList.remove('off'));
|
||||
return;
|
||||
}
|
||||
|
||||
elem.forEach(item => {
|
||||
const title = item.children[1].textContent;
|
||||
const host = item.children[3].value; // input radio
|
||||
item.classList.toggle('off', ![title, host].some(i => i.toLowerCase().includes(str)));
|
||||
});
|
||||
}
|
||||
}
|
||||
205
foxyproxy/content/popup.css
Normal file
@@ -0,0 +1,205 @@
|
||||
@import 'default.css';
|
||||
@import 'theme.css';
|
||||
|
||||
/* ----- Light Theme ----- */
|
||||
:root {
|
||||
--filter: opacity(0.4) grayscale(1);
|
||||
--selected: #fec8;
|
||||
}
|
||||
|
||||
/* for the default theme */
|
||||
:root:not([class]) {
|
||||
--nav-bg: #630;
|
||||
}
|
||||
|
||||
/* ----- Dark Theme ----- */
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--filter: opacity(1) grayscale(1);
|
||||
--selected: #222;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----- General ----- */
|
||||
body {
|
||||
opacity: 0;
|
||||
font-size: 12px;
|
||||
width: 25em;
|
||||
background-color: var(--bg);
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
/*
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1883896
|
||||
Remove UA styles for :is(article, aside, nav, section) h1 (Nightly only) Firefox 125
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1885509
|
||||
Remove UA styles for :is(article, aside, nav, section) h1 (staged rollout)
|
||||
*/
|
||||
h1 {
|
||||
color: var(--nav-color);
|
||||
background-color: var(--nav-bg);
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h1 img {
|
||||
width: 1.5em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/* ----- Buttons ----- */
|
||||
div.popup-buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
column-gap: 0.1em;
|
||||
}
|
||||
|
||||
button {
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.8em;
|
||||
/* font-weight: bold; */
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: var(--btn-hover);
|
||||
}
|
||||
/* ----- /Buttons ----- */
|
||||
|
||||
/* ----- Main Display ----- */
|
||||
div.list {
|
||||
padding-top: 0.5em;
|
||||
min-height: 15em;
|
||||
max-height: 30em;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
div.list label {
|
||||
display: grid;
|
||||
grid-template-columns: 2.5em auto 1fr;
|
||||
column-gap: 0.5em;
|
||||
padding: 0.2em 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.list label:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
div.list label.off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.list label:has(input[name="server"]:checked) {
|
||||
background-color: var(--selected);
|
||||
}
|
||||
|
||||
.flag img {
|
||||
width: 1.2em;
|
||||
}
|
||||
|
||||
.flag img.off {
|
||||
filter: var(--filter);
|
||||
}
|
||||
|
||||
.flag {
|
||||
grid-row: span 2;
|
||||
font-size: 1.8em;
|
||||
line-height: 1em;
|
||||
place-self: start center;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--header);
|
||||
font-size: 1.2em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.port {
|
||||
color: var(--dim);
|
||||
place-self: end start;
|
||||
}
|
||||
|
||||
.data {
|
||||
grid-column: span 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
color: var(--dim);
|
||||
}
|
||||
|
||||
.data.off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[name="server"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* --- more section --- */
|
||||
summary {
|
||||
background-color: var(--alt-bg);
|
||||
padding: 0.2em 0.5em;
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
|
||||
div.host {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0.5em;
|
||||
gap: 0.3em 0.5em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.host input.filter {
|
||||
background: url('../image/filter.svg') no-repeat left 0.5em center/1em;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
div.host button {
|
||||
background-color: unset;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--color);
|
||||
font-weight: normal;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
div.host button:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
|
||||
/* ----- show/hide elements ----- */
|
||||
/* --- Chrome --- */
|
||||
.chrome .firefox {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* ---managed --- */
|
||||
.managed .local {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* --- Basic --- */
|
||||
.basic .not-basic {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* --- scheme --- */
|
||||
.not-http select.http,
|
||||
.not-tab-proxy .tab-proxy {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
/* ----- /show/hide elements ----- */
|
||||
71
foxyproxy/content/popup.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Popup</title>
|
||||
<link href="popup.css" rel="stylesheet">
|
||||
<script type="module" src="popup.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<article>
|
||||
<section>
|
||||
<h1 data-i18n="extensionName"><img src="../image/icon.svg" alt=""> </h1>
|
||||
|
||||
<div class="list">
|
||||
<label class="pattern not-basic">
|
||||
<span class="flag"><img src="../image/power.svg" alt=""></span>
|
||||
<span class="title" data-i18n="proxyByPatterns"></span>
|
||||
<input type="radio" name="server" value="pattern">
|
||||
</label>
|
||||
|
||||
<label class="disable">
|
||||
<span class="flag"><img class="off" src="../image/power.svg" alt=""></span>
|
||||
<span class="title" data-i18n="disable"></span>
|
||||
<input type="radio" name="server" value="disable" checked>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary data-i18n="more"></summary>
|
||||
<div class="host">
|
||||
<input type="text" class="filter" autocomplete="off" spellcheck="false" placeholder="filter">
|
||||
|
||||
<select id="tabProxy" class="firefox tab-proxy">
|
||||
<option value="" data-i18n="tabProxy" disabled selected></option>
|
||||
<option value=""> </option>
|
||||
</select>
|
||||
|
||||
<select id="includeHost" class="local http not-basic">
|
||||
<option value="" data-i18n="includeHost" disabled selected></option>
|
||||
</select>
|
||||
|
||||
<select id="excludeHost" class="local http not-basic">
|
||||
<option value="" data-i18n="excludeHost" disabled selected></option>
|
||||
</select>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="popup-buttons">
|
||||
<button type="button" data-i18n="options"></button>
|
||||
<button type="button" data-i18n="log"></button>
|
||||
<button type="button" data-i18n="ip"></button>
|
||||
<button type="button" data-i18n="location"></button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- template -->
|
||||
<template>
|
||||
<label>
|
||||
<span class="flag"></span>
|
||||
<span class="title"></span>
|
||||
<span class="port"></span>
|
||||
<input type="radio" name="server">
|
||||
<span class="data"></span>
|
||||
</label>
|
||||
</template>
|
||||
<!-- /template -->
|
||||
</section>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
170
foxyproxy/content/popup.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import {pref, App} from './app.js';
|
||||
import {Location} from './location.js';
|
||||
import {Flag} from './flag.js';
|
||||
import './popup-filter.js';
|
||||
import './show.js';
|
||||
import './i18n.js';
|
||||
|
||||
// ---------- User Preferences -----------------------------
|
||||
await App.getPref();
|
||||
|
||||
// ---------- Popup ----------------------------------------
|
||||
class Popup {
|
||||
|
||||
static {
|
||||
// --- theme
|
||||
pref.theme && (document.documentElement.className = pref.theme);
|
||||
// show after
|
||||
document.body.style.opacity = 1;
|
||||
|
||||
document.querySelectorAll('button').forEach(i => i.addEventListener('click', e => this.processButtons(e)));
|
||||
|
||||
this.list = document.querySelector('div.list');
|
||||
|
||||
// --- Include/Exclude Host (not for storage.managed)
|
||||
this.includeHost = document.querySelector('select#includeHost');
|
||||
!pref.managed && this.includeHost.addEventListener('change', e => this.includeExclude(e));
|
||||
this.excludeHost = document.querySelector('select#excludeHost');
|
||||
!pref.managed && this.excludeHost.addEventListener('change', e => this.includeExclude(e));
|
||||
|
||||
// --- Tab Proxy (firefox only)
|
||||
this.tabProxy = document.querySelector('select#tabProxy');
|
||||
App.firefox && this.tabProxy.addEventListener('change', e => {
|
||||
if (!this.tab) { return; }
|
||||
|
||||
const {value, selectedOptions} = e.target;
|
||||
const proxy = value && this.proxyCache[selectedOptions[0].dataset.index];
|
||||
browser.runtime.sendMessage({id: 'setTabProxy', proxy, tab: this.tab});
|
||||
});
|
||||
|
||||
// disable buttons on storage.managed
|
||||
pref.managed && document.body.classList.add('managed');
|
||||
|
||||
// --- store details open toggle
|
||||
const details = document.querySelector('details');
|
||||
// defaults to true
|
||||
details.open = localStorage.getItem('more') !== 'false';
|
||||
details.addEventListener('toggle', () => localStorage.setItem('more', details.open));
|
||||
|
||||
this.process();
|
||||
}
|
||||
|
||||
static includeExclude(e) {
|
||||
const {id, value} = e.target;
|
||||
if (!value) { return; }
|
||||
// proxy object reference to pref is lost in chrome when sent from popup.js
|
||||
browser.runtime.sendMessage({id, pref, host: value, tab: this.tab});
|
||||
// reset select option
|
||||
e.target.selectedIndex = 0;
|
||||
}
|
||||
|
||||
static checkProxyByPatterns() {
|
||||
// check if there are patterns
|
||||
if (!pref.data.some(i => i.active && (i.include[0] || i.tabProxy?.[0]))) {
|
||||
// hide option if there are no patterns
|
||||
this.list.children[0].style.display = 'none';
|
||||
// show as disable
|
||||
pref.mode === 'pattern' && (pref.mode = 'disable');
|
||||
}
|
||||
|
||||
pref.mode === 'pattern' && (this.list.children[0].children[2].checked = true);
|
||||
}
|
||||
|
||||
static async process() {
|
||||
this.checkProxyByPatterns();
|
||||
|
||||
const labelTemplate = document.querySelector('template').content.firstElementChild;
|
||||
const docFrag = document.createDocumentFragment();
|
||||
|
||||
pref.data.filter(i => i.active).forEach(i => {
|
||||
const id = i.type === 'pac' ? i.pac : `${i.hostname}:${i.port}`;
|
||||
const label = labelTemplate.cloneNode(true);
|
||||
const [flag, title, port, radio, data] = label.children;
|
||||
flag.textContent = Flag.show(i);
|
||||
title.textContent = i.title || i.hostname;
|
||||
port.textContent = !i.title ? i.port : '';
|
||||
radio.value = i.type === 'direct' ? 'direct' : id;
|
||||
radio.checked = id === pref.mode;
|
||||
data.textContent = [i.city, Location.get(i.cc)].filter(Boolean).join(', ') || ' ';
|
||||
docFrag.append(label);
|
||||
});
|
||||
|
||||
this.list.append(docFrag);
|
||||
this.list.addEventListener('click', e =>
|
||||
// fires twice (click & label -> input)
|
||||
e.target.name === 'server' && this.processSelect(e.target.value, e)
|
||||
);
|
||||
|
||||
// --- Add Hosts to select
|
||||
// used to find proxy, filter out PAC, limit to 10
|
||||
this.proxyCache = pref.data.filter(i => i.active && i.type !== 'pac').slice(0, 10);
|
||||
|
||||
this.proxyCache.forEach((i, index) => {
|
||||
const flag = Flag.show(i);
|
||||
const value = `${i.hostname}:${i.port}`;
|
||||
const opt = new Option(flag + ' ' + (i.title || value), value);
|
||||
opt.dataset.index = index;
|
||||
// supported on Chrome, not on Firefox
|
||||
// opt.style.color = item.color;
|
||||
docFrag.append(opt);
|
||||
});
|
||||
|
||||
this.includeHost.append(docFrag.cloneNode(true));
|
||||
this.excludeHost.append(docFrag.cloneNode(true));
|
||||
this.tabProxy.append(docFrag);
|
||||
|
||||
// get active tab
|
||||
[this.tab] = await browser.tabs.query({currentWindow: true, active: true});
|
||||
|
||||
// --- show/hide selects
|
||||
document.body.classList.toggle('not-http', !this.tab.url.startsWith('http'));
|
||||
|
||||
// Check Tab proxy (Firefox only)
|
||||
const allowedTabProxy = App.firefox && App.allowedTabProxy(this.tab.url);
|
||||
allowedTabProxy && this.checkTabProxy();
|
||||
document.body.classList.toggle('not-tab-proxy', !allowedTabProxy);
|
||||
}
|
||||
|
||||
static checkTabProxy() {
|
||||
browser.runtime.sendMessage({id: 'getTabProxy', tab: this.tab})
|
||||
.then(i => i && (this.tabProxy.value = `${i.hostname}:${i.port}`));
|
||||
}
|
||||
|
||||
static processSelect(mode, e) {
|
||||
// disregard re-click
|
||||
if (mode === pref.mode) { return; }
|
||||
// not for storage.managed
|
||||
if (pref.managed) { return; }
|
||||
|
||||
// check 'prefers-color-scheme' since it is not available in background service worker
|
||||
const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// save mode
|
||||
pref.mode = mode;
|
||||
browser.storage.local.set({mode});
|
||||
browser.runtime.sendMessage({id: 'setProxy', pref, dark, noDataChange: true});
|
||||
}
|
||||
|
||||
static processButtons(e) {
|
||||
switch (e.target.dataset.i18n) {
|
||||
case 'options':
|
||||
browser.runtime.openOptionsPage();
|
||||
break;
|
||||
|
||||
case 'location':
|
||||
browser.tabs.create({url: 'https://getfoxyproxy.org/geoip/'});
|
||||
break;
|
||||
|
||||
case 'ip':
|
||||
// sending message to the background script to complete even if popup gets closed
|
||||
browser.runtime.sendMessage({id: 'getIP'});
|
||||
break;
|
||||
|
||||
case 'log':
|
||||
browser.tabs.create({url: '/content/options.html?log'});
|
||||
break;
|
||||
}
|
||||
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
22
foxyproxy/content/progress-bar.css
Normal file
@@ -0,0 +1,22 @@
|
||||
/* ----- Progress Bar ----- */
|
||||
.progress-bar {
|
||||
opacity: 0;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
background: #3bb3e0 linear-gradient(124deg, #ff2400, #e81d1d, #e8b71d, #e3e81d, #1de840, #1ddde8, #2b1de8, #dd00f3, #dd00f3);
|
||||
background-size: 900% 900%;
|
||||
transition: opacity 1s ease-in-out, width 0s ease-in-out 1s;
|
||||
animation: rainbow 3s ease infinite;
|
||||
}
|
||||
|
||||
.progress-bar.on {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
transition: opacity 1s ease-in-out, width 2s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes rainbow {
|
||||
0% { background-position: 0% 100%; }
|
||||
50% { background-position: 100% 200%; }
|
||||
100% { background-position: 0% 100%; }
|
||||
}
|
||||
10
foxyproxy/content/progress-bar.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// ---------- Progress Bar ---------------------------------
|
||||
export class ProgressBar {
|
||||
|
||||
static bar = document.querySelector('.progress-bar');
|
||||
|
||||
static show() {
|
||||
this.bar.classList.toggle('on');
|
||||
setTimeout(() => this.bar.classList.toggle('on'), 2000);
|
||||
}
|
||||
}
|
||||
350
foxyproxy/content/proxy.js
Normal file
@@ -0,0 +1,350 @@
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/common/extensions/api/proxy.json
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/schemas/proxy.json
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1804693
|
||||
// Setting single proxy for all fails
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1495756
|
||||
// Issue 1495756: Support bypassList for PAC scripts in the chrome.proxy API
|
||||
// https://chromium-review.googlesource.com/c/chromium/src/+/5227338
|
||||
// Implement bypassList for PAC scripts in chrome.proxy API
|
||||
// Chrome bypassList applies to 'fixed_servers', not 'pac_script' or URL
|
||||
// Firefox passthrough applies to all set in proxy.settings.set, i.e. PAC URL
|
||||
// manual bypass list:
|
||||
// Chrome: pac_script data, not possible for URL
|
||||
// Firefox proxy.onRequest
|
||||
|
||||
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/parent/ext-proxy.js#236
|
||||
// throw new ExtensionError("proxy.settings is not supported on android.");
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1725981
|
||||
// Support proxy.settings API on Android
|
||||
|
||||
import {App} from './app.js';
|
||||
import {Authentication} from './authentication.js';
|
||||
import {OnRequest} from './on-request.js';
|
||||
import {Location} from './location.js';
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Action} from './action.js';
|
||||
import {Menus} from './menus.js';
|
||||
|
||||
export class Proxy {
|
||||
|
||||
static {
|
||||
// from popup.js & options.js
|
||||
browser.runtime.onMessage.addListener((...e) => this.onMessage(...e));
|
||||
}
|
||||
|
||||
static onMessage(message) {
|
||||
// noDataChange comes from popup.js & test.js
|
||||
const {id, pref, host, proxy, dark, tab, noDataChange} = message;
|
||||
switch (id) {
|
||||
case 'setProxy':
|
||||
Action.dark = dark;
|
||||
this.set(pref, noDataChange);
|
||||
break;
|
||||
|
||||
case 'includeHost':
|
||||
case 'excludeHost':
|
||||
// proxy object reference to pref is lost in chrome when sent from popup.js
|
||||
const pxy = pref.data.find(i => i.active && host === `${i.hostname}:${i.port}`);
|
||||
this.includeHost(pref, pxy, tab, id);
|
||||
break;
|
||||
|
||||
case 'setTabProxy':
|
||||
OnRequest.setTabProxy(tab, proxy);
|
||||
break;
|
||||
|
||||
case 'getTabProxy':
|
||||
// need to return a promise for 'getTabProxy' from popup.js
|
||||
return Promise.resolve(OnRequest.tabProxy[tab.id]);
|
||||
|
||||
case 'getIP':
|
||||
this.getIP();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static async set(pref, noDataChange) {
|
||||
// check if proxy.settings is controlled_by_this_extension
|
||||
const conf = await this.getSettings();
|
||||
// not controlled_by_this_extension
|
||||
if (!conf) { return; }
|
||||
|
||||
// --- update authentication data
|
||||
noDataChange || Authentication.init(pref.data);
|
||||
|
||||
// --- update menus
|
||||
noDataChange || Menus.init(pref);
|
||||
|
||||
// --- check mode
|
||||
switch (true) {
|
||||
// no proxy, set to disable
|
||||
case !pref.data[0]:
|
||||
pref.mode = 'disable';
|
||||
break;
|
||||
|
||||
// no include pattern, set proxy to the first entry
|
||||
case pref.mode === 'pattern' && !pref.data.some(i => i.include[0] || i.exclude[0]):
|
||||
const pxy = pref.data[0];
|
||||
pref.mode = pxy.type === 'pac' ? pxy.pac : `${pxy.hostname}:${pxy.port}`;
|
||||
break;
|
||||
}
|
||||
|
||||
App.firefox ? Firefox.set(pref, conf) : Chrome.set(pref);
|
||||
Action.set(pref);
|
||||
}
|
||||
|
||||
static async getSettings() {
|
||||
if (App.android) { return {}; }
|
||||
|
||||
const conf = await browser.proxy.settings.get({});
|
||||
|
||||
// https://developer.chrome.com/docs/extensions/mv3/manifest/icons/
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=29683
|
||||
// Issue 29683: Extension icons should support SVG (Dec 8, 2009)
|
||||
// SVG is not supported by Chrome
|
||||
// Firefox: If each one of imageData and path is one of undefined, null or empty object,
|
||||
// the global icon will be reset to the manifest icon
|
||||
// Chrome -> Error: Either the path or imageData property must be specified.
|
||||
|
||||
// check if proxy.settings is controlled_by_this_extension
|
||||
const control = ['controlled_by_this_extension', 'controllable_by_this_extension'].includes(conf.levelOfControl);
|
||||
const path = control ? `/image/icon.png` : `/image/icon-off.png`;
|
||||
browser.action.setIcon({path});
|
||||
!control && browser.action.setTitle({title: browser.i18n.getMessage('controlledByOtherExtensions')});
|
||||
|
||||
// return null if Chrome and no control, allow Firefox to continue regardless
|
||||
return !App.firefox && !control ? null : conf;
|
||||
}
|
||||
|
||||
// ---------- Include/Exclude Host ----------------------
|
||||
static includeHost(pref, proxy, tab, inc) {
|
||||
// not for storage.managed
|
||||
if (pref.managed) { return; }
|
||||
|
||||
const url = this.getURL(tab.url);
|
||||
if (!url) { return; }
|
||||
|
||||
const pattern = url.origin + '/';
|
||||
const pat = {
|
||||
active: true,
|
||||
pattern,
|
||||
title: url.hostname,
|
||||
type: 'wildcard',
|
||||
};
|
||||
|
||||
inc === 'includeHost' ? proxy.include.push(pat) : proxy.exclude.push(pat);
|
||||
browser.storage.local.set({data: pref.data});
|
||||
// update Proxy, noDataChange
|
||||
pref.mode === 'pattern' && proxy.active && this.set(pref, true);
|
||||
}
|
||||
|
||||
static getURL(str) {
|
||||
const url = new URL(str);
|
||||
// unacceptable URLs
|
||||
if (!['http:', 'https:'].includes(url.protocol)) { return; }
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
// from popup.js
|
||||
static getIP() {
|
||||
fetch('https://getfoxyproxy.org/webservices/lookup.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!Object.keys(data)) {
|
||||
App.notify(browser.i18n.getMessage('error'));
|
||||
return;
|
||||
}
|
||||
|
||||
const [ip, {cc, city}] = Object.entries(data)[0];
|
||||
const text = [ip, city, Location.get(cc)].filter(Boolean).join('\n');
|
||||
App.notify(text);
|
||||
})
|
||||
.catch(error => App.notify(browser.i18n.getMessage('error') + '\n\n' + error.message));
|
||||
}
|
||||
}
|
||||
|
||||
class Firefox {
|
||||
|
||||
static async set(pref, conf) {
|
||||
// update OnRequest
|
||||
OnRequest.init(pref);
|
||||
|
||||
if (App.android) { return; }
|
||||
|
||||
// incognito access
|
||||
if (!await browser.extension.isAllowedIncognitoAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// retain settings as Network setting is partially customisable
|
||||
const value = conf.value;
|
||||
|
||||
switch (true) {
|
||||
// https://github.com/foxyproxy/browser-extension/issues/47
|
||||
// Unix domain socket SOCKS proxy support
|
||||
// regard file:///run/user/1000/proxy.socks:9999 as normal proxy (not PAC)
|
||||
|
||||
// sanitizeNoProxiesPref() "network.proxy.no_proxies_on"
|
||||
// https://searchfox.org/mozilla-central/source/browser/components/preferences/dialogs/connection.js#338
|
||||
|
||||
// --- Proxy Auto-Configuration (PAC) URL
|
||||
case pref.mode.includes('://') && !/:\d+$/.test(pref.mode):
|
||||
value.proxyType = 'autoConfig';
|
||||
value.autoConfigUrl = pref.mode;
|
||||
// convert to standard comma-separated
|
||||
value.passthrough = pref.passthrough.split(/[\s,;]+/).join(', ');
|
||||
value.proxyDNS = pref.proxyDNS;
|
||||
// no error if levelOfControl: "controlled_by_other_extensions"
|
||||
browser.proxy.settings.set({value});
|
||||
break;
|
||||
|
||||
// --- disable, direct, pattern, or single proxy
|
||||
default:
|
||||
browser.proxy.settings.clear({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Chrome {
|
||||
|
||||
static async set(pref) {
|
||||
// https://developer.chrome.com/docs/extensions/reference/types/
|
||||
// Scope and life cycle: regular | regular_only | incognito_persistent | incognito_session_only
|
||||
const config = {value: {}, scope: 'regular'};
|
||||
switch (true) {
|
||||
case pref.mode === 'disable':
|
||||
case pref.mode === 'direct':
|
||||
config.value.mode = 'system';
|
||||
break;
|
||||
|
||||
// --- Proxy Auto-Configuration (PAC) URL
|
||||
case pref.mode.includes('://') && !/:\d+$/.test(pref.mode):
|
||||
config.value.mode = 'pac_script';
|
||||
config.value.pacScript = {mandatory: true};
|
||||
config.value.pacScript.url = pref.mode;
|
||||
break;
|
||||
|
||||
// --- single proxy
|
||||
case pref.mode.includes(':'):
|
||||
const pxy = this.findProxy(pref);
|
||||
if (!pxy) { return; }
|
||||
|
||||
config.value.mode = 'fixed_servers';
|
||||
config.value.rules = this.getSingleProxyRule(pref, pxy);
|
||||
break;
|
||||
|
||||
// --- pattern
|
||||
default:
|
||||
config.value.mode = 'pac_script';
|
||||
config.value.pacScript = {mandatory: true};
|
||||
config.value.pacScript.data = this.getPacString(pref);
|
||||
}
|
||||
|
||||
browser.proxy.settings.set(config);
|
||||
|
||||
// --- incognito
|
||||
this.setIncognito(pref);
|
||||
}
|
||||
|
||||
static findProxy(pref, host = pref.mode) {
|
||||
return pref.data.find(i =>
|
||||
i.active && i.type !== 'pac' && i.hostname && host === `${i.hostname}:${i.port}`);
|
||||
}
|
||||
|
||||
static async setIncognito(pref) {
|
||||
// incognito access
|
||||
if (!await browser.extension.isAllowedIncognitoAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pxy = pref.container?.incognito && this.findProxy(pref, pref.container?.incognito);
|
||||
if (!pxy) {
|
||||
// unset incognito
|
||||
browser.proxy.settings.clear({scope: 'incognito_persistent'});
|
||||
return;
|
||||
}
|
||||
|
||||
const config = {value: {}, scope: 'incognito_persistent'};
|
||||
config.value.mode = 'fixed_servers';
|
||||
config.value.rules = this.getSingleProxyRule(pref, pxy);
|
||||
browser.proxy.settings.set(config);
|
||||
}
|
||||
|
||||
static getSingleProxyRule(pref, pxy) {
|
||||
return {
|
||||
singleProxy: {
|
||||
scheme: pxy.type,
|
||||
host: pxy.hostname,
|
||||
// must be number, prepare for augmented port
|
||||
port: parseInt(pxy.port),
|
||||
},
|
||||
bypassList: pref.passthrough ? pref.passthrough.split(/[\s,;]+/) : []
|
||||
};
|
||||
}
|
||||
|
||||
static getProxyString(proxy) {
|
||||
let {type, hostname, port} = proxy;
|
||||
switch (type) {
|
||||
case 'direct':
|
||||
return 'DIRECT';
|
||||
|
||||
// chrome PAC doesn't support HTTP
|
||||
case 'http':
|
||||
type = 'PROXY';
|
||||
break;
|
||||
|
||||
default:
|
||||
type = type.toUpperCase();
|
||||
}
|
||||
// prepare for augmented port
|
||||
return `${type} ${hostname}:${parseInt(port)}`;
|
||||
}
|
||||
|
||||
static getPacString(pref) {
|
||||
// --- proxy by pattern
|
||||
const [passthrough, net] = Pattern.getPassthrough(pref.passthrough);
|
||||
|
||||
// filter data
|
||||
let data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname);
|
||||
data = data.filter(i => i.include[0] || i.exclude[0]).map(item => {
|
||||
return {
|
||||
str: this.getProxyString(item),
|
||||
include: item.include.filter(i => i.active).map(i => Pattern.get(i.pattern, i.type)),
|
||||
exclude: item.exclude.filter(i => i.active).map(i => Pattern.get(i.pattern, i.type))
|
||||
};
|
||||
});
|
||||
|
||||
// add PAC rules from pacString
|
||||
let pacData = pref.data.filter(i => i.active && i.type === 'pac' && i.pacString);
|
||||
pacData = pacData.map((i, idx) => i.pacString.replace('FindProxyForURL', '$&' + idx) +
|
||||
`\nconst find${idx} = FindProxyForURL${idx}(url, host);
|
||||
if (find${idx} !== 'DIRECT') { return find${idx}; }`).join('\n\n');
|
||||
pacData &&= `\n${pacData}\n`;
|
||||
|
||||
// https://developer.chrome.com/docs/extensions/reference/proxy/#type-PacScript
|
||||
// https://github.com/w3c/webextensions/issues/339
|
||||
// Chrome pacScript doesn't support bypassList
|
||||
// https://issues.chromium.org/issues/40286640
|
||||
|
||||
// isInNet(host, "192.0.2.172", "255.255.255.255")
|
||||
|
||||
const pacString =
|
||||
String.raw`function FindProxyForURL(url, host) {
|
||||
const data = ${JSON.stringify(data)};
|
||||
const passthrough = ${JSON.stringify(passthrough)};
|
||||
const net = ${JSON.stringify(net)};
|
||||
const match = array => array.some(i => new RegExp(i, 'i').test(url));
|
||||
const inNet = () => net[0] && /^[\d.]+$/.test(host) && net.some(([ip, mask]) => isInNet(host, ip, mask));
|
||||
|
||||
if (match(passthrough) || inNet()) { return 'DIRECT'; }
|
||||
for (const proxy of data) {
|
||||
if (!match(proxy.exclude) && match(proxy.include)) { return proxy.str; }
|
||||
}
|
||||
${pacData}
|
||||
return 'DIRECT';
|
||||
}`;
|
||||
|
||||
return pacString;
|
||||
}
|
||||
}
|
||||
66
foxyproxy/content/schema.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["mode", "data"],
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"sync": {"type": "boolean"},
|
||||
"autoBackup": {"type": "boolean"},
|
||||
"passthrough": {"type": "string"},
|
||||
"theme": {"type": "string"},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"active": {"type": "boolean"},
|
||||
"title": {"type": "string"},
|
||||
"type": {"type": "string", "enum": ["http", "https", "socks4", "socks5", "pac", "direct"]},
|
||||
"hostname": {"type": "string", "format": "hostname"},
|
||||
"port": {"type": "string", "pattern": "^\\d*$"},
|
||||
"username": {"type": "string"},
|
||||
"password": {"type": "string"},
|
||||
"cc": {"type": "string", "pattern": "^([A-Z]{2}|)$"},
|
||||
"city": {"type": "string"},
|
||||
"color": {"type": "string"},
|
||||
"pac": {"type": "string"},
|
||||
"pacString": {"type": "string"},
|
||||
"proxyDNS": {"type": "boolean"},
|
||||
"include": {
|
||||
"type": "array",
|
||||
"id": "include",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {"type": "string", "enum": ["wildcard", "regex", "match"]},
|
||||
"title": {"type": "string"},
|
||||
"pattern": {"type": "string"},
|
||||
"active": {"type": "boolean"}
|
||||
},
|
||||
"required": ["type", "title", "pattern", "active"]
|
||||
}
|
||||
},
|
||||
"exclude": {"$ref": "include"},
|
||||
"tabProxy": {"$ref": "include"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"container": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"incognito": {"type": "string"},
|
||||
"container-1": {"type": "string"},
|
||||
"container-2": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"setProxy": {"type": "string"},
|
||||
"setTabProxy": {"type": "string"},
|
||||
"includeHost": {"type": "string"},
|
||||
"excludeHost": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
foxyproxy/content/show.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- Show (Side Effect) ---------------------------
|
||||
class Show {
|
||||
|
||||
static {
|
||||
const {basic, firefox} = App;
|
||||
basic && document.body.classList.add('basic');
|
||||
!firefox && document.body.classList.add('chrome');
|
||||
|
||||
const elem = document.querySelector('img[src="../image/icon.svg"]');
|
||||
elem?.addEventListener('click', () => {
|
||||
basic && document.body.classList.toggle('basic');
|
||||
!firefox && document.body.classList.toggle('chrome');
|
||||
});
|
||||
}
|
||||
}
|
||||
53
foxyproxy/content/spinner.css
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ----- Spinner ----- */
|
||||
div.spinner {
|
||||
display: none;
|
||||
grid-auto-flow: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #0003;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.spinner::before {
|
||||
content: '';
|
||||
width: 8em;
|
||||
height: 8em;
|
||||
border: 1em solid #ddd;
|
||||
border-color: #ddd var(--btn-bg) #ddd var(--btn-bg);
|
||||
border-radius: 50%;
|
||||
animation: spin 1.5s linear infinite;
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
||||
.spinner::after {
|
||||
content: '';
|
||||
background-image: url('../image/icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
width: 6em;
|
||||
height: 6em;
|
||||
margin-left: -8em;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.spinner.on {
|
||||
display: grid;
|
||||
animation: fade-in 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
13
foxyproxy/content/spinner.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// ---------- Spinner --------------------------------------
|
||||
export class Spinner {
|
||||
|
||||
static spinner = document.querySelector('.spinner');
|
||||
|
||||
static show() {
|
||||
this.spinner.classList.add('on');
|
||||
}
|
||||
|
||||
static hide() {
|
||||
this.spinner.classList.remove('on');
|
||||
}
|
||||
}
|
||||
93
foxyproxy/content/sync.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- Storage Sync ---------------------------------
|
||||
export class Sync {
|
||||
|
||||
static init(pref) {
|
||||
// not for storage.managed
|
||||
if (pref.managed) { return; }
|
||||
|
||||
// Firefox 101 (2022-05-31), Chrome 73
|
||||
browser.storage.sync.onChanged.addListener(e => this.onChanged(e));
|
||||
}
|
||||
|
||||
static async onChanged(changes) {
|
||||
const pref = await browser.storage.local.get();
|
||||
this.getSync(pref);
|
||||
}
|
||||
|
||||
static async get(pref) {
|
||||
// check storage.managed
|
||||
await this.getManaged(pref);
|
||||
// check storage.sync
|
||||
await this.getSync(pref);
|
||||
}
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1868153
|
||||
// On Firefox storage.managed returns undefined if not found
|
||||
static async getManaged(pref) {
|
||||
const result = await browser.storage.managed.get().catch(() => {});
|
||||
if (!Array.isArray(result?.data) || !result.data[0]) {
|
||||
// storage.managed not found, clean up
|
||||
if (Object.hasOwn(pref, 'managed')) {
|
||||
delete pref.managed;
|
||||
await browser.storage.local.remove('managed');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get default pref
|
||||
const db = App.getDefaultPref();
|
||||
// revert pref to default values
|
||||
Object.keys(db).forEach(i => pref[i] = db[i]);
|
||||
|
||||
// set data from storage.managed
|
||||
Object.keys(result).forEach(i => Object.hasOwn(pref, i) && (pref[i] = result[i]));
|
||||
// set pref.managed to use in options.js, popup.js
|
||||
pref.managed = true;
|
||||
// no sync for storage.managed
|
||||
pref.sync = false;
|
||||
|
||||
// --- update database
|
||||
await browser.storage.local.set(pref);
|
||||
}
|
||||
|
||||
static hasOldData(obj) {
|
||||
// FP v3 OR FP v7, null value causes an error
|
||||
return Object.hasOwn(obj, 'settings') || Object.values(obj).some(i => i && Object.hasOwn(i, 'address'));
|
||||
}
|
||||
|
||||
static async getSync(pref) {
|
||||
if (!pref.sync) { return; }
|
||||
if (pref.managed) { return; }
|
||||
|
||||
const syncPref = await browser.storage.sync.get();
|
||||
|
||||
// check sync from old version 3-7
|
||||
// (local has no data OR has old data) AND sync has old data
|
||||
if ((!Object.keys(pref)[0] || this.hasOldData(pref)) && this.hasOldData(syncPref)) {
|
||||
// set sync data to pref, to migrate next in background.js
|
||||
Object.keys(syncPref).forEach(i => pref[i] = syncPref[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
// convert object to array & filter proxies
|
||||
const data = Object.values(syncPref).filter(i => Object.hasOwn(i, 'hostname'));
|
||||
|
||||
const obj = {};
|
||||
if (data[0] && !App.equal(pref.data, data)) {
|
||||
obj.data = data;
|
||||
pref.data = data;
|
||||
}
|
||||
|
||||
App.syncProperties.forEach(i => {
|
||||
if (Object.hasOwn(syncPref, i)) {
|
||||
obj[i] = syncPref[i];
|
||||
pref[i] = syncPref[i];
|
||||
}
|
||||
});
|
||||
|
||||
// update saved pref
|
||||
Object.keys(obj)[0] && await browser.storage.local.set(obj);
|
||||
}
|
||||
}
|
||||
156
foxyproxy/content/test.js
Normal file
@@ -0,0 +1,156 @@
|
||||
import {App} from './app.js';
|
||||
import {Popup} from './options-popup.js';
|
||||
|
||||
// ---------- Proxy Text (Side Effect) ---------------------------
|
||||
class ProxyTest {
|
||||
|
||||
static {
|
||||
document.querySelector('.proxy-top button[data-i18n="test"]').addEventListener('click', () => this.selectOptions());
|
||||
this.popupProxy = document.querySelector('.popup select.popup-test-proxy');
|
||||
this.popupServer = document.querySelector('.popup select.popup-server');
|
||||
this.popupServer.addEventListener('change', () => this.process());
|
||||
}
|
||||
|
||||
static selectOptions() {
|
||||
if (this.popupProxy.options.length < 2) {
|
||||
Popup.show('Did not find a suitable proxy');
|
||||
Popup.show('Ending the test');
|
||||
return;
|
||||
}
|
||||
|
||||
this.popupProxy.classList.add('on');
|
||||
this.popupServer.classList.add('on');
|
||||
|
||||
!App.firefox && Popup.show('On Chrome, proxy authentication must be done before starting the test');
|
||||
Popup.show('Please select a proxy (or the first one will be selected) and then a server for the test\n');
|
||||
}
|
||||
|
||||
static async process(e) {
|
||||
this.server = this.popupServer.value;
|
||||
if (!this.server) { return; }
|
||||
|
||||
Popup.show('Starting the proxy Test\n');
|
||||
|
||||
// check 'prefers-color-scheme' since it is not available in background service worker
|
||||
this.dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// --- get the IP check server
|
||||
const serverText = this.popupServer.selectedOptions[0].textContent;
|
||||
Popup.show(`IP check server: ${serverText}`);
|
||||
|
||||
// --- get the proxy for the test
|
||||
// selected proxy or the first proxy
|
||||
!this.popupProxy.value && (this.popupProxy.selectedIndex = 1);
|
||||
const id = this.popupProxy.value;
|
||||
const host = this.popupProxy.selectedOptions[0].textContent;
|
||||
|
||||
const pref = await browser.storage.local.get();
|
||||
const pxy = pref.data.find(i => id === `${i.hostname}:${i.port}`);
|
||||
|
||||
Popup.show(`Testing proxy: ${host}\n`);
|
||||
|
||||
// --- get real IP
|
||||
Popup.show('Setting mode to "Disable" to get your real IP');
|
||||
pref.mode = 'disable';
|
||||
await this.setProxy(pref);
|
||||
const realIP = await this.getIP();
|
||||
|
||||
// --- test Tab Proxy with mode disable
|
||||
App.firefox && await this.tabProxy(pxy, realIP);
|
||||
|
||||
// --- test single proxy
|
||||
Popup.show(`Setting mode to "${host}"`);
|
||||
pref.mode = `${pxy.hostname}:${pxy.port}`;
|
||||
await this.setProxy(pref);
|
||||
await this.getIP();
|
||||
|
||||
// --- test Proxy by Patterns
|
||||
Popup.show('Setting mode to "Proxy by Patterns"');
|
||||
// adding patterns to test
|
||||
pxy.include = [
|
||||
{
|
||||
type: 'wildcard',
|
||||
title: 'test',
|
||||
pattern: new URL(this.server).hostname,
|
||||
active: true
|
||||
},
|
||||
];
|
||||
pref.mode = 'pattern';
|
||||
await this.setProxy(pref);
|
||||
await this.getIP();
|
||||
|
||||
// --- reset to the original state
|
||||
this.reset();
|
||||
}
|
||||
|
||||
static async setProxy(pref) {
|
||||
// await runtime.sendMessage resolves early on Chrome
|
||||
App.firefox ? await this.sendMessage(pref) : await this.chromeSendMessage(pref);
|
||||
}
|
||||
|
||||
static sendMessage(pref) {
|
||||
return browser.runtime.sendMessage({id: 'setProxy', pref, dark: this.dark, noDataChange: true});
|
||||
}
|
||||
|
||||
static async chromeSendMessage(pref) {
|
||||
await new Promise(resolve => {
|
||||
const listener = () => {
|
||||
browser.proxy.settings.onChange.removeListener(listener);
|
||||
resolve();
|
||||
};
|
||||
browser.proxy.settings.onChange.addListener(listener);
|
||||
this.sendMessage(pref);
|
||||
});
|
||||
}
|
||||
|
||||
static async tabProxy(pxy, realIP) {
|
||||
Popup.show('Setting Tab Proxy with mode "Disable"');
|
||||
const tab = await browser.tabs.create({active: false});
|
||||
await browser.runtime.sendMessage({id: 'setTabProxy', proxy: pxy, tab});
|
||||
await new Promise(resolve => {
|
||||
const listener = e => {
|
||||
browser.tabs.remove(tab.id);
|
||||
browser.webRequest.onBeforeRequest.removeListener(listener);
|
||||
Popup.show(`Your IP: ${e.proxyInfo.host || realIP}\n`);
|
||||
resolve();
|
||||
};
|
||||
browser.webRequest.onBeforeRequest.addListener(listener, {urls: ['<all_urls>'], tabId: tab.id});
|
||||
browser.tabs.update(tab.id, {url: this.server});
|
||||
});
|
||||
}
|
||||
|
||||
static async reset() {
|
||||
Popup.show('Resetting options to their original state');
|
||||
const pref = await browser.storage.local.get();
|
||||
await this.setProxy(pref);
|
||||
Popup.show('Ending the proxy test\n');
|
||||
|
||||
// reset select elements
|
||||
this.popupProxy.selectedIndex = 0;
|
||||
this.popupServer.selectedIndex = 0;
|
||||
}
|
||||
|
||||
static async getIP() {
|
||||
// Chrome a network request timeouts at 300 seconds, while in Firefox at 90 seconds.
|
||||
// AbortSignal.timeout FF100, Ch124
|
||||
return fetch(this.server, {cache: 'no-store', signal: AbortSignal.timeout(5000)})
|
||||
.then(r => r.ok ? r.text() : this.response(r))
|
||||
.then(text => {
|
||||
// HTML response is not acceptable
|
||||
const ip = text.includes('<') ? 'undefined' : text.trim();
|
||||
Popup.show(`Your IP: ${ip}\n`);
|
||||
return ip;
|
||||
})
|
||||
.catch(e => Popup.show(`Your IP: undefined\nStatus: ${e.message}\n`));
|
||||
}
|
||||
|
||||
static response(r) {
|
||||
switch (r.status) {
|
||||
case 403:
|
||||
return 'undefined\nStatus: 403 Forbidden';
|
||||
|
||||
default:
|
||||
return `undefined\nStatus: ${r.status} ${r.statusText}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
68
foxyproxy/content/tester.css
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ----- Tester ----- */
|
||||
.tester h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tester h3:nth-of-type(2) {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.tester .tester-pattern {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.tester input {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.tester .buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.tester button:first-child{
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.tester button:last-child {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.tester pre {
|
||||
color: inherit;
|
||||
/* background-color: var(--alt-bg); */
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.3em;
|
||||
padding: 0.5em;
|
||||
font-size: 1.1em;
|
||||
min-height: 10em;
|
||||
background: linear-gradient(to right, var(--hover) 0, var(--hover) 2em, transparent 2em, transparent 100%);
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
.tester pre span {
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
||||
.pass {
|
||||
color: var(--pass);
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: var(--fail);
|
||||
}
|
||||
|
||||
:is(.pass, .fail)::before {
|
||||
content: '✔';
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
margin-right: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fail::before {
|
||||
content: '✘';
|
||||
}
|
||||
116
foxyproxy/content/tester.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import {Pattern} from './pattern.js';
|
||||
import {Nav} from './nav.js';
|
||||
|
||||
export class Tester {
|
||||
|
||||
static {
|
||||
this.select = document.querySelector('.tester select');
|
||||
this.select.addEventListener('change', () => this.process());
|
||||
[this.pattern, this.url] = document.querySelectorAll('.tester input');
|
||||
[this.pre, this.pre2] = document.querySelectorAll('.tester pre');
|
||||
|
||||
// convert generated HTML to plaintext
|
||||
this.pre.addEventListener('input', e => {
|
||||
if ((e.data || e.inputType === 'insertFromPaste') && this.pre.matches('.tested')) {
|
||||
[...this.pre.children].forEach(i => i.replaceWith(i.textContent));
|
||||
this.pre.classList.remove('tested');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector('.tester button[data-i18n="back"]').addEventListener('click', () => this.back());
|
||||
document.querySelector('.tester button[data-i18n="test"]').addEventListener('click', () => this.process());
|
||||
document.querySelector('.tester button.proxyByPatterns').addEventListener('click', () => this.processURL());
|
||||
}
|
||||
|
||||
static process() {
|
||||
this.pattern.value = this.pattern.value.trim();
|
||||
const str = this.pre.textContent.trim();
|
||||
if (!this.pattern.value || !str) {
|
||||
return;
|
||||
}
|
||||
|
||||
// validate pattern
|
||||
const valid = Pattern.validate(this.pattern.value, this.select.value, true);
|
||||
if (!valid) { return; }
|
||||
|
||||
// convert pattern to regex string if needed
|
||||
const pat = Pattern.get(this.pattern.value, this.select.value);
|
||||
const regex = new RegExp(pat, 'i');
|
||||
const arr = [];
|
||||
str.split(/\s/).forEach(i => {
|
||||
const sp = document.createElement('span');
|
||||
sp.textContent = i;
|
||||
i.trim() && (sp.className = regex.test(i) ? 'pass' : 'fail');
|
||||
arr.push(sp, '\n');
|
||||
});
|
||||
|
||||
this.pre.textContent = '';
|
||||
this.pre.append(...arr);
|
||||
// mark pre, used for 'input' event
|
||||
this.pre.classList.add('tested');
|
||||
}
|
||||
|
||||
static back() {
|
||||
if (!this.target) { return; }
|
||||
|
||||
// show Proxy tab
|
||||
Nav.get('proxies');
|
||||
|
||||
// go to target proxy
|
||||
const details = this.target.closest('details');
|
||||
details.open = true;
|
||||
this.target.scrollIntoView({behavior: 'smooth'});
|
||||
this.target.focus();
|
||||
|
||||
// reset
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
static async processURL() {
|
||||
const url = this.url.value.trim();
|
||||
if (!url || !this.url.checkValidity()) { return; }
|
||||
|
||||
let {data} = await browser.storage.local.get({data: []});
|
||||
data = data.filter(i => i.active && !i.pac && (i.include[0] || i.exclude[0] || i.tabProxy?.[0])).map(item => {
|
||||
item.tabProxy ||= [];
|
||||
return {
|
||||
type: item.type,
|
||||
hostname: item.hostname,
|
||||
port: item.port,
|
||||
title: item.title,
|
||||
include: item.include.filter(i => i.active),
|
||||
exclude: item.exclude.filter(i => i.active),
|
||||
tabProxy: item.tabProxy.filter(i => i.active),
|
||||
};
|
||||
});
|
||||
|
||||
const match = arr => arr.find(i => new RegExp(Pattern.get(i.pattern, i.type), 'i').test(url));
|
||||
|
||||
let arr = [];
|
||||
|
||||
data.forEach(i => {
|
||||
const target = i.title || `${i.hostname}:${parseInt(i.port)}`;
|
||||
if (!match(i.exclude)) {
|
||||
const p = match(i.include);
|
||||
p && arr.push([target, p.type, p.pattern]);
|
||||
}
|
||||
const p = match(i.tabProxy);
|
||||
p && arr.push([target, p.type, p.pattern, '(Tab Proxy)']);
|
||||
});
|
||||
|
||||
if (!arr[0]) {
|
||||
this.pre2.textContent = '⮕ DIRECT';
|
||||
return;
|
||||
}
|
||||
|
||||
// --- text formatting
|
||||
const n = 4;
|
||||
const w = 'wildcard'.length + n;
|
||||
const p0 = Math.max(...arr.map(i => i[0].length)) + n;
|
||||
const p2 = Math.max(...arr.map(i => i[2].length)) + n;
|
||||
|
||||
// undefined or null is converted to an empty string in join()
|
||||
arr = arr.map(i => [i[0].padEnd(p0, ' '), i[1].padEnd(w, ' '), i[2].padEnd(p2, ' '), i[3]].join('').trim());
|
||||
this.pre2.textContent = arr.join('\n');
|
||||
}
|
||||
}
|
||||
55
foxyproxy/content/theme.css
Normal file
@@ -0,0 +1,55 @@
|
||||
/* ---------- Alternative Themes ---------- */
|
||||
|
||||
/* ----- moonlight ----- */
|
||||
:root.moonlight {
|
||||
--bg: #fff;
|
||||
--body-bg: #f9f9fb;
|
||||
|
||||
--nav-bg: #d9d9db;
|
||||
--nav-hover: var(--hover);
|
||||
--nav-color: var(--color);
|
||||
}
|
||||
|
||||
:root.moonlight .flat {
|
||||
color: inherit;
|
||||
background-color: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
:root.moonlight .flat:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
:root.moonlight .pattern-head button span.plus {
|
||||
filter: unset;
|
||||
}
|
||||
|
||||
:root.moonlight .popup-buttons button {
|
||||
color: inherit;
|
||||
background-color: var(--alt-bg);
|
||||
}
|
||||
|
||||
:root.moonlight .popup-buttons button:hover {
|
||||
background-color: var(--hover);
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root.moonlight {
|
||||
--bg: #333;
|
||||
--body-bg: #444;
|
||||
--nav-bg: #000;
|
||||
}
|
||||
|
||||
:root.moonlight .pattern-head button span.plus {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
/* ----- /moonlight ----- */
|
||||
|
||||
|
||||
|
||||
/* ----- alt ----- */
|
||||
:root.alt {
|
||||
--body-bg: var(--bg);
|
||||
}
|
||||
/* ----- /alt ----- */
|
||||
18
foxyproxy/content/theme.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// ---------- Theme (Side Effect) --------------------------
|
||||
class Theme {
|
||||
|
||||
static {
|
||||
this.elem = [document, ...[...document.querySelectorAll('iframe')].map(i => i.contentDocument)];
|
||||
document.getElementById('theme').addEventListener('change', e => this.set(e.target.value));
|
||||
|
||||
browser.storage.local.get('theme').then(i => {
|
||||
i.theme && this.set(i.theme);
|
||||
// show after
|
||||
document.body.style.opacity = 1;
|
||||
});
|
||||
}
|
||||
|
||||
static set(value) {
|
||||
this.elem.forEach(i => i.documentElement.className = value);
|
||||
}
|
||||
}
|
||||
43
foxyproxy/content/toggle-switch.css
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ----- Toggle Switch ----- */
|
||||
/* round() FF118, Ch125 */
|
||||
.toggle {
|
||||
--toggle-dot: 12px;
|
||||
--toggle-dot-margin: 1px;
|
||||
--toggle-wh-ratio: 2.4;
|
||||
--toggle-w: calc(var(--toggle-dot) * var(--toggle-wh-ratio) + var(--toggle-dot-margin) * 2);
|
||||
--toggle-h: calc(var(--toggle-dot) + var(--toggle-dot-margin) * 2);
|
||||
--toggle-trans: calc(var(--toggle-w) - var(--toggle-h));
|
||||
|
||||
appearance: none;
|
||||
width: var(--toggle-w);
|
||||
height: var(--toggle-h);
|
||||
position: relative;
|
||||
border-radius: 50px;
|
||||
background-color: #ccc;
|
||||
transition: background-color 0.5s;
|
||||
}
|
||||
|
||||
.toggle::before {
|
||||
content: '';
|
||||
display: block;
|
||||
margin: var(--toggle-dot-margin);
|
||||
width: var(--toggle-dot);
|
||||
height: var(--toggle-dot);
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.toggle:checked {
|
||||
background-color: var(--btn-bg);
|
||||
}
|
||||
|
||||
.toggle:checked::before {
|
||||
transform: translateX(var(--toggle-trans));
|
||||
}
|
||||
|
||||
/* smaller toggle switch for patterns */
|
||||
.pattern-row .toggle {
|
||||
--toggle-dot: 10px;
|
||||
}
|
||||
10
foxyproxy/content/toggle.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// ---------- Toggle ---------------------------------------
|
||||
export class Toggle {
|
||||
|
||||
static password(elem) {
|
||||
elem.addEventListener('click', () => {
|
||||
const input = elem.previousElementSibling;
|
||||
input.type = input.type === 'password' ? 'text' : 'password';
|
||||
});
|
||||
}
|
||||
}
|
||||
39
foxyproxy/content/webrtc.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import {App} from './app.js';
|
||||
|
||||
// ---------- WebRTC (Side Effect) -------------------------
|
||||
class WebRTC {
|
||||
|
||||
static {
|
||||
this.webRTC = document.querySelector('#limitWebRTC');
|
||||
// firefox only option
|
||||
!App.firefox && (this.webRTC.lastElementChild.disabled = true);
|
||||
this.webRTC.addEventListener('change', () => this.process());
|
||||
this.init();
|
||||
}
|
||||
|
||||
static async init() {
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/permissions/request
|
||||
// Any permissions granted are retained by the extension, even over upgrade and disable/enable cycling.
|
||||
// check if permission is granted
|
||||
this.permission = await browser.permissions.contains({permissions: ['privacy']});
|
||||
|
||||
// check webRTCIPHandlingPolicy
|
||||
if (this.permission) {
|
||||
const result = await browser.privacy.network.webRTCIPHandlingPolicy.get({});
|
||||
this.webRTC.value = result.value;
|
||||
}
|
||||
}
|
||||
|
||||
static async process() {
|
||||
if (!this.permission) {
|
||||
// request permission, Firefox for Android version 102
|
||||
this.permission = await browser.permissions.request({permissions: ['privacy']});
|
||||
if (!this.permission) { return; }
|
||||
}
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1790270
|
||||
// WebRTC bypasses Network settings & proxy.onRequest
|
||||
// {"levelOfControl": "controllable_by_this_extension", "value": "default"}
|
||||
browser.privacy.network.webRTCIPHandlingPolicy.set({value: this.webRTC.value});
|
||||
}
|
||||
}
|
||||
9
foxyproxy/image/beaker.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg fill="#09d" xmlns="http://www.w3.org/2000/svg" width="26" height="28" aria-hidden="true">
|
||||
<path d="M23.859 22.625C25.031 24.484 24.203 26 22 26H4c-2.203
|
||||
0-3.031-1.516-1.859-3.375L10 10.234V4H9c-.547 0-1-.453-1-1s.453-1
|
||||
1-1h8c.547 0 1 .453 1 1s-.453 1-1 1h-1v6.234zM11.688 11.297L7.438
|
||||
18h11.125l-4.25-6.703-.313-.484V4h-2v6.813z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 570 B |
4
foxyproxy/image/bin.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1000 1000" fill="#f90">
|
||||
<g><path d="M163.1,316.3v612.5c0,33.7,27.6,61.3,61.3,61.3h551.3c33.7,0,61.3-27.6,61.3-61.3V316.3H163.1z M346.9,867.5h-61.3V438.8h61.3V867.5z M469.4,867.5h-61.3V438.8h61.3V867.5z M591.9,867.5h-61.3V438.8h61.3V867.5z M714.4,867.5h-61.3V438.8h61.3V867.5z"/><path d="M852.2,132.5H653.1V55.9c0-25.3-20.7-45.9-45.9-45.9H392.8c-25.3,0-45.9,20.7-45.9,45.9v76.6H147.8c-25.3,0-45.9,20.7-45.9,45.9V255h796.3v-76.6C898.1,153.2,877.5,132.5,852.2,132.5z M591.9,132.5H408.1V72h183.8V132.5z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 644 B |
9
foxyproxy/image/container.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#fff">
|
||||
<rect x="1" y="1" width="6" height="6" rx="1"/>
|
||||
<path d="M14.75 3H13V1.25A0.25 0.25 0 0 0 12.75 1h-1.5A0.25 0.25 0 0 0 11 1.25V3H9.25A0.25 0.25 0 0 0 9 3.25v1.5A0.25 0.25 0 0 0 9.25 5H11v1.75A0.25 0.25 0 0 0 11.25 7h1.5A0.25 0.25 0 0 0 13 6.75V5h1.75A0.25 0.25 0 0 0 15 4.75v-1.5A0.25 0.25 0 0 0 14.75 3z" fill-rule="evenodd"/>
|
||||
<rect x="1" y="9" width="6" height="6" rx="1"/>
|
||||
<rect x="9" y="9" width="6" height="6" rx="1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
BIN
foxyproxy/image/ericjung.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
7
foxyproxy/image/filter.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
|
||||
<path fill="#aaa" opacity=".4" d="M5 9.2l2 1.6V6.1L8.55 4h-5.1L5 6.1v3.1z"/>
|
||||
<path fill="#aaa" d="M1.18 2.6A1 1 0 0 1 2 1H10a1 1 0 0 1 .8 1.6L8 6.4v4.82c0 .63-.72.98-1.22.6l-2.5-1.99A.75.75 0 0 1 4 9.25V6.41L1.18 2.6zM2 2L5 6.09v3.04l2 1.59V6.09L10.01 2H2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 543 B |
BIN
foxyproxy/image/icon-off.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
233
foxyproxy/image/icon-off.svg
Normal file
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2837"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="24x24.svg"
|
||||
style="filter: grayscale(1);">
|
||||
<metadata
|
||||
id="metadata2843"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2841"><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
id="perspective2845" /><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-7.9998322,76.665482,76.665482,7.9998322,231.64795,260.98437)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient2861"><stop
|
||||
style="stop-opacity:1;stop-color:#f89734"
|
||||
offset="0"
|
||||
id="stop2863" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb371"
|
||||
offset="1"
|
||||
id="stop2865" /></linearGradient><radialGradient
|
||||
fx="0"
|
||||
fy="0"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(50.959351,23.761917,23.092804,-49.525528,208.03369,303.08252)"
|
||||
spreadMethod="pad"
|
||||
id="radialGradient2881"><stop
|
||||
style="stop-opacity:1;stop-color:#e9eaeb"
|
||||
offset="0"
|
||||
id="stop2883" /><stop
|
||||
style="stop-opacity:1;stop-color:#838487"
|
||||
offset="1"
|
||||
id="stop2885" /></radialGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2893"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 z"
|
||||
id="path2895" /></clipPath><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(34.282654,-15.727585,-15.727585,-34.282654,213.80908,320.43213)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient2953"><stop
|
||||
style="stop-opacity:1;stop-color:#faa858"
|
||||
offset="0"
|
||||
id="stop2955" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb371"
|
||||
offset="1"
|
||||
id="stop2957" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2965"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 z"
|
||||
id="path2967" /></clipPath><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2861"
|
||||
id="linearGradient2917"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-7.9998322,76.665482,76.665482,7.9998322,231.64795,260.98437)"
|
||||
spreadMethod="pad"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0" /><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient2881"
|
||||
id="radialGradient2919"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(50.959351,23.761917,23.092804,-49.525528,208.03369,303.08252)"
|
||||
spreadMethod="pad"
|
||||
cx="0"
|
||||
cy="0"
|
||||
fx="0"
|
||||
fy="0"
|
||||
r="1" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2953"
|
||||
id="linearGradient2921"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(34.282654,-15.727585,-15.727585,-34.282654,213.80908,320.43213)"
|
||||
spreadMethod="pad"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0" /></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1152"
|
||||
inkscape:window-height="793"
|
||||
id="namedview2839"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6222222"
|
||||
inkscape:cx="135.88584"
|
||||
inkscape:cy="27.404163"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2847"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="false" /><g
|
||||
id="g2847"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="FOXYlogo"
|
||||
transform="matrix(1.25,0,0,-1.25,-224.11415,370.89237)"><g
|
||||
id="g2870"
|
||||
transform="matrix(0.42687693,0,0,0.42687693,102.75599,148.99767)"><g
|
||||
id="g2849"><g
|
||||
id="g2851"><g
|
||||
id="g2857"><g
|
||||
id="g2859"><path
|
||||
id="path2867"
|
||||
style="fill:url(#linearGradient2917);stroke:none"
|
||||
d="m 185.797,300.527 c 0,-22.396 18.681,-40.552 41.725,-40.552 l 0,0 c 23.044,0 41.725,18.156 41.725,40.552 l 0,0 c 0,22.397 -18.681,40.554 -41.725,40.554 l 0,0 c -23.044,0 -41.725,-18.157 -41.725,-40.554" /></g></g></g></g><g
|
||||
id="g2869"><g
|
||||
id="g2871"><g
|
||||
id="g2877"><g
|
||||
id="g2879"><path
|
||||
id="path2887"
|
||||
style="fill:url(#radialGradient2919);stroke:none"
|
||||
d="m 211.943,333.94 c -18.988,-8.854 -27.403,-30.99 -18.798,-49.443 l 0,0 c 8.604,-18.453 30.971,-26.234 49.958,-17.381 l 0,0 c 18.986,8.854 27.401,30.99 18.797,49.442 l 0,0 c -6.194,13.284 -19.518,21.037 -33.58,21.037 l 0,0 c -5.473,0 -11.058,-1.175 -16.377,-3.655" /></g></g></g></g><g
|
||||
id="g2889"><g
|
||||
clip-path="url(#clipPath2893)"
|
||||
id="g2891"><g
|
||||
transform="translate(261.9004,316.5576)"
|
||||
id="g2897"><path
|
||||
id="path2899"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1.76999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 0,0 c 8.604,-18.451 0.188,-40.588 -18.798,-49.441 -18.986,-8.854 -41.353,-1.072 -49.958,17.38 -8.605,18.454 -0.189,40.589 18.798,49.443 C -30.972,26.235 -8.605,18.454 0,0 z" /></g><g
|
||||
transform="translate(253.9063,323.9209)"
|
||||
id="g2901"><path
|
||||
id="path2903"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -0.38,6.903 -1.854,15.334 -6.533,20.806 -3.507,4.103 -8.978,-7.857 -10.103,-9.669 -1.38,-2.221 -2.706,-4.984 -4.8,-6.605 -2.945,-2.281 -6.431,-2.372 -9.833,-3.499 -5.391,-1.789 -10.688,-4.57 -14.938,-8.369 -5.214,-4.66 -10.154,-4.124 -16.67,-3.649 -2.711,0.199 -12.849,2.406 -11.04,-3.348 2.192,-6.973 7.778,-12.852 13.386,-17.248 5.302,-4.158 2.946,-8.926 -2.589,-11.503 5.72,-7.829 17.267,-8.914 26.01,-7.4 4.766,0.826 9.278,1.088 14.039,-0.052 3.533,-0.845 7.752,-4.399 11.49,-2.507 3.506,1.711 3.098,5.748 4.096,8.94 1.409,4.499 4.193,8.567 7.519,11.858 6.812,6.743 14.138,16.589 10.803,26.9 C 5.155,-8.506 -0.189,-7.015 0,0" /></g><g
|
||||
transform="translate(215.7368,321.6992)"
|
||||
id="g2905"><path
|
||||
id="path2907"
|
||||
style="fill:#f89734;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 11.985,5.765 21.784,5.146 0,0 7.573,-2.107 11.927,-4.1 0,0 6.191,-4.964 7.201,-8.663 0,0 2.938,-8.511 -7.186,-19.298 0,0 -5.095,-6.523 -6.302,-12.757 0,0 -0.092,-4.189 -2.53,-5.651 0,0 -4.019,-1.141 -6.971,0.514 -2.393,0.678 -4.844,1.214 -7.298,1.626 -5.684,0.954 -11.46,0.006 -17.159,1.061 -4.659,0.859 -10.534,2.956 -11.796,8.135 0,0 -2.888,2.565 0.798,13.578 0,0 2.001,12.357 17.532,20.409" /></g><g
|
||||
transform="translate(196.5059,295.9878)"
|
||||
id="g2909"><path
|
||||
id="path2911"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 2.679,-1.011 4.179,-2.636 10.782,-1.915 3.055,0.334 5.723,3.269 8.877,2.476 8.535,-2.152 22.752,-17.621 22.222,-18.163 -0.955,-0.761 -11.555,14.2 -19.136,10.842 -1.809,-0.8 -4.551,-2.507 -6.534,-2.023 -0.958,0.237 -1.792,0.686 -2.52,1.358 -0.96,0.89 -1.642,2.731 -2.91,3.235 -1.728,0.688 -3.12,0.784 -4.914,1.167 -1.929,0.412 -3.938,1.262 -5.436,2.518" /></g><g
|
||||
transform="translate(243.0967,279.8789)"
|
||||
id="g2913"><path
|
||||
id="path2915"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0.714,-1.26 -0.438,-4.142 -0.984,-4.45 -0.544,-0.309 -3.745,0.106 -4.459,1.367 -0.713,1.258 0.327,2.263 1.83,3.114 C -2.109,0.883 -0.713,1.259 0,0" /></g><g
|
||||
transform="translate(206.8965,302.1777)"
|
||||
id="g2917"><path
|
||||
id="path2919"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C -0.872,1.155 2.11,3.59 2.733,3.646 3.357,3.701 6.101,2.003 6.231,0.562 6.361,-0.879 3.944,0.677 2.223,0.523 0.502,0.367 0.369,-0.49 0,0" /></g><g
|
||||
transform="translate(262.0864,311.4141)"
|
||||
id="g2921"><path
|
||||
id="path2923"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -0.677,-9.71 -5.16,-14.594 -10.862,-21.908 -3.012,-3.861 -5.017,-8.005 -6.067,-12.766 -0.653,-2.955 -2.998,-5.396 -6.187,-5.296 -3.564,0.114 -6.928,2.23 -10.487,2.862 -4.641,0.826 -9.162,0.113 -13.801,-0.206 -4.671,-0.321 -8.985,0.475 -13.127,2.686 -0.069,0.036 -6.823,4.403 -6.476,4.649 4.916,3.463 2.528,8.458 -1.511,11.371 -5.349,3.857 -10.603,10.055 -12.841,16.365 -1.254,3.539 12.776,0.416 14.245,-0.23 4.956,-2.182 2.184,-9.504 1.431,-13.29 1.562,1.982 1.916,4.999 2.747,7.338 1.244,3.501 2.716,7.061 5.389,9.728 3.419,3.415 7.537,5.966 11.82,8.15 5.051,2.578 9.983,4.83 15.651,5.436 4.155,0.441 7.829,-0.055 11.682,-1.675 0.621,-0.264 4.807,-2.148 5.815,-1.898 -3.393,1.208 -10.569,4.446 -9.555,9.164 0.338,1.567 5.62,14.545 7.748,11.455 3.797,-5.514 5.733,-13.408 5.718,-20.002 C -8.68,6.954 -6.037,2.089 -0.348,4.037 -0.384,4.025 0.028,0.412 0,0 m -7.915,12.749 c -0.384,6.973 -1.874,15.488 -6.598,21.016 -3.544,4.145 -9.07,-7.937 -10.206,-9.767 -1.394,-2.243 -2.732,-5.033 -4.849,-6.672 -2.974,-2.305 -6.495,-2.395 -9.931,-3.535 -5.446,-1.808 -10.797,-4.617 -15.089,-8.453 -5.267,-4.707 -10.258,-4.165 -16.839,-3.686 -2.739,0.201 -12.979,2.429 -11.151,-3.382 2.213,-7.042 7.856,-12.98 13.52,-17.421 5.355,-4.199 2.976,-9.016 -2.615,-11.62 5.778,-7.908 17.443,-9.004 26.273,-7.474 4.815,0.834 9.372,1.096 14.18,-0.054 3.57,-0.851 7.831,-4.442 11.607,-2.531 3.541,1.728 3.129,5.806 4.139,9.031 1.422,4.543 4.234,8.652 7.594,11.977 C -1,-13.011 6.4,-3.065 3.032,7.352 -2.708,4.155 -8.107,5.663 -7.915,12.749" /></g><g
|
||||
transform="translate(240.9268,311.4058)"
|
||||
id="g2925"><path
|
||||
id="path2927"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0.288,1.418 -3.452,2.332 -4.038,2.113 -0.587,-0.218 -2.331,-2.933 -1.827,-4.29 0.502,-1.358 2.012,1.088 3.634,1.692 C -0.613,0.115 -0.123,-0.601 0,0" /></g><g
|
||||
transform="translate(215.5869,289.4712)"
|
||||
id="g2929"><path
|
||||
id="path2931"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0.181,0.136 0.365,0.415 0.297,0.387 0.506,0.79 0.527,1.284 0.305,1.718 -0.059,2.433 -0.937,2.72 -1.652,2.354 -2.369,1.99 -2.652,1.112 -2.286,0.396 -2.284,0.392 -2.281,0.389 -2.279,0.385 -2.35,0.414 -2.423,0.425 -2.492,0.459 -3.995,1.189 -4.734,2.865 -4.337,4.425 -4.47,4.253 -4.588,4.066 -4.688,3.865 -5.445,2.307 -4.793,0.431 -3.236,-0.324 c 0.9,-0.439 1.9,-0.392 2.733,0.017 0.058,0.021 0.117,0.039 0.174,0.068 C -0.204,-0.175 -0.098,-0.091 0,0" /></g><g
|
||||
transform="translate(201.3467,286.79)"
|
||||
id="g2933"><path
|
||||
id="path2935"
|
||||
style="fill:#f6ad44;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 4.649,-8.012 17.098,-5.728 0,0 8.646,2.306 13.661,-2.572 -1.023,0.995 -1.912,2.037 -3.159,2.8 -3.88,2.374 -8.425,1.586 -12.74,2.007 C 11.729,-3.186 8.814,-2.129 6.012,-0.74 4.997,-0.237 -0.543,3.323 0,0" /></g><g
|
||||
transform="translate(254.4907,311.8257)"
|
||||
id="g2937"><path
|
||||
id="path2939"
|
||||
style="fill:#f6ad44;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 2.953,-8.739 -7.189,-15.087 0,0 -5.659,-4.735 -5.408,-11.635 -0.051,1.406 -0.211,2.757 0.021,4.15 0.385,2.318 0.466,4.79 1.388,6.944 0.89,2.08 2.775,3.179 4.241,4.747 1.653,1.768 2.733,4.008 3.344,6.417 C -3.336,-3.416 -1.854,2.765 0,0" /></g></g></g><g
|
||||
id="g2941"><g
|
||||
id="g2943"><g
|
||||
id="g2949"><g
|
||||
id="g2951"><path
|
||||
id="path2959"
|
||||
style="fill:url(#linearGradient2921);stroke:none"
|
||||
d="m 219.217,320.557 c -8.361,-3.819 -11.245,-7.843 -10.12,-9.455 l 0,0 c 1.232,-1.763 6.564,-3.975 10.462,-4.716 l 0,0 c 0.437,-0.084 0.615,0.54 0.788,1.164 l 0,0 c 0.175,0.625 0.345,1.251 0.769,1.164 l 0,0 c 2.49,-0.513 5.947,-3.826 7.154,-4.78 l 0,0 c 0.615,-0.486 1.778,-1.525 2.335,-1.306 l 0,0 c 0.448,0.18 -0.171,1.544 -0.27,2.567 l 0,0 c -0.129,1.34 -1.24,6.507 -0.799,8.453 l 0,0 c 0.16,0.711 0.67,0.604 1.192,0.498 l 0,0 c 0.53,-0.109 1.071,-0.219 1.268,0.538 l 0,0 c 1.018,3.921 1.895,7.666 0.749,9.206 l 0,0 c -0.292,0.393 -0.865,0.571 -1.664,0.571 l 0,0 c -2.414,0 -6.885,-1.63 -11.864,-3.904" /></g></g></g></g><g
|
||||
id="g2961"><g
|
||||
clip-path="url(#clipPath2965)"
|
||||
id="g2963"><g
|
||||
transform="translate(250.4868,321.0234)"
|
||||
id="g3025"><path
|
||||
id="path3027"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -1.953,-3.398 -2.12,-7.398 -9.162,-12.051 -1.332,-0.88 -2.181,-1.787 -2.848,-3.422 -3.239,-7.934 2.034,-25.066 2.768,-24.956 1.152,0.325 -5.615,14.689 -1.916,21.923 0.592,1.157 1.516,2.073 2.438,2.968 0.984,0.957 1.551,1.517 2.647,2.326 1.162,0.856 2.677,2.038 4.037,1.981 -0.91,0.038 -2.517,0.43 -3.432,0.469 0.717,1.218 2.645,3.181 3.182,4.031 1.328,2.1 1.92,4.192 2.286,6.731" /></g><g
|
||||
transform="translate(245.6221,311.167)"
|
||||
id="g3029"><path
|
||||
id="path3031"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0.858,0.275 1.874,0.881 2.632,1.389 3.313,1.846 4.006,2.344 4.25,3.106 3.887,1.972 2.931,0.588 1.994,-0.055 1.517,-0.382 1.029,-0.701 0.617,-1.111 0.216,-1.509 -0.117,-1.482 -0.718,-1.59 -0.766,-1.337 -0.75,-1.075 -0.75,-0.813" /></g></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
BIN
foxyproxy/image/icon.png
Normal file
|
After Width: | Height: | Size: 737 B |
231
foxyproxy/image/icon.svg
Normal file
@@ -0,0 +1,231 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2837"
|
||||
version="1.1"
|
||||
inkscape:version="0.47pre4 r22446"
|
||||
width="48"
|
||||
height="48"
|
||||
viewBox="0 0 48 48"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="24x24.svg"><metadata
|
||||
id="metadata2843"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs2841"><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
id="perspective2845" /><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-7.9998322,76.665482,76.665482,7.9998322,231.64795,260.98437)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient2861"><stop
|
||||
style="stop-opacity:1;stop-color:#f89734"
|
||||
offset="0"
|
||||
id="stop2863" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb371"
|
||||
offset="1"
|
||||
id="stop2865" /></linearGradient><radialGradient
|
||||
fx="0"
|
||||
fy="0"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(50.959351,23.761917,23.092804,-49.525528,208.03369,303.08252)"
|
||||
spreadMethod="pad"
|
||||
id="radialGradient2881"><stop
|
||||
style="stop-opacity:1;stop-color:#e9eaeb"
|
||||
offset="0"
|
||||
id="stop2883" /><stop
|
||||
style="stop-opacity:1;stop-color:#838487"
|
||||
offset="1"
|
||||
id="stop2885" /></radialGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2893"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 z"
|
||||
id="path2895" /></clipPath><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(34.282654,-15.727585,-15.727585,-34.282654,213.80908,320.43213)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient2953"><stop
|
||||
style="stop-opacity:1;stop-color:#faa858"
|
||||
offset="0"
|
||||
id="stop2955" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb371"
|
||||
offset="1"
|
||||
id="stop2957" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath2965"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 z"
|
||||
id="path2967" /></clipPath><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2861"
|
||||
id="linearGradient2917"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-7.9998322,76.665482,76.665482,7.9998322,231.64795,260.98437)"
|
||||
spreadMethod="pad"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0" /><radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#radialGradient2881"
|
||||
id="radialGradient2919"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(50.959351,23.761917,23.092804,-49.525528,208.03369,303.08252)"
|
||||
spreadMethod="pad"
|
||||
cx="0"
|
||||
cy="0"
|
||||
fx="0"
|
||||
fy="0"
|
||||
r="1" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2953"
|
||||
id="linearGradient2921"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(34.282654,-15.727585,-15.727585,-34.282654,213.80908,320.43213)"
|
||||
spreadMethod="pad"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0" /></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1152"
|
||||
inkscape:window-height="793"
|
||||
id="namedview2839"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6222222"
|
||||
inkscape:cx="135.88584"
|
||||
inkscape:cy="27.404163"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2847"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="false" /><g
|
||||
id="g2847"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="FOXYlogo"
|
||||
transform="matrix(1.25,0,0,-1.25,-224.11415,370.89237)"><g
|
||||
id="g2870"
|
||||
transform="matrix(0.42687693,0,0,0.42687693,102.75599,148.99767)"><g
|
||||
id="g2849"><g
|
||||
id="g2851"><g
|
||||
id="g2857"><g
|
||||
id="g2859"><path
|
||||
id="path2867"
|
||||
style="fill:url(#linearGradient2917);stroke:none"
|
||||
d="m 185.797,300.527 c 0,-22.396 18.681,-40.552 41.725,-40.552 l 0,0 c 23.044,0 41.725,18.156 41.725,40.552 l 0,0 c 0,22.397 -18.681,40.554 -41.725,40.554 l 0,0 c -23.044,0 -41.725,-18.157 -41.725,-40.554" /></g></g></g></g><g
|
||||
id="g2869"><g
|
||||
id="g2871"><g
|
||||
id="g2877"><g
|
||||
id="g2879"><path
|
||||
id="path2887"
|
||||
style="fill:url(#radialGradient2919);stroke:none"
|
||||
d="m 211.943,333.94 c -18.988,-8.854 -27.403,-30.99 -18.798,-49.443 l 0,0 c 8.604,-18.453 30.971,-26.234 49.958,-17.381 l 0,0 c 18.986,8.854 27.401,30.99 18.797,49.442 l 0,0 c -6.194,13.284 -19.518,21.037 -33.58,21.037 l 0,0 c -5.473,0 -11.058,-1.175 -16.377,-3.655" /></g></g></g></g><g
|
||||
id="g2889"><g
|
||||
clip-path="url(#clipPath2893)"
|
||||
id="g2891"><g
|
||||
transform="translate(261.9004,316.5576)"
|
||||
id="g2897"><path
|
||||
id="path2899"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1.76999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 0,0 c 8.604,-18.451 0.188,-40.588 -18.798,-49.441 -18.986,-8.854 -41.353,-1.072 -49.958,17.38 -8.605,18.454 -0.189,40.589 18.798,49.443 C -30.972,26.235 -8.605,18.454 0,0 z" /></g><g
|
||||
transform="translate(253.9063,323.9209)"
|
||||
id="g2901"><path
|
||||
id="path2903"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -0.38,6.903 -1.854,15.334 -6.533,20.806 -3.507,4.103 -8.978,-7.857 -10.103,-9.669 -1.38,-2.221 -2.706,-4.984 -4.8,-6.605 -2.945,-2.281 -6.431,-2.372 -9.833,-3.499 -5.391,-1.789 -10.688,-4.57 -14.938,-8.369 -5.214,-4.66 -10.154,-4.124 -16.67,-3.649 -2.711,0.199 -12.849,2.406 -11.04,-3.348 2.192,-6.973 7.778,-12.852 13.386,-17.248 5.302,-4.158 2.946,-8.926 -2.589,-11.503 5.72,-7.829 17.267,-8.914 26.01,-7.4 4.766,0.826 9.278,1.088 14.039,-0.052 3.533,-0.845 7.752,-4.399 11.49,-2.507 3.506,1.711 3.098,5.748 4.096,8.94 1.409,4.499 4.193,8.567 7.519,11.858 6.812,6.743 14.138,16.589 10.803,26.9 C 5.155,-8.506 -0.189,-7.015 0,0" /></g><g
|
||||
transform="translate(215.7368,321.6992)"
|
||||
id="g2905"><path
|
||||
id="path2907"
|
||||
style="fill:#f89734;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 11.985,5.765 21.784,5.146 0,0 7.573,-2.107 11.927,-4.1 0,0 6.191,-4.964 7.201,-8.663 0,0 2.938,-8.511 -7.186,-19.298 0,0 -5.095,-6.523 -6.302,-12.757 0,0 -0.092,-4.189 -2.53,-5.651 0,0 -4.019,-1.141 -6.971,0.514 -2.393,0.678 -4.844,1.214 -7.298,1.626 -5.684,0.954 -11.46,0.006 -17.159,1.061 -4.659,0.859 -10.534,2.956 -11.796,8.135 0,0 -2.888,2.565 0.798,13.578 0,0 2.001,12.357 17.532,20.409" /></g><g
|
||||
transform="translate(196.5059,295.9878)"
|
||||
id="g2909"><path
|
||||
id="path2911"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 2.679,-1.011 4.179,-2.636 10.782,-1.915 3.055,0.334 5.723,3.269 8.877,2.476 8.535,-2.152 22.752,-17.621 22.222,-18.163 -0.955,-0.761 -11.555,14.2 -19.136,10.842 -1.809,-0.8 -4.551,-2.507 -6.534,-2.023 -0.958,0.237 -1.792,0.686 -2.52,1.358 -0.96,0.89 -1.642,2.731 -2.91,3.235 -1.728,0.688 -3.12,0.784 -4.914,1.167 -1.929,0.412 -3.938,1.262 -5.436,2.518" /></g><g
|
||||
transform="translate(243.0967,279.8789)"
|
||||
id="g2913"><path
|
||||
id="path2915"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0.714,-1.26 -0.438,-4.142 -0.984,-4.45 -0.544,-0.309 -3.745,0.106 -4.459,1.367 -0.713,1.258 0.327,2.263 1.83,3.114 C -2.109,0.883 -0.713,1.259 0,0" /></g><g
|
||||
transform="translate(206.8965,302.1777)"
|
||||
id="g2917"><path
|
||||
id="path2919"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C -0.872,1.155 2.11,3.59 2.733,3.646 3.357,3.701 6.101,2.003 6.231,0.562 6.361,-0.879 3.944,0.677 2.223,0.523 0.502,0.367 0.369,-0.49 0,0" /></g><g
|
||||
transform="translate(262.0864,311.4141)"
|
||||
id="g2921"><path
|
||||
id="path2923"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -0.677,-9.71 -5.16,-14.594 -10.862,-21.908 -3.012,-3.861 -5.017,-8.005 -6.067,-12.766 -0.653,-2.955 -2.998,-5.396 -6.187,-5.296 -3.564,0.114 -6.928,2.23 -10.487,2.862 -4.641,0.826 -9.162,0.113 -13.801,-0.206 -4.671,-0.321 -8.985,0.475 -13.127,2.686 -0.069,0.036 -6.823,4.403 -6.476,4.649 4.916,3.463 2.528,8.458 -1.511,11.371 -5.349,3.857 -10.603,10.055 -12.841,16.365 -1.254,3.539 12.776,0.416 14.245,-0.23 4.956,-2.182 2.184,-9.504 1.431,-13.29 1.562,1.982 1.916,4.999 2.747,7.338 1.244,3.501 2.716,7.061 5.389,9.728 3.419,3.415 7.537,5.966 11.82,8.15 5.051,2.578 9.983,4.83 15.651,5.436 4.155,0.441 7.829,-0.055 11.682,-1.675 0.621,-0.264 4.807,-2.148 5.815,-1.898 -3.393,1.208 -10.569,4.446 -9.555,9.164 0.338,1.567 5.62,14.545 7.748,11.455 3.797,-5.514 5.733,-13.408 5.718,-20.002 C -8.68,6.954 -6.037,2.089 -0.348,4.037 -0.384,4.025 0.028,0.412 0,0 m -7.915,12.749 c -0.384,6.973 -1.874,15.488 -6.598,21.016 -3.544,4.145 -9.07,-7.937 -10.206,-9.767 -1.394,-2.243 -2.732,-5.033 -4.849,-6.672 -2.974,-2.305 -6.495,-2.395 -9.931,-3.535 -5.446,-1.808 -10.797,-4.617 -15.089,-8.453 -5.267,-4.707 -10.258,-4.165 -16.839,-3.686 -2.739,0.201 -12.979,2.429 -11.151,-3.382 2.213,-7.042 7.856,-12.98 13.52,-17.421 5.355,-4.199 2.976,-9.016 -2.615,-11.62 5.778,-7.908 17.443,-9.004 26.273,-7.474 4.815,0.834 9.372,1.096 14.18,-0.054 3.57,-0.851 7.831,-4.442 11.607,-2.531 3.541,1.728 3.129,5.806 4.139,9.031 1.422,4.543 4.234,8.652 7.594,11.977 C -1,-13.011 6.4,-3.065 3.032,7.352 -2.708,4.155 -8.107,5.663 -7.915,12.749" /></g><g
|
||||
transform="translate(240.9268,311.4058)"
|
||||
id="g2925"><path
|
||||
id="path2927"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0.288,1.418 -3.452,2.332 -4.038,2.113 -0.587,-0.218 -2.331,-2.933 -1.827,-4.29 0.502,-1.358 2.012,1.088 3.634,1.692 C -0.613,0.115 -0.123,-0.601 0,0" /></g><g
|
||||
transform="translate(215.5869,289.4712)"
|
||||
id="g2929"><path
|
||||
id="path2931"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0.181,0.136 0.365,0.415 0.297,0.387 0.506,0.79 0.527,1.284 0.305,1.718 -0.059,2.433 -0.937,2.72 -1.652,2.354 -2.369,1.99 -2.652,1.112 -2.286,0.396 -2.284,0.392 -2.281,0.389 -2.279,0.385 -2.35,0.414 -2.423,0.425 -2.492,0.459 -3.995,1.189 -4.734,2.865 -4.337,4.425 -4.47,4.253 -4.588,4.066 -4.688,3.865 -5.445,2.307 -4.793,0.431 -3.236,-0.324 c 0.9,-0.439 1.9,-0.392 2.733,0.017 0.058,0.021 0.117,0.039 0.174,0.068 C -0.204,-0.175 -0.098,-0.091 0,0" /></g><g
|
||||
transform="translate(201.3467,286.79)"
|
||||
id="g2933"><path
|
||||
id="path2935"
|
||||
style="fill:#f6ad44;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 4.649,-8.012 17.098,-5.728 0,0 8.646,2.306 13.661,-2.572 -1.023,0.995 -1.912,2.037 -3.159,2.8 -3.88,2.374 -8.425,1.586 -12.74,2.007 C 11.729,-3.186 8.814,-2.129 6.012,-0.74 4.997,-0.237 -0.543,3.323 0,0" /></g><g
|
||||
transform="translate(254.4907,311.8257)"
|
||||
id="g2937"><path
|
||||
id="path2939"
|
||||
style="fill:#f6ad44;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,0 2.953,-8.739 -7.189,-15.087 0,0 -5.659,-4.735 -5.408,-11.635 -0.051,1.406 -0.211,2.757 0.021,4.15 0.385,2.318 0.466,4.79 1.388,6.944 0.89,2.08 2.775,3.179 4.241,4.747 1.653,1.768 2.733,4.008 3.344,6.417 C -3.336,-3.416 -1.854,2.765 0,0" /></g></g></g><g
|
||||
id="g2941"><g
|
||||
id="g2943"><g
|
||||
id="g2949"><g
|
||||
id="g2951"><path
|
||||
id="path2959"
|
||||
style="fill:url(#linearGradient2921);stroke:none"
|
||||
d="m 219.217,320.557 c -8.361,-3.819 -11.245,-7.843 -10.12,-9.455 l 0,0 c 1.232,-1.763 6.564,-3.975 10.462,-4.716 l 0,0 c 0.437,-0.084 0.615,0.54 0.788,1.164 l 0,0 c 0.175,0.625 0.345,1.251 0.769,1.164 l 0,0 c 2.49,-0.513 5.947,-3.826 7.154,-4.78 l 0,0 c 0.615,-0.486 1.778,-1.525 2.335,-1.306 l 0,0 c 0.448,0.18 -0.171,1.544 -0.27,2.567 l 0,0 c -0.129,1.34 -1.24,6.507 -0.799,8.453 l 0,0 c 0.16,0.711 0.67,0.604 1.192,0.498 l 0,0 c 0.53,-0.109 1.071,-0.219 1.268,0.538 l 0,0 c 1.018,3.921 1.895,7.666 0.749,9.206 l 0,0 c -0.292,0.393 -0.865,0.571 -1.664,0.571 l 0,0 c -2.414,0 -6.885,-1.63 -11.864,-3.904" /></g></g></g></g><g
|
||||
id="g2961"><g
|
||||
clip-path="url(#clipPath2965)"
|
||||
id="g2963"><g
|
||||
transform="translate(250.4868,321.0234)"
|
||||
id="g3025"><path
|
||||
id="path3027"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c -1.953,-3.398 -2.12,-7.398 -9.162,-12.051 -1.332,-0.88 -2.181,-1.787 -2.848,-3.422 -3.239,-7.934 2.034,-25.066 2.768,-24.956 1.152,0.325 -5.615,14.689 -1.916,21.923 0.592,1.157 1.516,2.073 2.438,2.968 0.984,0.957 1.551,1.517 2.647,2.326 1.162,0.856 2.677,2.038 4.037,1.981 -0.91,0.038 -2.517,0.43 -3.432,0.469 0.717,1.218 2.645,3.181 3.182,4.031 1.328,2.1 1.92,4.192 2.286,6.731" /></g><g
|
||||
transform="translate(245.6221,311.167)"
|
||||
id="g3029"><path
|
||||
id="path3031"
|
||||
style="fill:#5c5d5d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 0,0 C 0.858,0.275 1.874,0.881 2.632,1.389 3.313,1.846 4.006,2.344 4.25,3.106 3.887,1.972 2.931,0.588 1.994,-0.055 1.517,-0.382 1.029,-0.701 0.617,-1.111 0.216,-1.509 -0.117,-1.482 -0.718,-1.59 -0.766,-1.337 -0.75,-1.075 -0.75,-0.813" /></g></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
BIN
foxyproxy/image/icon128.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
foxyproxy/image/icon32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
foxyproxy/image/icon48.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
316
foxyproxy/image/logo.svg
Normal file
@@ -0,0 +1,316 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4071"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="119.26471"
|
||||
height="145.2471"
|
||||
viewBox="0 0 119.26471 145.2471"
|
||||
sodipodi:docname="FOXYlogo.svg"><metadata
|
||||
id="metadata4077"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4075"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4085"><path
|
||||
d="m 185.797,300.527 c 0,-22.396 18.681,-40.552 41.725,-40.552 l 0,0 c 23.044,0 41.725,18.156 41.725,40.552 l 0,0 c 0,22.397 -18.681,40.554 -41.725,40.554 l 0,0 c -23.044,0 -41.725,-18.157 -41.725,-40.554"
|
||||
id="path4087"
|
||||
inkscape:connector-curvature="0" /></clipPath><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-7.9998322,76.665482,76.665482,7.9998322,231.64795,260.98437)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient4093"><stop
|
||||
style="stop-opacity:1;stop-color:#f7931d"
|
||||
offset="0"
|
||||
id="stop4095" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb16b"
|
||||
offset="1"
|
||||
id="stop4097" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4105"><path
|
||||
d="m 211.943,333.94 c -18.988,-8.854 -27.403,-30.99 -18.798,-49.443 l 0,0 c 8.604,-18.453 30.971,-26.234 49.958,-17.381 l 0,0 c 18.986,8.854 27.401,30.99 18.797,49.442 l 0,0 c -6.194,13.284 -19.518,21.037 -33.58,21.037 l 0,0 c -5.473,0 -11.058,-1.175 -16.377,-3.655"
|
||||
id="path4107"
|
||||
inkscape:connector-curvature="0" /></clipPath><radialGradient
|
||||
fx="0"
|
||||
fy="0"
|
||||
cx="0"
|
||||
cy="0"
|
||||
r="1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(50.959351,23.761917,23.092804,-49.525528,208.03369,303.08252)"
|
||||
spreadMethod="pad"
|
||||
id="radialGradient4113"><stop
|
||||
style="stop-opacity:1;stop-color:#e9e9ea"
|
||||
offset="0"
|
||||
id="stop4115" /><stop
|
||||
style="stop-opacity:1;stop-color:#e9e9ea"
|
||||
offset="0.70231894"
|
||||
id="stop4117" /><stop
|
||||
style="stop-opacity:1;stop-color:#7e8082"
|
||||
offset="1"
|
||||
id="stop4119" /></radialGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4127"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 Z"
|
||||
id="path4129"
|
||||
inkscape:connector-curvature="0" /></clipPath><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4179"><path
|
||||
d="m 219.217,320.557 c -8.361,-3.819 -11.245,-7.843 -10.12,-9.455 l 0,0 c 1.232,-1.763 6.564,-3.975 10.462,-4.716 l 0,0 c 0.437,-0.084 0.615,0.54 0.788,1.164 l 0,0 c 0.175,0.625 0.345,1.251 0.769,1.164 l 0,0 c 2.49,-0.513 5.947,-3.826 7.154,-4.78 l 0,0 c 0.615,-0.486 1.778,-1.525 2.335,-1.306 l 0,0 c 0.448,0.18 -0.171,1.544 -0.27,2.567 l 0,0 c -0.129,1.34 -1.24,6.507 -0.799,8.453 l 0,0 c 0.16,0.711 0.67,0.604 1.192,0.498 l 0,0 c 0.53,-0.109 1.071,-0.219 1.268,0.538 l 0,0 c 1.018,3.921 1.895,7.666 0.749,9.206 l 0,0 c -0.292,0.393 -0.865,0.571 -1.664,0.571 l 0,0 c -2.414,0 -6.885,-1.63 -11.864,-3.904"
|
||||
id="path4181"
|
||||
inkscape:connector-curvature="0" /></clipPath><linearGradient
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(34.282654,-15.727585,-15.727585,-34.282654,213.80908,320.43213)"
|
||||
spreadMethod="pad"
|
||||
id="linearGradient4187"><stop
|
||||
style="stop-opacity:1;stop-color:#faa54f"
|
||||
offset="0"
|
||||
id="stop4189" /><stop
|
||||
style="stop-opacity:1;stop-color:#fbb16b"
|
||||
offset="1"
|
||||
id="stop4191" /></linearGradient><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath4199"><path
|
||||
d="M 0,576 576,576 576,0 0,0 0,576 Z"
|
||||
id="path4201"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="810"
|
||||
id="namedview4073"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
showguides="true"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="135.88583"
|
||||
inkscape:cy="72.697884"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4079" /><g
|
||||
id="g4079"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="FOXYlogo"
|
||||
transform="matrix(1.25,0,0,-1.25,-224.11416,432.54923)"><g
|
||||
id="g4081"><g
|
||||
id="g4083"
|
||||
clip-path="url(#clipPath4085)"><g
|
||||
id="g4089"><g
|
||||
id="g4091"><path
|
||||
d="m 185.797,300.527 c 0,-22.396 18.681,-40.552 41.725,-40.552 l 0,0 c 23.044,0 41.725,18.156 41.725,40.552 l 0,0 c 0,22.397 -18.681,40.554 -41.725,40.554 l 0,0 c -23.044,0 -41.725,-18.157 -41.725,-40.554"
|
||||
style="fill:url(#linearGradient4093);stroke:none"
|
||||
id="path4099"
|
||||
inkscape:connector-curvature="0" /></g></g></g></g><g
|
||||
id="g4101"><g
|
||||
id="g4103"
|
||||
clip-path="url(#clipPath4105)"><g
|
||||
id="g4109"><g
|
||||
id="g4111"><path
|
||||
d="m 211.943,333.94 c -18.988,-8.854 -27.403,-30.99 -18.798,-49.443 l 0,0 c 8.604,-18.453 30.971,-26.234 49.958,-17.381 l 0,0 c 18.986,8.854 27.401,30.99 18.797,49.442 l 0,0 c -6.194,13.284 -19.518,21.037 -33.58,21.037 l 0,0 c -5.473,0 -11.058,-1.175 -16.377,-3.655"
|
||||
style="fill:url(#radialGradient4113);stroke:none"
|
||||
id="path4121"
|
||||
inkscape:connector-curvature="0" /></g></g></g></g><g
|
||||
id="g4123"><g
|
||||
id="g4125"
|
||||
clip-path="url(#clipPath4127)"><g
|
||||
id="g4131"
|
||||
transform="translate(261.9004,316.5576)"><path
|
||||
d="m 0,0 c 8.604,-18.451 0.188,-40.588 -18.798,-49.441 -18.986,-8.854 -41.353,-1.072 -49.958,17.38 -8.605,18.454 -0.189,40.589 18.798,49.443 C -30.972,26.235 -8.605,18.454 0,0 Z"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:1.76999998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4133"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4135"
|
||||
transform="translate(253.9063,323.9209)"><path
|
||||
d="m 0,0 c -0.38,6.903 -1.854,15.334 -6.533,20.806 -3.507,4.103 -8.978,-7.857 -10.103,-9.669 -1.38,-2.221 -2.706,-4.984 -4.8,-6.605 -2.945,-2.281 -6.431,-2.372 -9.833,-3.499 -5.391,-1.789 -10.688,-4.57 -14.938,-8.369 -5.214,-4.66 -10.154,-4.124 -16.67,-3.649 -2.711,0.199 -12.849,2.406 -11.04,-3.348 2.192,-6.973 7.778,-12.852 13.386,-17.248 5.302,-4.158 2.946,-8.926 -2.589,-11.503 5.72,-7.829 17.267,-8.914 26.01,-7.4 4.766,0.826 9.278,1.088 14.039,-0.052 3.533,-0.845 7.752,-4.399 11.49,-2.507 3.506,1.711 3.098,5.748 4.096,8.94 1.409,4.499 4.193,8.567 7.519,11.858 6.812,6.743 14.138,16.589 10.803,26.9 C 5.155,-8.506 -0.189,-7.015 0,0"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4137"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4139"
|
||||
transform="translate(215.7368,321.6992)"><path
|
||||
d="m 0,0 c 0,0 11.985,5.765 21.784,5.146 0,0 7.573,-2.107 11.927,-4.1 0,0 6.191,-4.964 7.201,-8.663 0,0 2.938,-8.511 -7.186,-19.298 0,0 -5.095,-6.523 -6.302,-12.757 0,0 -0.092,-4.189 -2.53,-5.651 0,0 -4.019,-1.141 -6.971,0.514 -2.393,0.678 -4.844,1.214 -7.298,1.626 -5.684,0.954 -11.46,0.006 -17.159,1.061 -4.659,0.859 -10.534,2.956 -11.796,8.135 0,0 -2.888,2.565 0.798,13.578 0,0 2.001,12.357 17.532,20.409"
|
||||
style="fill:#f7931d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4141"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4143"
|
||||
transform="translate(196.5059,295.9878)"><path
|
||||
d="m 0,0 c 2.679,-1.011 4.179,-2.636 10.782,-1.915 3.055,0.334 5.723,3.269 8.877,2.476 8.535,-2.152 22.752,-17.621 22.222,-18.163 -0.955,-0.761 -11.555,14.2 -19.136,10.842 -1.809,-0.8 -4.551,-2.507 -6.534,-2.023 -0.958,0.237 -1.792,0.686 -2.52,1.358 -0.96,0.89 -1.642,2.731 -2.91,3.235 -1.728,0.688 -3.12,0.784 -4.914,1.167 -1.929,0.412 -3.938,1.262 -5.436,2.518"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4145"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4147"
|
||||
transform="translate(243.0967,279.8789)"><path
|
||||
d="m 0,0 c 0.714,-1.26 -0.438,-4.142 -0.984,-4.45 -0.544,-0.309 -3.745,0.106 -4.459,1.367 -0.713,1.258 0.327,2.263 1.83,3.114 C -2.109,0.883 -0.713,1.259 0,0"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4149"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4151"
|
||||
transform="translate(206.8965,302.1777)"><path
|
||||
d="M 0,0 C -0.872,1.155 2.11,3.59 2.733,3.646 3.357,3.701 6.101,2.003 6.231,0.562 6.361,-0.879 3.944,0.677 2.223,0.523 0.502,0.367 0.369,-0.49 0,0"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4153"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4155"
|
||||
transform="translate(262.0864,311.4141)"><path
|
||||
d="m 0,0 c -0.677,-9.71 -5.16,-14.594 -10.862,-21.908 -3.012,-3.861 -5.017,-8.005 -6.067,-12.766 -0.653,-2.955 -2.998,-5.396 -6.187,-5.296 -3.564,0.114 -6.928,2.23 -10.487,2.862 -4.641,0.826 -9.162,0.113 -13.801,-0.206 -4.671,-0.321 -8.985,0.475 -13.127,2.686 -0.069,0.036 -6.823,4.403 -6.476,4.649 4.916,3.463 2.528,8.458 -1.511,11.371 -5.349,3.857 -10.603,10.055 -12.841,16.365 -1.254,3.539 12.776,0.416 14.245,-0.23 4.956,-2.182 2.184,-9.504 1.431,-13.29 1.562,1.982 1.916,4.999 2.747,7.338 1.244,3.501 2.716,7.061 5.389,9.728 3.419,3.415 7.537,5.966 11.82,8.15 5.051,2.578 9.983,4.83 15.651,5.436 4.155,0.441 7.829,-0.055 11.682,-1.675 0.621,-0.264 4.807,-2.148 5.815,-1.898 -3.393,1.208 -10.569,4.446 -9.555,9.164 0.338,1.567 5.62,14.545 7.748,11.455 3.797,-5.514 5.733,-13.408 5.718,-20.002 C -8.68,6.954 -6.037,2.089 -0.348,4.037 -0.384,4.025 0.028,0.412 0,0 m -7.915,12.749 c -0.384,6.973 -1.874,15.488 -6.598,21.016 -3.544,4.145 -9.07,-7.937 -10.206,-9.767 -1.394,-2.243 -2.732,-5.033 -4.849,-6.672 -2.974,-2.305 -6.495,-2.395 -9.931,-3.535 -5.446,-1.808 -10.797,-4.617 -15.089,-8.453 -5.267,-4.707 -10.258,-4.165 -16.839,-3.686 -2.739,0.201 -12.979,2.429 -11.151,-3.382 2.213,-7.042 7.856,-12.98 13.52,-17.421 5.355,-4.199 2.976,-9.016 -2.615,-11.62 5.778,-7.908 17.443,-9.004 26.273,-7.474 4.815,0.834 9.372,1.096 14.18,-0.054 3.57,-0.851 7.831,-4.442 11.607,-2.531 3.541,1.728 3.129,5.806 4.139,9.031 1.422,4.543 4.234,8.652 7.594,11.977 C -1,-13.011 6.4,-3.065 3.032,7.352 -2.708,4.155 -8.107,5.663 -7.915,12.749"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4157"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4159"
|
||||
transform="translate(240.9268,311.4058)"><path
|
||||
d="m 0,0 c 0.288,1.418 -3.452,2.332 -4.038,2.113 -0.587,-0.218 -2.331,-2.933 -1.827,-4.29 0.502,-1.358 2.012,1.088 3.634,1.692 C -0.613,0.115 -0.123,-0.601 0,0"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4161"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4163"
|
||||
transform="translate(215.5869,289.4712)"><path
|
||||
d="M 0,0 C 0.181,0.136 0.365,0.415 0.297,0.387 0.506,0.79 0.527,1.284 0.305,1.718 -0.059,2.433 -0.937,2.72 -1.652,2.354 -2.369,1.99 -2.652,1.112 -2.286,0.396 -2.284,0.392 -2.281,0.389 -2.279,0.385 -2.35,0.414 -2.423,0.425 -2.492,0.459 -3.995,1.189 -4.734,2.865 -4.337,4.425 -4.47,4.253 -4.588,4.066 -4.688,3.865 -5.445,2.307 -4.793,0.431 -3.236,-0.324 c 0.9,-0.439 1.9,-0.392 2.733,0.017 0.058,0.021 0.117,0.039 0.174,0.068 C -0.204,-0.175 -0.098,-0.091 0,0"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4165"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4167"
|
||||
transform="translate(201.3467,286.79)"><path
|
||||
d="m 0,0 c 0,0 4.649,-8.012 17.098,-5.728 0,0 8.646,2.306 13.661,-2.572 -1.023,0.995 -1.912,2.037 -3.159,2.8 -3.88,2.374 -8.425,1.586 -12.74,2.007 C 11.729,-3.186 8.814,-2.129 6.012,-0.74 4.997,-0.237 -0.543,3.323 0,0"
|
||||
style="fill:#f5aa35;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4169"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4171"
|
||||
transform="translate(254.4907,311.8257)"><path
|
||||
d="m 0,0 c 0,0 2.953,-8.739 -7.189,-15.087 0,0 -5.659,-4.735 -5.408,-11.635 -0.051,1.406 -0.211,2.757 0.021,4.15 0.385,2.318 0.466,4.79 1.388,6.944 0.89,2.08 2.775,3.179 4.241,4.747 1.653,1.768 2.733,4.008 3.344,6.417 C -3.336,-3.416 -1.854,2.765 0,0"
|
||||
style="fill:#f5aa35;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4173"
|
||||
inkscape:connector-curvature="0" /></g></g></g><g
|
||||
id="g4175"><g
|
||||
id="g4177"
|
||||
clip-path="url(#clipPath4179)"><g
|
||||
id="g4183"><g
|
||||
id="g4185"><path
|
||||
d="m 219.217,320.557 c -8.361,-3.819 -11.245,-7.843 -10.12,-9.455 l 0,0 c 1.232,-1.763 6.564,-3.975 10.462,-4.716 l 0,0 c 0.437,-0.084 0.615,0.54 0.788,1.164 l 0,0 c 0.175,0.625 0.345,1.251 0.769,1.164 l 0,0 c 2.49,-0.513 5.947,-3.826 7.154,-4.78 l 0,0 c 0.615,-0.486 1.778,-1.525 2.335,-1.306 l 0,0 c 0.448,0.18 -0.171,1.544 -0.27,2.567 l 0,0 c -0.129,1.34 -1.24,6.507 -0.799,8.453 l 0,0 c 0.16,0.711 0.67,0.604 1.192,0.498 l 0,0 c 0.53,-0.109 1.071,-0.219 1.268,0.538 l 0,0 c 1.018,3.921 1.895,7.666 0.749,9.206 l 0,0 c -0.292,0.393 -0.865,0.571 -1.664,0.571 l 0,0 c -2.414,0 -6.885,-1.63 -11.864,-3.904"
|
||||
style="fill:url(#linearGradient4187);stroke:none"
|
||||
id="path4193"
|
||||
inkscape:connector-curvature="0" /></g></g></g></g><g
|
||||
id="g4195"><g
|
||||
id="g4197"
|
||||
clip-path="url(#clipPath4199)"><g
|
||||
id="g4203"
|
||||
transform="translate(190.7241,261.791)"><path
|
||||
d="m 0,0 13.065,0 0,-5.588 -5.924,0 0,-2.017 5.2,0 0,-5.072 -5.2,0 0,-6.83 -7.141,0 L 0,0 Z"
|
||||
style="fill:#f7931d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4205"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4207"
|
||||
transform="translate(214.5513,251.8828)"><path
|
||||
d="m 0,0 c 0,-2.226 1.501,-3.752 3.648,-3.752 2.147,0 3.648,1.526 3.648,3.752 0,2.069 -1.397,3.803 -3.648,3.803 C 1.397,3.803 0,2.069 0,0 m 14.437,0.104 c 0,-6.261 -4.838,-10.013 -10.789,-10.013 -5.95,0 -10.789,3.752 -10.789,10.013 0,6.131 4.865,10.115 10.789,10.115 5.925,0 10.789,-3.984 10.789,-10.115"
|
||||
style="fill:#f7931d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4209"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4211"
|
||||
transform="translate(251.9355,261.791)"><path
|
||||
d="m 0,0 -6.105,-9.132 6.959,-10.375 -8.072,0 -2.225,4.14 c -0.207,0.388 -0.336,0.776 -0.414,1.189 l -0.207,0 c -0.129,-0.465 -0.311,-0.905 -0.543,-1.344 l -2.174,-3.985 -8.072,0 6.831,10.375 -6.132,9.132 7.994,0 1.449,-2.768 c 0.285,-0.517 0.44,-1.06 0.595,-1.604 l 0.156,0 c 0.129,0.569 0.362,1.087 0.621,1.604 L -7.994,0 0,0 Z"
|
||||
style="fill:#f7931d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4213"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4215"
|
||||
transform="translate(262.3101,261.791)"><path
|
||||
d="m 0,0 2.173,-5.097 0.207,0 c 0.207,0.621 0.388,1.243 0.647,1.837 L 4.424,0 l 7.969,0 -6.676,-11.099 0,-8.408 -6.933,0 0,8.408 L -8.021,0 0,0 Z"
|
||||
style="fill:#f7931d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4217"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4219"
|
||||
transform="translate(196.2456,236.8574)"><path
|
||||
d="m 0,0 -2.792,0 0,-2.57 2.803,0 c 0.543,0 0.984,0.105 1.323,0.317 0.369,0.227 0.554,0.535 0.554,0.928 C 1.888,-0.441 1.258,0 0,0 M 1.892,-3.091 C 1.333,-3.4 0.64,-3.555 -0.19,-3.555 l -2.602,0 0,-3.133 -1.086,0 0,7.672 3.813,0 c 2.036,0 3.054,-0.765 3.054,-2.297 0,-0.786 -0.365,-1.377 -1.097,-1.778"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4221"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4223"
|
||||
transform="translate(196.2456,236.8574)"><path
|
||||
d="m 0,0 -2.792,0 0,-2.57 2.803,0 c 0.543,0 0.984,0.105 1.323,0.317 0.369,0.227 0.554,0.535 0.554,0.928 C 1.888,-0.441 1.258,0 0,0 Z M 1.892,-3.091 C 1.333,-3.4 0.64,-3.555 -0.19,-3.555 l -2.602,0 0,-3.133 -1.086,0 0,7.672 3.813,0 c 2.036,0 3.054,-0.765 3.054,-2.297 0,-0.786 -0.365,-1.377 -1.097,-1.778 z"
|
||||
style="fill:none;stroke:#535454;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4225"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4227"
|
||||
transform="translate(213.5576,236.8574)"><path
|
||||
d="m 0,0 -2.888,0 0,-2.477 2.707,0 c 0.604,0 1.083,0.102 1.438,0.306 0.393,0.226 0.589,0.558 0.589,0.995 C 1.846,-0.392 1.23,0 0,0 m 1.935,-2.942 c 0.586,-0.37 0.88,-0.928 0.88,-1.675 l 0,-2.071 -1.086,0 0,2.036 c 0,0.784 -0.619,1.175 -1.856,1.175 l -2.761,0 0,-3.211 -1.086,0 0,7.672 3.951,0 c 1.97,0 2.955,-0.709 2.955,-2.127 0,-0.807 -0.332,-1.407 -0.997,-1.799"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4229"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4231"
|
||||
transform="translate(213.5576,236.8574)"><path
|
||||
d="m 0,0 -2.888,0 0,-2.477 2.707,0 c 0.604,0 1.083,0.102 1.438,0.306 0.393,0.226 0.589,0.558 0.589,0.995 C 1.846,-0.392 1.23,0 0,0 Z m 1.935,-2.942 c 0.586,-0.37 0.88,-0.928 0.88,-1.675 l 0,-2.071 -1.086,0 0,2.036 c 0,0.784 -0.619,1.175 -1.856,1.175 l -2.761,0 0,-3.211 -1.086,0 0,7.672 3.951,0 c 1.97,0 2.955,-0.709 2.955,-2.127 0,-0.807 -0.332,-1.407 -0.997,-1.799 z"
|
||||
style="fill:none;stroke:#535454;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4233"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4235"
|
||||
transform="translate(233.5015,236.0957)"><path
|
||||
d="m 0,0 c -0.565,0.487 -1.365,0.73 -2.397,0.73 -1.034,0 -1.829,-0.245 -2.387,-0.735 -0.558,-0.491 -0.837,-1.188 -0.837,-2.095 0,-0.905 0.283,-1.618 0.849,-2.138 0.565,-0.521 1.356,-0.782 2.375,-0.782 1.032,0 1.832,0.259 2.397,0.776 0.565,0.516 0.848,1.231 0.848,2.144 0,0.913 -0.283,1.614 -0.848,2.1 m 0.763,-4.951 c -0.781,-0.702 -1.835,-1.053 -3.163,-1.053 -1.335,0 -2.389,0.349 -3.163,1.047 -0.772,0.698 -1.159,1.65 -1.159,2.857 0,1.208 0.396,2.151 1.188,2.83 0.762,0.657 1.806,0.985 3.134,0.985 1.328,0 2.377,-0.328 3.146,-0.985 0.792,-0.679 1.188,-1.622 1.188,-2.83 0,-1.199 -0.391,-2.15 -1.171,-2.851"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4237"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4239"
|
||||
transform="translate(233.5015,236.0957)"><path
|
||||
d="m 0,0 c -0.565,0.487 -1.365,0.73 -2.397,0.73 -1.034,0 -1.829,-0.245 -2.387,-0.735 -0.558,-0.491 -0.837,-1.188 -0.837,-2.095 0,-0.905 0.283,-1.618 0.849,-2.138 0.565,-0.521 1.356,-0.782 2.375,-0.782 1.032,0 1.832,0.259 2.397,0.776 0.565,0.516 0.848,1.231 0.848,2.144 0,0.913 -0.283,1.614 -0.848,2.1 z m 0.763,-4.951 c -0.781,-0.702 -1.835,-1.053 -3.163,-1.053 -1.335,0 -2.389,0.349 -3.163,1.047 -0.772,0.698 -1.159,1.65 -1.159,2.857 0,1.208 0.396,2.151 1.188,2.83 0.762,0.657 1.806,0.985 3.134,0.985 1.328,0 2.377,-0.328 3.146,-0.985 0.792,-0.679 1.188,-1.622 1.188,-2.83 0,-1.199 -0.391,-2.15 -1.171,-2.851 z"
|
||||
style="fill:none;stroke:#535454;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4241"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4243"
|
||||
transform="translate(251.9077,230.1699)"><path
|
||||
d="M 0,0 -2.891,3.174 -5.832,0 l -1.417,0 3.63,3.93 -3.456,3.742 1.434,0 2.742,-2.978 2.759,2.978 1.416,0 L -2.175,3.936 1.399,0 0,0 Z"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4245"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4247"
|
||||
transform="translate(251.9077,230.1699)"><path
|
||||
d="M 0,0 -2.891,3.174 -5.832,0 l -1.417,0 3.63,3.93 -3.456,3.742 1.434,0 2.742,-2.978 2.759,2.978 1.416,0 L -2.175,3.936 1.399,0 0,0 Z"
|
||||
style="fill:none;stroke:#535454;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4249"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4251"
|
||||
transform="translate(266.6904,233.792)"><path
|
||||
d="m 0,0 0,-3.622 -1.086,0 0,3.621 -3.977,4.051 1.446,0 3.062,-3.108 3.073,3.108 1.429,0 L 0,0 Z"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4253"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4255"
|
||||
transform="translate(266.6904,233.792)"><path
|
||||
d="m 0,0 0,-3.622 -1.086,0 0,3.621 -3.977,4.051 1.446,0 3.062,-3.108 3.073,3.108 1.429,0 L 0,0 Z"
|
||||
style="fill:none;stroke:#535454;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4257"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4259"
|
||||
transform="translate(250.4868,321.0234)"><path
|
||||
d="m 0,0 c -1.953,-3.398 -2.12,-7.398 -9.162,-12.051 -1.332,-0.88 -2.181,-1.787 -2.848,-3.422 -3.239,-7.934 2.034,-25.066 2.768,-24.956 1.152,0.325 -5.615,14.689 -1.916,21.923 0.592,1.157 1.516,2.073 2.438,2.968 0.984,0.957 1.551,1.517 2.647,2.326 1.162,0.856 2.677,2.038 4.037,1.981 -0.91,0.038 -2.517,0.43 -3.432,0.469 0.717,1.218 2.645,3.181 3.182,4.031 1.328,2.1 1.92,4.192 2.286,6.731"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4261"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g4263"
|
||||
transform="translate(245.6221,311.167)"><path
|
||||
d="M 0,0 C 0.858,0.275 1.874,0.881 2.632,1.389 3.313,1.846 4.006,2.344 4.25,3.106 3.887,1.972 2.931,0.588 1.994,-0.055 1.517,-0.382 1.029,-0.701 0.617,-1.111 0.216,-1.509 -0.117,-1.482 -0.718,-1.59 -0.766,-1.337 -0.75,-1.075 -0.75,-0.813"
|
||||
style="fill:#535454;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path4265"
|
||||
inkscape:connector-curvature="0" /></g></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
6
foxyproxy/image/power.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#08f" d="M8 6a1 1 0 0 0 1-1V1a1 1 0 0 0-2 0v4a1 1 0 0 0 1 1zm3.5-4.032a1 1 0 0 0-1 1.732A4.946 4.946 0 0 1 13 8 5 5 0 0 1 3 8a4.946 4.946 0 0 1 2.5-4.3 1 1 0 0 0-1-1.732 7 7 0 1 0 7.006 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 509 B |
6
foxyproxy/image/privateBrowsing.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#93e" d="M15.7 6.5c-.1 1.1.2 2.5-1.3 4.4C12.9 13 11.3 13 11 13c-1.8-.1-2-1.5-3-1.5-.9 0-1.6 1.4-3 1.5a4.224 4.224 0 0 1-3.4-2C.1 9 .4 7.7.3 6.5.2 5.4 0 4.2 0 4.2a3.05 3.05 0 0 0 1.6.8c.9.1 1.1-.3 3-.9C6.7 3.5 8 6 8 6s1.4-2.4 3.4-1.9 2 .9 2.9.9a3.618 3.618 0 0 0 1.7-.8s-.2 1.2-.3 2.3zM4.9 7a3.018 3.018 0 0 0-2.1.4l-.8.2s0 .6 1.2 1.2c1.1.6 3.5.3 3.5.3A1.973 1.973 0 0 0 4.9 7zm8.3.4a3.018 3.018 0 0 0-2.1-.4 1.973 1.973 0 0 0-1.8 2.1s2.4.3 3.5-.3C14 8.2 14 7.6 14 7.6a7.347 7.347 0 0 0-.8-.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 813 B |
73
foxyproxy/manifest.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"action": {
|
||||
"default_icon": {
|
||||
"16": "image/icon.png",
|
||||
"32": "image/icon32.png"
|
||||
},
|
||||
"default_popup": "content/popup.html",
|
||||
"default_title": "__MSG_extensionName__"
|
||||
},
|
||||
"author": "Eric Jung",
|
||||
"background": {
|
||||
"scripts": [ "content/background.js" ],
|
||||
"service_worker": "content/background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "foxyproxy@eric.h.jung",
|
||||
"strict_min_version": "128.0"
|
||||
},
|
||||
"gecko_android": {
|
||||
"strict_min_version": "128.0"
|
||||
}
|
||||
},
|
||||
"commands": {
|
||||
"disable": {
|
||||
"description": "__MSG_disable__"
|
||||
},
|
||||
"excludeHost": {
|
||||
"description": "__MSG_excludeHost__"
|
||||
},
|
||||
"includeHost": {
|
||||
"description": "__MSG_includeHost__"
|
||||
},
|
||||
"proxyByPatterns": {
|
||||
"description": "__MSG_proxyByPatterns__"
|
||||
},
|
||||
"setProxy": {
|
||||
"description": "__MSG_setProxy__"
|
||||
},
|
||||
"setTabProxy": {
|
||||
"description": "__MSG_setTabProxy__"
|
||||
},
|
||||
"unsetTabProxy": {
|
||||
"description": "__MSG_unsetTabProxy__"
|
||||
}
|
||||
},
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"homepage_url": "https://getfoxyproxy.org/",
|
||||
"host_permissions": [ "\u003Call_urls>" ],
|
||||
"icons": {
|
||||
"128": "image/icon128.png",
|
||||
"16": "image/icon.png",
|
||||
"32": "image/icon32.png",
|
||||
"48": "image/icon48.png"
|
||||
},
|
||||
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtm3ZHHlxexcA/qyQrdInfTvzbTOG1IQYyoels62bOQZes5N+G+tFkUmLzgzV5SgFImvma72PXNgXZnUWgckiYkqgilBTHblFO3heiPAuIXA/69iqsutd1iqT+/6g1cnuF08j2fbZUNaDXZKQPc/cj8KGZ+X50kMEYsDdbgodOWwIDAQAB",
|
||||
"manifest_version": 3,
|
||||
"minimum_chrome_version": "108",
|
||||
"name": "__MSG_extensionName__",
|
||||
"optional_permissions": [ "browsingData", "privacy" ],
|
||||
"options_ui": {
|
||||
"open_in_tab": true,
|
||||
"page": "content/options.html"
|
||||
},
|
||||
"permissions": [ "contextMenus", "downloads", "notifications", "proxy", "storage", "tabs", "webRequest", "webRequestAuthProvider" ],
|
||||
"storage": {
|
||||
"managed_schema": "content/schema.json"
|
||||
},
|
||||
"update_url": "https://clients2.google.com/service/update2/crx",
|
||||
"version": "9.2"
|
||||
}
|
||||
1016
svg-gubbler/_locales/en/messages.json
Normal file
1013
svg-gubbler/_locales/es/messages.json
Normal file
1013
svg-gubbler/_locales/fr/messages.json
Normal file
1013
svg-gubbler/_locales/hi/messages.json
Normal file
1013
svg-gubbler/_locales/ru/messages.json
Normal file
1013
svg-gubbler/_locales/zh_CN/messages.json
Normal file
1
svg-gubbler/_metadata/computed_hashes.json
Normal file
1
svg-gubbler/_metadata/verified_contents.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"description":"treehash per file","signed_content":{"payload":"eyJjb250ZW50X2hhc2hlcyI6W3siYmxvY2tfc2l6ZSI6NDA5NiwiZGlnZXN0Ijoic2hhMjU2IiwiZmlsZXMiOlt7InBhdGgiOiJfbG9jYWxlcy9lbi9tZXNzYWdlcy5qc29uIiwicm9vdF9oYXNoIjoibUoyVVJ1SFhvMzNWd3JSYXhPcWdGMzhhT0hiSTVZOXZVNUVNQnRSUXJmUSJ9LHsicGF0aCI6Il9sb2NhbGVzL2VzL21lc3NhZ2VzLmpzb24iLCJyb290X2hhc2giOiJORVAxOFdqZkdoVFkzazU0TmNRb2pZOHZUU1VzRC1zY0NKU0JFcVJMX0VjIn0seyJwYXRoIjoiX2xvY2FsZXMvZnIvbWVzc2FnZXMuanNvbiIsInJvb3RfaGFzaCI6IjZrYVNXLWk3cE43bHhRY3R3NkZ2b2FCOTBjZVloejJQdG54SDNoUXdSZzgifSx7InBhdGgiOiJfbG9jYWxlcy9oaS9tZXNzYWdlcy5qc29uIiwicm9vdF9oYXNoIjoiWEx5RnF1TjRHbFl5S2xadXFkMkl6cEY2Wll4WWV4LUprRm1fTGUzQlI2ayJ9LHsicGF0aCI6Il9sb2NhbGVzL3J1L21lc3NhZ2VzLmpzb24iLCJyb290X2hhc2giOiJjMFI3bGstc3dTOUpHRmVNSGRscnNBY3l6S21VbC1QTXFrVG9vYV9GN19NIn0seyJwYXRoIjoiX2xvY2FsZXMvemhfQ04vbWVzc2FnZXMuanNvbiIsInJvb3RfaGFzaCI6ImV6SmVlU1Q1VlBLaVV2Wl9ucDZLX3hDa2xzVkdKU1NNZXJvMmlHcE5DRWcifSx7InBhdGgiOiJhc3NldHMvYmFja2dyb3VuZC50cy1DMU1XYU05Yy5qcyIsInJvb3RfaGFzaCI6Im5ueUdMVUVaTi1TTk1kZEl0YlpXM2hSekJ1RHNyTW1LQzRsYTYtanh4QkUifSx7InBhdGgiOiJhc3NldHMvZGV2LzE2LnBuZyIsInJvb3RfaGFzaCI6IjUwVlZ4MTVCQ25qUEdoQXZlX3NCazZqUTZ2M3BJZk84WTdGcUZsZUFONUUifSx7InBhdGgiOiJhc3NldHMvZGV2LzI0LnBuZyIsInJvb3RfaGFzaCI6ImNuLWhWeEk1WnBlYmNpNVBQblRPSURYNnpkVDEyYW5kZ0Rsby03R0lfS00ifSx7InBhdGgiOiJhc3NldHMvZGV2LzMyLnBuZyIsInJvb3RfaGFzaCI6IjU4bFI0V2NnTGIyX1Qxcmk1RXlMZ0hGRkdrWno3SDdyWDlGOHk5cE5nN2sifSx7InBhdGgiOiJhc3NldHMvZGV2LzQ4LnBuZyIsInJvb3RfaGFzaCI6IjdGQl81b0xpZExlSE1GSWZNWk1jOHd2RlFISVEzeDFCenFTVjJFcnFORm8ifSx7InBhdGgiOiJhc3NldHMvZGV2LzY0LnBuZyIsInJvb3RfaGFzaCI6InVnV01zMERmRnVxdGVraGs4eXNTbGdJaEhvY1FLYnRUV1c3eHU4T1VtYW8ifSx7InBhdGgiOiJhc3NldHMvZXh0ZW5zaW9uLXV0aWxpdGllcy1EVTA1ZnByLS5qcyIsInJvb3RfaGFzaCI6IkdIcHphMWZkS192M2FmV01odjc4TUZoeGRGbHZTbEZTdnhJTGlqTWh2eWsifSx7InBhdGgiOiJhc3NldHMvaW5kZXgtQlNIckQtb24uanMiLCJyb290X2hhc2giOiJtSlJZM0JQbHd5NjNjcTUwSmRDUjBncnFJdldaVTVxWVRBNjBUcDhOT0pvIn0seyJwYXRoIjoiYXNzZXRzL2luZGV4LUNmSTdubGVDLmpzIiwicm9vdF9oYXNoIjoiZ0xYTnR1Qi00YXdSVWw3RXVlTGxsV3BEVnpKU2VfTUQ1d0dqcU4wU3ZNayJ9LHsicGF0aCI6ImFzc2V0cy9pbmRleC1Ea21mR010Uy5jc3MiLCJyb290X2hhc2giOiJaVGpnaExObl96bFoydF9XRTAwOEpSSkdVdnlZSWtITzQzRjl4N3ZXTUJVIn0seyJwYXRoIjoiYXNzZXRzL2luZGV4LVhDTXRYNHdFLmpzIiwicm9vdF9oYXNoIjoiV0MtWEtSVU5FMTM3cmJzTkFmUVk4cUJfMzBoWUo1MU96a0VvODlMZE5EVSJ9LHsicGF0aCI6ImFzc2V0cy9vbmJvYXJkaW5nLTVmVHBaUlNWLmpzIiwicm9vdF9oYXNoIjoiTzk2M3l6b3Z3VkJMcnZLNE5MZUJqRzFQbTlFaWtYTHNXcUM4TlkxT0dWNCJ9LHsicGF0aCI6ImFzc2V0cy9vbmJvYXJkaW5nLWdyYXBoaWMtQy1td2xsTlYucG5nIiwicm9vdF9oYXNoIjoiT1E3LVEtalQ1LVNBRHpnakFCRmVZY0dnNzJCNFNKVlo5Z1hXODF2N3otdyJ9LHsicGF0aCI6ImFzc2V0cy9wcm9kLzEyOC5wbmciLCJyb290X2hhc2giOiJzWkhUY2FGbmc2QlVlN1djWmZTSFVBbnZDdno0cjhKM3NLUmw2T1ZKSjNBIn0seyJwYXRoIjoiYXNzZXRzL3Byb2QvMTYucG5nIiwicm9vdF9oYXNoIjoiVGhoUWtMTXRnRHUxbFRTQjVobUoxczNtdzZKejVnTklJRElYS09BdWlqcyJ9LHsicGF0aCI6ImFzc2V0cy9wcm9kLzI0LnBuZyIsInJvb3RfaGFzaCI6InM1M3g4bmhKT1ltcGFoU0pqUUhuTE1PMGF3ZkVRUWs3cnRfMi1FaE8xLTQifSx7InBhdGgiOiJhc3NldHMvcHJvZC8yNTYucG5nIiwicm9vdF9oYXNoIjoiTzFNMHgwQk1tQ3BKUDM1NG9LSVJOY3Bmb2FHcGJ4QjAtYWRLSGIzRjg5ZyJ9LHsicGF0aCI6ImFzc2V0cy9wcm9kLzMyLnBuZyIsInJvb3RfaGFzaCI6IndfVkI4RzlDZVAxYy1uZVlXVm50Mk5NV2MzQXgzSXBNQm90b3FJN3BtaE0ifSx7InBhdGgiOiJhc3NldHMvcHJvZC80OC5wbmciLCJyb290X2hhc2giOiJVWEJrckkyYkY5WUY4Sy1Bd29KY0RNc2t3SVpybVVBSV9CM19iN21Cc0dBIn0seyJwYXRoIjoiYXNzZXRzL3Byb2QvNjQucG5nIiwicm9vdF9oYXNoIjoiTHR3VngzemN0Zk1ubzY4cTUwUG9mVlJXMms0NU8yQ19tT0J2bGNLRks1USJ9LHsicGF0aCI6ImZhdmljb24uaWNvIiwicm9vdF9oYXNoIjoiV2VhRGx2cmt4ajR3bGZzVHRhekY2amdxQ0NCXzNxOE83dVJNLURtVERRQSJ9LHsicGF0aCI6ImluZGV4Lmh0bWwiLCJyb290X2hhc2giOiJTY1p5U3owLUdmOXlnMjZOaThSRHpvaG44RlpJMFEwUHhZczFpNVpQNFZVIn0seyJwYXRoIjoibWFuaWZlc3QuanNvbiIsInJvb3RfaGFzaCI6Il9uOFQ1NWJLWV9EQ2pkbmsxcHloWW5UVk5Fd08tYUI2N1JjclhjeUVWYmcifSx7InBhdGgiOiJvbmJvYXJkaW5nLmh0bWwiLCJyb290X2hhc2giOiJVX1RvSVlaOUduVUpnMXUwSlFvcGNEX0VRRHlsV3VvaThsMzZOY1FpWmRZIn0seyJwYXRoIjoic2VydmljZS13b3JrZXItbG9hZGVyLmpzIiwicm9vdF9oYXNoIjoieWtOc1RpX2M4OUtBMmhYZnBSdklKTUoyYkpDNGJQazNxMjA2MDAwbUpuayJ9XSwiZm9ybWF0IjoidHJlZWhhc2giLCJoYXNoX2Jsb2NrX3NpemUiOjQwOTZ9XSwiaXRlbV9pZCI6Im1wYm1mbGNvZGFkaGdhZmJiYWtqZWFocGFuZGdjYmNoIiwiaXRlbV92ZXJzaW9uIjoiNS4zMC4xIiwicHJvdG9jb2xfdmVyc2lvbiI6MX0","signatures":[{"header":{"kid":"publisher"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"Gwa4CkbKtG5Wlfh_rckorqeiArb0YHRYfYb_S67m0oaGXwKMHn4kOVamBtOAgui7wKnjMORo0ARUeVF1clSj3ONcn3UK7kRe9_xtrJsjtjsctL_BwMNjyxZ3zMLJ2BcKdI07UuB3_uVS7LxRTBd4CqISmxRcoWcwdorhEqpYfbEn-rzds1POLkNA7-a3PXq1z9uFsqN2voQ22reerq-NSIks2aMHFneaXjW1hwc2uDF123YKi3hLGjF6iDq0VR-D6cwa7H1usv-vw8UARTV7M9cWhKPmS4M_aTBUZn-wzuBDQ2dByJ3A67eNoQj4qMXHgFl6xKorOD0VGWfJAmQ80g"},{"header":{"kid":"webstore"},"protected":"eyJhbGciOiJSUzI1NiJ9","signature":"SQdwTYzCw-KqV9X0b4_YQUDKJp_Qy3nKZlDu7aMzuaIWfPgiYcFOOT8tUd5LjOwSZYXaGx865DbmotvqhloWRmA4c_13grR_-ZNpJ2PUBUXwQFYfQU8fRMx4_PAXMrj1YseGONfpzmOTmReH6sFBcoSp8vRt184JUwkG4QwAow7jlilmlf91mVnx8OVaeJwiptdPRhlcFzNQxm2zG7TZiIMquMteGYaJhnr-ct4oMOcXd-D-I3Jd7fF4Dgq2DfUr57WIA3Bsyz_K189e6EISBk__ddwdVD_uS683y86GtGV2wcywfezvvmoCMPQGYmCwr7N87SHpY0stbtuMwAJj_Q"}]}}]
|
||||