commit
631453600b
@ -1,5 +1,9 @@
|
||||
# Cookie Monster Dialog Browser Extension
|
||||
|
||||
This browser extension was designed to remove cookie consent dialogs that appear on websites without setting your preferences. Only in a few cases, it operates based on predefined rules specified in the file `data/fixes.txt` from where the extension will automatically decline or accept the cookie consent dialogs.
|
||||
|
||||
Please note that the `data/fixes.txt` file should be regularly updated to reflect changes in websites' cookie consent practices and to ensure accurate handling of the dialogs by the extension.
|
||||
|
||||
## Downloads
|
||||
|
||||
- [Chrome Web Store](https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg)
|
||||
@ -11,9 +15,9 @@
|
||||
- All browsers based on Chromium 88+ (Blisk, Brave, Colibri, Epic Browser, Iron Browser, Vivaldi and many more)
|
||||
- Google Chrome 88+
|
||||
- Microsoft Edge 88+
|
||||
- ~~Mozilla Firefox 54+~~ (development stalled until further notice, you can still download and use this extension in its **5.5.5** version)
|
||||
- ~~Mozilla Firefox~~ (development stalled until further notice)
|
||||
|
||||
## Installation (only for developers)
|
||||
## Installation (only for developers or Mozilla Firefox users)
|
||||
|
||||
1. Clone this repository and then run `yarn install`
|
||||
2. Build this repository running the command `yarn workspace browser-extension run build`
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Webseite melden..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Liste leeren"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Ausschlussliste"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Liste exportieren"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Nach dem Eintippen ENTER drücken, um zu filtern"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Liste importieren"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Tragen Sie zu diesem Projekt bei"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Report site..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Clear list"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Exclusion list"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Export list"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Press ENTER to filter after typing"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Import list"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribute to this project"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Reportar sitio..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Borrar lista"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Lista de exclusión"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Exportar lista"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Presiona ENTER para filtrar después de escribir"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importar lista"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribuye a este proyecto"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Signaler le site..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Effacer la liste"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Liste d'exclusion"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Exporter la liste"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Appuyez sur ENTRÉE pour filtrer après avoir tapé"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importer la liste"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribuez à ce projet"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Segnala sito..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Cancella elenco"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Elenco di esclusione"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Esporta elenco"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Premi INVIO per filtrare dopo aver digitato"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importa elenco"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribuisci a questo progetto"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Reportar site..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Limpar lista"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Lista de exclusão"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Exportar lista"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Pressione ENTER para filtrar após digitar"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importar lista"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribua para este projeto"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Reportar site..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Limpar lista"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Lista de exclusão"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Exportar lista"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Pressione ENTER para filtrar após digitar"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importar lista"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribua para este projeto"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Raportați site-ul..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Ștergeți lista"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Listă de excludere"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Exportați lista"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Apăsați ENTER pentru a filtra după ce ați tastat"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Importați lista"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Contribuie la acest proiect"
|
||||
},
|
||||
|
@ -5,6 +5,21 @@
|
||||
"contextMenu_reportOption": {
|
||||
"message": "Сообщить о сайте..."
|
||||
},
|
||||
"options_clearButton": {
|
||||
"message": "Очистить список"
|
||||
},
|
||||
"options_exclusionListTitle": {
|
||||
"message": "Список исключений"
|
||||
},
|
||||
"options_exportButton": {
|
||||
"message": "Экспорт списка"
|
||||
},
|
||||
"options_filterPlaceholder": {
|
||||
"message": "Нажмите ENTER для фильтрации после ввода"
|
||||
},
|
||||
"options_importButton": {
|
||||
"message": "Импорт списка"
|
||||
},
|
||||
"popup_contributeOption": {
|
||||
"message": "Внести свой вклад в этот проект"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Cookie Dialog Monster",
|
||||
"version": "6.3.4",
|
||||
"version": "6.4.0",
|
||||
"default_locale": "en",
|
||||
"description": "__MSG_appDesc__",
|
||||
"icons": {
|
||||
@ -13,6 +13,7 @@
|
||||
"default_icon": "assets/icons/disabled.png",
|
||||
"default_title": "Cookie Dialog Monster"
|
||||
},
|
||||
"options_page": "options.html",
|
||||
"author": "wanhose",
|
||||
"background": {
|
||||
"service_worker": "scripts/background.js"
|
||||
@ -25,6 +26,7 @@
|
||||
"*://*.sharepoint.com/*",
|
||||
"*://*.youtube.com/embed/*",
|
||||
"*://*.youtube-nocookie.com/embed/*",
|
||||
"https://translate.google.com/*",
|
||||
"https://www.cookie-dialog-monster.com/*"
|
||||
],
|
||||
"js": ["scripts/content.js", "scripts/dialog.js"],
|
||||
|
110
packages/browser-extension/src/options.html
Normal file
110
packages/browser-extension/src/options.html
Normal file
@ -0,0 +1,110 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Cookie Dialog Monster > Exclusion List</title>
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/options.css" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
|
||||
<script src="/scripts/options.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div>
|
||||
<h1 class="header-title">
|
||||
Cookie Dialog Monster > <span data-i18n="options_exclusionListTitle"></span>
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
<main>
|
||||
<div class="button-group">
|
||||
<button data-variant="large" id="clear-button">
|
||||
<span data-i18n="options_clearButton"></span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="14"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
>
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path
|
||||
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
|
||||
/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button data-variant="large" id="import-button">
|
||||
<span data-i18n="options_importButton"></span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="14"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
<input accept=".cdm" id="file-input" style="display: none" type="file" />
|
||||
</button>
|
||||
<button data-variant="large" id="export-button">
|
||||
<span data-i18n="options_exportButton"></span>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="14"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="7 10 12 15 17 10"></polyline>
|
||||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input id="filter-input" data-i18n-placeholder="options_filterPlaceholder" />
|
||||
<ul id="exclusion-list">
|
||||
<li id="exclusion-list-item-template" style="display: none">
|
||||
<span></span>
|
||||
<button>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="14"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="14"
|
||||
>
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path
|
||||
d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"
|
||||
/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li id="exclusion-list-item-empty" style="display: none"></li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer></footer>
|
||||
</body>
|
||||
</html>
|
@ -1,7 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="author" content="wanhose" />
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/popup.css" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter" />
|
||||
@ -10,6 +9,24 @@
|
||||
<body>
|
||||
<header>
|
||||
<h1 class="header-title">Cookie Dialog Monster</h1>
|
||||
<button id="settings-button">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="18"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path
|
||||
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
<main>
|
||||
<popup-button id="power-option" role="button" tabindex="0">
|
||||
@ -17,10 +34,10 @@
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
>
|
||||
@ -34,10 +51,10 @@
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
>
|
||||
@ -57,10 +74,10 @@
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
>
|
||||
@ -75,10 +92,10 @@
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
>
|
||||
|
@ -5,13 +5,6 @@
|
||||
|
||||
const apiUrl = 'https://api.cookie-dialog-monster.com/rest/v2';
|
||||
|
||||
/**
|
||||
* @description Initial state
|
||||
* @type {{ enabled: boolean }}
|
||||
*/
|
||||
|
||||
const initial = { enabled: true };
|
||||
|
||||
/**
|
||||
* @description Context menu identifier
|
||||
* @type {string}
|
||||
@ -90,31 +83,65 @@ chrome.runtime.onMessage.addListener((message, sender, callback) => {
|
||||
|
||||
switch (message.type) {
|
||||
case 'DISABLE_ICON':
|
||||
if (isPage && tabId) chrome.action.setIcon({ path: '/assets/icons/disabled.png', tabId });
|
||||
if (isPage && tabId) {
|
||||
chrome.action.setIcon({ path: '/assets/icons/disabled.png', tabId });
|
||||
}
|
||||
break;
|
||||
case 'ENABLE_ICON':
|
||||
if (isPage && tabId) chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId });
|
||||
if (isPage && tabId) {
|
||||
chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId });
|
||||
}
|
||||
break;
|
||||
case 'ENABLE_POPUP':
|
||||
if (isPage && tabId) chrome.action.setPopup({ popup: '/popup.html', tabId });
|
||||
if (isPage && tabId) {
|
||||
chrome.action.setPopup({ popup: '/popup.html', tabId });
|
||||
}
|
||||
break;
|
||||
case 'GET_DATA':
|
||||
storage.get('data', ({ data }) => (data ? callback(data) : refreshData(callback)));
|
||||
storage.get('data', ({ data }) => {
|
||||
if (data) {
|
||||
callback(data);
|
||||
} else {
|
||||
refreshData(callback);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
case 'GET_STATE':
|
||||
if (hostname) storage.get(hostname, (state) => callback(state[hostname] ?? initial));
|
||||
case 'GET_EXCLUSION_LIST':
|
||||
storage.get(null, (exclusions) => {
|
||||
const exclusionList = Object.entries(exclusions || {}).flatMap((x) =>
|
||||
x[0] !== 'data' && !x[1]?.enabled ? [x[0]] : []
|
||||
);
|
||||
callback(exclusionList);
|
||||
});
|
||||
return true;
|
||||
case 'GET_HOSTNAME_STATE':
|
||||
if (hostname) {
|
||||
storage.get(hostname, (state) => {
|
||||
callback(state[hostname] ?? { enabled: true });
|
||||
});
|
||||
}
|
||||
return true;
|
||||
case 'GET_TAB':
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => callback(tabs[0]));
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
callback(tabs[0]);
|
||||
});
|
||||
return true;
|
||||
case 'INSERT_DIALOG_CSS':
|
||||
if (isPage && tabId) script.insertCSS({ files: ['styles/dialog.css'], target: { tabId } });
|
||||
if (isPage && tabId) {
|
||||
script.insertCSS({ files: ['styles/dialog.css'], target: { tabId } });
|
||||
}
|
||||
break;
|
||||
case 'REPORT':
|
||||
if (tabId) report(message, sender.tab);
|
||||
if (tabId) {
|
||||
report(message, sender.tab);
|
||||
}
|
||||
break;
|
||||
case 'UPDATE_STATE':
|
||||
if (hostname) storage.set({ [hostname]: message.state });
|
||||
case 'SET_HOSTNAME_STATE':
|
||||
if (hostname && message.state.enabled === false) {
|
||||
storage.set({ [hostname]: message.state });
|
||||
} else if (hostname) {
|
||||
storage.remove(hostname);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -42,6 +42,7 @@ let state = { enabled: true };
|
||||
* @description Cleans DOM
|
||||
* @param {Element[]} elements
|
||||
* @param {boolean?} skipMatch
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function clean(elements, skipMatch) {
|
||||
@ -59,6 +60,7 @@ function clean(elements, skipMatch) {
|
||||
/**
|
||||
* @description Forces a DOM clean in the specific element
|
||||
* @param {HTMLElement} element
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function forceClean(element) {
|
||||
@ -73,6 +75,7 @@ function forceClean(element) {
|
||||
/**
|
||||
* @description Forces element to have these styles
|
||||
* @param {HTMLElement} element
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function forceElementStyles(element) {
|
||||
@ -112,7 +115,7 @@ function match(element, skipMatch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data?.tags.includes(element.tagName?.toUpperCase?.())) {
|
||||
if (!data?.tags?.length || data.tags.includes(element.tagName?.toUpperCase?.())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -136,6 +139,7 @@ function match(element, skipMatch) {
|
||||
|
||||
/**
|
||||
* @description Fixes scroll issues
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function fix() {
|
||||
@ -198,6 +202,29 @@ function readingTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Sets up everything
|
||||
* @param {boolean} skipReadyStateHack
|
||||
*/
|
||||
|
||||
async function runSetup(skipReadyStateHack) {
|
||||
state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state;
|
||||
dispatch({ type: 'ENABLE_POPUP' });
|
||||
|
||||
if (state.enabled) {
|
||||
data = await dispatch({ hostname, type: 'GET_DATA' });
|
||||
|
||||
// 2023-06-13: hack to force clean when data request takes too long and there are no changes later
|
||||
if (document.readyState === 'complete' && !skipReadyStateHack) {
|
||||
window.dispatchEvent(new Event('run'));
|
||||
}
|
||||
|
||||
dispatch({ type: 'ENABLE_ICON' });
|
||||
observer.observe(document.body ?? document.documentElement, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Mutation Observer instance
|
||||
* @type {MutationObserver}
|
||||
@ -212,22 +239,40 @@ const observer = new MutationObserver((mutations) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Runs setup if the page wasn't focused yet
|
||||
* @listens window#focus
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
window.addEventListener('focus', async () => {
|
||||
if (!data) {
|
||||
await runSetup(true);
|
||||
forceClean(document.body);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Fixes still existing elements when page fully load
|
||||
* @listens window#load
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
window.dispatchEvent(new Event('run'));
|
||||
if (document.hasFocus()) {
|
||||
window.dispatchEvent(new Event('run'));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Fixes bfcache issues
|
||||
* @listens window#pageshow
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
window.addEventListener('pageshow', (event) => {
|
||||
if (event.persisted) {
|
||||
if (document.hasFocus() && event.persisted) {
|
||||
window.dispatchEvent(new Event('run'));
|
||||
}
|
||||
});
|
||||
@ -235,6 +280,7 @@ window.addEventListener('pageshow', (event) => {
|
||||
/**
|
||||
* @description Forces a clean when this event is fired
|
||||
* @listens window#run
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
window.addEventListener('run', () => {
|
||||
@ -249,23 +295,9 @@ window.addEventListener('run', () => {
|
||||
});
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Sets up everything
|
||||
* @description As this extension do really expensive work, it only runs if the user is on the page
|
||||
*/
|
||||
|
||||
(async () => {
|
||||
state = (await dispatch({ hostname, type: 'GET_STATE' })) ?? state;
|
||||
dispatch({ type: 'ENABLE_POPUP' });
|
||||
|
||||
if (state.enabled) {
|
||||
data = await dispatch({ hostname, type: 'GET_DATA' });
|
||||
|
||||
// 2023-06-13: hack to force clean when data request takes too long and there are no changes later
|
||||
if (document.readyState === 'complete') {
|
||||
window.dispatchEvent(new Event('run'));
|
||||
}
|
||||
|
||||
dispatch({ type: 'ENABLE_ICON' });
|
||||
observer.observe(document.body ?? document.documentElement, options);
|
||||
}
|
||||
})();
|
||||
if (document.hasFocus()) {
|
||||
runSetup();
|
||||
}
|
||||
|
244
packages/browser-extension/src/scripts/options.js
Normal file
244
packages/browser-extension/src/scripts/options.js
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* @description Shortcut to send messages to background script
|
||||
*/
|
||||
|
||||
const dispatch = chrome.runtime.sendMessage;
|
||||
|
||||
/**
|
||||
* @description Exclusion list, URLs where the user prefers to disable the extension
|
||||
* @type {string[]}
|
||||
*/
|
||||
|
||||
let exclusionList = [];
|
||||
|
||||
/**
|
||||
* @description Renders exclusion items into exclusion list
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function createList() {
|
||||
const emptyItemElement = document.getElementById('exclusion-list-item-empty');
|
||||
const exclusionListElement = document.getElementById('exclusion-list');
|
||||
const exclusionListItemTemplateElement = document.getElementById('exclusion-list-item-template');
|
||||
|
||||
Array.from(exclusionListElement.querySelectorAll('[data-value]')).forEach((exclusionItem) => {
|
||||
exclusionItem.remove();
|
||||
});
|
||||
|
||||
if (exclusionList.length) {
|
||||
for (const exclusionValue of exclusionList) {
|
||||
const ariaLabelOrTitle = `Delete ${exclusionValue}`;
|
||||
const itemElement = exclusionListItemTemplateElement.cloneNode(true);
|
||||
const deleteButtonElement = itemElement.getElementsByTagName('button')[0];
|
||||
|
||||
deleteButtonElement.addEventListener('click', handleDeleteClick);
|
||||
deleteButtonElement.setAttribute('aria-label', ariaLabelOrTitle);
|
||||
deleteButtonElement.setAttribute('title', ariaLabelOrTitle);
|
||||
itemElement.removeAttribute('id');
|
||||
itemElement.getElementsByTagName('span')[0].innerText = exclusionValue;
|
||||
itemElement.setAttribute('data-value', exclusionValue);
|
||||
itemElement.style.removeProperty('display');
|
||||
exclusionListElement.appendChild(itemElement);
|
||||
}
|
||||
} else {
|
||||
emptyItemElement.innerText = "You don't have any exclusions yet";
|
||||
emptyItemElement.style.removeProperty('display');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Clear all items from the exclusion list
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
async function handleClearClick() {
|
||||
const filterInputElement = document.getElementById('filter-input');
|
||||
|
||||
for (const exclusionValue of exclusionList) {
|
||||
const state = { enabled: true };
|
||||
await dispatch({ hostname: exclusionValue, state, type: 'SET_HOSTNAME_STATE' });
|
||||
}
|
||||
|
||||
exclusionList = [];
|
||||
createList();
|
||||
updateList(filterInputElement.value.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Setup handlers and items
|
||||
*/
|
||||
|
||||
async function handleContentLoaded() {
|
||||
exclusionList = await dispatch({ type: 'GET_EXCLUSION_LIST' });
|
||||
createList();
|
||||
|
||||
const clearButtonElement = document.getElementById('clear-button');
|
||||
clearButtonElement.addEventListener('click', handleClearClick);
|
||||
|
||||
const exportButtonElement = document.getElementById('export-button');
|
||||
exportButtonElement.addEventListener('click', handleExportClick);
|
||||
|
||||
const fileInputElement = document.getElementById('file-input');
|
||||
fileInputElement.addEventListener('change', handleFileChange);
|
||||
|
||||
const filterInputElement = document.getElementById('filter-input');
|
||||
filterInputElement.addEventListener('keydown', handleFilterKeyDown);
|
||||
|
||||
const importButtonElement = document.getElementById('import-button');
|
||||
importButtonElement.addEventListener('click', handleImportClick);
|
||||
|
||||
translate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Deletes the clicked element from the exclusion list
|
||||
* @param {MouseEvent} event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
async function handleDeleteClick(event) {
|
||||
const filterInputElement = document.getElementById('filter-input');
|
||||
const { value } = event.currentTarget.parentElement.dataset;
|
||||
const state = { enabled: true };
|
||||
|
||||
await dispatch({ hostname: value, state, type: 'SET_HOSTNAME_STATE' });
|
||||
exclusionList = exclusionList.filter((exclusionValue) => exclusionValue !== value);
|
||||
itemElement.remove();
|
||||
updateList(filterInputElement.value.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Exports a file with the current exclusion list
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function handleExportClick() {
|
||||
const anchor = document.createElement('a');
|
||||
const text = exclusionList.join('\n');
|
||||
const blob = new Blob([text], { type: 'octet/stream' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
anchor.href = url;
|
||||
anchor.download = `${new Date().valueOf()}.cdm`;
|
||||
anchor.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Processes a file and sends the updates
|
||||
* @param {InputEvent} event
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function handleFileChange(event) {
|
||||
const file = event.currentTarget.files[0];
|
||||
const filterInputElement = document.getElementById('filter-input');
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.addEventListener('load', async (event) => {
|
||||
const newExclusionList = event.currentTarget.result.split('\n').filter((x) => x.trim());
|
||||
|
||||
for (const exclusionValue of newExclusionList) {
|
||||
const state = { enabled: false };
|
||||
await dispatch({ hostname: exclusionValue, state, type: 'SET_HOSTNAME_STATE' });
|
||||
}
|
||||
|
||||
if (newExclusionList.length) {
|
||||
exclusionList = [...new Set([...exclusionList, ...newExclusionList])].sort();
|
||||
createList();
|
||||
updateList(filterInputElement.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
event.currentTarget.value = '';
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Applies filter to the exclusion list when the user presses ENTER key
|
||||
* @param {KeyboardEvent} event
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function handleFilterKeyDown(event) {
|
||||
if (event.key === 'Enter') {
|
||||
const filterValue = event.currentTarget.value.trim();
|
||||
updateList(filterValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Shallow clicks an hidden input to open the file explorer
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function handleImportClick() {
|
||||
const fileInputElement = document.getElementById('file-input');
|
||||
fileInputElement.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Applies translations to tags with i18n data attribute
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function translate() {
|
||||
const nodes = document.querySelectorAll('[data-i18n], [data-i18n-placeholder]');
|
||||
|
||||
for (let i = nodes.length; i--; ) {
|
||||
const node = nodes[i];
|
||||
const { i18n, i18nPlaceholder } = node.dataset;
|
||||
|
||||
if (i18n) {
|
||||
node.innerHTML = chrome.i18n.getMessage(i18n);
|
||||
}
|
||||
|
||||
if (i18nPlaceholder) {
|
||||
node.setAttribute('placeholder', chrome.i18n.getMessage(i18nPlaceholder));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Updates exclusion items in DOM
|
||||
* @param {string | undefined} filterValue
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function updateList(filterValue) {
|
||||
const emptyItemElement = document.getElementById('exclusion-list-item-empty');
|
||||
const exclusionListElement = document.getElementById('exclusion-list');
|
||||
const exclusionListElements = exclusionListElement.querySelectorAll(`[data-value]`);
|
||||
|
||||
if (exclusionListElements.length) {
|
||||
let isEmpty = true;
|
||||
emptyItemElement.style.setProperty('display', 'none');
|
||||
|
||||
for (const exclusionItemElement of Array.from(exclusionListElements)) {
|
||||
if (exclusionItemElement.matches(`[data-value*="${filterValue}"]`) || !filterValue) {
|
||||
exclusionItemElement.style.removeProperty('display');
|
||||
isEmpty = false;
|
||||
} else {
|
||||
exclusionItemElement.style.setProperty('display', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
emptyItemElement.innerText = 'No exclusions found';
|
||||
emptyItemElement.style.removeProperty('display');
|
||||
}
|
||||
} else {
|
||||
emptyItemElement.innerText = "You don't have any exclusions yet";
|
||||
emptyItemElement.style.removeProperty('display');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Listen to document ready
|
||||
* @listens document#DOMContentLoaded
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', handleContentLoaded);
|
@ -64,80 +64,104 @@ let state = { enabled: true };
|
||||
/**
|
||||
* @async
|
||||
* @description Setup stars handlers and result message links
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
const handleContentLoaded = async () => {
|
||||
async function handleContentLoaded() {
|
||||
const tab = await dispatch({ type: 'GET_TAB' });
|
||||
|
||||
hostname = tab?.url
|
||||
? new URL(tab.url).hostname.split('.').slice(-3).join('.').replace('www.', '')
|
||||
: undefined;
|
||||
state = (await dispatch({ hostname, type: 'GET_STATE' })) ?? state;
|
||||
state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state;
|
||||
|
||||
const host = document.getElementById('host');
|
||||
host.innerText = hostname ?? 'unknown';
|
||||
const hostTextElement = document.getElementById('host');
|
||||
hostTextElement.innerText = hostname ?? 'unknown';
|
||||
|
||||
const contribute = document.getElementById('contribute-option');
|
||||
contribute?.addEventListener('click', handleLinkRedirect);
|
||||
const contributeButtonElement = document.getElementById('contribute-option');
|
||||
contributeButtonElement?.addEventListener('click', handleLinkRedirect);
|
||||
|
||||
const help = document.getElementById('help-option');
|
||||
help?.addEventListener('click', handleLinkRedirect);
|
||||
const helpButtonElement = document.getElementById('help-option');
|
||||
helpButtonElement?.addEventListener('click', handleLinkRedirect);
|
||||
|
||||
const power = document.getElementById('power-option');
|
||||
power?.addEventListener('click', handlePowerToggle);
|
||||
if (state.enabled) power?.setAttribute('data-value', 'on');
|
||||
else power?.setAttribute('data-value', 'off');
|
||||
const powerButtonElement = document.getElementById('power-option');
|
||||
powerButtonElement?.addEventListener('click', handlePowerToggle);
|
||||
if (state.enabled) powerButtonElement?.setAttribute('data-value', 'on');
|
||||
else powerButtonElement?.setAttribute('data-value', 'off');
|
||||
|
||||
const rate = document.getElementById('rate-option');
|
||||
rate?.addEventListener('click', handleLinkRedirect);
|
||||
if (isEdge) rate?.setAttribute('data-href', edgeUrl);
|
||||
else if (isChromium) rate?.setAttribute('data-href', chromeUrl);
|
||||
else if (isFirefox) rate?.setAttribute('data-href', firefoxUrl);
|
||||
const rateButtonElement = document.getElementById('rate-option');
|
||||
rateButtonElement?.addEventListener('click', handleLinkRedirect);
|
||||
if (isEdge) rateButtonElement?.setAttribute('data-href', edgeUrl);
|
||||
else if (isChromium) rateButtonElement?.setAttribute('data-href', chromeUrl);
|
||||
else if (isFirefox) rateButtonElement?.setAttribute('data-href', firefoxUrl);
|
||||
|
||||
const settingsButtonElement = document.getElementById('settings-button');
|
||||
settingsButtonElement.addEventListener('click', handleSettingsClick);
|
||||
|
||||
translate();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Opens a new tab
|
||||
* @param {MouseEvent} event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
const handleLinkRedirect = async (event) => {
|
||||
async function handleLinkRedirect(event) {
|
||||
const { href } = event.currentTarget.dataset;
|
||||
|
||||
if (href) {
|
||||
await chrome.tabs.create({ url: href });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @description Disables or enables extension on current page
|
||||
* @param {MouseEvent} event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
|
||||
const handlePowerToggle = async (event) => {
|
||||
async function handlePowerToggle(event) {
|
||||
state = { enabled: !state.enabled };
|
||||
dispatch({ hostname, state, type: 'UPDATE_STATE' });
|
||||
dispatch({ hostname, state, type: 'SET_HOSTNAME_STATE' });
|
||||
if (state.enabled) event.currentTarget.setAttribute('data-value', 'on');
|
||||
else event.currentTarget.setAttribute('data-value', 'off');
|
||||
await chrome.tabs.reload({ bypassCache: true });
|
||||
};
|
||||
window.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Opens options page
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function handleSettingsClick() {
|
||||
chrome.runtime.openOptionsPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Applies translations to tags with i18n data attribute
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
const translate = () => {
|
||||
const nodes = document.querySelectorAll('[data-i18n]');
|
||||
function translate() {
|
||||
const nodes = document.querySelectorAll('[data-i18n], [data-i18n-placeholder]');
|
||||
|
||||
for (let i = nodes.length; i--; ) {
|
||||
const node = nodes[i];
|
||||
const { i18n } = node.dataset;
|
||||
const { i18n, i18nPlaceholder } = node.dataset;
|
||||
|
||||
node.innerHTML = chrome.i18n.getMessage(i18n);
|
||||
if (i18n) {
|
||||
node.innerHTML = chrome.i18n.getMessage(i18n);
|
||||
}
|
||||
|
||||
if (i18nPlaceholder) {
|
||||
node.setAttribute('placeholder', chrome.i18n.getMessage(i18nPlaceholder));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Listen to document ready
|
||||
|
137
packages/browser-extension/src/styles/options.css
Normal file
137
packages/browser-extension/src/styles/options.css
Normal file
@ -0,0 +1,137 @@
|
||||
:root {
|
||||
--color-error: #cc0000;
|
||||
--color-primary: #3dd9eb;
|
||||
--color-secondary: #34495e;
|
||||
--color-success: #5cb85c;
|
||||
--color-tertiary: #6b7280;
|
||||
--color-transparent: transparent;
|
||||
--color-warning: #ffdf00;
|
||||
--color-white: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
color: var(--color-tertiary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: Inter, Arial, Helvetica, sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
body * {
|
||||
box-sizing: border-box;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
align-items: center;
|
||||
background-color: var(--color-white);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: var(--color-secondary);
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
outline: none;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
button[data-variant='large'] {
|
||||
direction: rtl;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:hover {
|
||||
background-color: var(--color-secondary);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--color-secondary);
|
||||
font-size: 12px;
|
||||
height: 4px;
|
||||
margin-top: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--color-secondary);
|
||||
color: var(--color-white);
|
||||
font-size: 16px !important;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
header > div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
margin: auto 0px;
|
||||
}
|
||||
|
||||
main input {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color: var(--color-white);
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--color-tertiary);
|
||||
border-radius: 0px;
|
||||
color: var(--color-secondary);
|
||||
font-size: 16px;
|
||||
height: 42px;
|
||||
outline: none;
|
||||
padding: 0px 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main input::placeholder {
|
||||
color: var(--color-tertiary);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
main input:focus,
|
||||
main input:hover {
|
||||
border-bottom: 1px solid var(--color-primary);
|
||||
}
|
||||
|
||||
header > div,
|
||||
main {
|
||||
margin: 0px auto;
|
||||
max-width: 768px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#exclusion-list {
|
||||
font-size: 14px;
|
||||
list-style: none;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#exclusion-list > li {
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
#exclusion-list > li:focus-within,
|
||||
#exclusion-list > li:hover {
|
||||
background-color: var(--color-secondary);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
#exclusion-list > li > button {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-error);
|
||||
padding: 4px;
|
||||
}
|
@ -21,6 +21,25 @@ body * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button {
|
||||
align-items: center;
|
||||
background-color: var(--color-secondary);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: var(--color-white);
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
outline: none;
|
||||
padding: 2px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:hover {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--color-secondary);
|
||||
font-size: 12px;
|
||||
|
Loading…
Reference in New Issue
Block a user