feat(scripts): improve content and popup scripts

This commit is contained in:
wanhose 2021-04-08 23:50:24 +02:00
parent e4b1289d0f
commit 4108c4cb8a
2 changed files with 396 additions and 0 deletions

245
scripts/content.js Normal file
View File

@ -0,0 +1,245 @@
/**
* @var attempts
* @description Number of attempts
* @type {number}
*/
let attempts = 1;
/**
* @var enabled
* @description Is extension enabled?
* @type {boolean}
*/
let enabled = true;
/**
* @var selectors
* @description Array of selectors
* @type {string[]}
*/
let selectors = [];
/**
* @constant url
* @description Database link
* @type {string}
*/
const url = chrome.runtime.getURL("data/elements.txt");
/*const url =
"https://raw.githubusercontent.com/wanhose/do-not-consent/master/data/elements.txt";*/
/**
* @function commit
* @description Commits selector to cache
* @param {string} selector
*/
const commit = (selector) => {
chrome.storage.local.get(null, (cache) => {
const current = cache[document.location.hostname];
chrome.storage.local.set({
[document.location.hostname]: {
...current,
matches: [...new Set([...cache.matches, selector])],
},
});
});
};
/**
* @function fix
* @description Fix scroll issues
*/
const fix = () => {
const html = document.documentElement;
const body = document.body;
html.style.setProperty("overflow-y", "unset", "important");
body.style.setProperty("overflow-y", "unset", "important");
};
/**
* @function search
* @description Retrieves HTML element if selector exists
*
* @param {string} selector
* @returns {HTMLElement | null} An HTML element or null
*/
const search = (selector) => {
if (!selector.includes("[") && !selector.includes(">")) {
if (selector.startsWith(".")) {
return document.getElementsByClassName(selector.slice(1))[0];
}
if (selector.startsWith("#")) {
return document.getElementById(selector.slice(1));
}
} else {
return document.querySelector(selector);
}
return null;
};
/**
* @async
* @function check
* @description Checks if extension is enabled
* @returns {Promise<boolean>}
*/
const check = () =>
new Promise((resolve) => {
chrome.storage.local.get(null, (store) => {
try {
const cache = store[document.location.hostname];
resolve(cache.enabled);
} catch {
chrome.storage.local.set(
{
[document.location.hostname]: {
enabled: true,
matches: [],
},
},
() => resolve(true)
);
}
});
});
/**
* @function removeFromCache
* @description Removes matched elements from cache results
*/
const removeFromCache = () => {
chrome.storage.local.get(null, (store) => {
const cache = store[document.location.hostname];
const matches = cache.matches;
if (!!matches.length) {
for (let i = matches.length; i--; ) {
const selector = selectors[i];
const element = search(selector);
if (element) {
const tagName = element.tagName.toUpperCase();
if (!["BODY", "HTML"].includes(tagName)) {
element.remove();
}
}
}
}
});
};
/**
* @function removeFromNetwork
* @description Removes matched elements from network results
*/
const removeFromNetwork = () => {
for (let i = selectors.length; i--; ) {
const selector = selectors[i];
const element = search(selector);
if (element) {
const tagName = element.tagName.toUpperCase();
if (!["BODY", "HTML"].includes(tagName)) {
element.remove();
commit(selector);
}
}
}
};
/**
* @async
* @function query
* @description Retrieves selectors list
*
* @returns {Promise<string[]>} A selectors list
*/
const query = async () => {
try {
const response = await fetch(url);
const data = await response.text();
if (response.status !== 200) throw new Error();
return data.split("\n");
} catch {
return [];
}
};
/**
* @constant observer
* @description Observer instance
* @type {MutationObserver}
*/
const observer = new MutationObserver((_, instance) => {
instance.disconnect();
fix();
removeFromCache();
if (attempts <= 5) removeFromNetwork();
attempts += 1;
observe();
});
/**
* @function observe
* @description Starts observing document.body element
*/
const observe = () => {
observer.observe(document.body, {
attributes: true,
childList: true,
});
};
/**
* @async
* @function handleContentLoaded
* @description Cleans, fixes scroll issues and observes document.body element
*/
const handleContentLoaded = async () => {
chrome.runtime.sendMessage({ type: "ENABLE_POPUP" });
enabled = await check();
if (enabled) {
chrome.runtime.sendMessage({ type: "ENABLE_ICON" });
selectors = await query();
if (selectors.length > 0) {
fix();
removeFromCache();
removeFromNetwork();
observe();
}
}
};
/**
* @description Listen to document ready
*
* @type {Document}
* @listens document#ready
*/
document.addEventListener("DOMContentLoaded", handleContentLoaded);

