docs(browser-extension): improve jsdoc

This commit is contained in:
wanhose 2024-02-24 19:13:40 +01:00
parent d124f3bfd9
commit 2937f6f86e
6 changed files with 85 additions and 140 deletions

View File

@ -26,7 +26,7 @@
"*://*.facebook.com/*",
"*://*.googleapis.com/embed/*",
"*://*.messenger.com/*",
"*://*.office365.com*",
"*://*.office365.com/*",
"*://*.officeapps.live.com/*",
"*://*.sharepoint.com/*",
"*://*.suite.office.com/*",

View File

@ -2,54 +2,47 @@
* @description API URL
* @type {string}
*/
const apiUrl = 'https://api.cookie-dialog-monster.com/rest/v2';
/**
* @description Context menu identifier
* @type {string}
*/
const extensionMenuItemId = 'CDM-MENU';
/**
* @description Context menu identifier
* @type {string}
*/
const reportMenuItemId = 'CDM-REPORT';
/**
* @description Context menu identifier
* @type {string}
*/
const settingsMenuItemId = 'CDM-SETTINGS';
/**
* @description A shortcut for chrome.scripting
* @type {chrome.scripting}
*/
const script = chrome.scripting;
/**
* @description The storage to use
* @type {chrome.storage.LocalStorageArea}
*/
const storage = chrome.storage.local;
/**
* @description Refreshes data
* @description Refresh data
* @param {void?} callback
*/
const refreshData = (callback) => {
try {
fetch(`${apiUrl}/data/`).then((result) => {
result.json().then(({ data }) => {
chrome.storage.local.set({ data });
chrome.storage.local.set({ data }, suppressLastError);
callback?.(data);
});
});
@ -60,30 +53,39 @@ const refreshData = (callback) => {
/**
* @async
* @description Reports active tab URL
* @description Report active tab URL
* @param {any} message
* @param {chrome.tabs.Tab} tab
*/
const report = async (message, tab) => {
const reason = message.reason;
const userAgent = message.userAgent;
const version = chrome.runtime.getManifest().version;
const body = JSON.stringify({ reason, url: tab.url, userAgent, version });
const headers = { 'Content-type': 'application/json' };
const url = `${apiUrl}/report/`;
try {
const reason = message.reason;
const userAgent = message.userAgent;
const version = chrome.runtime.getManifest().version;
const body = JSON.stringify({ reason, url: tab.url, userAgent, version });
const headers = { 'Content-type': 'application/json' };
const url = `${apiUrl}/report/`;
await fetch(url, { body, headers, method: 'POST' });
await fetch(url, { body, headers, method: 'POST' });
} catch {
console.error("Can't send report");
}
};
/**
* @description Listens to context menus clicked
* @description Supress `chrome.runtime.lastError`
*/
const suppressLastError = () => void chrome.runtime.lastError;
/**
* @description Listen to context menus clicked
*/
chrome.contextMenus.onClicked.addListener((info, tab) => {
const tabId = tab?.id;
switch (info.menuItemId) {
case reportMenuItemId:
if (tab) chrome.tabs.sendMessage(tab.id, { type: 'SHOW_REPORT_DIALOG' });
if (tabId) chrome.tabs.sendMessage(tabId, { type: 'SHOW_REPORT_DIALOG' }, suppressLastError);
break;
case settingsMenuItemId:
chrome.runtime.openOptionsPage();
@ -96,7 +98,6 @@ chrome.contextMenus.onClicked.addListener((info, tab) => {
/**
* @description Listens to messages
*/
chrome.runtime.onMessage.addListener((message, sender, callback) => {
const hostname = message.hostname;
const isPage = sender.frameId === 0;
@ -105,17 +106,17 @@ 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 });
chrome.action.setIcon({ path: '/assets/icons/disabled.png', tabId }, suppressLastError);
}
break;
case 'ENABLE_ICON':
if (isPage && tabId) {
chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId });
chrome.action.setIcon({ path: '/assets/icons/enabled.png', tabId }, suppressLastError);
}
break;
case 'ENABLE_POPUP':
if (isPage && tabId) {
chrome.action.setPopup({ popup: '/popup.html', tabId });
chrome.action.setPopup({ popup: '/popup.html', tabId }, suppressLastError);
}
break;
case 'GET_DATA':
@ -172,34 +173,41 @@ chrome.runtime.onMessage.addListener((message, sender, callback) => {
/**
* @description Listens to extension installed
*/
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: extensionMenuItemId,
title: 'Cookie Dialog Monster',
});
chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: settingsMenuItemId,
parentId: extensionMenuItemId,
title: chrome.i18n.getMessage('contextMenu_settingsOption'),
});
chrome.contextMenus.create({
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: reportMenuItemId,
parentId: extensionMenuItemId,
title: chrome.i18n.getMessage('contextMenu_reportOption'),
});
chrome.contextMenus.create(
{
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: extensionMenuItemId,
title: 'Cookie Dialog Monster',
},
suppressLastError
);
chrome.contextMenus.create(
{
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: settingsMenuItemId,
parentId: extensionMenuItemId,
title: chrome.i18n.getMessage('contextMenu_settingsOption'),
},
suppressLastError
);
chrome.contextMenus.create(
{
contexts: ['all'],
documentUrlPatterns: chrome.runtime.getManifest().content_scripts[0].matches,
id: reportMenuItemId,
parentId: extensionMenuItemId,
title: chrome.i18n.getMessage('contextMenu_reportOption'),
},
suppressLastError
);
});
/**
* @description Listens to first start
* @description Listen to first start
*/
chrome.runtime.onStartup.addListener(() => {
refreshData();
});

View File

@ -2,49 +2,42 @@
* @description Data properties
* @type {{ classes: string[], commonWords?: string[], fixes: string[], elements: string[], skips: string[], tags: string[] }?}
*/
let data = null;
/**
* @description Shortcut to send messages to background script
*/
const dispatch = chrome.runtime.sendMessage;
/**
* @description Current hostname
* @type {string}
*/
const hostname = getHostname();
/**
* @description Options provided to observer
* @type {MutationObserverInit}
*/
const options = { childList: true, subtree: true };
/**
* @description Is consent preview page?
*/
const preview = hostname.startsWith('consent.') || hostname.startsWith('myprivacy.');
/**
* @description Extension state
* @type {{ enabled: boolean }}
*/
let state = { enabled: true };
/**
* @description Cleans DOM
* @description Clean DOM
* @param {Element[]} elements
* @param {boolean?} skipMatch
* @returns {void}
*/
function clean(elements, skipMatch) {
for (const element of elements) {
if (match(element, skipMatch)) {
@ -58,11 +51,10 @@ function clean(elements, skipMatch) {
}
/**
* @description Forces a DOM clean in the specific element
* @description Force a DOM clean in the specific element
* @param {HTMLElement} element
* @returns {void}
*/
function forceClean(element) {
const elements = [...element.querySelectorAll(data.elements)];
@ -71,20 +63,18 @@ function forceClean(element) {
}
/**
* @description Forces element to have these styles
* @description Force element to have these styles
* @param {HTMLElement} element
* @returns {void}
*/
function forceElementStyles(element) {
element.style.setProperty('display', 'none', 'important');
}
/**
* @description Calculates current hostname
* @description Calculate current hostname
* @returns {string}
*/
function getHostname() {
let hostname = document.location.hostname;
const referrer = document.referrer;
@ -97,11 +87,10 @@ function getHostname() {
}
/**
* @description Checks if an element is visible in the viewport
* @description Check if an element is visible in the viewport
* @param {HTMLElement} element
* @returns {boolean}
*/
function isInViewport(element) {
const height = window.innerHeight || document.documentElement.clientHeight;
const position = element.getBoundingClientRect();
@ -114,12 +103,11 @@ function isInViewport(element) {
}
/**
* @description Checks if element element is removable
* @description Check if element element is removable
* @param {Element} element
* @param {boolean?} skipMatch
* @returns {boolean}
*/
function match(element, skipMatch) {
if (!(element instanceof HTMLElement) || !element.tagName) {
return false;
@ -152,10 +140,9 @@ function match(element, skipMatch) {
}
/**
* @description Fixes data, consent page and scroll issues
* @description Fix data, consent page and scroll issues
* @returns {void}
*/
function fix() {
const backdrop = document.getElementsByClassName('modal-backdrop')[0];
const fixes = data?.fixes ?? [];
@ -200,10 +187,9 @@ function fix() {
}
/**
* @description Calculates reading time for the current page to avoid lags in large pages
* @description Calculate reading time for the current page to avoid lags in large pages
* @returns {number}
*/
function readingTime() {
const text = document.body.innerText;
const wpm = 225;
@ -215,10 +201,9 @@ function readingTime() {
/**
* @async
* @description Sets up everything
* @description Set up everything
* @param {boolean} skipReadyStateHack
*/
async function runSetup(skipReadyStateHack) {
state = (await dispatch({ hostname, type: 'GET_HOSTNAME_STATE' })) ?? state;
dispatch({ type: 'ENABLE_POPUP' });
@ -240,7 +225,6 @@ async function runSetup(skipReadyStateHack) {
* @description Mutation Observer instance
* @type {MutationObserver}
*/
const observer = new MutationObserver((mutations) => {
const elements = mutations.map((mutation) => Array.from(mutation.addedNodes)).flat();
@ -250,24 +234,22 @@ const observer = new MutationObserver((mutations) => {
/**
* @async
* @description Runs setup if the page wasn't focused yet
* @description Run setup if the page wasn't focused yet
* @listens window#focus
* @returns {void}
*/
window.addEventListener('focus', async () => {
if (!data) {
if (document.body && !data) {
await runSetup(true);
clean([...document.body.children]);
}
});
/**
* @description Fixes still existing elements when page fully load
* @description Fix still existing elements when page fully load
* @listens window#load
* @returns {void}
*/
window.addEventListener('load', () => {
if (document.hasFocus()) {
window.dispatchEvent(new Event('run'));
@ -275,11 +257,10 @@ window.addEventListener('load', () => {
});
/**
* @description Fixes bfcache issues
* @description Fix bfcache issues
* @listens window#pageshow
* @returns {void}
*/
window.addEventListener('pageshow', (event) => {
if (document.hasFocus() && event.persisted) {
window.dispatchEvent(new Event('run'));
@ -287,11 +268,10 @@ window.addEventListener('pageshow', (event) => {
});
/**
* @description Forces a clean when this event is fired
* @description Force clean when this event is fired
* @listens window#run
* @returns {void}
*/
window.addEventListener('run', () => {
if (data?.elements.length && document.body && state.enabled && !preview) {
if (readingTime() < 4) {
@ -303,10 +283,6 @@ window.addEventListener('run', () => {
}
});
/**
* @description As this extension do really expensive work, it only runs if the user is on the page
*/
if (document.hasFocus()) {
runSetup();
}

View File

@ -2,7 +2,6 @@
* @description Report reasons
* @type {string[]}
*/
const reasons = [
'Cannot click',
'Page contains visual glitches',
@ -15,13 +14,11 @@ const reasons = [
/**
* @description Report dialog ID
*/
const reportDialogId = 'report-dialog';
/**
* @description Report dialog outer HTML
*/
const reportDialogHtml = `
<dialog id="${reportDialogId}" tabindex="0">
<report-dialog-header>
@ -124,7 +121,6 @@ const reportDialogHtml = `
* @description Dialog close button click handler
* @param {MouseEvent} event
*/
const closeButtonClickHandler = (event) => {
const dialog = document.getElementById(reportDialogId);
@ -133,9 +129,8 @@ const closeButtonClickHandler = (event) => {
};
/**
* @description Hides report dialog
* @description Hide report dialog
*/
const hideReportDialog = () => {
document.getElementById(reportDialogId)?.remove();
};
@ -144,7 +139,6 @@ const hideReportDialog = () => {
* @description Dialog radio input click handler
* @param {MouseEvent} event
*/
const radioClickHandler = (event) => {
const dialog = document.getElementById(reportDialogId);
const radios = dialog.getElementsByTagName('report-dialog-radio');
@ -161,9 +155,8 @@ const radioClickHandler = (event) => {
};
/**
* @description Shows report dialog
* @description Show report dialog
*/
const showReportDialog = () => {
const parser = new DOMParser();
const result = parser.parseFromString(reportDialogHtml, 'text/html');
@ -194,7 +187,6 @@ const showReportDialog = () => {
* @description Dialog submit button click handler
* @param {MouseEvent} event
*/
const submitButtonClickHandler = (event) => {
const dialog = document.getElementById(reportDialogId);
const formView = dialog?.getElementsByTagName('report-dialog-form-view')[0];
@ -211,9 +203,8 @@ const submitButtonClickHandler = (event) => {
};
/**
* @description Listens to messages
* @description Listen to messages
*/
chrome.runtime.onMessage.addListener((message) => {
const isPage = window === window.top;

View File

@ -1,27 +1,23 @@
/**
* @description Shortcut to send messages to background script
*/
const dispatch = chrome.runtime.sendMessage;
/**
* @description Domain RegExp
*/
const domainRx = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/g;
/**
* @description Exclusion list, URLs where the user prefers to disable the extension
* @type {string[]}
*/
let exclusionList = [];
/**
* @description Renders exclusion items into exclusion list
* @description Render exclusion items into exclusion list
* @returns {void}
*/
function createList() {
const emptyItemElement = document.getElementById('exclusion-list-item-empty');
const exclusionListElement = document.getElementById('exclusion-list');
@ -57,7 +53,6 @@ function createList() {
* @description Add a new item to the exclusion list
* @returns {Promise<void>}
*/
async function handleAddClick() {
const exclusionValue = window.prompt(chrome.i18n.getMessage('options_addPrompt'));
@ -77,7 +72,6 @@ async function handleAddClick() {
* @description Clear all items from the exclusion list
* @returns {Promise<void>}
*/
async function handleClearClick() {
const filterInputElement = document.getElementById('filter-input');
@ -95,7 +89,6 @@ async function handleClearClick() {
* @async
* @description Setup handlers and items
*/
async function handleContentLoaded() {
exclusionList = await dispatch({ type: 'GET_EXCLUSION_LIST' });
createList();
@ -123,11 +116,10 @@ async function handleContentLoaded() {
/**
* @async
* @description Deletes the clicked element from the exclusion list
* @description Delete 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;
@ -141,10 +133,9 @@ async function handleDeleteClick(event) {
}
/**
* @description Exports a file with the current exclusion list
* @description Export a file with the current exclusion list
* @returns {void}
*/
function handleExportClick() {
const anchor = document.createElement('a');
const now = new Date();
@ -164,11 +155,10 @@ function handleExportClick() {
}
/**
* @description Processes a file and sends the updates
* @description Process a file and send the updates
* @param {InputEvent} event
* @returns {void}
*/
function handleFileChange(event) {
const file = event.currentTarget.files[0];
const filterInputElement = document.getElementById('filter-input');
@ -194,11 +184,10 @@ function handleFileChange(event) {
}
/**
* @description Applies filter to the exclusion list when the user presses ENTER key
* @description Apply 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();
@ -207,20 +196,18 @@ function handleFilterKeyDown(event) {
}
/**
* @description Shallow clicks an hidden input to open the file explorer
* @description Shallow click 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
* @description Apply translations to tags with i18n data attribute
* @returns {void}
*/
function translate() {
const nodes = document.querySelectorAll('[data-i18n], [data-i18n-placeholder]');
@ -239,11 +226,10 @@ function translate() {
}
/**
* @description Updates exclusion items in DOM
* @description Update 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');
@ -276,5 +262,4 @@ function updateList(filterValue) {
* @description Listen to document ready
* @listens document#DOMContentLoaded
*/
document.addEventListener('DOMContentLoaded', handleContentLoaded);

View File

@ -2,20 +2,17 @@
* @description Chrome Web Store link
* @type {string}
*/
const chromeUrl = 'https://chrome.google.com/webstore/detail/djcbfpkdhdkaflcigibkbpboflaplabg';
/**
* @description Shortcut to send messages to background script
*/
const dispatch = chrome.runtime.sendMessage;
/**
* @description Edge Add-ons link
* @type {string}
*/
const edgeUrl =
'https://microsoftedge.microsoft.com/addons/detail/hbogodfciblakeneadpcolhmfckmjcii';
@ -23,42 +20,36 @@ const edgeUrl =
* @description Firefox Add-ons link
* @type {string}
*/
const firefoxUrl = 'https://addons.mozilla.org/firefox/addon/cookie-dialog-monster';
/**
* @description Current hostname
* @type {string}
*/
let hostname = '?';
/**
* @description Is current browser an instance of Chromium?
* @type {boolean}
*/
const isChromium = navigator.userAgent.indexOf('Chrome') !== -1;
/**
* @description Is current browser an instance of Edge?
* @type {boolean}
*/
const isEdge = navigator.userAgent.indexOf('Edg') !== -1;
/**
* @description Is current browser an instance of Firefox?
* @type {boolean}
*/
const isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;
/**
* @description Extension state
* @type {{ enabled: boolean }}
*/
let state = { enabled: true };
/**
@ -66,7 +57,6 @@ let state = { enabled: true };
* @description Setup stars handlers and result message links
* @returns {Promise<void>}
*/
async function handleContentLoaded() {
const tab = await dispatch({ type: 'GET_TAB' });
@ -103,11 +93,10 @@ async function handleContentLoaded() {
/**
* @async
* @description Opens a new tab
* @description Open a new tab
* @param {MouseEvent} event
* @returns {Promise<void>}
*/
async function handleLinkRedirect(event) {
const { href } = event.currentTarget.dataset;
@ -118,11 +107,10 @@ async function handleLinkRedirect(event) {
/**
* @async
* @description Disables or enables extension on current page
* @description Disable or enable extension on current page
* @param {MouseEvent} event
* @returns {Promise<void>}
*/
async function handlePowerToggle(event) {
state = { enabled: !state.enabled };
dispatch({ hostname, state, type: 'SET_HOSTNAME_STATE' });
@ -133,19 +121,17 @@ async function handlePowerToggle(event) {
}
/**
* @description Opens options page
* @description Open options page
* @returns {void}
*/
function handleSettingsClick() {
chrome.runtime.openOptionsPage();
}
/**
* @description Applies translations to tags with i18n data attribute
* @description Apply translations to tags with i18n data attribute
* @returns {void}
*/
function translate() {
const nodes = document.querySelectorAll('[data-i18n], [data-i18n-placeholder]');
@ -167,5 +153,4 @@ function translate() {
* @description Listen to document ready
* @listens document#DOMContentLoaded
*/
document.addEventListener('DOMContentLoaded', handleContentLoaded);