diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..539a042 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +liberapay: SimonBrazell +custom: https://www.buymeacoffee.com/SimonBrazell diff --git a/README.md b/README.md index 2bb6c3a..0e710e5 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -# ![privacy-redirect](images/icon32.png) Privacy Redirect +# ![privacy-redirect](assets/images/logo-small.png) -[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/SimonBrazell/donate) [![Buy me a coffee](images/buy-me-a-coffee.png)](https://www.buymeacoffee.com/SimonBrazell) +[![Awesome Humane Tech](https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true)](https://github.com/humanetech-community/awesome-humane-tech) -- [Chrome Extension](https://chrome.google.com/webstore/detail/privacy-redirect/pmcmeagblkinmogikoikkdjiligflglb) -- [Firefox Add-on](https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect/) +[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/SimonBrazell/donate) [![Buy me a coffee](assets/images/buy-me-a-coffee.png)](https://www.buymeacoffee.com/SimonBrazell) + +[![Firefox Add-on](assets/images/badge-amo.png)](https://addons.mozilla.org/en-US/firefox/addon/privacy-redirect/) [![Chrome Extension](assets/images/badge-chrome.png)](https://chrome.google.com/webstore/detail/privacy-redirect/pmcmeagblkinmogikoikkdjiligflglb) [![Edge Extension](assets/images/badge-ms.png)](https://microsoftedge.microsoft.com/addons/detail/privacy-redirect/elnabkhcgpajchapppkhiaifkgikgihj) A web extension that redirects Twitter, YouTube, Instagram & Google Maps requests to privacy friendly alternatives - [Nitter](https://github.com/zedeus/nitter), [Invidious](https://github.com/omarroth/invidious), [Bibliogram](https://github.com/cloudrac3r/bibliogram) & [OpenStreetMap](https://www.openstreetmap.org/). @@ -12,9 +13,13 @@ Allows for setting custom [Nitter](https://github.com/zedeus/nitter/wiki/Instanc ## Build 1. `npm install --global web-ext` -2. `web-ext build` +2. `web-ext build --overwrite-dest` 3. See `web-ext-artifacts/` for outputs. ## License Code released under [the MIT license](LICENSE.txt). + +## Privacy Policy + +See the [Project Wiki](https://github.com/SimonBrazell/privacy-redirect/wiki/Privacy-Policy). diff --git a/_locales/de/messages.json b/_locales/de/messages.json new file mode 100644 index 0000000..431ed1d --- /dev/null +++ b/_locales/de/messages.json @@ -0,0 +1,110 @@ +{ + "extensionName": { + "message": "Privacy Redirect", + "description": "Name of the extension." + }, + "extensionDescription": { + "message": "Leitet Anfragen von Twitter, YouTube, Instagram & Google Maps auf datenschutzfreundliche Alternativen weiter.", + "description": "Description of the extension." + }, + "nitterInstance": { + "message": "Nitter-Instanz", + "description": "Label for Nitter instance field option (options)." + }, + "invidiousInstance": { + "message": "Invidious-Instanz", + "description": "Label for Invidious instance field option (options)." + }, + "bibliogramInstance": { + "message": "Bibliogram-Instanz", + "description": "Label for Bibliogram instance field option (options)." + }, + "osmInstance": { + "message": "OpenStreetMap-Instanz", + "description": "Label for OSM instance field option (options)." + }, + "disableNitter": { + "message": "Nitter-Weiterleitungen", + "description": "Label for enable/disable Nitter redirects option (options & pop-up)." + }, + "disableInvidious": { + "message": "Invidious-Weiterleitungen", + "description": "Label for enable/disable Invidious redirects option (options & pop-up)." + }, + "disableBibliogram": { + "message": "Bibliogram-Weiterleitungen", + "description": "Label for enable/disable Bibliogram redirects option (options & pop-up)." + }, + "disableOsm": { + "message": "OpenStreetMap-Weiterleitungen", + "description": "Label for enable/disable OSM redirects option (options & pop-up)." + }, + "alwaysProxy": { + "message": "Videos immer durch Invidious-Proxy leiten", + "description": "Label for 'Always proxy videos through Invidious' option (options)." + }, + "onlyEmbeddedVideo": { + "message": "Nur eingebettete Videos zu Invidious weiterleiten", + "description": "Label for 'Only redirect embedded video to Invidious' option (options)." + }, + "videoQuality": { + "message": "Videoqualität bei Invidious", + "description": "Label for 'Invidious Video Quality' option (options)." + }, + "removeTwitterSW": { + "message": "Twitter-Serviceworker proaktiv entfernen", + "description": "Label for 'Proactively remove Twitter service worker' option (options)." + }, + "invidiousDarkMode": { + "message": "Dunklen Modus auf Invidious immer anschalten", + "description": "Label for 'Invidious dark mode always on' option (options)." + }, + "persistInvidiousPrefs": { + "message": "Invidious-Einstellungen dauerhaft beibehalten (als Cookie)", + "description": "Label for 'Persist Invidious preferences (as cookie)' option (options)." + }, + "generalTab": { + "message": "Allgmein", + "description": "General tab (options)." + }, + "advancedTab": { + "message": "Erweitert", + "description": "Advanced tab (options)." + }, + "exceptionsTab": { + "message": "Ausnahmen", + "description": "Exceptions tab (options)." + }, + "exceptionsDescriptionP1": { + "message": "Gib eine URL oder einen regulären Ausdruck (Regex) ein, die/der von Weiterleitungen ausgeschlossen werden soll.", + "description": "A description of the 'Exceptions' feature paragraph 1 (options)." + }, + "exceptionsDescriptionP2": { + "message": "Alle Anfragen für oder ausgehend von einer URL, die auf die Ausnahme zutrifft, werden von Weiterleitungen ausgeschlossen.", + "description": "A description of the 'Exceptions' feature paragraph 2 (options)." + }, + "exceptionsDescriptionP3": { + "message": "Hinweis – Unterstützt reguläre JavaScript-Ausdrücke (Regex), mit Ausnahme der einschließenden Schrägstriche.", + "description": "A description of the 'Exceptions' feature paragraph 3 (options)." + }, + "addException": { + "message": "Ausnahme hinzufügen", + "description": "'Add Exceptions' button (options)." + }, + "moreOptions": { + "message": "Weitere Optionen", + "description": "More Options button (pop-up)." + }, + "privacy": { + "message": "Privacy", + "description": "Extension title - Privacy (pop-up)." + }, + "redirect": { + "message": "Redirect", + "description": "Extension title - Redirect (pop-up)." + }, + "version": { + "message": "Version", + "description": "Version" + } +} diff --git a/_locales/de/store.md b/_locales/de/store.md new file mode 100644 index 0000000..7dc315a --- /dev/null +++ b/_locales/de/store.md @@ -0,0 +1,36 @@ +# Extension Store (AMO & Chrome Web Store) Listing + +## Summary: +``` +Eine einfache Browser-Erweiterung, die Anfragen an Twitter, YouTube, Instagram & Google Maps zu datenschutzfreundlichen Alternativen weiterleitet. +``` +## Description: +``` +Leitet Anfragen an Twitter, YouTube, Instagram & Google Maps zu datenschutzfreundlichen Alternativen weiter – Nitter, Invidious, Bibliogram, & OpenStreetMap. + +Erlaubt das Einstellen benutzerdefinierter Instanzen, das Ein- und Ausschalten aller Weiterleitungen und vieles mehr. + +★ Weitere Infos: ℹ️ + + +Der Code dieser Browser-Erweiterung ist verfügbar auf Github. + +★ Spenden: 👨🏻‍💻 +Wenn Ihnen diese Erweiterung gefällt und Sie finanziell in der Lage sind, erwägen Sie bitte mir auf BuyMeACoffee einen Kaffee zu kaufen ☕️ ️ um Ihre Wertschätzung zu zeigen und die Fortsetzung des Projekts zu unterstützen. + +★ Was ist neu in dieser Version (v1.1.36) 🆕 + + +★ Berechtigungen: ℹ️ + +``` diff --git a/_locales/en/messages.json b/_locales/en/messages.json new file mode 100644 index 0000000..2e106a7 --- /dev/null +++ b/_locales/en/messages.json @@ -0,0 +1,110 @@ +{ + "extensionName": { + "message": "Privacy Redirect", + "description": "Name of the extension." + }, + "extensionDescription": { + "message": "Redirects Twitter, YouTube, Instagram & Google Maps requests to privacy friendly alternatives.", + "description": "Description of the extension." + }, + "nitterInstance": { + "message": "Nitter Instance", + "description": "Label for Nitter instance field option (options)." + }, + "invidiousInstance": { + "message": "Invidious Instance", + "description": "Label for Invidious instance field option (options)." + }, + "bibliogramInstance": { + "message": "Bibliogram Instance", + "description": "Label for Bibliogram instance field option (options)." + }, + "osmInstance": { + "message": "OpenStreetMap Instance", + "description": "Label for OSM instance field option (options)." + }, + "disableNitter": { + "message": "Nitter Redirects", + "description": "Label for enable/disable Nitter redirects option (options & pop-up)." + }, + "disableInvidious": { + "message": "Invidious Redirects", + "description": "Label for enable/disable Invidious redirects option (options & pop-up)." + }, + "disableBibliogram": { + "message": "Bibliogram Redirects", + "description": "Label for enable/disable Bibliogram redirects option (options & pop-up)." + }, + "disableOsm": { + "message": "OpenStreetMap Redirects", + "description": "Label for enable/disable OSM redirects option (options & pop-up)." + }, + "alwaysProxy": { + "message": "Always proxy videos through Invidious", + "description": "Label for 'Always proxy videos through Invidious' option (options)." + }, + "onlyEmbeddedVideo": { + "message": "Only redirect embedded video to Invidious", + "description": "Label for 'Only redirect embedded video to Invidious' option (options)." + }, + "videoQuality": { + "message": "Invidious Video Quality", + "description": "Label for 'Invidious Video Quality' option (options)." + }, + "removeTwitterSW": { + "message": "Proactively remove Twitter service worker", + "description": "Label for 'Proactively remove Twitter service worker' option (options)." + }, + "invidiousDarkMode": { + "message": "Invidious dark mode always on", + "description": "Label for 'Invidious dark mode always on' option (options)." + }, + "persistInvidiousPrefs": { + "message": "Persist Invidious preferences (as cookie)", + "description": "Label for 'Persist Invidious preferences (as cookie)' option (options)." + }, + "generalTab": { + "message": "General", + "description": "General tab (options)." + }, + "advancedTab": { + "message": "Advanced", + "description": "Advanced tab (options)." + }, + "exceptionsTab": { + "message": "Exceptions", + "description": "Exceptions tab (options)." + }, + "exceptionsDescriptionP1": { + "message": "Enter a URL or Regular Expression to be excluded from redirects.", + "description": "A description of the 'Exceptions' feature paragraph 1 (options)." + }, + "exceptionsDescriptionP2": { + "message": "All requests for or initiating from a URL that matches the exception will be excluded from redirects.", + "description": "A description of the 'Exceptions' feature paragraph 2 (options)." + }, + "exceptionsDescriptionP3": { + "message": "Note - Supports JavaScript regular expressions, excluding the enclosing forward slashes.", + "description": "A description of the 'Exceptions' feature paragraph 3 (options)." + }, + "addException": { + "message": "Add Exception", + "description": "'Add Exceptions' button (options)." + }, + "moreOptions": { + "message": "More Options", + "description": "More Options button (pop-up)." + }, + "privacy": { + "message": "Privacy", + "description": "Extension title - Privacy (pop-up)." + }, + "redirect": { + "message": "Redirect", + "description": "Extension title - Redirect (pop-up)." + }, + "version": { + "message": "Version", + "description": "Version" + } +} diff --git a/_locales/en/store.md b/_locales/en/store.md new file mode 100644 index 0000000..deef009 --- /dev/null +++ b/_locales/en/store.md @@ -0,0 +1,36 @@ +# Extension Store (AMO & Chrome Web Store) Listing + +## Summary: +``` +A simple web extension that redirects Twitter, YouTube, Instagram & Google Maps requests to privacy friendly alternatives. +``` +## Description: +``` +Redirects Twitter, YouTube, Instagram, & Google Maps requests to privacy friendly alternatives - Nitter, Invidious, Bibliogram, & OpenStreetMap. + +Allows for setting custom instances, toggling all redirects on/off and more. + +★ More Info: ℹ️ + + +The code for this web extension is available on Github. + +★ Donate: 👨🏻‍💻 +If you like this extension and are financially able please consider buying me a coffee ☕️ ️to show your appreciation and support the continuation of the project. + +★ What's New in This Version (v1.1.36) 🆕 + + +★ Permissions: ℹ️ + +``` diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json new file mode 100644 index 0000000..7b66ab4 --- /dev/null +++ b/_locales/fr/messages.json @@ -0,0 +1,110 @@ +{ + "extensionName": { + "message": "Privacy Redirect", + "description": "Nom du module complémentaire." + }, + "extensionDescription": { + "message": "Redirige les requêtes les demandes Twitter, YouTube, Instagram et Google Maps vers des alternatives respectueuses de la vie privée. pour Twitter, YouTube, Instagram et Google Maps vers des alternatives respectueuses de la vie privée.", + "description": "Description du module complémentaire." + }, + "nitterInstance": { + "message": "Instance de Nitter", + "description": "Étiquette pour l'option de champ d'instance Nitter (options)." + }, + "invidiousInstance": { + "message": "Instance de Invidious", + "description": "Étiquette pour l'option de champ d'instance Invidious (options)." + }, + "bibliogramInstance": { + "message": "Instance de Bibliogram", + "description": "Étiquette pour l'option de champ d'instance Bibliogram (options)." + }, + "osmInstance": { + "message": "Instance de OpenStreetMap", + "description": "Étiquette pour l'option de champ d'instance OpenStreetMap (options)." + }, + "disableNitter": { + "message": "Redirection vers Nitter", + "description": "Étiquette pour activer / désactiver l'option de redirection vers Nitter (options et pop-up)." + }, + "disableInvidious": { + "message": "Redirection vers Invidious", + "description": "Étiquette pour activer / désactiver l'option de redirection vers Invidious (options et pop-up)." + }, + "disableBibliogram": { + "message": "Redirection vers Bibliogram", + "description": "Étiquette pour activer / désactiver l'option de redirection vers Bibliogram (options et pop-up)." + }, + "disableOsm": { + "message": "Redirection vers OpenStreetMap", + "description": "Étiquette pour activer / désactiver l'option de redirection vers OpenStreetMap (options et pop-up)." + }, + "alwaysProxy": { + "message": "Toujours transiter par proxy les vidéos via Invidious", + "description": "Libellé pour l'option 'Toujours transiter par proxy les vidéos via Invidious' (options)." + }, + "onlyEmbeddedVideo": { + "message": "Rediriger uniquement les vidéos intégrées vers Invidious", + "description": "Libellé pour l'option 'Rediriger uniquement les vidéos intégrées vers Invidious' (options)." + }, + "videoQuality": { + "message": "Qualité des vidéos Invidious", + "description": "Libellé pour l'option 'Qualité des vidéos Invidious' (options)." + }, + "removeTwitterSW": { + "message": "Supprimer proactivement le service worker de Twitter", + "description": "Libellé pour l'option 'Supprimer proactivement le service worker de Twitter' (options)." + }, + "invidiousDarkMode": { + "message": "Mode sombre toujours activé pour Invidious", + "description": "Libellé pour l'option 'Mode sombre toujours activé pour Invidious' (options)." + }, + "persistInvidiousPrefs": { + "message": "Conserver les préférences d’Invidious (sous forme de cookie)", + "description": "Libellé pour 'Conserver les préférences d’Invidious (sous forme de cookie)' option (options)." + }, + "generalTab": { + "message": "Général", + "description": "Onglet général (options)." + }, + "advancedTab": { + "message": "Avancé", + "description": "Onglet avancé (options)." + }, + "exceptionsTab": { + "message": "Exceptions", + "description": "Onglet des Exceptions dans les options." + }, + "exceptionsDescriptionP1": { + "message": "Entrez une adresse URL ou une expression régulière qui sera exclue des redirections.", + "description": "Description pour la rubrique 'Exceptions' dans les options." + }, + "exceptionsDescriptionP2": { + "message": "Toutes les demandes émanant d'une adresse URL et correspondant à l'exception seront exclues des redirections.", + "description": "Description pour la rubrique 'Exceptions' dans les options." + }, + "exceptionsDescriptionP3": { + "message": "Remarque - Prend en charge les expressions régulières JavaScript, à l'exclusion des barres slash.", + "description": "Description pour la rubrique 'Exceptions' dans les options." + }, + "addException": { + "message": "Ajoutez une exception", + "description": "boutton 'Ajoutez une exception' dans les options." + }, + "moreOptions": { + "message": "Options supplémentaires", + "description": "Boutton des options supplémentaires (pop-up)." + }, + "privacy": { + "message": "Vie privée", + "description": "Titre du module complémentaire - Vie privée (pop-up)." + }, + "redirect": { + "message": "Redirection", + "description": "Titre du module complémentaire - Redirection (pop-up)." + }, + "version": { + "message": "Version", + "description": "Version" + } +} diff --git a/_locales/fr/store.md b/_locales/fr/store.md new file mode 100644 index 0000000..99a388c --- /dev/null +++ b/_locales/fr/store.md @@ -0,0 +1,36 @@ +# Extension Store (AMO & Chrome Web Store) Listing + +## Summary: +``` +Redirige les requêtes les demandes Twitter, YouTube, Instagram et Google Maps vers des alternatives respectueuses de la vie privée. pour Twitter, YouTube, Instagram et Google Maps vers des alternatives respectueuses de la vie privée. +``` +## Description: +``` +Redirige les requètes pour Twitter, YouTube, Instagram et Google Maps vers des alternatives respectueuses de la confidentialité - Nitter, Invidious, Bibliogram, & OpenStreetMap. + +Permet de définir des instances personnalisées et d'activer ou désactiver toutes les redirections. + +★ Plus d'informations: ℹ️ + + +Le code de cette extension Web est disponible sur Github. + +★ Faire un don: 👨🏻‍💻 +Si vous aimez ce module complémentaire et si vous avez une bonne situation financière, pensez à m'acheter un café ☕️ pour montrer que vous appréciez et soutenez la poursuite du projet. + +★ Nouveautés de cette version (v1.1.36) 🆕 + + +★ Autorisations: ℹ️ + +``` \ No newline at end of file diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json new file mode 100644 index 0000000..358554c --- /dev/null +++ b/_locales/pl/messages.json @@ -0,0 +1,110 @@ +{ + "extensionName": { + "message": "Privacy Redirect", + "description": "Nazwa rozszerzenia." + }, + "extensionDescription": { + "message": "Przekierowuje serwisy takie jak: Twitter, YouTube, Instagram i Google Maps do alternatyw sprzyjających prywatności.", + "description": "Opis rozszerzenia." + }, + "nitterInstance": { + "message": "Instancja Nitter", + "description": "Etykieta dla pola wyboru instancji Nitter (opcje)." + }, + "invidiousInstance": { + "message": "Instancja Invidious", + "description": "Etykieta dla pola wyboru instancji Invidious (opcje)." + }, + "bibliogramInstance": { + "message": "Instancja Bibliogram", + "description": "Etykieta dla pola wyboru instancji Bibliogram (opcje)." + }, + "osmInstance": { + "message": "Instancja OpenStreetMap", + "description": "Etykieta dla pola wyboru instancji OpenStreetMap (opcje)." + }, + "disableNitter": { + "message": "Przekierowanie Nitter", + "description": "Etykieta dla włączenia/wyłączenia opcji przekierowania Nitter (opcje i okno pop-up)." + }, + "disableInvidious": + "message": "Przekierowanie Invidious ", + "description": "Etykieta dla włączenia/wyłączenia opcji przekierowania Invidious (opcje i okno pop-up)." + }, + "disableBibliogram": { + "message": "Przekierowanie Bibliogram", + "description": "Etykieta dla włączenia/wyłączenia opcji przekierowania Bibliogram (opcje i okno pop-up)." + }, + "disableOsm": { + "message": "Przekierowanie OpenStreetMap", + "description": "Etykieta dla włączenia/wyłączenia opcji przekierowania OpenStreetMap (opcje i okno pop-up)." + }, + "alwaysProxy": { + "message": "Zawsze buforuj i wyświetlaj odnośniki wideo z YouTube poprzez Invidious", + "description": "Etykieta dla opcji 'Zawsze buforuj i wyświetlaj odnośniki wideo poprzez Invidious'." + }, + "onlyEmbeddedVideo": { + "message": "Jedynie przekieruj odnośniki wideo do instancji Invidious", + "description": "Etykieta dla opcji 'Jedynie przekieruj odnośniki wideo do instancji Invidious'." + }, + "videoQuality": { + "message": "Jakość wideo Invidious", + "description": "Etykieta dla opcji 'Jakość wideo Invidious'." + }, + "removeTwitterSW": { + "message": "Proaktywnie usuwaj skrypty serwisowe (service worker) Twittera", + "description": "Etykieta dla opcji 'Proaktywnie usuwaj skrypty serwisowe (service worker) Twittera'." + }, + "invidiousDarkMode": { + "message": "Tryb ciemny dla Invidious zawsze włączony", + "description": "Etykieta dla opcji 'Tryb ciemny dla Invidious zawsze włączony'." + }, + "persistInvidiousPrefs": { + "message": "Zapisz ustawienia Invidious (jako plik cookie)", + "description": "Etykieta dla 'Zapisz ustawienia Invidious (jako plik cookie)'." + }, + "generalTab": { + "message": "Główne", + "description": "Zakładka Główne (opcje)." + }, + "advancedTab": { + "message": "Advanced", + "description": "Zakładka zaawansowane (opcje)." + }, + "exceptionsTab": { + "message": "Wyjątki", + "description": "Zakładka wyjątki (opcje)." + }, + "exceptionsDescriptionP1": { + "message": "Wprowadź adres URL lub wyrażenie regularne, które zostanie wyłączone z przekierowania. ", + "description": "Opis funkcji 'Wyjątki' paragraf 1 (opcje)." + }, + "exceptionsDescriptionP2": { + "message": "Wszystkie żądania dla lub inicjalizujące z adresu URL które spełniają wyjątek zostaną wyłączone z przekierowania.", + "description": "Opis funkcji 'Wyjątki' paragraf 2 (opcje)." + }, + "exceptionsDescriptionP3": { + "message": "Informacja - Wspierane są wyrażenia regularne JavaScript, wyłączając ukośniki lewe zamykające.", + "description": "Opis funkcji 'Wyjątki' paragraf 3 (opcje)." + }, + "addException": { + "message": "Dodaj wyjątek", + "description": "Przycisk 'Dodaj wyjątek' (opcje)." + }, + "moreOptions": { + "message": "Więcej opcji", + "description": "Przycisk 'Więcej opcji' (okno pop-up)." + }, + "privacy": { + "message": "Prywatność", + "description": "Tytuł rozszerzenia - Privacy (okno pop-up)." + }, + "redirect": { + "message": "Redirect", + "description": "Tytuł rozszerzenia - Redirect (pop-up)." + }, + "version": { + "message": "Version", + "description": "Wersja" + } +} diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json new file mode 100644 index 0000000..e3d442f --- /dev/null +++ b/_locales/ru/messages.json @@ -0,0 +1,130 @@ +{ + "extensionName": { + "message": "Privacy Redirect", + "description": "Название расширения." + }, + "extensionDescription": { + "message": "Перенаправляет запросы к Twitter, YouTube, Instagram и Google Maps на альтернативные сервисы, дружелюбные к приватности.", + "description": "Описание расширения." + }, + "nitterInstance": { + "message": "Сервис Nitter", + "description": "Название настройки (в настройках) поля сервиса Nitter." + }, + "invidiousInstance": { + "message": "Сервис Invidious", + "description": "Название настройки (в настройках) поля сервиса Invidious." + }, + "bibliogramInstance": { + "message": "Сервис Bibliogram", + "description": "Название настройки (в настройках) поля сервиса Bibliogram." + }, + "osmInstance": { + "message": "Сервис OpenStreetMap", + "description": "Название настройки (в настройках) поля сервиса OSM." + }, + "disableNitter": { + "message": "Перенаправление на Nitter", + "description": "Название настройки для включения/выключения перенаправления на Nitter (в настройках и всплывающем окне)." + }, + "disableInvidious": { + "message": "Перенаправление на Invidious", + "description": "Название настройки для включения/выключения перенаправления на Invidious redirects option (в настройках и всплывающем окне)." + }, + "disableBibliogram": { + "message": "Перенаправление на Bibliogram", + "description": "Название настройки для включения/выключения перенаправления на Bibliogram (в настройках и всплывающем окне)." + }, + "disableOsm": { + "message": "Перенаправление на OpenStreetMap", + "description": "Название настройки для включения/выключения перенаправления на OSM (в настройках и всплывающем окне)." + }, + "theme": { + "message": "Тема", + "description": "Название настройки для 'Тема' (в настройках)." + }, + "alwaysProxy": { + "message": "Всегда проксировать видео через Invidious", + "description": "Название настройки для 'Всегда проксировать видео через Invidious' (в настройках)." + }, + "onlyEmbeddedVideo": { + "message": "Перенаправлять только встроенные видео на Invidious", + "description": "Название настройки для 'Перенаправлять только встроенные видео на Invidious' (в настройках)." + }, + "videoQuality": { + "message": "Качество видео Invidious", + "description": "Название настройки для 'Качество видео Invidious' (в настройках)." + }, + "removeTwitterSW": { + "message": "Проактивно удалять service worker для Twitter", + "description": "Название настройки для 'Проактивно удалять service worker для Twitter' (в настройках)." + }, + "invidiousDarkMode": { + "message": "Всегда включать темный режим Invidious", + "description": "Название настройки для 'Всегда включать темный режим Invidious' (в настройках)." + }, + "invidiousVolume": { + "message": "Громкость Invidious", + "description": "Название настройки для 'Громкость Invidious' (в настройках)." + }, + "invidiousPlayerStyle": { + "message": "Стиль плеера Invidious", + "description": "Название настройки для 'Стиль плеера Invidious' (в настройках)." + }, + "invidiousSubtitles": { + "message": "Субтитры Invidious - коды языков (разделяются запятой)", + "description": "Название настройки для 'Субтитры Invidious - коды языков (разделяются запятой)' (в настройках)." + }, + "invidiousAutoplay": { + "message": "Автоматически воспроизводить видео Invidious при загрузке", + "description": "Название настройки для 'Автоматически воспроизводить видео Invidious при загрузке' (в настройках)." + }, + "persistInvidiousPrefs": { + "message": "Сохранять настройки Invidious (в файле cookie)", + "description": "Название настройки для 'Сохранять настройки Invidious (в файле cookie)' (в настройках)." + }, + "generalTab": { + "message": "Общие", + "description": "Вкладка 'Общие' (в настройках)." + }, + "advancedTab": { + "message": "Дополнительно", + "description": "Вкладка 'Дополнительно' (в настройках)." + }, + "exceptionsTab": { + "message": "Исключения", + "description": "Вкладка 'Исключения' (в настройках)." + }, + "exceptionsDescriptionP1": { + "message": "Введите URL или регулярное выражение для исключения из перенаправлений.", + "description": "Описание функции 'Исключения' (в настройках)." + }, + "exceptionsDescriptionP2": { + "message": "Все запросы на URL, совпадающие с исключениями, или инициированные с них будут исключены из перенаправлений.", + "description": "Описание функции 'Исключения' (в настройках)." + }, + "exceptionsDescriptionP3": { + "message": "Примечание: Поддерживает регулярные выражения JavaScript, кроме закрывающих обратных слэшей.", + "description": "Описание функции 'Исключения' (в настройках)." + }, + "addException": { + "message": "Добавить исключение", + "description": "Кнопка 'Добавить исключения' (в настройках)." + }, + "moreOptions": { + "message": "Другие настройки", + "description": "Кнопка 'Другие настройки' (во всплывающем окне)." + }, + "privacy": { + "message": "Privacy", + "description": "Название расширения - Privacy (во всплывающем окне)." + }, + "redirect": { + "message": "Redirect", + "description": "Название расширения - Redirect (во всплывающем окне)." + }, + "version": { + "message": "Версия", + "description": "Версия." + } +} diff --git a/_locales/ru/store.md b/_locales/ru/store.md new file mode 100644 index 0000000..31b6715 --- /dev/null +++ b/_locales/ru/store.md @@ -0,0 +1,36 @@ +# Extension Store (AMO & Chrome Web Store) Listing + +## Summary: +``` +Простое расширение для браузера, которое перенаправляет запросы к Twitter, YouTube, Instagram & Google Maps на альтернативные сервисы, дружелюбные к приватности. +``` +## Description: +``` +Перенаправляет запросы к Twitter, YouTube, Instagram, & Google Maps на альтернативные сервисы, дружелюбные к приватности - Nitter, Invidious, Bibliogram и OpenStreetMap. + +Позволяет настраивать пользовательские сервисы, включать/выключать все перенаправления и т.д. + +★ Дополнительная информация: ℹ + + +Исходный код данного расширения доступен на Github. + +★ Пожертвовать: 👨🏻‍💻 +Если вам нравится это расширение и вы можете помочь финансово, пожалуйста, купите мне кофе ☕ и покажите ваше одобрение и поддержку развития проекта. + +★ Что нового в этой версии (v1.1.36) 🆕 + + +★ Разрешения: ℹ + +``` diff --git a/_locales/store.md b/_locales/store.md new file mode 100644 index 0000000..0385351 --- /dev/null +++ b/_locales/store.md @@ -0,0 +1,35 @@ +# Extension Store (AMO & Chrome Web Store) Listing + +## Summary: +``` +Prosta wtyczka która przekierowuje żądania Twitter, YouTube, Instagram oraz Google Maps na odpowiedniki przyjazne dla prywatności. +``` +## Description: +``` +Przekierowuje żądania Twitter, YouTube, Instagram oraz Google Maps na odpowiedniki przyjazne dla prywatności - Nitter, Invidious, Bibliogram, & OpenStreetMap. + +Umożliwia ustawienie niestandardowych instancji usług, przełączania przekierowania jako aktywne/nieaktwne i o wiele więcej. +★ Więcej informacji: ℹ️ + + +Kod źródłowy tego rozszerzenia jest dostępny na: Github. + +★ Donate: 👨🏻‍💻 +Jeżeli lubisz to rozszerzenie i chcesz wesprzeć autora finanosowo, proszę pomyśl o zaserwowaniu małej czarnej ☕️ ️aby pokazać swoją wdzięczność i wesprzeć możliwości kontynuowania projektu. + +★ Co nowego w tej wersji (v1.1.36) 🆕 + + +★ Uprawnienia: ℹ️ + +``` diff --git a/assets/iframe_api.js b/assets/iframe_api.js deleted file mode 100644 index 494cb3a..0000000 --- a/assets/iframe_api.js +++ /dev/null @@ -1,5 +0,0 @@ -/* - Retrieved February 19, 2020, from https://www.youtube.com/iframe_api -*/ - -if (!window['YT']) { var YT = { loading: 0, loaded: 0 }; } if (!window['YTConfig']) { var YTConfig = { 'host': 'http://www.youtube.com' }; } if (!YT.loading) { YT.loading = 1; (function () { var l = []; YT.ready = function (f) { if (YT.loaded) { f(); } else { l.push(f); } }; window.onYTReady = function () { YT.loaded = 1; for (var i = 0; i < l.length; i++) { try { l[i](); } catch (e) { } } }; YT.setConfig = function (c) { for (var k in c) { if (c.hasOwnProperty(k)) { YTConfig[k] = c[k]; } } }; var a = document.createElement('script'); a.type = 'text/javascript'; a.id = 'www-widgetapi-script'; a.src = 'https://s.ytimg.com/yts/jsbin/www-widgetapi-vflYl14TA/www-widgetapi.js'; a.async = true; var c = document.currentScript; if (c) { var n = c.nonce || c.getAttribute('nonce'); if (n) { a.setAttribute('nonce', n); } } var b = document.getElementsByTagName('script')[0]; b.parentNode.insertBefore(a, b); })(); } \ No newline at end of file diff --git a/assets/images/Screen Shot Chrome 1.png b/assets/images/Screen Shot Chrome 1.png new file mode 100644 index 0000000..bb416ad Binary files /dev/null and b/assets/images/Screen Shot Chrome 1.png differ diff --git a/assets/images/Screen Shot Chrome 2.png b/assets/images/Screen Shot Chrome 2.png new file mode 100644 index 0000000..0611bd3 Binary files /dev/null and b/assets/images/Screen Shot Chrome 2.png differ diff --git a/assets/images/Screen Shot Chrome 3.png b/assets/images/Screen Shot Chrome 3.png new file mode 100644 index 0000000..9dd3488 Binary files /dev/null and b/assets/images/Screen Shot Chrome 3.png differ diff --git a/assets/images/Screen Shot Chrome 4.png b/assets/images/Screen Shot Chrome 4.png new file mode 100644 index 0000000..c36505e Binary files /dev/null and b/assets/images/Screen Shot Chrome 4.png differ diff --git a/assets/images/Screen Shot Chrome 5.png b/assets/images/Screen Shot Chrome 5.png new file mode 100644 index 0000000..2026b9f Binary files /dev/null and b/assets/images/Screen Shot Chrome 5.png differ diff --git a/assets/images/Screen Shot FF 1.png b/assets/images/Screen Shot FF 1.png new file mode 100644 index 0000000..c8c6a24 Binary files /dev/null and b/assets/images/Screen Shot FF 1.png differ diff --git a/assets/images/Screen Shot FF 2.png b/assets/images/Screen Shot FF 2.png new file mode 100644 index 0000000..c945a6a Binary files /dev/null and b/assets/images/Screen Shot FF 2.png differ diff --git a/assets/images/Screen Shot FF 3.png b/assets/images/Screen Shot FF 3.png new file mode 100644 index 0000000..c3bf57c Binary files /dev/null and b/assets/images/Screen Shot FF 3.png differ diff --git a/assets/images/Screen Shot FF 4.png b/assets/images/Screen Shot FF 4.png new file mode 100644 index 0000000..0b9b45e Binary files /dev/null and b/assets/images/Screen Shot FF 4.png differ diff --git a/assets/images/Screen Shot FF 5.png b/assets/images/Screen Shot FF 5.png new file mode 100644 index 0000000..f0aff95 Binary files /dev/null and b/assets/images/Screen Shot FF 5.png differ diff --git a/assets/images/badge-amo.png b/assets/images/badge-amo.png new file mode 100644 index 0000000..9cb49bb Binary files /dev/null and b/assets/images/badge-amo.png differ diff --git a/assets/images/badge-chrome.png b/assets/images/badge-chrome.png new file mode 100644 index 0000000..4e48b8a Binary files /dev/null and b/assets/images/badge-chrome.png differ diff --git a/assets/images/badge-ms.png b/assets/images/badge-ms.png new file mode 100644 index 0000000..96fa058 Binary files /dev/null and b/assets/images/badge-ms.png differ diff --git a/images/buy-me-a-coffee.png b/assets/images/buy-me-a-coffee.png similarity index 100% rename from images/buy-me-a-coffee.png rename to assets/images/buy-me-a-coffee.png diff --git a/assets/images/chevron-down.svg b/assets/images/chevron-down.svg new file mode 100644 index 0000000..7679f26 --- /dev/null +++ b/assets/images/chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/images/icon128.png b/assets/images/icon128.png similarity index 100% rename from images/icon128.png rename to assets/images/icon128.png diff --git a/images/icon16.png b/assets/images/icon16.png similarity index 100% rename from images/icon16.png rename to assets/images/icon16.png diff --git a/images/icon32.png b/assets/images/icon32.png similarity index 100% rename from images/icon32.png rename to assets/images/icon32.png diff --git a/images/icon48.png b/assets/images/icon48.png similarity index 100% rename from images/icon48.png rename to assets/images/icon48.png diff --git a/assets/images/logo-small.png b/assets/images/logo-small.png new file mode 100644 index 0000000..09e50d1 Binary files /dev/null and b/assets/images/logo-small.png differ diff --git a/assets/images/logo-store.png b/assets/images/logo-store.png new file mode 100644 index 0000000..d7bd51d Binary files /dev/null and b/assets/images/logo-store.png differ diff --git a/images/small-tile.png b/assets/images/logo-tile.png similarity index 100% rename from images/small-tile.png rename to assets/images/logo-tile.png diff --git a/images/logo.png b/assets/images/logo.png similarity index 100% rename from images/logo.png rename to assets/images/logo.png diff --git a/assets/javascript/localise.js b/assets/javascript/localise.js new file mode 100644 index 0000000..cbe5c19 --- /dev/null +++ b/assets/javascript/localise.js @@ -0,0 +1,19 @@ +window.browser = window.browser || window.chrome; + +function localisePage() { + var data = document.querySelectorAll("[data-localise]"); + + for (var i in data) + if (data.hasOwnProperty(i)) { + var obj = data[i]; + var tag = obj.getAttribute("data-localise").toString(); + + var msg = tag.replace(/__MSG_(\w+)__/g, function (_match, v1) { + return v1 ? browser.i18n.getMessage(v1) : null; + }); + + if (msg && msg !== tag) obj.textContent = msg; + } +} + +localisePage(); diff --git a/assets/persist-invidious-prefs.js b/assets/javascript/persist-invidious-prefs.js similarity index 100% rename from assets/persist-invidious-prefs.js rename to assets/javascript/persist-invidious-prefs.js diff --git a/assets/javascript/remove-twitter-sw.js b/assets/javascript/remove-twitter-sw.js new file mode 100644 index 0000000..222a728 --- /dev/null +++ b/assets/javascript/remove-twitter-sw.js @@ -0,0 +1,91 @@ +"use strict"; + +const nitterInstances = [ + "https://nitter.net", + "https://nitter.snopyta.org", + "https://nitter.42l.fr", + "https://nitter.nixnet.services", + "https://nitter.13ad.de", + "https://nitter.pussthecat.org", + "https://nitter.mastodont.cat", + "https://nitter", + "https://nitter.tedomum.net", + "https://nitter.cattube.org", + "https://nitter.fdn.fr", + "https://nitter.1d4.us", + "https://nitter.kavin.rocks", +]; + +let disableNitter; +let nitterInstance; +let redirectBypassFlag; +let exceptions; + +window.browser = window.browser || window.chrome; + +function getRandomInstance() { + return nitterInstances[~~(nitterInstances.length * Math.random())]; +} + +function isNotException(url) { + return !exceptions.some((regex) => regex.test(url.href)); +} + +function shouldRedirect(url) { + return ( + !redirectBypassFlag && + isNotException(url) && + !disableNitter && + url.host !== nitterInstance && + !url.pathname.includes("/home") + ); +} + +function redirectTwitter(url) { + if (url.host.split(".")[0] === "pbs") { + return `${nitterInstance}/pic/${encodeURIComponent(url.href)}`; + } else if (url.host.split(".")[0] === "video") { + return `${nitterInstance}/gif/${encodeURIComponent(url.href)}`; + } else { + return `${nitterInstance}${url.pathname}${url.search}`; + } +} + +browser.storage.sync.get( + [ + "nitterInstance", + "disableNitter", + "removeTwitterSW", + "redirectBypassFlag", + "exceptions", + ], + (result) => { + redirectBypassFlag = result.redirectBypassFlag; + browser.storage.sync.set({ + redirectBypassFlag: false, + }); + if (!result.removeTwitterSW) { + disableNitter = result.disableNitter; + nitterInstance = result.nitterInstance || getRandomInstance(); + exceptions = result.exceptions + ? result.exceptions.map((e) => { + return new RegExp(e); + }) + : []; + navigator.serviceWorker.getRegistrations().then((registrations) => { + for (let registration of registrations) { + if (registration.scope === "https://twitter.com/") { + registration.unregister(); + console.log("Unregistered Twitter SW", registration); + } + } + }); + const url = new URL(window.location); + if (shouldRedirect()) { + const redirect = redirectTwitter(url); + console.info("Redirecting", `"${url.href}"`, "=>", `"${redirect}"`); + window.location = redirect; + } + } + } +); diff --git a/assets/remove-twitter-sw.js b/assets/remove-twitter-sw.js deleted file mode 100644 index d13de3e..0000000 --- a/assets/remove-twitter-sw.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -const nitterDefault = 'https://nitter.net'; - -let disableNitter; -let nitterInstance; - -window.browser = window.browser || window.chrome; - -function redirectTwitter(url) { - if (url.host.split('.')[0] === 'pbs') { - return `${nitterInstance}/pic/${encodeURIComponent(url.href)}`; - } else if (url.host.split('.')[0] === 'video') { - return `${nitterInstance}/gif/${encodeURIComponent(url.href)}`; - } else { - return `${nitterInstance}${url.pathname}${url.search}`; - }; -} - -browser.storage.sync.get( - ['nitterInstance', 'disableNitter', 'removeTwitterSW'], - (result) => { - if (!result.removeTwitterSW) { - disableNitter = result.disableNitter; - nitterInstance = result.nitterInstance || nitterDefault; - navigator.serviceWorker.getRegistrations().then(registrations => { - for (let registration of registrations) { - if (registration.scope === 'https://twitter.com/') { - registration.unregister(); - console.log('Unregistered Twitter SW', registration); - } - } - }); - const url = new URL(window.location); - if (!disableNitter && url.host !== nitterInstance) { - const redirect = redirectTwitter(url); - console.info( - 'Redirecting', `"${url.href}"`, '=>', `"${redirect}"` - ); - window.location = redirect; - } - } - } -); diff --git a/assets/www-widgetapi.js b/assets/www-widgetapi.js deleted file mode 100644 index a29cab0..0000000 --- a/assets/www-widgetapi.js +++ /dev/null @@ -1,268 +0,0 @@ -/* - Retrieved February 19, 2020, from https://s.ytimg.com/yts/jsbin/www-widgetapi-vflYl14TA/www-widgetapi.js -*/ - -(function () {/* - - Copyright The Closure Library Authors. - SPDX-License-Identifier: Apache-2.0 -*/ - var k; function aa() { var a = l, b = 0; return function () { return b < a.length ? { done: !1, value: a[b++] } : { done: !0 } } } - var ba = "function" == typeof Object.create ? Object.create : function (a) { - function b() { } - b.prototype = a; return new b - }, p; - if ("function" == typeof Object.setPrototypeOf) p = Object.setPrototypeOf; else { var q; a: { var ca = { K: !0 }, da = {}; try { da.__proto__ = ca; q = da.K; break a } catch (a) { } q = !1 } p = q ? function (a, b) { a.__proto__ = b; if (a.__proto__ !== b) throw new TypeError(a + " is not extensible"); return a } : null } var ea = p; - function fa(a, b) { a.prototype = ba(b.prototype); a.prototype.constructor = a; if (ea) ea(a, b); else for (var c in b) if ("prototype" != c) if (Object.defineProperties) { var d = Object.getOwnPropertyDescriptor(b, c); d && Object.defineProperty(a, c, d) } else a[c] = b[c]; a.J = b.prototype } - var ha = "function" == typeof Object.defineProperties ? Object.defineProperty : function (a, b, c) { a != Array.prototype && a != Object.prototype && (a[b] = c.value) }; - function ia(a) { a = ["object" == typeof window && window, "object" == typeof self && self, "object" == typeof global && global, a]; for (var b = 0; b < a.length; ++b) { var c = a[b]; if (c && c.Math == Math) return c } return globalThis } - var ja = ia(this); function ka(a, b) { if (b) { for (var c = ja, d = a.split("."), e = 0; e < d.length - 1; e++) { var f = d[e]; f in c || (c[f] = {}); c = c[f] } d = d[d.length - 1]; e = c[d]; f = b(e); f != e && null != f && ha(c, d, { configurable: !0, writable: !0, value: f }) } } - var la = "function" == typeof Object.assign ? Object.assign : function (a, b) { for (var c = 1; c < arguments.length; c++) { var d = arguments[c]; if (d) for (var e in d) Object.prototype.hasOwnProperty.call(d, e) && (a[e] = d[e]) } return a }; - ka("Object.assign", function (a) { return a || la }); - var r = this || self; function v(a) { a = a.split("."); for (var b = r, c = 0; c < a.length; c++)if (b = b[a[c]], null == b) return null; return b } - function ma() { } - function w(a) { - var b = typeof a; if ("object" == b) if (a) { if (a instanceof Array) return "array"; if (a instanceof Object) return b; var c = Object.prototype.toString.call(a); if ("[object Window]" == c) return "object"; if ("[object Array]" == c || "number" == typeof a.length && "undefined" != typeof a.splice && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("splice")) return "array"; if ("[object Function]" == c || "undefined" != typeof a.call && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("call")) return "function" } else return "null"; - else if ("function" == b && "undefined" == typeof a.call) return "object"; return b - } - function x(a) { var b = typeof a; return "object" == b && null != a || "function" == b } - function na(a) { return Object.prototype.hasOwnProperty.call(a, y) && a[y] || (a[y] = ++oa) } - var y = "closure_uid_" + (1E9 * Math.random() >>> 0), oa = 0; function pa(a, b, c) { return a.call.apply(a.bind, arguments) } - function qa(a, b, c) { if (!a) throw Error(); if (2 < arguments.length) { var d = Array.prototype.slice.call(arguments, 2); return function () { var e = Array.prototype.slice.call(arguments); Array.prototype.unshift.apply(e, d); return a.apply(b, e) } } return function () { return a.apply(b, arguments) } } - function z(a, b, c) { Function.prototype.bind && -1 != Function.prototype.bind.toString().indexOf("native code") ? z = pa : z = qa; return z.apply(null, arguments) } - var ra = Date.now || function () { return +new Date }; - function A(a, b) { var c = a.split("."), d = r; c[0] in d || "undefined" == typeof d.execScript || d.execScript("var " + c[0]); for (var e; c.length && (e = c.shift());)c.length || void 0 === b ? d[e] && d[e] !== Object.prototype[e] ? d = d[e] : d = d[e] = {} : d[e] = b } - function sa(a, b) { - function c() { } - c.prototype = b.prototype; a.J = b.prototype; a.prototype = new c; a.prototype.constructor = a - } - ; var ta = Array.prototype.indexOf ? function (a, b) { return Array.prototype.indexOf.call(a, b, void 0) } : function (a, b) { - if ("string" === typeof a) return "string" !== typeof b || 1 != b.length ? -1 : a.indexOf(b, 0); - for (var c = 0; c < a.length; c++)if (c in a && a[c] === b) return c; return -1 - }, B = Array.prototype.forEach ? function (a, b, c) { Array.prototype.forEach.call(a, b, c) } : function (a, b, c) { for (var d = a.length, e = "string" === typeof a ? a.split("") : a, f = 0; f < d; f++)f in e && b.call(c, e[f], f, a) }, ua = Array.prototype.reduce ? function (a, b, c) { return Array.prototype.reduce.call(a, b, c) } : function (a, b, c) { - var d = c; - B(a, function (e, f) { d = b.call(void 0, d, e, f, a) }); - return d - }; - function va(a, b) { a: { var c = a.length; for (var d = "string" === typeof a ? a.split("") : a, e = 0; e < c; e++)if (e in d && b.call(void 0, d[e], e, a)) { c = e; break a } c = -1 } return 0 > c ? null : "string" === typeof a ? a.charAt(c) : a[c] } - function wa(a) { return Array.prototype.concat.apply([], arguments) } - function xa(a) { var b = a.length; if (0 < b) { for (var c = Array(b), d = 0; d < b; d++)c[d] = a[d]; return c } return [] } - ; function ya(a) { var b = !1, c; return function () { b || (c = a(), b = !0); return c } } - ; var za = /&/g, Aa = //g, Ca = /"/g, Da = /'/g, Ea = /\x00/g, Fa = /[\x00&<>"']/; var E; a: { var Ga = r.navigator; if (Ga) { var Ha = Ga.userAgent; if (Ha) { E = Ha; break a } } E = "" }; function Ia(a, b) { for (var c in a) b.call(void 0, a[c], c, a) } - function Ja(a) { var b = F, c; for (c in b) if (a.call(void 0, b[c], c, b)) return c } - ; function G(a, b) { this.b = a === Ka && b || ""; this.a = La } - var La = {}, Ka = {}, H = new G(Ka, ""); function I(a, b) { this.b = a === Ma && b || ""; this.a = Na } - var Na = {}, Ma = {}; function Oa() { this.a = ""; this.b = Pa } - var Pa = {}; function Qa(a) { var b = new Oa; b.a = a; return b } - Qa(""); var J = Qa(""); Qa("
"); function Ra(a) { var b = new I(Ma, H instanceof G && H.constructor === G && H.a === La ? H.b : "type_error:Const"); a.src = (b instanceof I && b.constructor === I && b.a === Na ? b.b : "type_error:TrustedResourceUrl").toString() } - ; var K = window; function L(a, b) { this.width = a; this.height = b } - L.prototype.aspectRatio = function () { return this.width / this.height }; - L.prototype.ceil = function () { this.width = Math.ceil(this.width); this.height = Math.ceil(this.height); return this }; - L.prototype.floor = function () { this.width = Math.floor(this.width); this.height = Math.floor(this.height); return this }; - L.prototype.round = function () { this.width = Math.round(this.width); this.height = Math.round(this.height); return this }; function Sa(a, b) { var c, d; var e = document; e = b || e; if (e.querySelectorAll && e.querySelector && a) return e.querySelectorAll(a ? "." + a : ""); if (a && e.getElementsByClassName) { var f = e.getElementsByClassName(a); return f } f = e.getElementsByTagName("*"); if (a) { var g = {}; for (c = d = 0; e = f[c]; c++) { var h = e.className, m; if (m = "function" == typeof h.split) m = 0 <= ta(h.split(/\s+/), a); m && (g[d++] = e) } g.length = d; return g } return f } - function M(a) { var b = document; a = String(a); "application/xhtml+xml" === b.contentType && (a = a.toLowerCase()); return b.createElement(a) } - function Ta(a, b) { for (var c = 0; a;) { if (b(a)) return a; a = a.parentNode; c++ } return null } - ; var Ua = /^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/\\#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#([\s\S]*))?$/; function Va(a) { var b = a.match(Ua); a = b[1]; var c = b[2], d = b[3]; b = b[4]; var e = ""; a && (e += a + ":"); d && (e += "//", c && (e += c + "@"), e += d, b && (e += ":" + b)); return e } - function Wa(a, b, c) { if ("array" == w(b)) for (var d = 0; d < b.length; d++)Wa(a, String(b[d]), c); else null != b && c.push(a + ("" === b ? "" : "=" + encodeURIComponent(String(b)))) } - function Xa(a) { var b = [], c; for (c in a) Wa(c, a[c], b); return b.join("&") } - var Ya = /#|$/; function Za(a) { var b = $a; if (b) for (var c in b) Object.prototype.hasOwnProperty.call(b, c) && a.call(void 0, b[c], c, b) } - function ab() { - var a = []; Za(function (b) { a.push(b) }); - return a - } - var $a = { M: "allow-forms", N: "allow-modals", O: "allow-orientation-lock", P: "allow-pointer-lock", R: "allow-popups", S: "allow-popups-to-escape-sandbox", T: "allow-presentation", U: "allow-same-origin", V: "allow-scripts", W: "allow-top-navigation", X: "allow-top-navigation-by-user-activation" }, bb = ya(function () { return ab() }); - function cb() { - var a = M("IFRAME"), b = {}; B(bb(), function (c) { a.sandbox && a.sandbox.supports && a.sandbox.supports(c) && (b[c] = !0) }); - return b - } - ; var db = (new Date).getTime(); function eb() { this.b = []; this.a = -1 } - eb.prototype.set = function (a, b) { b = void 0 === b ? !0 : b; 0 <= a && 52 > a && 0 === a % 1 && this.b[a] != b && (this.b[a] = b, this.a = -1) }; - eb.prototype.get = function (a) { return !!this.b[a] }; - function fb(a) { - -1 == a.a && (a.a = ua(a.b, function (b, c, d) { return c ? b + Math.pow(2, d) : b }, 0)); - return a.a - } - ; function gb(a, b) { this.f = a; this.g = b; this.b = 0; this.a = null } - gb.prototype.get = function () { if (0 < this.b) { this.b--; var a = this.a; this.a = a.next; a.next = null } else a = this.f(); return a }; function hb(a) { r.setTimeout(function () { throw a; }, 0) } - var ib; - function jb() { - var a = r.MessageChannel; "undefined" === typeof a && "undefined" !== typeof window && window.postMessage && window.addEventListener && -1 == E.indexOf("Presto") && (a = function () { - var e = M("IFRAME"); e.style.display = "none"; Ra(e); document.documentElement.appendChild(e); var f = e.contentWindow; e = f.document; e.open(); e.write(J instanceof Oa && J.constructor === Oa && J.b === Pa ? J.a : "type_error:SafeHtml"); e.close(); var g = "callImmediate" + Math.random(), h = "file:" == f.location.protocol ? "*" : f.location.protocol + "//" + f.location.host; e = - z(function (m) { if (("*" == h || m.origin == h) && m.data == g) this.port1.onmessage() }, this); - f.addEventListener("message", e, !1); this.port1 = {}; this.port2 = { postMessage: function () { f.postMessage(g, h) } } - }); - if ("undefined" !== typeof a && -1 == E.indexOf("Trident") && -1 == E.indexOf("MSIE")) { - var b = new a, c = {}, d = c; b.port1.onmessage = function () { if (void 0 !== c.next) { c = c.next; var e = c.C; c.C = null; e() } }; - return function (e) { d.next = { C: e }; d = d.next; b.port2.postMessage(0) } - } return "undefined" !== typeof document && "onreadystatechange" in M("SCRIPT") ? function (e) { - var f = M("SCRIPT"); - f.onreadystatechange = function () { f.onreadystatechange = null; f.parentNode.removeChild(f); f = null; e(); e = null }; - document.documentElement.appendChild(f) - } : function (e) { r.setTimeout(e, 0) } - } - ; function kb() { this.b = this.a = null } - var mb = new gb(function () { return new lb }, function (a) { a.reset() }); - kb.prototype.add = function (a, b) { var c = mb.get(); c.set(a, b); this.b ? this.b.next = c : this.a = c; this.b = c }; - kb.prototype.remove = function () { var a = null; this.a && (a = this.a, this.a = this.a.next, this.a || (this.b = null), a.next = null); return a }; - function lb() { this.next = this.b = this.a = null } - lb.prototype.set = function (a, b) { this.a = a; this.b = b; this.next = null }; - lb.prototype.reset = function () { this.next = this.b = this.a = null }; function nb(a) { N || ob(); pb || (N(), pb = !0); qb.add(a, void 0) } - var N; function ob() { - if (r.Promise && r.Promise.resolve) { var a = r.Promise.resolve(void 0); N = function () { a.then(rb) } } else N = function () { - var b = rb, c; - !(c = "function" != w(r.setImmediate)) && (c = r.Window && r.Window.prototype) && (c = -1 == E.indexOf("Edge") && r.Window.prototype.setImmediate == r.setImmediate); c ? (ib || (ib = jb()), ib(b)) : r.setImmediate(b) - } - } - var pb = !1, qb = new kb; function rb() { for (var a; a = qb.remove();) { try { a.a.call(a.b) } catch (c) { hb(c) } var b = mb; b.g(a); 100 > b.b && (b.b++ , a.next = b.a, b.a = a) } pb = !1 } - ; function O() { this.f = this.f; this.g = this.g } - O.prototype.f = !1; O.prototype.dispose = function () { this.f || (this.f = !0, this.w()) }; - O.prototype.w = function () { if (this.g) for (; this.g.length;)this.g.shift()() }; var sb = r.JSON.stringify; function P(a) { O.call(this); this.m = 1; this.h = []; this.i = 0; this.a = []; this.b = {}; this.o = !!a } - sa(P, O); k = P.prototype; k.subscribe = function (a, b, c) { var d = this.b[a]; d || (d = this.b[a] = []); var e = this.m; this.a[e] = a; this.a[e + 1] = b; this.a[e + 2] = c; this.m = e + 3; d.push(e); return e }; - function tb(a, b, c) { var d = Q; if (a = d.b[a]) { var e = d.a; (a = va(a, function (f) { return e[f + 1] == b && e[f + 2] == c })) && d.B(a) } } - k.B = function (a) { var b = this.a[a]; if (b) { var c = this.b[b]; if (0 != this.i) this.h.push(a), this.a[a + 1] = ma; else { if (c) { var d = ta(c, a); 0 <= d && Array.prototype.splice.call(c, d, 1) } delete this.a[a]; delete this.a[a + 1]; delete this.a[a + 2] } } return !!b }; - k.G = function (a, b) { var c = this.b[a]; if (c) { for (var d = Array(arguments.length - 1), e = 1, f = arguments.length; e < f; e++)d[e - 1] = arguments[e]; if (this.o) for (e = 0; e < c.length; e++) { var g = c[e]; ub(this.a[g + 1], this.a[g + 2], d) } else { this.i++; try { for (e = 0, f = c.length; e < f; e++)g = c[e], this.a[g + 1].apply(this.a[g + 2], d) } finally { if (this.i-- , 0 < this.h.length && 0 == this.i) for (; c = this.h.pop();)this.B(c) } } return 0 != e } return !1 }; - function ub(a, b, c) { nb(function () { a.apply(b, c) }) } - k.clear = function (a) { if (a) { var b = this.b[a]; b && (B(b, this.B, this), delete this.b[a]) } else this.a.length = 0, this.b = {} }; - k.w = function () { P.J.w.call(this); this.clear(); this.h.length = 0 }; var R = window.yt && window.yt.config_ || window.ytcfg && window.ytcfg.data_ || {}; A("yt.config_", R); function vb(a) { var b = arguments; 1 < b.length ? R[b[0]] = b[1] : 1 === b.length && Object.assign(R, b[0]) } - function wb() { var a = []; return "ERRORS" in R ? R.ERRORS : a } - ; var xb = []; function yb(a) { xb.forEach(function (b) { return b(a) }) } - function zb(a) { return a && window.yterr ? function () { try { return a.apply(this, arguments) } catch (d) { var b = d, c = v("yt.logging.errors.log"); c ? c(b, "ERROR", void 0, void 0, void 0) : (c = wb(), c.push([b, "ERROR", void 0, void 0, void 0]), vb("ERRORS", c)); yb(d) } } : a } - ; var Ab = 0; A("ytDomDomGetNextId", v("ytDomDomGetNextId") || function () { return ++Ab }); var Bb = { stopImmediatePropagation: 1, stopPropagation: 1, preventMouseEvent: 1, preventManipulation: 1, preventDefault: 1, layerX: 1, layerY: 1, screenX: 1, screenY: 1, scale: 1, rotation: 1, webkitMovementX: 1, webkitMovementY: 1 }; - function S(a) { - this.type = ""; this.state = this.source = this.data = this.currentTarget = this.relatedTarget = this.target = null; this.charCode = this.keyCode = 0; this.metaKey = this.shiftKey = this.ctrlKey = this.altKey = !1; this.clientY = this.clientX = 0; this.changedTouches = this.touches = null; try { - if (a = a || window.event) { - this.event = a; for (var b in a) b in Bb || (this[b] = a[b]); var c = a.target || a.srcElement; c && 3 == c.nodeType && (c = c.parentNode); this.target = c; var d = a.relatedTarget; if (d) try { d = d.nodeName ? d : null } catch (e) { d = null } else "mouseover" == - this.type ? d = a.fromElement : "mouseout" == this.type && (d = a.toElement); this.relatedTarget = d; this.clientX = void 0 != a.clientX ? a.clientX : a.pageX; this.clientY = void 0 != a.clientY ? a.clientY : a.pageY; this.keyCode = a.keyCode ? a.keyCode : a.which; this.charCode = a.charCode || ("keypress" == this.type ? this.keyCode : 0); this.altKey = a.altKey; this.ctrlKey = a.ctrlKey; this.shiftKey = a.shiftKey; this.metaKey = a.metaKey - } - } catch (e) { } - } - S.prototype.preventDefault = function () { this.event && (this.event.returnValue = !1, this.event.preventDefault && this.event.preventDefault()) }; - S.prototype.stopPropagation = function () { this.event && (this.event.cancelBubble = !0, this.event.stopPropagation && this.event.stopPropagation()) }; - S.prototype.stopImmediatePropagation = function () { this.event && (this.event.cancelBubble = !0, this.event.stopImmediatePropagation && this.event.stopImmediatePropagation()) }; var F = v("ytEventsEventsListeners") || {}; A("ytEventsEventsListeners", F); var Cb = v("ytEventsEventsCounter") || { count: 0 }; A("ytEventsEventsCounter", Cb); - function Db(a, b, c, d) { - d = void 0 === d ? {} : d; a.addEventListener && ("mouseenter" != b || "onmouseenter" in document ? "mouseleave" != b || "onmouseenter" in document ? "mousewheel" == b && "MozBoxSizing" in document.documentElement.style && (b = "MozMousePixelScroll") : b = "mouseout" : b = "mouseover"); return Ja(function (e) { - var f = "boolean" === typeof e[4] && e[4] == !!d, g; if (g = x(e[4]) && x(d)) a: { g = e[4]; for (var h in g) if (!(h in d) || g[h] !== d[h]) { g = !1; break a } for (var m in d) if (!(m in g)) { g = !1; break a } g = !0 } return !!e.length && e[0] == a && e[1] == b && e[2] == - c && (f || g) - }) - } - function Eb(a) { a && ("string" == typeof a && (a = [a]), B(a, function (b) { if (b in F) { var c = F[b], d = c[0], e = c[1], f = c[3]; c = c[4]; d.removeEventListener ? Fb() || "boolean" === typeof c ? d.removeEventListener(e, f, c) : d.removeEventListener(e, f, !!c.capture) : d.detachEvent && d.detachEvent("on" + e, f); delete F[b] } })) } - var Fb = ya(function () { - var a = !1; try { - var b = Object.defineProperty({}, "capture", { get: function () { a = !0 } }); - window.addEventListener("test", null, b) - } catch (c) { } return a - }); - function Gb(a, b, c) { - var d = void 0 === d ? {} : d; if (a && (a.addEventListener || a.attachEvent)) { - var e = Db(a, b, c, d); if (!e) { - e = ++Cb.count + ""; var f = !("mouseenter" != b && "mouseleave" != b || !a.addEventListener || "onmouseenter" in document); var g = f ? function (h) { h = new S(h); if (!Ta(h.relatedTarget, function (m) { return m == a })) return h.currentTarget = a, h.type = b, c.call(a, h) } : function (h) { - h = new S(h); - h.currentTarget = a; return c.call(a, h) - }; - g = zb(g); a.addEventListener ? ("mouseenter" == b && f ? b = "mouseover" : "mouseleave" == b && f ? b = "mouseout" : "mousewheel" == b && "MozBoxSizing" in document.documentElement.style && (b = "MozMousePixelScroll"), Fb() || "boolean" === typeof d ? a.addEventListener(b, g, d) : a.addEventListener(b, g, !!d.capture)) : a.attachEvent("on" + b, g); F[e] = [a, b, c, g, d] - } - } - } - ; function Hb(a) { "function" == w(a) && (a = zb(a)); return window.setInterval(a, 250) } - ; function Ib(a) { - var b = []; Ia(a, function (c, d) { var e = encodeURIComponent(String(d)), f; "array" == w(c) ? f = c : f = [c]; B(f, function (g) { "" == g ? b.push(e) : b.push(e + "=" + encodeURIComponent(String(g))) }) }); - return b.join("&") - } - ; var Jb = {}; function Kb(a) { return Jb[a] || (Jb[a] = String(a).replace(/\-([a-z])/g, function (b, c) { return c.toUpperCase() })) } - ; var T = {}, l = [], Q = new P, Lb = {}; function Mb() { var a = "undefined" != typeof Symbol && Symbol.iterator && l[Symbol.iterator]; var b; a ? b = a.call(l) : b = { next: aa() }; a = b; for (b = a.next(); !b.done; b = a.next())b = b.value, b() } - function Nb(a, b) { b || (b = document); var c = xa(b.getElementsByTagName("yt:" + a)), d = "yt-" + a, e = b || document; d = xa(e.querySelectorAll && e.querySelector ? e.querySelectorAll("." + d) : Sa(d, b)); return wa(c, d) } - function U(a, b) { var c; "yt:" == a.tagName.toLowerCase().substr(0, 3) ? c = a.getAttribute(b) : c = a ? a.dataset ? a.dataset[Kb(b)] : a.getAttribute("data-" + b) : null; return c } - function Ob(a, b) { Q.G.apply(Q, arguments) } - ; function Pb(a) { this.b = a || {}; this.f = this.a = !1; a = document.getElementById("www-widgetapi-script"); if (this.a = !!("https:" == document.location.protocol || a && 0 == a.src.indexOf("https:"))) { a = [this.b, window.YTConfig || {}]; for (var b = 0; b < a.length; b++)a[b].host && (a[b].host = a[b].host.replace("http://", "https://")) } } - function V(a, b) { for (var c = [a.b, window.YTConfig || {}], d = 0; d < c.length; d++) { var e = c[d][b]; if (void 0 != e) return e } return null } - function Qb(a, b, c) { W || (W = {}, Gb(window, "message", z(a.g, a))); W[c] = b } - Pb.prototype.g = function (a) { if (a.origin == V(this, "host") || a.origin == V(this, "host").replace(/^http:/, "https:")) { try { var b = JSON.parse(a.data) } catch (c) { return } this.f = !0; this.a || 0 != a.origin.indexOf("https:") || (this.a = !0); if (a = W[b.id]) a.o = !0, a.o && (B(a.m, a.A, a), a.m.length = 0), a.H(b) } }; - var W = null; function Rb() { - var a = Sb, b = {}; b.dt = db; b.flash = "0"; a: { try { var c = a.a.top.location.href } catch (f) { a = 2; break a } a = c ? c === a.b.location.href ? 0 : 1 : 2 } b = (b.frm = a, b); b.u_tz = -(new Date).getTimezoneOffset(); var d = void 0 === d ? K : d; try { var e = d.history.length } catch (f) { e = 0 } b.u_his = e; b.u_java = !!K.navigator && "unknown" !== typeof K.navigator.javaEnabled && !!K.navigator.javaEnabled && K.navigator.javaEnabled(); K.screen && (b.u_h = K.screen.height, b.u_w = K.screen.width, b.u_ah = K.screen.availHeight, b.u_aw = K.screen.availWidth, b.u_cd = K.screen.colorDepth); - K.navigator && K.navigator.plugins && (b.u_nplug = K.navigator.plugins.length); K.navigator && K.navigator.mimeTypes && (b.u_nmime = K.navigator.mimeTypes.length); return b - } - function Tb() { - var a = Sb; var b = a.a; try { var c = b.screenX; var d = b.screenY } catch (t) { } try { var e = b.outerWidth; var f = b.outerHeight } catch (t) { } try { var g = b.innerWidth; var h = b.innerHeight } catch (t) { } b = [b.screenLeft, b.screenTop, c, d, b.screen ? b.screen.availWidth : void 0, b.screen ? b.screen.availTop : void 0, e, f, g, h]; c = a.a.top; try { var m = (c || window).document, n = "CSS1Compat" == m.compatMode ? m.documentElement : m.body; var u = (new L(n.clientWidth, n.clientHeight)).round() } catch (t) { u = new L(-12245933, -12245933) } m = u; u = {}; n = new eb; r.SVGElement && - r.document.createElementNS && n.set(0); c = cb(); c["allow-top-navigation-by-user-activation"] && n.set(1); c["allow-popups-to-escape-sandbox"] && n.set(2); r.crypto && r.crypto.subtle && n.set(3); r.TextDecoder && r.TextEncoder && n.set(4); n = fb(n); u.bc = n; u.bih = m.height; u.biw = m.width; u.brdim = b.join(); a = a.b; return u.vis = { visible: 1, hidden: 2, prerender: 3, preview: 4, unloaded: 5 }[a.visibilityState || a.webkitVisibilityState || a.mozVisibilityState || ""] || 0, u.wgl = !!K.WebGLRenderingContext, u - } - var Sb = new function () { var a = window.document; this.a = window; this.b = a }; - A("yt.ads_.signals_.getAdSignalsString", function (a) { a = void 0 === a ? v("yt.ads.biscotti.lastId_") || "" : a; var b = Object.assign(Rb(), Tb()); b.ca_type = "image"; a && (b.bid = a); return Ib(b) }); ra(); function X(a, b, c) { - this.i = this.a = this.b = null; this.h = na(this); this.f = 0; this.o = !1; this.m = []; this.g = null; this.D = c; this.I = {}; c = document; if (a = "string" === typeof a ? c.getElementById(a) : a) if (c = "iframe" == a.tagName.toLowerCase(), b.host || (b.host = c ? Va(a.src) : "https://www.youtube.com"), this.b = new Pb(b), c || (b = Ub(this, a), this.i = a, (c = a.parentNode) && c.replaceChild(b, a), a = b), this.a = a, this.a.id || (this.a.id = "widget" + na(this.a)), T[this.a.id] = this, window.postMessage) { - this.g = new P; Vb(this); b = V(this.b, "events"); for (var d in b) b.hasOwnProperty(d) && - this.addEventListener(d, b[d]); for (var e in Lb) Wb(this, e) - } - } - k = X.prototype; k.setSize = function (a, b) { this.a.width = a; this.a.height = b; return this }; - k.L = function () { return this.a }; - k.H = function (a) { this.s(a.event, a) }; - k.addEventListener = function (a, b) { - var c = b; "string" == typeof b && (c = function () { window[b].apply(window, arguments) }); - if (!c) return this; this.g.subscribe(a, c); Xb(this, a); return this - }; - function Wb(a, b) { var c = b.split("."); if (2 == c.length) { var d = c[1]; a.D == c[0] && Xb(a, d) } } - k.destroy = function () { this.a.id && (T[this.a.id] = null); var a = this.g; a && "function" == typeof a.dispose && a.dispose(); if (this.i) { a = this.a; var b = a.parentNode; b && b.replaceChild(this.i, a) } else (a = this.a) && a.parentNode && a.parentNode.removeChild(a); W && (W[this.h] = null); this.b = null; a = this.a; for (var c in F) F[c][0] == a && Eb(c); this.i = this.a = null }; - k.u = function () { return {} }; - function Yb(a, b, c) { c = c || []; c = Array.prototype.slice.call(c); b = { event: "command", func: b, args: c }; a.o ? a.A(b) : a.m.push(b) } - k.s = function (a, b) { if (!this.g.f) { var c = { target: this, data: b }; this.g.G(a, c); Ob(this.D + "." + a, c) } }; - function Ub(a, b) { - for (var c = document.createElement("iframe"), d = b.attributes, e = 0, f = d.length; e < f; e++) { var g = d[e].value; null != g && "" != g && "null" != g && c.setAttribute(d[e].name, g) } c.setAttribute("frameBorder", 0); c.setAttribute("allowfullscreen", 1); c.setAttribute("allow", "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"); c.setAttribute("title", "YouTube " + V(a.b, "title")); (d = V(a.b, "width")) && c.setAttribute("width", d); (d = V(a.b, "height")) && c.setAttribute("height", d); var h = a.u(); h.enablejsapi = - window.postMessage ? 1 : 0; window.location.host && (h.origin = window.location.protocol + "//" + window.location.host); h.widgetid = a.h; window.location.href && B(["debugjs", "debugcss"], function (m) { - var n = window.location.href; var u = n.search(Ya); b: { var t = 0; for (var C = m.length; 0 <= (t = n.indexOf(m, t)) && t < u;) { var D = n.charCodeAt(t - 1); if (38 == D || 63 == D) if (D = n.charCodeAt(t + C), !D || 61 == D || 38 == D || 35 == D) break b; t += C + 1 } t = -1 } if (0 > t) n = null; else { - C = n.indexOf("&", t); if (0 > C || C > u) C = u; t += m.length + 1; n = decodeURIComponent(n.substr(t, C - t).replace(/\+/g, - " ")) - } null !== n && (h[m] = n) - }); - c.src = V(a.b, "host") + a.v() + "?" + Xa(h); return c - } - k.F = function () { this.a && this.a.contentWindow ? this.A({ event: "listening" }) : window.clearInterval(this.f) }; - function Vb(a) { Qb(a.b, a, a.h); a.f = Hb(z(a.F, a)); Gb(a.a, "load", z(function () { window.clearInterval(this.f); this.f = Hb(z(this.F, this)) }, a)) } - function Xb(a, b) { a.I[b] || (a.I[b] = !0, Yb(a, "addEventListener", [b])) } - k.A = function (a) { - a.id = this.h; a.channel = "widget"; a = sb(a); var b = this.b; var c = Va(this.a.src || ""); b = 0 == c.indexOf("https:") ? [c] : b.a ? [c.replace("http:", "https:")] : b.f ? [c] : [c, c.replace("http:", "https:")]; if (this.a.contentWindow) for (c = 0; c < b.length; c++)try { this.a.contentWindow.postMessage(a, b[c]) } catch (f) { - if (f.name && "SyntaxError" == f.name) { - if (!(f.message && 0 < f.message.indexOf("target origin ''"))) { - var d = f, e = v("yt.logging.errors.log"); e ? e(d, "WARNING", void 0, void 0, !1, void 0) : (e = wb(), e.push([d, "WARNING", void 0, - void 0, !1, void 0]), vb("ERRORS", e)) - } - } else throw f; - } else console && console.warn && console.warn("The YouTube player is not attached to the DOM. API calls should be made after the onReady event. See more: https://developers.google.com/youtube/iframe_api_reference#Events") - }; function Zb(a) { return (0 == a.search("cue") || 0 == a.search("load")) && "loadModule" != a } - function $b(a) { return 0 == a.search("get") || 0 == a.search("is") } - ; function Y(a, b) { if (!a) throw Error("YouTube player element ID required."); var c = { title: "video player", videoId: "", width: 640, height: 360 }; if (b) for (var d in b) c[d] = b[d]; X.call(this, a, c, "player"); this.j = {}; this.l = {} } - fa(Y, X); k = Y.prototype; k.v = function () { return "/embed/" + V(this.b, "videoId") }; - k.u = function () { var a = V(this.b, "playerVars"); if (a) { var b = {}, c; for (c in a) b[c] = a[c]; a = b } else a = {}; window != window.top && document.referrer && (a.widget_referrer = document.referrer.substring(0, 256)); if (c = V(this.b, "embedConfig")) { if (x(c)) try { c = JSON.stringify(c) } catch (d) { console.error("Invalid embed config JSON", d) } a.embed_config = c } return a }; - k.H = function (a) { var b = a.event; a = a.info; switch (b) { case "apiInfoDelivery": if (x(a)) for (var c in a) this.j[c] = a[c]; break; case "infoDelivery": ac(this, a); break; case "initialDelivery": window.clearInterval(this.f); this.l = {}; this.j = {}; bc(this, a.apiInterface); ac(this, a); break; default: this.s(b, a) } }; - function ac(a, b) { if (x(b)) for (var c in b) a.l[c] = b[c] } - function bc(a, b) { - B(b, function (c) { - this[c] || ("getCurrentTime" == c ? this[c] = function () { var d = this.l.currentTime; if (1 == this.l.playerState) { var e = (ra() / 1E3 - this.l.currentTimeLastUpdated_) * this.l.playbackRate; 0 < e && (d += Math.min(e, 1)) } return d } : Zb(c) ? this[c] = function () { - this.l = {}; - this.j = {}; Yb(this, c, arguments); return this - } : $b(c) ? this[c] = function () { - var d = 0; - 0 == c.search("get") ? d = 3 : 0 == c.search("is") && (d = 2); return this.l[c.charAt(d).toLowerCase() + c.substr(d + 1)] - } : this[c] = function () { - Yb(this, c, arguments); - return this - }) - }, a) - } - k.getVideoEmbedCode = function () { - var a = parseInt(V(this.b, "width"), 10); var b = parseInt(V(this.b, "height"), 10), c = V(this.b, "host") + this.v(); Fa.test(c) && (-1 != c.indexOf("&") && (c = c.replace(za, "&")), -1 != c.indexOf("<") && (c = c.replace(Aa, "<")), -1 != c.indexOf(">") && (c = c.replace(Ba, ">")), -1 != c.indexOf('"') && (c = c.replace(Ca, """)), -1 != c.indexOf("'") && (c = c.replace(Da, "'")), -1 != c.indexOf("\x00") && (c = c.replace(Ea, "�"))); a = ''; - return a - }; - k.getOptions = function (a) { return this.j.namespaces ? a ? this.j[a].options || [] : this.j.namespaces || [] : [] }; - k.getOption = function (a, b) { if (this.j.namespaces && a && b) return this.j[a][b] }; - function cc(a) { if ("iframe" != a.tagName.toLowerCase()) { var b = U(a, "videoid"); b && (b = { videoId: b, width: U(a, "width"), height: U(a, "height") }, new Y(a, b)) } } - ; function Z(a, b) { var c = { title: "Thumbnail", videoId: "", width: 120, height: 68 }; if (b) for (var d in b) c[d] = b[d]; X.call(this, a, c, "thumbnail") } - fa(Z, X); Z.prototype.v = function () { return "/embed/" + V(this.b, "videoId") }; - Z.prototype.u = function () { return { player: 0, thumb_width: V(this.b, "thumbWidth"), thumb_height: V(this.b, "thumbHeight"), thumb_align: V(this.b, "thumbAlign") } }; - Z.prototype.s = function (a, b) { X.prototype.s.call(this, a, b ? b.info : void 0) }; - function dc(a) { if ("iframe" != a.tagName.toLowerCase()) { var b = U(a, "videoid"); if (b) { b = { videoId: b, events: {}, width: U(a, "width"), height: U(a, "height"), thumbWidth: U(a, "thumb-width"), thumbHeight: U(a, "thumb-height"), thumbAlign: U(a, "thumb-align") }; var c = U(a, "onclick"); c && (b.events.onClick = c); new Z(a, b) } } } - ; A("YT.PlayerState.UNSTARTED", -1); A("YT.PlayerState.ENDED", 0); A("YT.PlayerState.PLAYING", 1); A("YT.PlayerState.PAUSED", 2); A("YT.PlayerState.BUFFERING", 3); A("YT.PlayerState.CUED", 5); A("YT.get", function (a) { return T[a] }); - A("YT.scan", Mb); A("YT.subscribe", function (a, b, c) { Q.subscribe(a, b, c); Lb[a] = !0; for (var d in T) Wb(T[d], a) }); - A("YT.unsubscribe", function (a, b, c) { tb(a, b, c) }); - A("YT.Player", Y); A("YT.Thumbnail", Z); X.prototype.destroy = X.prototype.destroy; X.prototype.setSize = X.prototype.setSize; X.prototype.getIframe = X.prototype.L; X.prototype.addEventListener = X.prototype.addEventListener; Y.prototype.getVideoEmbedCode = Y.prototype.getVideoEmbedCode; Y.prototype.getOptions = Y.prototype.getOptions; Y.prototype.getOption = Y.prototype.getOption; l.push(function (a) { a = Nb("player", a); B(a, cc) }); - l.push(function () { var a = Nb("thumbnail"); B(a, dc) }); - "undefined" != typeof YTConfig && YTConfig.parsetags && "onload" != YTConfig.parsetags || Mb(); var ec = v("onYTReady"); ec && ec(); var fc = v("onYouTubeIframeAPIReady"); fc && fc(); var gc = v("onYouTubePlayerAPIReady"); gc && gc(); -}).call(this); \ No newline at end of file diff --git a/background.js b/background.js index 5c84702..3f5145c 100644 --- a/background.js +++ b/background.js @@ -1,25 +1,47 @@ -'use strict'; +"use strict"; -const invidiousDefault = 'https://invidio.us'; const youtubeDomains = [ - 'm.youtube.com', - 'youtube.com', - 'img.youtube.com', - 'www.youtube.com', - 'youtube-nocookie.com', - 'www.youtube-nocookie.com', - 'youtu.be', - 's.ytimg.com', + "m.youtube.com", + "youtube.com", + "img.youtube.com", + "www.youtube.com", + "youtube-nocookie.com", + "www.youtube-nocookie.com", + "youtu.be", + "s.ytimg.com", + "music.youtube.com", +]; +const invidiousInstances = [ + "https://invidious.snopyta.org", + "https://yewtu.be", + "https://invidious.13ad.de", + "https://invidious.xyz", + "https://invidious.site", + "https://invidiou.site", + "https://invidious.fdn.fr", + "https://invidious.toot.koeln", ]; -const nitterDefault = 'https://nitter.net'; const twitterDomains = [ - 'twitter.com', - 'www.twitter.com', - 'mobile.twitter.com', - 'pbs.twimg.com', - 'video.twimg.com', + "twitter.com", + "www.twitter.com", + "mobile.twitter.com", + "pbs.twimg.com", + "video.twimg.com", +]; +const nitterInstances = [ + "https://nitter.net", + "https://nitter.snopyta.org", + "https://nitter.42l.fr", + "https://nitter.nixnet.services", + "https://nitter.13ad.de", + "https://nitter.pussthecat.org", + "https://nitter.mastodont.cat", + "https://nitter.tedomum.net", + "https://nitter.cattube.org", + "https://nitter.fdn.fr", + "https://nitter.1d4.us", + "https://nitter.kavin.rocks", ]; -const bibliogramDefault = 'https://bibliogram.art'; const instagramDomains = [ "instagram.com", "www.instagram.com", @@ -27,50 +49,57 @@ const instagramDomains = [ "about.instagram.com", ]; const instagramReservedPaths = [ - 'about', - 'explore', - 'support', - 'press', - 'api', - 'privacy', - 'safety', - 'admin', - 'graphql', - 'accounts', - 'help', - 'terms', - 'contact', - 'blog', - 'igtv', - 'u', - 'p', - 'fragment', - 'imageproxy', - 'videoproxy', - '.well-known' + "about", + "explore", + "support", + "press", + "api", + "privacy", + "safety", + "admin", + "graphql", + "accounts", + "help", + "terms", + "contact", + "blog", + "igtv", + "u", + "p", + "fragment", + "imageproxy", + "videoproxy", + ".well-known", ]; const bibliogramBypassPaths = /\/(accounts\/|embeds?.js)/; const bibliogramInstances = [ - 'https://bibliogram.art', - 'https://bibliogram.snopyta.org' + "https://bibliogram.art", + "https://bibliogram.snopyta.org", + "https://bibliogram.pussthecat.org", + "https://bibliogram.nixnet.services", + "https://bg.endl.site", + "https://bibliogram.13ad.de ", + "https://bibliogram.stemy.me ", + "https://bibliogram.hamster.dance", + "https://bibliogram.ggc-project.de", ]; -const osmDefault = 'https://openstreetmap.org'; -const googleMapsRegex = /https?:\/\/(((www|maps)\.)?(google).*(\/maps)|maps\.(google).*)/; +const osmDefault = "https://openstreetmap.org"; +const googleMapsRegex = /https?:\/\/(((www|maps)\.)?(google\.).*(\/maps)|maps\.(google\.).*)/; const mapCentreRegex = /@(-?\d[0-9.]*),(-?\d[0-9.]*),(\d{1,2})[.z]/; const dataLatLngRegex = /(!3d|!4d)(-?[0-9]{1,10}.[0-9]{1,10})/g; const placeRegex = /\/place\/(.*)\//; const travelModes = { - 'driving': 'fossgis_osrm_car', - 'walking': 'fossgis_osrm_foot', - 'bicycling': 'fossgis_osrm_bike', - 'transit': 'fossgis_osrm_car' // not implemented on OSM, default to car. + driving: "fossgis_osrm_car", + walking: "fossgis_osrm_foot", + bicycling: "fossgis_osrm_bike", + transit: "fossgis_osrm_car", // not implemented on OSM, default to car. }; const layers = { - 'none': 'S', - 'transit': 'T', - 'traffic': 'S', // not implemented on OSM, default to standard. - 'bicycling': 'C' -} + none: "S", + transit: "T", + traffic: "S", // not implemented on OSM, default to standard. + bicycling: "C", +}; let disableNitter; let disableInvidious; @@ -84,85 +113,119 @@ let alwaysProxy; let onlyEmbeddedVideo; let videoQuality; let invidiousDarkMode; -let whitelist; +let invidiousVolume; +let invidiousPlayerStyle; +let invidiousSubtitles; +let invidiousAutoplay; +let exceptions; window.browser = window.browser || window.chrome; browser.storage.sync.get( [ - 'nitterInstance', - 'invidiousInstance', - 'bibliogramInstance', - 'osmInstance', - 'disableNitter', - 'disableInvidious', - 'disableBibliogram', - 'disableOsm', - 'alwaysProxy', - 'onlyEmbeddedVideo', - 'videoQuality', - 'invidiousDarkMode', - 'whitelist' + "nitterInstance", + "invidiousInstance", + "bibliogramInstance", + "osmInstance", + "disableNitter", + "disableInvidious", + "disableBibliogram", + "disableOsm", + "alwaysProxy", + "onlyEmbeddedVideo", + "videoQuality", + "invidiousDarkMode", + "invidiousVolume", + "invidiousPlayerStyle", + "invidiousSubtitles", + "invidiousAutoplay", + "exceptions", ], - result => { + (result) => { disableNitter = result.disableNitter; disableInvidious = result.disableInvidious; disableBibliogram = result.disableBibliogram; disableOsm = result.disableOsm; - nitterInstance = result.nitterInstance || nitterDefault; - invidiousInstance = result.invidiousInstance || invidiousDefault; - bibliogramInstance = result.bibliogramInstance || bibliogramDefault; + nitterInstance = result.nitterInstance; + invidiousInstance = result.invidiousInstance; + bibliogramInstance = result.bibliogramInstance; osmInstance = result.osmInstance || osmDefault; alwaysProxy = result.alwaysProxy; onlyEmbeddedVideo = result.onlyEmbeddedVideo; videoQuality = result.videoQuality; invidiousDarkMode = result.invidiousDarkMode; - whitelist = result.whitelist ? result.whitelist.map(e => new RegExp(e)) : []; + exceptions = result.exceptions + ? result.exceptions.map((e) => { + return new RegExp(e); + }) + : []; + invidiousVolume = result.invidiousVolume; + invidiousPlayerStyle = result.invidiousPlayerStyle; + invidiousSubtitles = result.invidiousSubtitles || ""; + invidiousAutoplay = result.invidiousAutoplay; } ); -browser.storage.onChanged.addListener(changes => { - if ('nitterInstance' in changes) { - nitterInstance = changes.nitterInstance.newValue || nitterDefault; +browser.storage.onChanged.addListener((changes) => { + if ("nitterInstance" in changes) { + nitterInstance = changes.nitterInstance.newValue; } - if ('invidiousInstance' in changes) { - invidiousInstance = changes.invidiousInstance.newValue || invidiousDefault; + if ("invidiousInstance" in changes) { + invidiousInstance = changes.invidiousInstance.newValue; } - if ('bibliogramInstance' in changes) { - bibliogramInstance = changes.bibliogramInstance.newValue || bibliogramDefault; + if ("bibliogramInstance" in changes) { + bibliogramInstance = changes.bibliogramInstance.newValue; } - if ('osmInstance' in changes) { + if ("osmInstance" in changes) { osmInstance = changes.osmInstance.newValue || osmDefault; } - if ('disableNitter' in changes) { + if ("disableNitter" in changes) { disableNitter = changes.disableNitter.newValue; } - if ('disableInvidious' in changes) { + if ("disableInvidious" in changes) { disableInvidious = changes.disableInvidious.newValue; } - if ('disableBibliogram' in changes) { + if ("disableBibliogram" in changes) { disableBibliogram = changes.disableBibliogram.newValue; } - if ('disableOsm' in changes) { + if ("disableOsm" in changes) { disableOsm = changes.disableOsm.newValue; } - if ('alwaysProxy' in changes) { + if ("alwaysProxy" in changes) { alwaysProxy = changes.alwaysProxy.newValue; } - if ('onlyEmbeddedVideo' in changes) { + if ("onlyEmbeddedVideo" in changes) { onlyEmbeddedVideo = changes.onlyEmbeddedVideo.newValue; } - if ('videoQuality' in changes) { + if ("videoQuality" in changes) { videoQuality = changes.videoQuality.newValue; } - if ('invidiousDarkMode' in changes) { + if ("invidiousDarkMode" in changes) { invidiousDarkMode = changes.invidiousDarkMode.newValue; } - if ('whitelist' in changes) { - whitelist = changes.whitelist.newValue.map(e => new RegExp(e)); + if ("invidiousVolume" in changes) { + invidiousVolume = changes.invidiousVolume.newValue; + } + if ("invidiousPlayerStyle" in changes) { + invidiousPlayerStyle = changes.invidiousPlayerStyle.newValue; + } + if ("invidiousSubtitles" in changes) { + invidiousSubtitles = changes.invidiousSubtitles.newValue; + } + if ("invidiousAutoplay" in changes) { + invidiousAutoplay = changes.invidiousAutoplay.newValue; + } + if ("exceptions" in changes) { + exceptions = changes.exceptions.newValue.map((e) => { + return new RegExp(e); + }); } }); +function getRandomInstance(instanceList) { + return instanceList[~~(instanceList.length * Math.random())]; +} + function addressToLatLng(address, callback) { const xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = () => { @@ -175,120 +238,181 @@ function addressToLatLng(address, callback) { `${json.boundingbox[2]},${json.boundingbox[1]},${json.boundingbox[3]},${json.boundingbox[0]}` ); } - } - else { + } else { console.info("Error: Status is " + xmlhttp.status); } } }; xmlhttp.open( - 'GET', + "GET", `https://nominatim.openstreetmap.org/search/${address}?format=json&limit=1`, false ); xmlhttp.send(); } -function isWhitelisted(initiator) { - return initiator && whitelist.some(regex => (regex.test(initiator.href))); +function isException(url, initiator) { + return ( + exceptions.some((regex) => regex.test(url.href)) || + (initiator && exceptions.some((regex) => regex.test(initiator.href))) + ); +} + +function isFirefox() { + return typeof InstallTrigger !== "undefined"; } function redirectYouTube(url, initiator, type) { - if (disableInvidious || isWhitelisted(initiator)) { + if (disableInvidious || isException(url, initiator)) { return null; } - if (initiator && (initiator.origin === invidiousInstance || youtubeDomains.includes(initiator.host))) { + if ( + initiator && + (initiator.origin === invidiousInstance || + invidiousInstances.includes(initiator.origin) || + youtubeDomains.includes(initiator.host)) + ) { return null; } - if (url.pathname.match(/iframe_api/)) { - // Redirect requests for YouTube Player API to local files instead - return browser.runtime.getURL('assets/iframe_api.js'); - } else if (url.pathname.match(/www-widgetapi/)) { - // Redirect requests for YouTube Player API to local files instead - return browser.runtime.getURL('assets/www-widgetapi.js'); - } else { - // Proxy video through the server if enabled by user - if (alwaysProxy) { - url.searchParams.append('local', true); - } - if (videoQuality) { - url.searchParams.append('quality', videoQuality); - } - if (onlyEmbeddedVideo && type !== 'sub_frame') { - return null; - } - if (invidiousDarkMode) { - url.searchParams.append('dark_mode', invidiousDarkMode); - } - return `${invidiousInstance}${url.pathname}${url.search}`; + if (url.pathname.match(/iframe_api/) || url.pathname.match(/www-widgetapi/)) { + // Don't redirect YouTube Player API. + return null; } + if (url.host.split(".")[0] === "studio") { + // Avoid redirecting `studio.youtube.com` + return null; + } + if (onlyEmbeddedVideo && type !== "sub_frame") { + return null; + } + // Apply settings + if (alwaysProxy) { + url.searchParams.append("local", true); + } + if (videoQuality) { + url.searchParams.append("quality", videoQuality); + } + if (invidiousDarkMode) { + url.searchParams.append("dark_mode", invidiousDarkMode); + } + if (invidiousVolume) { + url.searchParams.append("volume", invidiousVolume); + } + if (invidiousPlayerStyle) { + url.searchParams.append("player_style", invidiousPlayerStyle); + } + if (invidiousSubtitles) { + url.searchParams.append("subtitles", invidiousSubtitles); + } + url.searchParams.append("autoplay", invidiousAutoplay ? 1 : 0); + + return `${invidiousInstance || getRandomInstance(invidiousInstances)}${ + url.pathname + }${url.search}`; } function redirectTwitter(url, initiator) { - if (disableNitter || isWhitelisted(initiator)) { + if (disableNitter || isException(url, initiator)) { return null; } - if (url.host.split('.')[0] === 'pbs') { - return `${nitterInstance}/pic/${encodeURIComponent(url.href)}`; - } else if (url.host.split('.')[0] === 'video') { - return `${nitterInstance}/gif/${encodeURIComponent(url.href)}`; - } else if (url.pathname.includes('tweets')) { - return `${nitterInstance}${url.pathname.replace('/tweets', '')}${url.search}`; + if (url.pathname.includes("/home")) { + return null; + } + if ( + isFirefox() && + initiator && + (initiator.origin === nitterInstance || + nitterInstances.includes(initiator.origin) || + twitterDomains.includes(initiator.host)) + ) { + browser.storage.sync.set({ + redirectBypassFlag: true, + }); + return null; + } + if (url.host.split(".")[0] === "pbs") { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }/pic/${encodeURIComponent(url.href)}`; + } else if (url.host.split(".")[0] === "video") { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }/gif/${encodeURIComponent(url.href)}`; + } else if (url.pathname.includes("tweets")) { + return `${ + nitterInstance || getRandomInstance(nitterInstances) + }${url.pathname.replace("/tweets", "")}${url.search}`; } else { - return `${nitterInstance}${url.pathname}${url.search}`; + return `${nitterInstance || getRandomInstance(nitterInstances)}${ + url.pathname + }${url.search}`; } } function redirectInstagram(url, initiator, type) { - if (disableBibliogram || isWhitelisted(initiator)) { + if (disableBibliogram || isException(url, initiator)) { return null; } // Do not redirect Bibliogram view on Instagram links - if (initiator && (initiator.origin === bibliogramInstance || instagramDomains.includes(initiator.host))) { + if ( + initiator && + (initiator.origin === bibliogramInstance || + bibliogramInstances.includes(initiator.origin) || + instagramDomains.includes(initiator.host)) + ) { return null; } // Do not redirect /accounts, /embeds.js, or anything other than main_frame - if (type !== 'main_frame' || url.pathname.match(bibliogramBypassPaths)) { + if (type !== "main_frame" || url.pathname.match(bibliogramBypassPaths)) { return null; } - if (url.pathname === '/' || instagramReservedPaths.includes(url.pathname.split('/')[1])) { - return `${bibliogramInstance}${url.pathname}${url.search}`; + if ( + url.pathname === "/" || + instagramReservedPaths.includes(url.pathname.split("/")[1]) + ) { + return `${bibliogramInstance || getRandomInstance(bibliogramInstances)}${ + url.pathname + }${url.search}`; } else { // Likely a user profile, redirect to '/u/...' - return `${bibliogramInstance}/u${url.pathname}${url.search}`; + return `${bibliogramInstance || getRandomInstance(bibliogramInstances)}/u${ + url.pathname + }${url.search}`; } } function redirectGoogleMaps(url, initiator) { - if (disableOsm || isWhitelisted(initiator)) { + if (disableOsm || isException(url, initiator)) { return null; } let redirect; - let mapCentre = ''; - let params = ''; + let mapCentre = ""; + let params = ""; // Set map centre if present if (url.pathname.match(mapCentreRegex)) { const [, lat, lon, zoom] = url.pathname.match(mapCentreRegex); mapCentre = `#map=${zoom}/${lat}/${lon}`; - } else if (url.search.includes('center=')) { - const [lat, lon] = url.searchParams.get('center').split(','); - mapCentre = `#map=${url.searchParams.get('zoom') || '17'}/${lat}/${lon}`; + } else if (url.search.includes("center=")) { + const [lat, lon] = url.searchParams.get("center").split(","); + mapCentre = `#map=${url.searchParams.get("zoom") || "17"}/${lat}/${lon}`; // Set default zoom if mapCentre not present } else { - params = '&zoom=17'; + params = "&zoom=17"; } // Set map layer - params = `${params}&layers=${layers[url.searchParams.get('layer')] || layers['none']}`; + params = `${params}&layers=${ + layers[url.searchParams.get("layer")] || layers["none"] + }`; // Handle Google Maps Embed API - if (url.pathname.includes('/embed')) { - let query = ''; - if (url.searchParams.has('q')) { - query = url.searchParams.get('q'); - } else if (url.searchParams.has('query')) { - query = url.searchParams.has('query'); - } else if (url.searchParams.has('pb')) { + if (url.pathname.includes("/embed")) { + let query = ""; + if (url.searchParams.has("q")) { + query = url.searchParams.get("q"); + } else if (url.searchParams.has("query")) { + query = url.searchParams.has("query"); + } else if (url.searchParams.has("pb")) { try { - query = url.searchParams.get('pb').split(/!2s(.*?)!/)[1]; + query = url.searchParams.get("pb").split(/!2s(.*?)!/)[1]; } catch (error) { console.error(error); // Unable to find map marker in URL. @@ -301,92 +425,118 @@ function redirectGoogleMaps(url, initiator) { }); redirect = `${osmInstance}/export/embed.html?bbox=${bbox}&layer=mapnik&marker=${marker}`; // Handle Google Maps Directions - } else if (url.pathname.includes('/dir')) { - const travelMode = travelModes[url.searchParams.get('travelmode')] || travelModes['driving']; + } else if (url.pathname.includes("/dir")) { + const travelMode = + travelModes[url.searchParams.get("travelmode")] || travelModes["driving"]; let origin; - addressToLatLng(url.searchParams.get('origin'), coords => { + addressToLatLng(url.searchParams.get("origin"), (coords) => { origin = coords; }); let destination; - addressToLatLng(url.searchParams.get('destination'), coords => { + addressToLatLng(url.searchParams.get("destination"), (coords) => { destination = coords; }); redirect = `${osmInstance}/directions?engine=${travelMode}&route=${origin}%3B${destination}${mapCentre}${params}`; // Get marker from data attribute - } else if (url.pathname.includes('data=') && url.pathname.match(dataLatLngRegex)) { + } else if ( + url.pathname.includes("data=") && + url.pathname.match(dataLatLngRegex) + ) { const [mlat, mlon] = url.pathname.match(dataLatLngRegex); - redirect = `${osmInstance}/?mlat=${mlat.replace('!3d', '')}&mlon=${mlon.replace('!4d', '')}${mapCentre}${params}`; + redirect = `${osmInstance}/?mlat=${mlat.replace( + "!3d", + "" + )}&mlon=${mlon.replace("!4d", "")}${mapCentre}${params}`; // Get marker from ll param - } else if (url.searchParams.has('ll')) { - const [mlat, mlon] = url.searchParams.get('ll').split(','); + } else if (url.searchParams.has("ll")) { + const [mlat, mlon] = url.searchParams.get("ll").split(","); redirect = `${osmInstance}/?mlat=${mlat}&mlon=${mlon}${mapCentre}${params}`; // Get marker from viewpoint param. - } else if (url.searchParams.has('viewpoint')) { - const [mlat, mlon] = url.searchParams.get('viewpoint').split(','); + } else if (url.searchParams.has("viewpoint")) { + const [mlat, mlon] = url.searchParams.get("viewpoint").split(","); redirect = `${osmInstance}/?mlat=${mlat}&mlon=${mlon}${mapCentre}${params}`; // Use query as search if present. } else { let query; - if (url.searchParams.has('q')) { - query = url.searchParams.get('q'); - } else if (url.searchParams.has('query')) { - query = url.searchParams.get('query'); + if (url.searchParams.has("q")) { + query = url.searchParams.get("q"); + } else if (url.searchParams.has("query")) { + query = url.searchParams.get("query"); } else if (url.pathname.match(placeRegex)) { query = url.pathname.match(placeRegex)[1]; } - redirect = `${osmInstance}/${query ? 'search?query=' + query : ''}${mapCentre || '#'}${params}`; + redirect = `${osmInstance}/${query ? "search?query=" + query : ""}${ + mapCentre || "#" + }${params}`; } return redirect; } browser.webRequest.onBeforeRequest.addListener( - details => { + (details) => { const url = new URL(details.url); let initiator; - if (details.initiator) { - initiator = new URL(details.initiator); - } else if (details.originUrl) { + if (details.originUrl) { initiator = new URL(details.originUrl); + } else if (details.initiator) { + initiator = new URL(details.initiator); } let redirect; if (youtubeDomains.includes(url.host)) { redirect = { - redirectUrl: redirectYouTube(url, initiator, details.type) + redirectUrl: redirectYouTube(url, initiator, details.type), }; } else if (twitterDomains.includes(url.host)) { redirect = { - redirectUrl: redirectTwitter(url, initiator) + redirectUrl: redirectTwitter(url, initiator), }; } else if (instagramDomains.includes(url.host)) { redirect = { - redirectUrl: redirectInstagram(url, initiator, details.type) + redirectUrl: redirectInstagram(url, initiator, details.type), }; } else if (url.href.match(googleMapsRegex)) { redirect = { - redirectUrl: redirectGoogleMaps(url, initiator) + redirectUrl: redirectGoogleMaps(url, initiator), }; } if (redirect && redirect.redirectUrl) { console.info( - 'Redirecting', `"${url.href}"`, '=>', `"${redirect.redirectUrl}"` + "Redirecting", + `"${url.href}"`, + "=>", + `"${redirect.redirectUrl}"` ); - console.info('Details', details); + console.info("Details", details); } return redirect; }, { - urls: [""] + urls: [""], }, - ['blocking'] + ["blocking"] ); -browser.runtime.onInstalled.addListener( - details => { - if (details.reason === 'install') { - browser.storage.sync.set({ - bibliogramInstance: bibliogramInstances[~~(bibliogramInstances.length * Math.random())] - }); - } +browser.runtime.onInstalled.addListener((details) => { + if (details.reason === "update") { + browser.storage.sync.get( + ["whitelist", "exceptions", "invidiousInstance"], + (result) => { + if (result.whitelist) { + let whitelist = result.whitelist.map((e) => + e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + ); + browser.storage.sync.set({ + exceptions: result.exceptions.concat(whitelist), + whitelist: null, + }); + } + if (result.invidiousInstance === "https://invidio.us") { + browser.storage.sync.set({ + invidiousInstance: null, + }); + } + } + ); } -); +}); diff --git a/images/Screen Shot 1.png b/images/Screen Shot 1.png deleted file mode 100644 index c45dacf..0000000 Binary files a/images/Screen Shot 1.png and /dev/null differ diff --git a/images/Screen Shot 2.png b/images/Screen Shot 2.png deleted file mode 100644 index 4413aa6..0000000 Binary files a/images/Screen Shot 2.png and /dev/null differ diff --git a/images/Screen Shot 3.png b/images/Screen Shot 3.png deleted file mode 100644 index 17ad807..0000000 Binary files a/images/Screen Shot 3.png and /dev/null differ diff --git a/images/Screen Shot 4.png b/images/Screen Shot 4.png deleted file mode 100644 index d0789b4..0000000 Binary files a/images/Screen Shot 4.png and /dev/null differ diff --git a/images/Screen Shot 5.png b/images/Screen Shot 5.png deleted file mode 100644 index 76040b4..0000000 Binary files a/images/Screen Shot 5.png and /dev/null differ diff --git a/images/Screen Shot FF 1.png b/images/Screen Shot FF 1.png deleted file mode 100644 index 71d41a1..0000000 Binary files a/images/Screen Shot FF 1.png and /dev/null differ diff --git a/images/Screen Shot FF 2.png b/images/Screen Shot FF 2.png deleted file mode 100644 index 745a576..0000000 Binary files a/images/Screen Shot FF 2.png and /dev/null differ diff --git a/images/Screen Shot FF 3.png b/images/Screen Shot FF 3.png deleted file mode 100644 index dec2ef6..0000000 Binary files a/images/Screen Shot FF 3.png and /dev/null differ diff --git a/images/Screen Shot FF 4.png b/images/Screen Shot FF 4.png deleted file mode 100644 index 6424b70..0000000 Binary files a/images/Screen Shot FF 4.png and /dev/null differ diff --git a/images/Screen Shot FF 5.png b/images/Screen Shot FF 5.png deleted file mode 100644 index 111f98b..0000000 Binary files a/images/Screen Shot FF 5.png and /dev/null differ diff --git a/manifest.json b/manifest.json index b6cce54..ea0756b 100644 --- a/manifest.json +++ b/manifest.json @@ -1,34 +1,28 @@ { - "name": "Privacy Redirect", - "description": "Redirects Twitter, YouTube, Instagram & Google Maps requests to privacy friendly alternatives.", - "version": "1.1.29", + "name": "__MSG_extensionName__", + "description": "__MSG_extensionDescription__", + "version": "1.1.41", "manifest_version": 2, "background": { - "scripts": [ - "background.js" - ], + "scripts": ["background.js"], "persistent": true }, + "default_locale": "en", "icons": { - "16": "images/icon16.png", - "32": "images/icon32.png", - "48": "images/icon48.png", - "128": "images/icon128.png" + "16": "assets/images/icon16.png", + "32": "assets/images/icon32.png", + "48": "assets/images/icon48.png", + "128": "assets/images/icon128.png" }, - "permissions": [ - "storage", - "webRequest", - "webRequestBlocking", - "" - ], + "permissions": ["storage", "webRequest", "webRequestBlocking", ""], "browser_action": { "default_title": "Privacy Redirect", "default_popup": "pages/popup/popup.html", "default_icon": { - "16": "images/icon16.png", - "32": "images/icon32.png", - "48": "images/icon48.png", - "128": "images/icon128.png" + "16": "assets/images/icon16.png", + "32": "assets/images/icon32.png", + "48": "assets/images/icon48.png", + "128": "assets/images/icon128.png" } }, "content_scripts": [ @@ -40,32 +34,32 @@ "*://pbs.twimg.com/*", "*://video.twimg.com/*" ], - "js": [ - "assets/remove-twitter-sw.js" - ], + "js": ["assets/javascript/remove-twitter-sw.js"], "run_at": "document_start" }, { "matches": [ - "*://invidio.us/*", - "*://invidio.us/*", "*://invidious.snopyta.org/*", - "*://invidiou.sh/*", "*://yewtu.be/*", - "*://yt.maisputain.ovh/*", - "*://invidious.toot.koeln/*", + "*://invidious.xyz/*", + "*://invidious.site/*", + "*://invidiou.site/*", "*://invidious.ggc-project.de/*", + "*://invidious.13ad.de/*", "*://invidious.toot.koeln/*", + "*://invidious.fdn.fr/*", + "*://watch.nettohikari.com/*", + "*://yt.iswleuven.be/*", + "*://yt.maisputain.ovh/*", "*://kgg2m7yk5aybusll.onion/*", "*://axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4bzzsg2ii4fv2iid.onion/*", "*://fz253lmuao3strwbfbmx46yu7acac2jz27iwtorgmbqlkurlclmancad.onion/*", "*://qklhadlycap4cnod.onion/*", "*://c7hqkpkpemu6e7emz5b4vyz7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion/*", - "*://mfqczy4mysscub2s.onio/*n" - ], - "js": [ - "assets/persist-invidious-prefs.js" + "*://mfqczy4mysscub2s.onion/*", + "*://4l2dgddgsrkf2ous66i6seeyi6etzfgrue332grh2n7madpwopotugyd.onion/*" ], + "js": ["assets/javascript/persist-invidious-prefs.js"], "run_at": "document_start" } ], @@ -73,13 +67,9 @@ "page": "pages/options/options.html", "open_in_tab": false }, - "web_accessible_resources": [ - "assets/iframe_api.js", - "assets/www-widgetapi.js" - ], "browser_specific_settings": { "gecko": { "id": "{b7f9d2cd-d772-4302-8c3f-eb941af36f76}" } } -} \ No newline at end of file +} diff --git a/pages/options/options.html b/pages/options/options.html index 35ef7a4..6641e5c 100644 --- a/pages/options/options.html +++ b/pages/options/options.html @@ -1,183 +1,448 @@ + + + + + + Privacy Redirect Options + - - - - - - Privacy Redirect Options - + +
+ + + +
- +
+
+ + + + + + + +
+