151
scripts/popup.js Normal file
View File

@ -0,0 +1,151 @@
/**
* @constant chromeUrl
* @description Chrome Web Store link
* @type {string}
*/
const chromeUrl =
"https://chrome.google.com/webstore/detail/do-not-consent/djcbfpkdhdkaflcigibkbpboflaplabg";
/**
* @constant firefoxUrl
* @description Firefox Add-ons link
* @type {string}
*/
const firefoxUrl =
"https://addons.mozilla.org/es/firefox/addon/do-not-consent/";
/**
* @constant isChromium
* @description Is current browser an instance of Chromium?
* @type {boolean}
*/
const isChromium = chrome.runtime.getURL("").startsWith("chrome-extension://");
/**
* @async
* @function currentTab
* @description Returns current tab state
*
* @returns {Promise<{ id: string, location: URL }>}
*/
const currentTab = () =>
new Promise((resolve) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
resolve({
id: tabs[0].id,
location: new URL(tabs[0].url),
});
});
});
/**
* @async
* @function currentState
* @description Returns current extension state
*
* @returns {Promise<Object<string, { enabled: boolean, matches: string[] }>>}>}
*/
const currentState = async () => {
const tab = await currentTab();
return new Promise((resolve) => {
chrome.storage.local.get(null, (store) => {
resolve(store[tab.location.hostname]);
});
});
};
/**
* @function handleButtonClick
* @description Disables or enables extension
*
* @param {MouseEvent} event
*/
const handleStateButtonClick = async () => {
const state = await currentState();
const tab = await currentTab();
chrome.storage.local.set(
{
[tab.location.hostname]: {
...state,
enabled: !state.enabled,
},
},
() => {
const stateButton = document.getElementById("state-button");
stateButton.innerHTML = state.enabled
? "Enable extension"
: "Disable extension";
chrome.runtime.sendMessage({
type: state.enabled ? "DISABLE_ICON" : "ENABLE:ICON",
});
chrome.tabs.reload(tab.id, { bypassCache: true });
}
);
};
/**
* @function handleStarClick
* @description Hides stars and shows negative or positive messages
*
* @param {MouseEvent} event
*/
const handleStarClick = (event) => {
const negative = document.getElementById("negative");
const positive = document.getElementById("positive");
const { score } = event.currentTarget.dataset;
const stars = document.getElementById("stars");
switch (score) {
case "1":
case "2":
case "3":
stars.setAttribute("hidden", "true");
negative.removeAttribute("hidden");
break;
case "4":
case "5":
stars.setAttribute("hidden", "true");
positive.removeAttribute("hidden");
break;
default:
break;
}
};
/**
* @function handleContentLoaded
* @description Setup stars handlers and result message links
*/
const handleContentLoaded = async () => {
const stars = Array.from(document.getElementsByClassName("star"));
const state = await currentState();
const stateButton = document.getElementById("state-button");
const storeLink = document.getElementById("store-link");
stars.forEach((star) => star.addEventListener("click", handleStarClick));
stateButton.innerHTML = state.enabled
? "Disable extension"
: "Enable extension";
stateButton.addEventListener("click", handleStateButtonClick);
storeLink.setAttribute("href", isChromium ? chromeUrl : firefoxUrl);
};
/**
* @description Listen to document ready
*
* @type {Document}
* @listens document#ready
*/
document.addEventListener("DOMContentLoaded", handleContentLoaded);