Nitter Redirects

+
+   + +
+
+
+ + + + + + + +
+

+ Invidious Redirects +

+
+   + +
+
+
+ + + + + + + +
+

+ Bibliogram Redirects +

+
+   + +
+
+
+ + + + + + + +
+

+ OpenStreetMap Redirects +

+
+   + +
+
+
+

Nitter Instance

+
+ +
+
+
+

Invidious Instance

+
+ +
+
+
+

Bibliogram Instance

+
+ +
+
+
+

OpenStreetMap Instance

+
+ +
+
+
+

Theme

+ +
+
-
- - - -
+
+
+ + + + + + + +
+

+ Always proxy videos through Invidious +

+
+   + +
+
+
+ + + + + + + +
+

+ Only redirect embedded video to Invidious +

+
+   + +
+
+
+

Invidious Video Quality

+ +
+
+ + + + + + + +
+

+ Invidious dark mode always on +

+
+   + +
+
+
+

+ Invidious Volume +

+ +
+
+

+ Invidious Player Style +

+ +
+
+

+ Invidious Subtitles - language codes (comma-separated) +

+ +
+
+ + + + + + + +
+

+ Invidious automatically play video on load +

+
+   + +
+
+
+ + + + + + + +
+

+ Persist Invidious preferences (as cookie) +

+
+   + +
+
+
+ + + + + + + +
+

+ Proactively remove Twitter service worker +

+
+   + +
+
+
-
-
-
-

Nitter Redirects

-   - -
-
+
+
+

+ Enter a URL or Regular Expression to be excluded from redirects. +

+

+ All requests for or initiating from a URL that matches your exception + will be excluded from redirects. +

+

+ Note - Supports JavaScript regular expressions, excluding the + enclosing forward slashes. +

+
+
+ + + + + + + + + + + +
+

Add Exception

+
+ + + + + + + + +
+
+
    +
    -
    -
    -

    Invidious Redirects

    -   - -
    -
    - -
    -
    -

    Bibliogram Redirects

    -   - -
    -
    - -
    -
    -

    OpenStreetMap Redirects

    -   - -
    -
    - -
    -

    Nitter Instance

    - - - -

    Invidious Instance

    - - - -

    Bibliogram Instance

    - - - -

    OpenStreetMap Instance

    - - - -
    -
    - -
    -
    -
    -

    Always proxy videos through Invidious

    -   - -
    -
    - -
    -
    -

    Only redirect embedded video to Invidious

    -   - -
    -
    - -
    -

    Invidious Video Quality

    - -
    - -
    -
    -

    Invidious dark mode always on

    -   - -
    -
    - -
    -
    -

    Persist Invidious preferences (as cookie)

    -   - -
    -
    - -
    -
    -

    Proactively remove Twitter service worker

    -   - -
    -
    -
    - -
    -
    -

    Whitelisted Sites

    -
    - - -
    -
    -
      -
      - - - - - - \ No newline at end of file + + + + diff --git a/pages/options/options.js b/pages/options/options.js index 5e25a49..8149be7 100644 --- a/pages/options/options.js +++ b/pages/options/options.js @@ -1,130 +1,213 @@ -'use strict'; +"use strict"; -let nitterInstance = document.getElementById('nitter-instance'); -let invidiousInstance = document.getElementById('invidious-instance'); -let bibliogramInstance = document.getElementById('bibliogram-instance'); -let osmInstance = document.getElementById('osm-instance'); -let disableNitter = document.getElementById('disable-nitter'); -let disableInvidious = document.getElementById('disable-invidious'); -let disableBibliogram = document.getElementById('disable-bibliogram'); -let disableOsm = document.getElementById('disable-osm'); -let alwaysProxy = document.getElementById('always-proxy'); -let onlyEmbeddedVideo = document.getElementById('only-embed'); -let videoQuality = document.getElementById('video-quality'); -let removeTwitterSW = document.getElementById('remove-twitter-sw'); -let invidiousDarkMode = document.getElementById('invidious-dark-mode'); -let persistInvidiousPrefs = document.getElementById('persist-invidious-prefs'); -let whitelist; +const nitterInstances = [ + "https://nitter.net", + "https://nitter.snopyta.org", + "https://nitter.42l.fr", + "https://nitter.nixnet.services", + "https://nitter.13ad.de", + "https://nitter.pussthecat.org", + "https://nitter.mastodont.cat", + "https://nitter.dark.fail", + "https://nitter.tedomum.net", + "https://t.maisputain.ovh", + "http://3nzoldnxplag42gqjs23xvghtzf6t6yzssrtytnntc6ppc7xxuoneoad.onion", + "http://nitter.l4qlywnpwqsluw65ts7md3khrivpirse744un3x7mlskqauz5pyuzgqd.onion", +]; +const invidiousInstances = [ + "https://invidious.snopyta.org", + "https://yewtu.be", + "https://invidious.ggc-project.de", + "https://invidious.13ad.de", + "https://invidious.xyz", + "https://invidious.toot.koeln", + "https://invidious.site", + "https://invidiou.site", + "https://invidious.fdn.fr", + "https://watch.nettohikari.com", + "https://yt.iswleuven.be", + "https://yt.maisputain.ovh", + "http://kgg2m7yk5aybusll.onion", + "http://axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4bzzsg2ii4fv2iid.onion", + "http://fz253lmuao3strwbfbmx46yu7acac2jz27iwtorgmbqlkurlclmancad.onion", + "http://qklhadlycap4cnod.onion", + "http://c7hqkpkpemu6e7emz5b4vyz7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion", + "http://mfqczy4mysscub2s.onion", + "http://4l2dgddgsrkf2ous66i6seeyi6etzfgrue332grh2n7madpwopotugyd.onion", +]; +const bibliogramInstances = [ + "https://bibliogram.art", + "https://bibliogram.snopyta.org", + "https://bibliogram.pussthecat.org", + "https://bibliogram.nixnet.services", + "https://bibliogram.hamster.dance", + "https://insta.maisputain.ovh", + "https://bibliogram.ggc-project.de", +]; +const osmInstances = ["https://openstreetmap.org"]; +const autocompletes = [ + { id: "nitter-instance", instances: nitterInstances }, + { id: "invidious-instance", instances: invidiousInstances }, + { id: "bibliogram-instance", instances: bibliogramInstances }, + { id: "osm-instance", instances: osmInstances }, +]; + +let nitterInstance = document.getElementById("nitter-instance"); +let invidiousInstance = document.getElementById("invidious-instance"); +let bibliogramInstance = document.getElementById("bibliogram-instance"); +let osmInstance = document.getElementById("osm-instance"); +let disableNitter = document.getElementById("disable-nitter"); +let disableInvidious = document.getElementById("disable-invidious"); +let disableBibliogram = document.getElementById("disable-bibliogram"); +let disableOsm = document.getElementById("disable-osm"); +let alwaysProxy = document.getElementById("always-proxy"); +let onlyEmbeddedVideo = document.getElementById("only-embed"); +let videoQuality = document.getElementById("video-quality"); +let removeTwitterSW = document.getElementById("remove-twitter-sw"); +let invidiousDarkMode = document.getElementById("invidious-dark-mode"); +let persistInvidiousPrefs = document.getElementById("persist-invidious-prefs"); +let invidiousVolume = document.getElementById("invidious-volume"); +let invidiousPlayerStyle = document.getElementById("invidious-player-style"); +let invidiousSubtitles = document.getElementById("invidious-subtitles"); +let invidiousAutoplay = document.getElementById("invidious-autoplay"); +let theme = document.getElementById("theme"); +let exceptions; window.browser = window.browser || window.chrome; -function prependWhitelistItem(item, index) { - const li = document.createElement('li'); +function prependExceptionsItem(item, index) { + const li = document.createElement("li"); li.appendChild(document.createTextNode(item.toString())); - const button = document.createElement('button'); - button.appendChild(document.createTextNode('X')); - button.addEventListener('click', () => { - li.remove(); - whitelist.splice(index, 1); - browser.storage.sync.set({ - whitelist: whitelist - }); - }); + const button = document.createElement("button"); li.appendChild(button); - document.getElementById('whitelist-items').prepend(li); + document.getElementById("exceptions-items").prepend(li); + const svg = ` + + + `; + button.innerHTML = svg; + button.addEventListener("click", () => { + exceptions.splice(index, 1); + browser.storage.sync.set({ + exceptions: exceptions, + }); + li.remove(); + }); } browser.storage.sync.get( [ - 'nitterInstance', - 'invidiousInstance', - 'bibliogramInstance', - 'osmInstance', - 'disableNitter', - 'disableInvidious', - 'disableBibliogram', - 'disableOsm', - 'alwaysProxy', - 'onlyEmbeddedVideo', - 'videoQuality', - 'removeTwitterSW', - 'whitelist', - 'invidiousDarkMode', - 'persistInvidiousPrefs' + "nitterInstance", + "invidiousInstance", + "bibliogramInstance", + "osmInstance", + "disableNitter", + "disableInvidious", + "disableBibliogram", + "disableOsm", + "alwaysProxy", + "onlyEmbeddedVideo", + "videoQuality", + "removeTwitterSW", + "invidiousDarkMode", + "persistInvidiousPrefs", + "invidiousVolume", + "invidiousPlayerStyle", + "invidiousSubtitles", + "invidiousAutoplay", + "exceptions", + "theme", ], - result => { - nitterInstance.value = result.nitterInstance || ''; - invidiousInstance.value = result.invidiousInstance || ''; - bibliogramInstance.value = result.bibliogramInstance || ''; - osmInstance.value = result.osmInstance || ''; + (result) => { + theme.value = result.theme || ""; + if (result.theme) document.body.classList.add(result.theme); + nitterInstance.value = result.nitterInstance || ""; + invidiousInstance.value = result.invidiousInstance || ""; + bibliogramInstance.value = result.bibliogramInstance || ""; + osmInstance.value = result.osmInstance || ""; disableNitter.checked = !result.disableNitter; disableInvidious.checked = !result.disableInvidious; disableBibliogram.checked = !result.disableBibliogram; disableOsm.checked = !result.disableOsm; alwaysProxy.checked = result.alwaysProxy; onlyEmbeddedVideo.checked = result.onlyEmbeddedVideo; - videoQuality.value = result.videoQuality || ''; + videoQuality.value = result.videoQuality || ""; removeTwitterSW.checked = !result.removeTwitterSW; invidiousDarkMode.checked = result.invidiousDarkMode; persistInvidiousPrefs.checked = result.persistInvidiousPrefs; - whitelist = result.whitelist || []; - whitelist.forEach(prependWhitelistItem); + exceptions = result.exceptions || []; + exceptions.forEach(prependExceptionsItem); + invidiousVolume.value = result.invidiousVolume; + document.querySelector("#volume-value").textContent = result.invidiousVolume + ? `${result.invidiousVolume}%` + : " - "; + invidiousPlayerStyle.value = result.invidiousPlayerStyle || ""; + invidiousSubtitles.value = result.invidiousSubtitles || ""; + invidiousAutoplay.checked = result.invidiousAutoplay; } ); function openTab(tab, event) { let i, tabcontent, tablinks; - tabcontent = document.getElementsByClassName('tabcontent'); + tabcontent = document.getElementsByClassName("tabcontent"); for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = 'none'; + tabcontent[i].style.display = "none"; } - tablinks = document.getElementsByClassName('tablinks'); + tablinks = document.getElementsByClassName("tablinks"); for (i = 0; i < tablinks.length; i++) { - tablinks[i].className = tablinks[i].className.replace(' active', ''); + tablinks[i].className = tablinks[i].className.replace(" active", ""); } - document.getElementById(tab).style.display = 'block'; - event.currentTarget.className += ' active'; + document.getElementById(tab).style.display = "block"; + event.currentTarget.className += " active"; } -document.getElementById('general-tab').addEventListener( - 'click', openTab.bind(null, 'general') -); -document.getElementById('advanced-tab').addEventListener( - 'click', openTab.bind(null, 'advanced') -); -document.getElementById('whitelist-tab').addEventListener( - 'click', openTab.bind(null, 'whitelist') -); +document + .getElementById("general-tab") + .addEventListener("click", openTab.bind(null, "general")); +document + .getElementById("advanced-tab") + .addEventListener("click", openTab.bind(null, "advanced")); +document + .getElementById("exceptions-tab") + .addEventListener("click", openTab.bind(null, "exceptions")); -document.getElementById('general-tab').click(); +document.getElementById("general-tab").click(); -function addToWhitelist() { - const input = document.getElementById('new-whitelist-item'); +function addToExceptions() { + const input = document.getElementById("new-exceptions-item"); + const type = document.querySelector('input[name="type"]:checked').value; if (input.value) { try { + let value = input.value; new RegExp(input.value); - const index = whitelist.push(input.value); - prependWhitelistItem(input.value, index); + if (type === "URL") { + value = value.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + } + exceptions.push(value); browser.storage.sync.set({ - whitelist: whitelist + exceptions: exceptions, }); - input.value = ''; + prependExceptionsItem(value, exceptions.indexOf(value)); + input.value = ""; } catch (error) { - input.setCustomValidity('Invalid RegExp'); + input.setCustomValidity("Invalid RegExp"); } } else { - input.setCustomValidity('Invalid RegExp'); + input.setCustomValidity("Invalid RegExp"); } } -document.getElementById('add-to-whitelist').addEventListener( - 'click', addToWhitelist -); +document + .getElementById("add-to-exceptions") + .addEventListener("click", addToExceptions); function debounce(func, wait, immediate) { let timeout; return () => { - let context = this, args = arguments; + let context = this, + args = arguments; let later = () => { timeout = null; if (!immediate) func.apply(context, args); @@ -134,100 +217,239 @@ function debounce(func, wait, immediate) { timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; -}; +} function parseURL(urlString) { if (urlString) { try { const url = new URL(urlString); if (url.username && url.password) { - return `${url.protocol}//${url.username}:${url.password}@${url.host}` + return `${url.protocol}//${url.username}:${url.password}@${url.host}`; } else { return url.origin; } } catch (error) { console.log(error); - return ''; + return ""; } } else { - return ''; + return ""; } } let nitterInstanceChange = debounce(() => { if (nitterInstance.checkValidity()) { browser.storage.sync.set({ - nitterInstance: parseURL(nitterInstance.value) + nitterInstance: parseURL(nitterInstance.value), }); } }, 500); -nitterInstance.addEventListener('input', nitterInstanceChange); +nitterInstance.addEventListener("input", nitterInstanceChange); let invidiousInstanceChange = debounce(() => { if (invidiousInstance.checkValidity()) { browser.storage.sync.set({ - invidiousInstance: parseURL(invidiousInstance.value) + invidiousInstance: parseURL(invidiousInstance.value), }); } }, 500); -invidiousInstance.addEventListener('input', invidiousInstanceChange); +invidiousInstance.addEventListener("input", invidiousInstanceChange); let bibliogramInstanceChange = debounce(() => { if (bibliogramInstance.checkValidity()) { browser.storage.sync.set({ - bibliogramInstance: parseURL(bibliogramInstance.value) + bibliogramInstance: parseURL(bibliogramInstance.value), }); } }, 500); -bibliogramInstance.addEventListener('input', bibliogramInstanceChange); +bibliogramInstance.addEventListener("input", bibliogramInstanceChange); let osmInstanceChange = debounce(() => { if (osmInstance.checkValidity()) { browser.storage.sync.set({ - osmInstance: parseURL(osmInstance.value) + osmInstance: parseURL(osmInstance.value), }); } }, 500); -osmInstance.addEventListener('input', osmInstanceChange); +osmInstance.addEventListener("input", osmInstanceChange); -disableNitter.addEventListener('change', event => { +disableNitter.addEventListener("change", (event) => { browser.storage.sync.set({ disableNitter: !event.target.checked }); }); -disableInvidious.addEventListener('change', event => { +disableInvidious.addEventListener("change", (event) => { browser.storage.sync.set({ disableInvidious: !event.target.checked }); }); -disableBibliogram.addEventListener('change', event => { +disableBibliogram.addEventListener("change", (event) => { browser.storage.sync.set({ disableBibliogram: !event.target.checked }); }); -disableOsm.addEventListener('change', event => { +disableOsm.addEventListener("change", (event) => { browser.storage.sync.set({ disableOsm: !event.target.checked }); }); -alwaysProxy.addEventListener('change', event => { +alwaysProxy.addEventListener("change", (event) => { browser.storage.sync.set({ alwaysProxy: event.target.checked }); }); -onlyEmbeddedVideo.addEventListener('change', event => { +onlyEmbeddedVideo.addEventListener("change", (event) => { browser.storage.sync.set({ onlyEmbeddedVideo: event.target.checked }); }); -videoQuality.addEventListener('change', event => { +videoQuality.addEventListener("change", (event) => { browser.storage.sync.set({ - videoQuality: event.target.options[videoQuality.selectedIndex].value + videoQuality: event.target.options[videoQuality.selectedIndex].value, }); }); -removeTwitterSW.addEventListener('change', event => { +removeTwitterSW.addEventListener("change", (event) => { browser.storage.sync.set({ removeTwitterSW: !event.target.checked }); }); -invidiousDarkMode.addEventListener('change', event => { +invidiousDarkMode.addEventListener("change", (event) => { browser.storage.sync.set({ invidiousDarkMode: event.target.checked }); }); -persistInvidiousPrefs.addEventListener('change', event => { +persistInvidiousPrefs.addEventListener("change", (event) => { browser.storage.sync.set({ persistInvidiousPrefs: event.target.checked }); }); + +let invidiousVolumeChange = debounce(() => { + document.querySelector( + "#volume-value" + ).textContent = `${invidiousVolume.value}%`; + browser.storage.sync.set({ + invidiousVolume: invidiousVolume.value, + }); +}, 500); +invidiousVolume.addEventListener("input", invidiousVolumeChange); + +invidiousPlayerStyle.addEventListener("change", (event) => { + browser.storage.sync.set({ + invidiousPlayerStyle: + event.target.options[invidiousPlayerStyle.selectedIndex].value, + }); +}); + +let invidiousSubtitlesChange = debounce(() => { + if (invidiousInstance.checkValidity()) { + browser.storage.sync.set({ + invidiousSubtitles: invidiousSubtitles.value, + }); + } +}, 500); +invidiousSubtitles.addEventListener("input", invidiousSubtitlesChange); + +invidiousAutoplay.addEventListener("change", (event) => { + browser.storage.sync.set({ invidiousAutoplay: event.target.checked }); +}); + +theme.addEventListener("change", (event) => { + const value = event.target.options[theme.selectedIndex].value; + switch (value) { + case "dark-theme": + document.body.classList.add("dark-theme"); + document.body.classList.remove("light-theme"); + break; + case "light-theme": + document.body.classList.add("light-theme"); + document.body.classList.remove("dark-theme"); + break; + default: + document.body.classList.remove("light-theme"); + document.body.classList.remove("dark-theme"); + } + browser.storage.sync.set({ + theme: value, + }); +}); + +function autocomplete(input, list) { + let currentFocus; + input.addEventListener("focus", (e) => { + showOptions(e, true); + }); + input.addEventListener("input", (e) => { + const val = e.target.value; + if (!val) { + return false; + } + currentFocus = -1; + showOptions(e); + }); + input.addEventListener("keydown", function (e) { + let x = document.getElementById(this.id + "autocomplete-list"); + if (x) x = x.getElementsByTagName("div"); + if (e.keyCode == 40) { + currentFocus++; + addActive(x); + } else if (e.keyCode == 38) { + currentFocus--; + addActive(x); + } else if (e.keyCode == 13) { + e.preventDefault(); + if (currentFocus > -1) { + if (x) x[currentFocus].click(); + } + } + }); + function showOptions(event, showAll = false) { + let div, + i, + val = event.target.value; + closeAllLists(); + div = document.createElement("div"); + div.setAttribute("id", event.target.id + "autocomplete-list"); + div.setAttribute("class", "autocomplete-items"); + event.target.parentNode.appendChild(div); + for (i = 0; i < list.length; i++) { + if (list[i].toLowerCase().indexOf(val.toLowerCase()) > -1) { + div.appendChild(getItem(list[i], val)); + } else if (showAll) { + div.appendChild(getItem(list[i], val)); + } + } + } + function getItem(item, val) { + let div = document.createElement("div"); + div.innerHTML = "" + item.substr(0, val.length) + ""; + div.innerHTML += item.substr(val.length); + div.innerHTML += ""; + div.addEventListener("click", function (e) { + input.value = e.target.getElementsByTagName("input")[0].value; + input.dispatchEvent(new Event("input")); + closeAllLists(); + }); + return div; + } + function addActive(x) { + if (!x) return false; + removeActive(x); + if (currentFocus >= x.length) currentFocus = 0; + if (currentFocus < 0) currentFocus = x.length - 1; + x[currentFocus].classList.add("autocomplete-active"); + } + function removeActive(x) { + for (let i = 0; i < x.length; i++) { + x[i].classList.remove("autocomplete-active"); + } + } + function closeAllLists(elmnt) { + let x = document.getElementsByClassName("autocomplete-items"); + for (let i = 0; i < x.length; i++) { + if (elmnt != x[i] && elmnt != input) { + x[i].parentNode.removeChild(x[i]); + } + } + } + document.addEventListener("click", (e) => { + if (!autocompletes.find((element) => element.id === e.target.id)) { + closeAllLists(e.target); + } + }); +} + +autocompletes.forEach((value) => { + autocomplete(document.getElementById(value.id), value.instances); +}); diff --git a/pages/popup/open.svg b/pages/popup/open.svg deleted file mode 100644 index 04f56c1..0000000 --- a/pages/popup/open.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/popup/popup.html b/pages/popup/popup.html index 2e0fd68..e06159a 100644 --- a/pages/popup/popup.html +++ b/pages/popup/popup.html @@ -1,122 +1,170 @@ - + + + + + + + - - - - - - + + - -
      -
      - Privacy Redirect logo -

      Privacy
      Redirect

      -
      -
      - Version:  -
      -
      +
      + + + + + + + +
      +

      Nitter Redirects

      +
      +   + +
      +
      -
      -
      -

      Nitter Redirects

      -   - -
      -
      +
      + + + + + + + +
      +

      + Invidious Redirects +

      +
      +   + +
      +
      -
      -
      -

      Invidious Redirects

      -   - -
      -
      +
      + + + + + + + +
      +

      + Bibliogram Redirects +

      +
      +   + +
      +
      -
      -
      -

      Bibliogram Redirects

      -   - -
      -
      +
      + + + + + + + +
      +

      + OpenStreetMap Redirects +

      +
      +   + +
      +
      -
      -
      -

      OpenStreetMap Redirects

      -   - -
      -
      +
      -
      -

      Nitter Instance

      - - - -

      Invidious Instance

      - - - -

      Bibliogram Instance

      - - - -

      OpenStreetMap Instance

      - - - -
      + - - - - - - - \ No newline at end of file + + + + diff --git a/pages/popup/popup.js b/pages/popup/popup.js index 66842c7..54eae40 100644 --- a/pages/popup/popup.js +++ b/pages/popup/popup.js @@ -1,33 +1,23 @@ -'use strict'; +"use strict"; -let nitterInstance = document.querySelector('#nitter-instance'); -let invidiousInstance = document.querySelector('#invidious-instance'); -let bibliogramInstance = document.querySelector('#bibliogram-instance'); -let osmInstance = document.querySelector('#osm-instance'); -let disableNitter = document.querySelector('#disable-nitter'); -let disableInvidious = document.querySelector('#disable-invidious'); -let disableBibliogram = document.querySelector('#disable-bibliogram'); -let disableOsm = document.querySelector('#disable-osm'); -let version = document.querySelector('#version'); +let disableNitter = document.querySelector("#disable-nitter"); +let disableInvidious = document.querySelector("#disable-invidious"); +let disableBibliogram = document.querySelector("#disable-bibliogram"); +let disableOsm = document.querySelector("#disable-osm"); +let version = document.querySelector("#version"); window.browser = window.browser || window.chrome; browser.storage.sync.get( [ - 'nitterInstance', - 'invidiousInstance', - 'bibliogramInstance', - 'osmInstance', - 'disableNitter', - 'disableInvidious', - 'disableBibliogram', - 'disableOsm' + "disableNitter", + "disableInvidious", + "disableBibliogram", + "disableOsm", + "theme", ], - result => { - nitterInstance.value = result.nitterInstance || ''; - invidiousInstance.value = result.invidiousInstance || ''; - bibliogramInstance.value = result.bibliogramInstance || ''; - osmInstance.value = result.osmInstance || ''; + (result) => { + if (result.theme) document.body.classList.add(result.theme); disableNitter.checked = !result.disableNitter; disableInvidious.checked = !result.disableInvidious; disableBibliogram.checked = !result.disableBibliogram; @@ -37,91 +27,22 @@ browser.storage.sync.get( version.textContent = browser.runtime.getManifest().version; -function debounce(func, wait, immediate) { - let timeout; - return () => { - let context = this, args = arguments; - let later = () => { - timeout = null; - if (!immediate) func.apply(context, args); - }; - let callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -}; - -function parseURL(urlString) { - if (urlString) { - try { - const url = new URL(urlString); - if (url.username && url.password) { - return `${url.protocol}//${url.username}:${url.password}@${url.host}` - } else { - return url.origin; - } - } catch (error) { - console.log(error); - return ''; - } - } else { - return ''; - } -} - -let nitterInstanceChange = debounce(() => { - if (nitterInstance.checkValidity()) { - browser.storage.sync.set({ - nitterInstance: parseURL(nitterInstance.value) - }); - } -}, 500); -nitterInstance.addEventListener('input', nitterInstanceChange); - -let invidiousInstanceChange = debounce(() => { - if (invidiousInstance.checkValidity()) { - browser.storage.sync.set({ - invidiousInstance: parseURL(invidiousInstance.value) - }); - } -}, 500); -invidiousInstance.addEventListener('input', invidiousInstanceChange); - -let bibliogramInstanceChange = debounce(() => { - if (bibliogramInstance.checkValidity()) { - browser.storage.sync.set({ - bibliogramInstance: parseURL(bibliogramInstance.value) - }); - } -}, 500); -bibliogramInstance.addEventListener('input', bibliogramInstanceChange); - -let osmInstanceChange = debounce(() => { - if (osmInstance.checkValidity()) { - browser.storage.sync.set({ - osmInstance: parseURL(osmInstance.value) - }); - } -}, 500); -osmInstance.addEventListener('input', osmInstanceChange); - -disableNitter.addEventListener('change', event => { +disableNitter.addEventListener("change", (event) => { browser.storage.sync.set({ disableNitter: !event.target.checked }); }); -disableInvidious.addEventListener('change', event => { +disableInvidious.addEventListener("change", (event) => { browser.storage.sync.set({ disableInvidious: !event.target.checked }); }); -disableBibliogram.addEventListener('change', event => { +disableBibliogram.addEventListener("change", (event) => { browser.storage.sync.set({ disableBibliogram: !event.target.checked }); }); -disableOsm.addEventListener('change', event => { +disableOsm.addEventListener("change", (event) => { browser.storage.sync.set({ disableOsm: !event.target.checked }); }); -document.querySelector('#more-options').addEventListener('click', () => { +document.querySelector("#more-options").addEventListener("click", () => { browser.runtime.openOptionsPage(); }); diff --git a/pages/styles.css b/pages/styles.css index 17b5f7f..dd58676 100644 --- a/pages/styles.css +++ b/pages/styles.css @@ -1,31 +1,41 @@ -:root { - --text-main: #FFF; - --text-secondary: #000; - --dark: #3C4043; - --darker: #292A2D; - --light: #E3E7EA; - --lighter: #FFF; - --active: #FF5B56; +body { + --text: #fff; + --bg-main: #3c4043; + --bg-secondary: #292a2d; + --active: #ff5b56; --space: 5px; --danger: #f04141; - --danger-light: #F9D0D5; + --danger-light: #f9d0d5; + --dark-grey: #767676; + --light-grey: #c3c3c3; +} + +body.light-theme { + --text: #000; + --bg-main: #e3e7ea; + --bg-secondary: #fff; +} + +.light-theme.popup, +.light-theme .popup { + background-color: var(--bg-secondary); } body { - color: var(--text-secondary); margin: 0; - width: 400px; + max-width: 400px; margin: auto; min-height: 572px; font-family: Sans-Serif; - background-color: var(--darker); + background-color: var(--bg-secondary); + color: var(--text); } .popup { width: 300px; min-height: auto; overflow: hidden; - background-color: var(--dark); + background-color: var(--bg-main); } input { @@ -35,8 +45,8 @@ input { } header { - background-color: var(--lighter); - color: var(--text-secondary); + background-color: var(--bg-main); + color: var(--text); display: flex; padding: var(--space); } @@ -45,7 +55,7 @@ header .logo-container { width: 100%; margin: var(--space) 0 var(--space) 0; display: flex; - align-items:center; + align-items: center; } header .logo-container img { @@ -75,7 +85,7 @@ header .version { h1 { font-size: 14px; - margin: var(--space) auto; + margin: 7px auto; } i { @@ -95,7 +105,7 @@ h3 { h1, h2 { - color: var(--text-main); + color: var(--text); } footer { @@ -106,26 +116,40 @@ footer a.button { margin: var(--space); } -/* Elements */ - -input[type=url], input[type=text], select { +input[type="url"], +input[type="text"], +select { width: 100%; box-sizing: border-box; margin-bottom: var(--space); + background-color: var(--bg-main); + border-style: inset; + color: var(--text); } -input[type=url] { +input[type="url"] { padding: 1px 2px; } -input[type=checkbox] { +input[type="checkbox"] { opacity: 0; } +input[type="radio"] { + appearance: radio; + -moz-appearance: radio; + -webkit-appearance: radio; +} + +input[type="radio"]:checked + label { + background: transparent; +} + .checkbox-label { + margin-left: 5px; background: grey; border-radius: 25px; - color: var(--text-main); + color: var(--text); cursor: pointer; display: block; float: right; @@ -137,37 +161,39 @@ input[type=checkbox] { } .checkbox-label:after { - background: #fff; + background: white; border-radius: 90px; - content: ''; + content: ""; height: 20px; left: var(--space); position: absolute; top: var(--space); - transition: 0.3s; /* Acts on transform below */ + transition: 0.3s; width: 20px; } -input:checked+label { + +input:checked + label { background: var(--active); } -/* position when active*/ -input:checked+label:after { + +input:checked + label:after { left: calc(100% - var(--space)); transform: translateX(-100%); } -.settings_block { +.settings-block { display: block; - padding: 10px 1em 1em 1em; + padding: 5px 10px 5px 10px; } -.settings_block h1 { +.settings-block h1 { float: left; } .button { border: var(--active) solid 1px; - color: var(--text-main); + color: var(--text); + stroke: var(--text); display: block; font-size: 12px; font-weight: bold; @@ -186,12 +212,13 @@ input:checked+label:after { .button:hover { background-color: var(--active); - color: #fff; + color: var(--text); + stroke: var(--text); } .button:active { background-color: var(--active); - box-shadow: 0 var(--space) var(--dark); + box-shadow: 0 var(--space) var(--bg-main); transform: translateY(4px); } @@ -207,13 +234,13 @@ input:invalid { .tab { overflow: hidden; - background-color: var(--darker); + background-color: var(--bg-secondary); } .tab button { border-top-left-radius: 10px; border-top-right-radius: 10px; - color: var(--text-main); + color: var(--text); background-color: inherit; float: left; border: none; @@ -221,7 +248,7 @@ input:invalid { cursor: pointer; padding: 14px 16px; transition: 0.3s; - border: solid 1px var(--dark); + border: solid 1px var(--bg-main); width: 33.333%; font-size: 14px; } @@ -231,106 +258,206 @@ input:invalid { } .tab button.active { - background-color: var(--dark); + background-color: var(--bg-main); } .tabcontent { padding-top: 10px; display: none; - border: solid 1px var(--dark); - background-color: var(--dark); + background-color: var(--bg-main); min-height: 510px; } -div.whitelist { +div.exceptions { clear: left; } -div.whitelist > input { +div.exceptions > input { width: 240px; float: left; } -#add-to-whitelist { - width: 120px; +#add-to-exceptions { float: right; border: var(--active) solid 1px; background-color: var(--active); - color: var(--text-main); + color: var(--text); font-weight: bold; cursor: pointer; - border-radius: 25px; + border-radius: 50%; + padding: 1px 1px 0px 1px; + margin-right: 5px; +} + +#add-to-exceptions svg { + height: 20px; + width: 20px; } ul { padding: 0; list-style-type: none; - color: var(--text-main); - margin-right: 20px; - margin-left: 20px; + color: var(--text); + margin: 20px 20px 0 20px; } li { - border-bottom: solid 0.5px var(--darker); + border-bottom: solid 0.5px var(--bg-secondary); padding: 20px 0px 20px 20px; } -#whitelist-items button { +#exceptions-items button { float: right; margin-right: -5px; border: var(--active) solid 1px; background-color: var(--active); - color: var(--text-main); + color: var(--text); font-weight: bold; cursor: pointer; border-radius: 50%; + padding: 2px 2px 0px 2px; } -@media (prefers-color-scheme: dark) { +.button svg { + height: 18px; + width: 18px; +} - body.popup, header, h1, input, select, div.tabcontent, button.tablinks.active { - background-color: var(--dark); - color: var(--text-main); - } +.autocomplete { + position: relative; + display: inline-block; + width: 100%; +} - body { - background-color: var(--darker); - } +.autocomplete input { + background: url(../assets/images/chevron-down.svg) right no-repeat; +} - a.button { - color: var(--text-main); - } +.autocomplete-items { + position: absolute; + border: 1px solid var(--bg-main); + border-bottom: none; + border-top: none; + z-index: 99; + top: 85%; + left: 0; + right: 0; + overflow-y: auto; + max-height: 175px; + color: var(--text); + overflow-x: hidden; + max-width: 380px; +} - ::placeholder { - color: var(--text-main); - opacity: 0.7; - } +.autocomplete-items div { + padding: 10px; + cursor: pointer; + background-color: var(--bg-secondary); + border-bottom: 1px solid var(--bg-main); +} +.autocomplete-items div:hover { + background-color: var(--active); +} + +.autocomplete-active { + background-color: var(--active); + color: var(--text); +} + +.option { + width: 100%; +} + +.option td { + vertical-align: middle; +} + +input[type="range"] { + -webkit-appearance: none; + margin: 18px 0; + width: 100%; +} + +input[type="range"]:focus { + outline: none; +} + +input[type="range"]::-webkit-slider-runnable-track { + width: 100%; + height: 8.4px; + cursor: pointer; + border-color: var(--dark-grey), var(--light-grey); + background: var(--bg-main); + border-radius: 1.3px; + border: 0.2px inset var(--dark-grey); +} + +input[type="range"]::-webkit-slider-thumb { + border-color: var(--active); + border: 1px solid var(--dark-grey); + height: 36px; + width: 16px; + border-radius: 3px; + background: var(--active); + cursor: pointer; + -webkit-appearance: none; + margin-top: -14px; +} + +input[type="range"]:focus::-webkit-slider-runnable-track { + background: var(--bg-main); +} + +input[type="range"]::-moz-range-track { + width: 100%; + height: 8.4px; + cursor: pointer; + border-color: var(--dark-grey), var(--light-grey); + background: var(--bg-main); + border-radius: 1.3px; + border: 0.2px inset var(--dark-grey); +} + +input[type="range"]::-moz-range-thumb { + border-color: var(--active); + border: 1px solid var(--dark-grey); + height: 36px; + width: 16px; + border-radius: 3px; + background: var(--active); + cursor: pointer; +} + +::placeholder { + color: var(--text); + opacity: 0.7; +} + +*:focus { + outline: var(--active) solid 2px; } @media (prefers-color-scheme: light) { - - body.popup, header, h1, input, select, div.tabcontent, button.tablinks.active { - background-color: var(--lighter); - color: var(--text-secondary); - } - body { - background-color: var(--light); + --text: #000; + --text-secondary: #fff; + --bg-main: #e3e7ea; + --bg-secondary: #fff; } - a.button { - color: var(--text-secondary); + body.dark-theme { + --text: #fff; + --text-secondary: #000; + --bg-main: #3c4043; + --bg-secondary: #292a2d; } - button.tablinks { - background-color: var(--light); - color: var(--text-secondary); + .popup { + background-color: var(--bg-secondary); } - - ::placeholder { - color: var(--text-secondary); - opacity: 0.7; - } - +} + +#volume-value { + float: right; } diff --git a/privacy-policy.md b/privacy-policy.md deleted file mode 100644 index 909ec9d..0000000 --- a/privacy-policy.md +++ /dev/null @@ -1,19 +0,0 @@ -# Privacy Policy for Privacy Redirect - -**I solemnly swear this extension is up to only good! 🙂** - -All aspects of the extension work locally on your device. With the exception of -OpenStreetMap (OSM) reverse geocoding, done via the [OSM Nomantim API](https://nominatim.org/release-docs/latest/), -used as part of OSM redirects, the extension does not connect to any other -third-party web services and it doesn’t collect or transmit -any information about you, your browsing habits, or your device. - -The extension requires access to request data for **all websites** in order -to intercept requests made to targeted services (Twitter, YouTube, Instagram & -Google Maps) so that they may be redirected to the appropriate privacy -respecting alternative, all other requests are ignored. - -The extension also requires **local storage** permissions in order to store -extension specific user preferences and settings. - -The extension development is funded through gifts, donations and love. diff --git a/web-ext-config.js b/web-ext-config.js index 96c7c87..1fd87f6 100644 --- a/web-ext-config.js +++ b/web-ext-config.js @@ -1,7 +1,8 @@ module.exports = { ignoreFiles: [ - 'images/Screen*.png', - 'images/small-tile.png', - 'buy-me-a-coffee.png' + 'assets/images/Screen Shot*.png', + 'assets/images/buy-me-a-coffee.png', + 'assets/images/logo*.png', + 'assets/images/badge*.png' ], };