From 61712e77a2875d7ecdf6773a3d99b2166cbdde84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20R=C3=B6bert?= Date: Sat, 9 Nov 2019 01:40:17 +0100 Subject: [PATCH] Version 1.9.2 #290 --- .gitlab-ci.yml | 5 + CHANGELOG.md | 15 + build_tools/minifyDataJSON.js | 87 +++ clearurls.js | 1138 ++++++++++++++++----------------- core_js/storage.js | 176 +++-- core_js/tools.js | 266 ++++---- data/data.min.json | 2 +- manifest.json | 2 +- 8 files changed, 874 insertions(+), 817 deletions(-) create mode 100644 build_tools/minifyDataJSON.js diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47419c6..f22bf71 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,6 +4,7 @@ before_script: - export DEBIAN_FRONTEND= noninteractive - apt-get update -y - apt-get install -y zip unzip jq + - apt-get install nodejs stages: - build @@ -13,9 +14,13 @@ hash rules: stage: build script: - sha256sum data/data.min.json | awk '{print $1}' > rules.min.hash + - node build_tools/minifyDataJSON.js "..\data\data.min.json" "..\data\data.minify.json" + - sha256sum data/data.minify.json | awk '{print $1}' > rules.minify.hash artifacts: paths: - rules.min.hash + - data.minify.json + - rules.minify.hash build firefox: stage: build diff --git a/CHANGELOG.md b/CHANGELOG.md index 448de33..8c8919d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.9.2] - 2019-11-09 + +### Compatibility note +- Require Firefox >= 55 +- Require Chrome >= 22 + +### Fixed +- Fixed [#290](https://gitlab.com/KevinRoebert/ClearUrls/issues/290) + +## Changed +- Updated some strings of Italian translation by [@gioxx](https://gitlab.com/gioxx) + +### Added +- Added a minimal version of the data.min.json file where all line breaks and spaces, as well as default values and empty lists are removed. + ## [1.9.1] - 2019-10-24 ### Compatibility note diff --git a/build_tools/minifyDataJSON.js b/build_tools/minifyDataJSON.js new file mode 100644 index 0000000..fd30972 --- /dev/null +++ b/build_tools/minifyDataJSON.js @@ -0,0 +1,87 @@ +/* +* ClearURLs +* Copyright (c) 2017-2019 Kevin Röbert +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*/ + +/*jshint esversion: 6 */ +/* +* This script is responsible for minification of the data.min.json file and deletes also empty entries. +*/ +let fs = require('fs'); +const inFileLocation = process.argv.slice(2)[0]; +const outFileLocation = process.argv.slice(2)[1]; + +if(inFileLocation === undefined || outFileLocation === undefined) { + throw "in- and output must be set!"; +} + +const fileContent = fs.readFileSync(inFileLocation).toString(); + +/** + * Builds a minify version of the data.min.json file. + */ +function build() { + const data = JSON.parse(fileContent); + let minifiedData = {"providers":{}}; + + for(let provider in data.providers) { + minifiedData.providers[provider] = {}; + let self = minifiedData.providers[provider]; + + if(data.providers[provider].completeProvider === true) { + self.completeProvider = true; + } + + if(data.providers[provider].forceRedirection === true) { + self.forceRedirection = true; + } + + if(data.providers[provider].urlPattern !== "") { + self.urlPattern = data.providers[provider].urlPattern; + } + + if(data.providers[provider].rules.length !== 0) { + self.rules = data.providers[provider].rules; + } + + if(data.providers[provider].rawRules.length !== 0) { + self.rawRules = data.providers[provider].rawRules; + } + + if(data.providers[provider].referralMarketing.length !== 0) { + self.referralMarketing = data.providers[provider].referralMarketing; + } + + if(data.providers[provider].exceptions.length !== 0) { + self.exceptions = data.providers[provider].exceptions; + } + + if(data.providers[provider].redirections.length !== 0) { + self.redirections = data.providers[provider].redirections; + } + } + + fs.writeFile(outFileLocation, JSON.stringify(minifiedData), function(err) { + + if(err) { + return console.log(err); + } + + console.log("The file was saved!"); + }); +} + +build(); \ No newline at end of file diff --git a/clearurls.js b/clearurls.js index 3b8c26f..95476ea 100644 --- a/clearurls.js +++ b/clearurls.js @@ -39,8 +39,7 @@ var currentURL; * @param {boolean} quiet if the action should be displayed in log and statistics * @return {Array} Array with changes and url fields */ -function removeFieldsFormURL(provider, pureUrl, quiet = false) -{ +function removeFieldsFormURL(provider, pureUrl, quiet = false) { let url = pureUrl; let domain = ""; let fragments = ""; @@ -50,7 +49,7 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) let cancel = false; let rawRules = provider.getRawRules(); - if(storage.localHostsSkipping && checkLocalURL(pureUrl)) { + if (storage.localHostsSkipping && checkLocalURL(pureUrl)) { return { "changes": false, "url": url, @@ -61,28 +60,25 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) /* * Apply raw rules to the URL. */ - rawRules.forEach(function(rawRule) { + rawRules.forEach(function (rawRule) { let beforeReplace = url; url = url.replace(new RegExp(rawRule, "gi"), ""); - if(beforeReplace !== url) { + if (beforeReplace !== url) { //Log the action - if(storage.loggingStatus && !quiet) - { + if (storage.loggingStatus && !quiet) { pushToLog(beforeReplace, url, rawRule); } - if(badges[tabid] == null) badges[tabid] = 0; + if (badges[tabid] == null) badges[tabid] = 0; - if(!quiet) increaseURLCounter(); + if (!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { - if(!res) { - if(storage.badgedStatus && !quiet) { + if (!res) { + if (storage.badgedStatus && !quiet) { browser.browserAction.setBadgeText({text: (++badges[tabid]).toString(), tabId: tabid}); - } - else - { + } else { browser.browserAction.setBadgeText({text: "", tabId: tabid}); } } @@ -92,10 +88,10 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) } }); - if(existsFragments(url)) { + if (existsFragments(url)) { domain = url.replace(new RegExp("#.*", "i"), ""); } - if(existsFields(url)) { + if (existsFields(url)) { domain = url.replace(new RegExp("\\?.*", "i"), ""); } @@ -104,12 +100,11 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) * url redirections form sites to sites. */ let re = provider.getRedirection(url); - if(re !== null) - { + if (re !== null) { url = decodeURL(re); //Log the action - if(!quiet) pushToLog(pureUrl, url, translate('log_redirect')); + if (!quiet) pushToLog(pureUrl, url, translate('log_redirect')); return { "redirect": true, @@ -117,52 +112,47 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) }; } - if(existsFields(url)) { - fields = "?"+extractFileds(url).rmEmpty().join("&"); + if (existsFields(url)) { + fields = "?" + extractFileds(url).rmEmpty().join("&"); } - if(existsFragments(url)) { - fragments = "#"+extractFragments(url).rmEmpty().join("&"); + if (existsFragments(url)) { + fragments = "#" + extractFragments(url).rmEmpty().join("&"); } /** - * Only test for matches, if there are fields or fragments that can be cleaned. - */ - if(fields !== "" || fragments !== "") - { - rules.forEach(function(rule) { + * Only test for matches, if there are fields or fragments that can be cleaned. + */ + if (fields !== "" || fragments !== "") { + rules.forEach(function (rule) { let beforeReplace = fields; let beforeReplaceFragments = fragments; fields = fields.replace(new RegExp(rule, "gi"), ""); fragments = fragments.replace(new RegExp(rule, "gi"), ""); - if(beforeReplace !== fields || beforeReplaceFragments !== fragments) - { + if (beforeReplace !== fields || beforeReplaceFragments !== fragments) { //Log the action - if(storage.loggingStatus) - { + if (storage.loggingStatus) { let tempURL = domain; let tempBeforeURL = domain; - if(fields !== "") tempURL += "?"+fields.replace("?&", "?").replace("?", ""); - if(fragments !== "") tempURL += "#"+fragments.replace("#&","#").replace("#", ""); - if(beforeReplace !== "") tempBeforeURL += "?"+beforeReplace.replace("?&", "?").replace("?", ""); - if(beforeReplaceFragments !== "") tempBeforeURL += "#"+beforeReplaceFragments.replace("#&","#").replace("#", ""); + if (fields !== "") tempURL += "?" + fields.replace("?&", "?").replace("?", ""); + if (fragments !== "") tempURL += "#" + fragments.replace("#&", "#").replace("#", ""); + if (beforeReplace !== "") tempBeforeURL += "?" + beforeReplace.replace("?&", "?").replace("?", ""); + if (beforeReplaceFragments !== "") tempBeforeURL += "#" + beforeReplaceFragments.replace("#&", "#").replace("#", ""); - if(!quiet) pushToLog(tempBeforeURL, tempURL, rule); + if (!quiet) pushToLog(tempBeforeURL, tempURL, rule); } - if(badges[tabid] == null) badges[tabid] = 0; + if (badges[tabid] == null) badges[tabid] = 0; - if(!quiet) increaseURLCounter(); + if (!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { - if(!res) { - if(storage.badgedStatus && !quiet) { + if (!res) { + if (storage.badgedStatus && !quiet) { browser.browserAction.setBadgeText({text: (++badges[tabid]).toString(), tabId: tabid}); - } - else - { + } else { browser.browserAction.setBadgeText({text: "", tabId: tabid}); } } @@ -174,28 +164,25 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) let finalURL = domain; - if(fields !== "") finalURL += "?"+fields.replace("?", ""); - if(fragments !== "") finalURL += "#"+fragments.replace("#", ""); + if (fields !== "") finalURL += "?" + fields.replace("?", ""); + if (fragments !== "") finalURL += "#" + fragments.replace("#", ""); url = finalURL.replace(new RegExp("\\?&"), "?").replace(new RegExp("#&"), "#"); } - if(provider.isCaneling()){ - if(!quiet) pushToLog(pureUrl, pureUrl, translate('log_domain_blocked')); - if(badges[tabid] == null) - { + if (provider.isCaneling()) { + if (!quiet) pushToLog(pureUrl, pureUrl, translate('log_domain_blocked')); + if (badges[tabid] == null) { badges[tabid] = 0; } - if(!quiet) increaseURLCounter(); + if (!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { - if(!res) { - if(storage.badgedStatus && !quiet) { + if (!res) { + if (storage.badgedStatus && !quiet) { browser.browserAction.setBadgeText({text: (++badges[tabid]).toString(), tabId: tabid}); - } - else - { + } else { browser.browserAction.setBadgeText({text: "", tabId: tabid}); } } @@ -211,90 +198,86 @@ function removeFieldsFormURL(provider, pureUrl, quiet = false) }; } -function start() -{ +function start() { /** - * Initialize the JSON provider object keys. - * - * @param {JSON Object} obj - */ - function getKeys(obj){ - for(const key in obj){ + * Initialize the JSON provider object keys. + * + * @param {object} obj + */ + function getKeys(obj) { + for (const key in obj) { prvKeys.push(key); } } /** - * Initialize the providers form the JSON object. - * - */ - function createProviders() - { + * Initialize the providers form the JSON object. + * + */ + function createProviders() { let data = storage.ClearURLsData; - for(let p = 0; p < prvKeys.length; p++) - { + for (let p = 0; p < prvKeys.length; p++) { //Create new provider - providers.push(new Provider(prvKeys[p], data.providers[prvKeys[p]].completeProvider, - data.providers[prvKeys[p]].forceRedirection)); + providers.push(new Provider(prvKeys[p], data.providers[prvKeys[p]].getOrDefault('completeProvider', false), + data.providers[prvKeys[p]].getOrDefault('forceRedirection', false))); - //Add URL Pattern - providers[p].setURLPattern(data.providers[prvKeys[p]].urlPattern); + //Add URL Pattern + providers[p].setURLPattern(data.providers[prvKeys[p]].getOrDefault('urlPattern', '')); - //Add rules to provider - for(let r = 0; r < data.providers[prvKeys[p]].rules.length; r++) - { - providers[p].addRule(data.providers[prvKeys[p]].rules[r]); - } + let rules = data.providers[prvKeys[p]].getOrDefault('rules', []); + //Add rules to provider + for (let r = 0; r < rules.length; r++) { + providers[p].addRule(rules[r]); + } - //Add raw rules to provider - for(let raw = 0; raw < data.providers[prvKeys[p]].rawRules.length; raw++) - { - providers[p].addRawRule(data.providers[prvKeys[p]].rawRules[raw]); - } + let rawRules = data.providers[prvKeys[p]].getOrDefault('rawRules', []); + //Add raw rules to provider + for (let raw = 0; raw < rawRules.length; raw++) { + providers[p].addRawRule(rawRules[raw]); + } - //Add referral marketing rules to provider - for(let referralMarketing = 0; referralMarketing < data.providers[prvKeys[p]].referralMarketing.length; referralMarketing++) - { - providers[p].addReferralMarketing(data.providers[prvKeys[p]].referralMarketing[referralMarketing]); - } + let referralMarketingRules = data.providers[prvKeys[p]].getOrDefault('referralMarketing', []); + //Add referral marketing rules to provider + for (let referralMarketing = 0; referralMarketing < referralMarketingRules.length; referralMarketing++) { + providers[p].addReferralMarketing(referralMarketingRules[referralMarketing]); + } - //Add exceptions to provider - for(let e = 0; e < data.providers[prvKeys[p]].exceptions.length; e++) - { - providers[p].addException(data.providers[prvKeys[p]].exceptions[e]); - } + let exceptions = data.providers[prvKeys[p]].getOrDefault('exceptions', []); + //Add exceptions to provider + for (let e = 0; e < exceptions.length; e++) { + providers[p].addException(exceptions[e]); + } - //Add redirections to provider - for(let re = 0; re < data.providers[prvKeys[p]].redirections.length; re++) - { - providers[p].addRedirection(data.providers[prvKeys[p]].redirections[re]); - } + let redirections = data.providers[prvKeys[p]].getOrDefault('redirections', []); + //Add redirections to provider + for (let re = 0; re < redirections.length; re++) { + providers[p].addRedirection(redirections[re]); } } + } - /** - * Convert the external data to Objects and - * call the create provider function. - * - * @param {String} retrievedText - pure data form github - */ - function toObject(retrievedText) { - getKeys(storage.ClearURLsData.providers); - createProviders(); - } + /** + * Convert the external data to Objects and + * call the create provider function. + * + * @param {String} retrievedText - pure data form github + */ + function toObject(retrievedText) { + getKeys(storage.ClearURLsData.providers); + createProviders(); + } - /** - * Get the hash for the rule file on github. - * Check the hash with the hash form the local file. - * If the hash has changed, then download the new rule file. - * Else do nothing. - */ - function getHash() - { - //Get the target hash from github - fetch(storage.hashURL) - .then(function(response){ + /** + * Get the hash for the rule file on github. + * Check the hash with the hash form the local file. + * If the hash has changed, then download the new rule file. + * Else do nothing. + */ + function getHash() { + //Get the target hash from github + fetch(storage.hashURL) + .then(function (response) { const responseTextHash = response.clone().text().then(function (responseTextHash) { if (response.ok && $.trim(responseTextHash)) { dataHash = responseTextHash; @@ -311,491 +294,470 @@ function start() } }); }); - } + } - /* - * ################################################################## - * # Fetch Rules & Exception from URL # - * ################################################################## - */ - function fetchFromURL() - { - fetch(storage.ruleURL) + /* + * ################################################################## + * # Fetch Rules & Exception from URL # + * ################################################################## + */ + function fetchFromURL() { + fetch(storage.ruleURL) .then(checkResponse); - function checkResponse(response) - { - const responseText = response.clone().text().then(function (responseText) { - if (response.ok && $.trim(responseText)) { - const downloadedFileHash = $.sha256(responseText); + function checkResponse(response) { + const responseText = response.clone().text().then(function (responseText) { + if (response.ok && $.trim(responseText)) { + const downloadedFileHash = $.sha256(responseText); - if ($.trim(downloadedFileHash) === $.trim(dataHash)) { - storage.ClearURLsData = responseText; - storage.dataHash = downloadedFileHash; - storeHashStatus(2); - } else { - storeHashStatus(3); - } - storage.ClearURLsData = JSON.parse(storage.ClearURLsData); - toObject(storage.ClearURLsData); - saveOnDisk(['ClearURLsData', 'dataHash', 'hashStatus']); - } - }); - } - } - - // ################################################################## - - /* - * ################################################################## - * # Supertyp Provider # - * ################################################################## - */ - /** - * Declare constructor - * - * @param {String} _name Provider name - * @param {boolean} _completeProvider Set URL Pattern as rule - * @param {boolean} _forceRedirection Whether redirects should be enforced via a "tabs.update" - * @param {boolean} _isActive Is the provider active? - */ - function Provider(_name, _completeProvider = false, _forceRedirection = false, _isActive = true){ - let name = _name; - let urlPattern; - let enabled_rules = {}; - let disabled_rules = {}; - let enabled_exceptions = {}; - let disabled_exceptions = {}; - let canceling = _completeProvider; - let enabled_redirections = {}; - let disabled_redirections = {}; - let active = _isActive; - let enabled_rawRules = {}; - let disabled_rawRules = {}; - let enabled_referralMarketing = {}; - let disabled_referralMarketing = {}; - - if(_completeProvider){ - enabled_rules[".*"] = true; - } - - /** - * Returns whether redirects should be enforced via a "tabs.update" - * @return {boolean} whether redirects should be enforced - */ - this.shouldForceRedirect = function() { - return _forceRedirection; - }; - - /** - * Returns the provider name. - * @return {String} - */ - this.getName = function() { - return name; - }; - - /** - * Add URL pattern. - * - * @require urlPatterns as RegExp - */ - this.setURLPattern = function(urlPatterns) { - urlPattern = new RegExp(urlPatterns, "i"); - }; - - /** - * Return if the Provider Request is canceled - * @return {Boolean} isCanceled - */ - this.isCaneling = function() { - return canceling; - }; - - /** - * Check the url is matching the ProviderURL. - * - * @return {boolean} ProviderURL as RegExp - */ - this.matchURL = function(url) { - return urlPattern.test(url) && !(this.matchException(url)); - }; - - /** - * Apply a rule to a given tuple of rule array. - * @param enabledRuleArray array for enabled rules - * @param disabledRulesArray array for disabled rules - * @param {String} rule RegExp as string - * @param {boolean} isActive Is this rule active? - */ - this.applyRule = (enabledRuleArray, disabledRulesArray, rule, isActive = true) => { - if(isActive) - { - enabledRuleArray[rule] = true; - - if(disabledRulesArray[rule] !== undefined) - { - delete disabledRulesArray[rule]; + if ($.trim(downloadedFileHash) === $.trim(dataHash)) { + storage.ClearURLsData = responseText; + storage.dataHash = downloadedFileHash; + storeHashStatus(2); + } else { + storeHashStatus(3); } + storage.ClearURLsData = JSON.parse(storage.ClearURLsData); + toObject(storage.ClearURLsData); + saveOnDisk(['ClearURLsData', 'dataHash', 'hashStatus']); } - else { - disabledRulesArray[rule] = true; - - if(enabledRuleArray[rule] !== undefined) - { - delete enabledRuleArray[rule]; - } - } - }; - - /** - * Add a rule to the rule array - * and replace old rule with new rule. - * - * @param {String} rule RegExp as string - * @param {boolean} isActive Is this rule active? - */ - this.addRule = function(rule, isActive = true) { - rule = "([\\/|\\?|#]|(&|&))+("+rule+"=[^\\/|\\?|&]*)"; - - this.applyRule(enabled_rules, disabled_rules, rule, isActive); - }; - - /** - * Return all active rules as an array. - * - * @return Array RegExp strings - */ - this.getRules = function() { - return Object.keys(enabled_rules); - }; - - /** - * Add a raw rule to the raw rule array - * and replace old raw rule with new raw rule. - * - * @param {String} rule RegExp as string - * @param {boolean} isActive Is this rule active? - */ - this.addRawRule = function(rule, isActive = true) { - this.applyRule(enabled_rawRules, disabled_rawRules, rule, isActive); - }; - - /** - * Return all active raw rules as an array. - * - * @return Array RegExp strings - */ - this.getRawRules = function() { - if(!storage.referralMarketing) { - return Object.keys(Object.assign(enabled_rawRules, enabled_referralMarketing)); - } - - return Object.keys(enabled_rawRules); - }; - - /** - * Add a referral marketing rule to the referral marketing array - * and replace old referral marketing rule with new referral marketing rule. - * - * @param {String} rule RegExp as string - * @param {boolean} isActive Is this rule active? - */ - this.addReferralMarketing = function(rule, isActive = true) { - this.applyRule(enabled_referralMarketing, disabled_referralMarketing, rule, isActive); - }; - - /** - * Add a exception to the exceptions array - * and replace old with new exception. - * - * @param {String} exception RegExp as string - * @param {Boolean} isActive Is this exception acitve? - */ - this.addException = function(exception, isActive = true) { - if(isActive) - { - enabled_exceptions[exception] = true; - - if(disabled_exceptions[exception] !== undefined) - { - delete disabled_exceptions[exception]; - } - } - else { - disabled_exceptions[exception] = true; - - if(enabled_exceptions[exception] !== undefined) - { - delete enabled_exceptions[exception]; - } - } - }; - - /** - * Private helper method to check if the url - * an exception. - * - * @param {String} url RegExp as string - * @return {boolean} if matching? true: false - */ - this.matchException = function(url) { - let result = false; - - //Add the site blocked alert to every exception - if(url === siteBlockedAlert) return true; - - for(const exception in enabled_exceptions) { - if(result) break; - - let exception_regex = new RegExp(exception, "i"); - result = exception_regex.test(url); - } - - return result; - }; - - /** - * Add a redirection to the redirections array - * and replace old with new redirection. - * - * @param {String} redirection RegExp as string - * @param {Boolean} isActive Is this redirection active? - */ - this.addRedirection = function(redirection, isActive = true) { - if(isActive) - { - enabled_redirections[redirection] = true; - - if(disabled_redirections[redirection] !== undefined) - { - delete disabled_redirections[redirection]; - } - } - else { - disabled_redirections[redirection] = true; - - if(enabled_redirections[redirection] !== undefined) - { - delete enabled_redirections[redirection]; - } - } - }; - - /** - * Return all redirection. - * - * @return url - */ - this.getRedirection = function(url) { - let re = null; - - for(const redirection in enabled_redirections) { - let result = (url.match(new RegExp(redirection, "i"))); - - if (result && result.length > 0 && redirection) - { - re = (new RegExp(redirection, "i")).exec(url)[1]; - - break; - } - } - - return re; - }; - } - // ################################################################## - - /** - * Function which called from the webRequest to - * remove the tracking fields from the url. - * - * @param {webRequest} request webRequest-Object - * @return {Array} redirectUrl or none - */ - function clearUrl(request) - { - const URLbeforeReplaceCount = countFields(request.url); - - //Add Fields form Request to global url counter - increaseGlobalURLCounter(URLbeforeReplaceCount); - - if(storage.globalStatus){ - let result = { - "changes": false, - "url": "", - "redirect": false, - "cancel": false - }; - - /* - * Call for every provider the removeFieldsFormURL method. - */ - for (let i = 0; i < providers.length; i++) { - - if(providers[i].matchURL(request.url)) - { - result = removeFieldsFormURL(providers[i], request.url); - } - - /* - * Expand urls and bypass tracking. - * Cancel the active request. - */ - if(result.redirect) - { - if(providers[i].shouldForceRedirect() && - request.type === 'main_frame') { - browser.tabs.update(request.tabId, {url: result.url}); - return {cancel: true}; - } - - return { - redirectUrl: result.url - }; - } - - /* - * Cancel the Request and redirect to the site blocked alert page, - * to inform the user about the full url blocking. - */ - if(result.cancel){ - if(request.type === 'main_frame') { - const blockingPage = browser.extension.getURL("html/siteBlockedAlert.html?source="+encodeURIComponent(request.url)); - browser.tabs.update(request.tabId, {url: blockingPage}); - - return {cancel: true}; - } else { - return { - redirectUrl: siteBlockedAlert - }; - } - } - - /* - * Ensure that the function go not into - * a loop. - */ - if(result.changes){ - return { - redirectUrl: result.url - }; - } - } - } - - // Default case - return {}; - } - - /** - * Call loadOldDataFromStore, getHash, counter, status and log functions - */ - - loadOldDataFromStore(); - getHash(); - setBadgedStatus(); - - /** - * Call by each tab is updated. - * And if url has changed. - */ - function handleUpdated(tabId, changeInfo, tabInfo) { - if(changeInfo.url) - { - delete badges[tabId]; - } - currentURL = tabInfo.url; - } - - /** - * Call by each tab is updated. - */ - browser.tabs.onUpdated.addListener(handleUpdated); - - /** - * Call by each tab change to set the actual tab id - */ - function handleActivated(activeInfo) { - tabid = activeInfo.tabId; - browser.tabs.get(tabid).then(function (tab) { - currentURL = tab.url; }); } + } - /** - * Call by each tab change. - */ - browser.tabs.onActivated.addListener(handleActivated); + // ################################################################## - /** - * Check the request. - */ - function promise(requestDetails) - { - if(isDataURL(requestDetails)) - { - return {}; - } - else { - return clearUrl(requestDetails); - } + /* + * ################################################################## + * # Supertyp Provider # + * ################################################################## + */ + /** + * Declare constructor + * + * @param {String} _name Provider name + * @param {boolean} _completeProvider Set URL Pattern as rule + * @param {boolean} _forceRedirection Whether redirects should be enforced via a "tabs.update" + * @param {boolean} _isActive Is the provider active? + */ + function Provider(_name, _completeProvider = false, _forceRedirection = false, _isActive = true) { + let name = _name; + let urlPattern; + let enabled_rules = {}; + let disabled_rules = {}; + let enabled_exceptions = {}; + let disabled_exceptions = {}; + let canceling = _completeProvider; + let enabled_redirections = {}; + let disabled_redirections = {}; + let active = _isActive; + let enabled_rawRules = {}; + let disabled_rawRules = {}; + let enabled_referralMarketing = {}; + let disabled_referralMarketing = {}; + + if (_completeProvider) { + enabled_rules[".*"] = true; } /** - * To prevent long loading on data urls - * we will check here for data urls. - * - * @type {requestDetails} - * @return {boolean} - */ - function isDataURL(requestDetails) { - const s = requestDetails.url; - - return s.substring(0,4) === "data"; - } + * Returns whether redirects should be enforced via a "tabs.update" + * @return {boolean} whether redirects should be enforced + */ + this.shouldForceRedirect = function () { + return _forceRedirection; + }; /** - * Call by each Request and checking the url. - * - * @type {Array} - */ - browser.webRequest.onBeforeRequest.addListener( - promise, - {urls: [""], types: getData("types")}, - ["blocking"] - ); + * Returns the provider name. + * @return {String} + */ + this.getName = function () { + return name; + }; + + /** + * Add URL pattern. + * + * @require urlPatterns as RegExp + */ + this.setURLPattern = function (urlPatterns) { + urlPattern = new RegExp(urlPatterns, "i"); + }; + + /** + * Return if the Provider Request is canceled + * @return {Boolean} isCanceled + */ + this.isCaneling = function () { + return canceling; + }; + + /** + * Check the url is matching the ProviderURL. + * + * @return {boolean} ProviderURL as RegExp + */ + this.matchURL = function (url) { + return urlPattern.test(url) && !(this.matchException(url)); + }; + + /** + * Apply a rule to a given tuple of rule array. + * @param enabledRuleArray array for enabled rules + * @param disabledRulesArray array for disabled rules + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.applyRule = (enabledRuleArray, disabledRulesArray, rule, isActive = true) => { + if (isActive) { + enabledRuleArray[rule] = true; + + if (disabledRulesArray[rule] !== undefined) { + delete disabledRulesArray[rule]; + } + } else { + disabledRulesArray[rule] = true; + + if (enabledRuleArray[rule] !== undefined) { + delete enabledRuleArray[rule]; + } + } + }; + + /** + * Add a rule to the rule array + * and replace old rule with new rule. + * + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.addRule = function (rule, isActive = true) { + rule = "([\\/|\\?|#]|(&|&))+(" + rule + "=[^\\/|\\?|&]*)"; + + this.applyRule(enabled_rules, disabled_rules, rule, isActive); + }; + + /** + * Return all active rules as an array. + * + * @return Array RegExp strings + */ + this.getRules = function () { + if (!storage.referralMarketing) { + return Object.keys(Object.assign(enabled_rules, enabled_referralMarketing)); + } + + return Object.keys(enabled_rules); + }; + + /** + * Add a raw rule to the raw rule array + * and replace old raw rule with new raw rule. + * + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.addRawRule = function (rule, isActive = true) { + this.applyRule(enabled_rawRules, disabled_rawRules, rule, isActive); + }; + + /** + * Return all active raw rules as an array. + * + * @return Array RegExp strings + */ + this.getRawRules = function () { + return Object.keys(enabled_rawRules); + }; + + /** + * Add a referral marketing rule to the referral marketing array + * and replace old referral marketing rule with new referral marketing rule. + * + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.addReferralMarketing = function (rule, isActive = true) { + rule = "([\\/|\\?|#]|(&|&))+(" + rule + "=[^\\/|\\?|&]*)"; + + this.applyRule(enabled_referralMarketing, disabled_referralMarketing, rule, isActive); + }; + + /** + * Add a exception to the exceptions array + * and replace old with new exception. + * + * @param {String} exception RegExp as string + * @param {Boolean} isActive Is this exception acitve? + */ + this.addException = function (exception, isActive = true) { + if (isActive) { + enabled_exceptions[exception] = true; + + if (disabled_exceptions[exception] !== undefined) { + delete disabled_exceptions[exception]; + } + } else { + disabled_exceptions[exception] = true; + + if (enabled_exceptions[exception] !== undefined) { + delete enabled_exceptions[exception]; + } + } + }; + + /** + * Private helper method to check if the url + * an exception. + * + * @param {String} url RegExp as string + * @return {boolean} if matching? true: false + */ + this.matchException = function (url) { + let result = false; + + //Add the site blocked alert to every exception + if (url === siteBlockedAlert) return true; + + for (const exception in enabled_exceptions) { + if (result) break; + + let exception_regex = new RegExp(exception, "i"); + result = exception_regex.test(url); + } + + return result; + }; + + /** + * Add a redirection to the redirections array + * and replace old with new redirection. + * + * @param {String} redirection RegExp as string + * @param {Boolean} isActive Is this redirection active? + */ + this.addRedirection = function (redirection, isActive = true) { + if (isActive) { + enabled_redirections[redirection] = true; + + if (disabled_redirections[redirection] !== undefined) { + delete disabled_redirections[redirection]; + } + } else { + disabled_redirections[redirection] = true; + + if (enabled_redirections[redirection] !== undefined) { + delete enabled_redirections[redirection]; + } + } + }; + + /** + * Return all redirection. + * + * @return url + */ + this.getRedirection = function (url) { + let re = null; + + for (const redirection in enabled_redirections) { + let result = (url.match(new RegExp(redirection, "i"))); + + if (result && result.length > 0 && redirection) { + re = (new RegExp(redirection, "i")).exec(url)[1]; + + break; + } + } + + return re; + }; + } + + // ################################################################## + + /** + * Function which called from the webRequest to + * remove the tracking fields from the url. + * + * @param {webRequest} request webRequest-Object + * @return {Array} redirectUrl or none + */ + function clearUrl(request) { + const URLbeforeReplaceCount = countFields(request.url); + + //Add Fields form Request to global url counter + increaseGlobalURLCounter(URLbeforeReplaceCount); + + if (storage.globalStatus) { + let result = { + "changes": false, + "url": "", + "redirect": false, + "cancel": false + }; + + /* + * Call for every provider the removeFieldsFormURL method. + */ + for (let i = 0; i < providers.length; i++) { + + if (providers[i].matchURL(request.url)) { + result = removeFieldsFormURL(providers[i], request.url); + } + + /* + * Expand urls and bypass tracking. + * Cancel the active request. + */ + if (result.redirect) { + if (providers[i].shouldForceRedirect() && + request.type === 'main_frame') { + browser.tabs.update(request.tabId, {url: result.url}); + return {cancel: true}; + } + + return { + redirectUrl: result.url + }; + } + + /* + * Cancel the Request and redirect to the site blocked alert page, + * to inform the user about the full url blocking. + */ + if (result.cancel) { + if (request.type === 'main_frame') { + const blockingPage = browser.extension.getURL("html/siteBlockedAlert.html?source=" + encodeURIComponent(request.url)); + browser.tabs.update(request.tabId, {url: blockingPage}); + + return {cancel: true}; + } else { + return { + redirectUrl: siteBlockedAlert + }; + } + } + + /* + * Ensure that the function go not into + * a loop. + */ + if (result.changes) { + return { + redirectUrl: result.url + }; + } + } + } + + // Default case + return {}; } /** - * Function to log all activities from ClearUrls. - * Only logging when activated. - * The log is only temporary saved in the cache and will - * permanently saved with the saveLogOnClose function. - * - * @param beforeProcessing the url before the clear process - * @param afterProcessing the url after the clear process - * @param rule the rule that triggered the process - */ - function pushToLog(beforeProcessing, afterProcessing, rule) - { - const limit = storage.logLimit; - if(storage.loggingStatus && limit !== 0) - { - if(limit > 0 && !isNaN(limit)) { - while(storage.log.log.length >= limit) { - storage.log.log.shift(); - } - } + * Call loadOldDataFromStore, getHash, counter, status and log functions + */ - storage.log.log.push( - { - "before": beforeProcessing, - "after": afterProcessing, - "rule": rule, - "timestamp": Date.now() - } - ); - deferSaveOnDisk('log'); + loadOldDataFromStore(); + getHash(); + setBadgedStatus(); + + /** + * Call by each tab is updated. + * And if url has changed. + */ + function handleUpdated(tabId, changeInfo, tabInfo) { + if (changeInfo.url) { + delete badges[tabId]; + } + currentURL = tabInfo.url; + } + + /** + * Call by each tab is updated. + */ + browser.tabs.onUpdated.addListener(handleUpdated); + + /** + * Call by each tab change to set the actual tab id + */ + function handleActivated(activeInfo) { + tabid = activeInfo.tabId; + browser.tabs.get(tabid).then(function (tab) { + currentURL = tab.url; + }); + } + + /** + * Call by each tab change. + */ + browser.tabs.onActivated.addListener(handleActivated); + + /** + * Check the request. + */ + function promise(requestDetails) { + if (isDataURL(requestDetails)) { + return {}; + } else { + return clearUrl(requestDetails); } } + + /** + * To prevent long loading on data urls + * we will check here for data urls. + * + * @type {requestDetails} + * @return {boolean} + */ + function isDataURL(requestDetails) { + const s = requestDetails.url; + + return s.substring(0, 4) === "data"; + } + + /** + * Call by each Request and checking the url. + * + * @type {Array} + */ + browser.webRequest.onBeforeRequest.addListener( + promise, + {urls: [""], types: getData("types")}, + ["blocking"] + ); +} + +/** + * Function to log all activities from ClearUrls. + * Only logging when activated. + * The log is only temporary saved in the cache and will + * permanently saved with the saveLogOnClose function. + * + * @param beforeProcessing the url before the clear process + * @param afterProcessing the url after the clear process + * @param rule the rule that triggered the process + */ +function pushToLog(beforeProcessing, afterProcessing, rule) { + const limit = storage.logLimit; + if (storage.loggingStatus && limit !== 0) { + if (limit > 0 && !isNaN(limit)) { + while (storage.log.log.length >= limit) { + storage.log.log.shift(); + } + } + + storage.log.log.push( + { + "before": beforeProcessing, + "after": afterProcessing, + "rule": rule, + "timestamp": Date.now() + } + ); + deferSaveOnDisk('log'); + } +} diff --git a/core_js/storage.js b/core_js/storage.js index 68c3f59..59c3995 100644 --- a/core_js/storage.js +++ b/core_js/storage.js @@ -25,10 +25,9 @@ var hasPendingSaves = false; var pendingSaves = new Set(); /** -* Writes the storage variable to the disk. -*/ -function saveOnExit() -{ + * Writes the storage variable to the disk. + */ +function saveOnExit() { saveOnDisk(Object.keys(storage)); } @@ -65,14 +64,13 @@ function storageDataAsString(key) { } /** -* Save multiple keys on the disk. -* @param {String[]} keys -*/ -function saveOnDisk(keys) -{ + * Save multiple keys on the disk. + * @param {String[]} keys + */ +function saveOnDisk(keys) { let json = {}; - keys.forEach(function(key) { + keys.forEach(function (key) { json[key] = storageDataAsString(key); }); @@ -81,17 +79,16 @@ function saveOnDisk(keys) } /** -* Schedule to save a key to disk in 30 seconds. -* @param {String} key -*/ -function deferSaveOnDisk(key) -{ + * Schedule to save a key to disk in 30 seconds. + * @param {String} key + */ +function deferSaveOnDisk(key) { if (hasPendingSaves) { pendingSaves.add(key); return; } - setTimeout(function() { + setTimeout(function () { saveOnDisk(Array.from(pendingSaves)); pendingSaves.clear(); hasPendingSaves = false; @@ -100,10 +97,9 @@ function deferSaveOnDisk(key) } /** -* Start sequence for ClearURLs. -*/ -function genesis() -{ + * Start sequence for ClearURLs. + */ +function genesis() { browser.storage.local.get(null).then((items) => { initStorage(items); @@ -119,35 +115,32 @@ function genesis() } /** -* Return the value under the key. -* @param {String} key -* @return {Object} -*/ -function getData(key) -{ + * Return the value under the key. + * @param {String} key + * @return {Object} + */ +function getData(key) { return storage[key]; } /** -* Return the entire storage object. -* @return {Object} -*/ -function getEntireData() -{ + * Return the entire storage object. + * @return {Object} + */ +function getEntireData() { return storage; } /** -* Save the value under the key on the RAM. -* -* Note: To store the data on the hard disk, one of -* deferSaveOnDisk(), saveOnDisk(), or saveOnExit() -* must be called. -* @param {String} key -* @param {Object} value -*/ -function setData(key, value) -{ + * Save the value under the key on the RAM. + * + * Note: To store the data on the hard disk, one of + * deferSaveOnDisk(), saveOnDisk(), or saveOnExit() + * must be called. + * @param {String} key + * @param {Object} value + */ +function setData(key, value) { switch (key) { case "ClearURLsData": case "log": @@ -169,23 +162,21 @@ function setData(key, value) } /** -* Write error on console. -*/ -function error(e) -{ + * Write error on console. + */ +function error(e) { console.log(translate('core_error')); console.error(e); } /** -* Set default values, if the storage is empty. -* @param {Object} items -*/ -function initStorage(items) -{ + * Set default values, if the storage is empty. + * @param {Object} items + */ +function initStorage(items) { initSettings(); - if(!isEmpty(items)) { + if (!isEmpty(items)) { Object.entries(items).forEach(([key, value]) => { setData(key, value); }); @@ -193,10 +184,9 @@ function initStorage(items) } /** -* Set default values for the settings. -*/ -function initSettings() -{ + * Set default values for the settings. + */ +function initSettings() { storage.ClearURLsData = []; storage.dataHash = ""; storage.badgedStatus = true; @@ -208,15 +198,15 @@ function initSettings() storage.log = {"log": []}; storage.statisticsStatus = true; storage.badged_color = "ffa500"; - storage.hashURL = "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; - storage.ruleURL = "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; + storage.hashURL = "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.minify.hash?job=hash%20rules"; + storage.ruleURL = "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.minify.json"; storage.contextMenuEnabled = true; storage.historyListenerEnabled = true; storage.localHostsSkipping = true; storage.referralMarketing = false; storage.logLimit = -1; - if(getBrowser() === "Firefox") { + if (getBrowser() === "Firefox") { storage.types = ["font", "image", "imageset", "main_frame", "media", "object", "object_subrequest", "other", "script", "stylesheet", "sub_frame", "websocket", "xbl", "xml_dtd", "xmlhttprequest", "xslt"]; } else if (getBrowser() === "Chrome") { storage.types = ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "csp_report", "media", "websocket", "other"]; @@ -224,53 +214,57 @@ function initSettings() } /** -* Replace the old URLs with the -* new GitLab URLs. -*/ -function replaceOldURLs(url) -{ + * Replace the old URLs with the + * new GitLab URLs. + */ +function replaceOldURLs(url) { switch (url) { case "https://raw.githubusercontent.com/KevinRoebert/ClearUrls/master/data/rules.hash?flush_cache=true": - return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; + return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; case "https://raw.githubusercontent.com/KevinRoebert/ClearUrls/master/data/data.json?flush_cache=true": - return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; + return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; case "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/rules.hash": - return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; + return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; case "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.json": - return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; + return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; + case "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules": + return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.minify.hash?job=hash%20rules"; + case "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json": + return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/data.minify.json?job=hash%20rules"; default: - return url; + return url; } } /** -* Load local saved data, if the browser is offline or -* some other network trouble. -*/ -function loadOldDataFromStore() -{ + * Load local saved data, if the browser is offline or + * some other network trouble. + */ +function loadOldDataFromStore() { localDataHash = storage.dataHash; } /** -* Save the hash status to the local storage (RAM). -* The status can have the following values: -* 1 "up to date" -* 2 "updated" -* 3 "update available" -* @param status_code the number for the status -*/ -function storeHashStatus(status_code) -{ - switch(status_code) - { - case 1: status_code = "hash_status_code_1"; - break; - case 2: status_code = "hash_status_code_2"; - break; - case 3: status_code = "hash_status_code_3"; - break; - default: status_code = "hash_status_code_4"; + * Save the hash status to the local storage (RAM). + * The status can have the following values: + * 1 "up to date" + * 2 "updated" + * 3 "update available" + * @param status_code the number for the status + */ +function storeHashStatus(status_code) { + switch (status_code) { + case 1: + status_code = "hash_status_code_1"; + break; + case 2: + status_code = "hash_status_code_2"; + break; + case 3: + status_code = "hash_status_code_3"; + break; + default: + status_code = "hash_status_code_4"; } storage.hashStatus = status_code; diff --git a/core_js/tools.js b/core_js/tools.js index 3be2d6e..566e486 100644 --- a/core_js/tools.js +++ b/core_js/tools.js @@ -24,53 +24,49 @@ /* * To support Waterfox. */ -Array.prototype.rmEmpty = function() { +Array.prototype.rmEmpty = function () { return this.filter(v => v); }; /* * To support Waterfox. */ -Array.prototype.flatten = function() { +Array.prototype.flatten = function () { return this.reduce((a, b) => a.concat(b), []); }; /** -* Check if an object is empty. -* @param {Object} obj -* @return {Boolean} -*/ -function isEmpty(obj) -{ + * Check if an object is empty. + * @param {Object} obj + * @return {Boolean} + */ +function isEmpty(obj) { return (Object.getOwnPropertyNames(obj).length === 0); } /** -* Translate a string with the i18n API. -* -* @param {string} string Name of the attribute used for localization -*/ -function translate(string) -{ + * Translate a string with the i18n API. + * + * @param {string} string Name of the attribute used for localization + */ +function translate(string) { return browser.i18n.getMessage(string); } /** -* Reloads the extension. -*/ -function reload() -{ + * Reloads the extension. + */ +function reload() { browser.runtime.reload(); } /** -* Check if it is an android device. -* @return bool -*/ -async function checkOSAndroid() -{ - if(os === undefined || os === null || os === "") { - await chrome.runtime.getPlatformInfo(function(info) { + * Check if it is an android device. + * @return bool + */ +async function checkOSAndroid() { + if (os === undefined || os === null || os === "") { + await chrome.runtime.getPlatformInfo(function (info) { os = info.os; }); } @@ -79,10 +75,10 @@ async function checkOSAndroid() } /** -* Extract the host without port from an url. -* @param {String} url URL as String -* @return {String} host as string -*/ + * Extract the host without port from an url. + * @param {String} url URL as String + * @return {String} host as string + */ function extractHost(url) { let parsed_url = new URL(url); @@ -90,36 +86,34 @@ function extractHost(url) { } /** -* Returns true if the url has a local host. -* @param {String} url URL as String -* @return {boolean} -*/ + * Returns true if the url has a local host. + * @param {String} url URL as String + * @return {boolean} + */ function checkLocalURL(url) { let host = extractHost(url); return ipRangeCheck(host, ["10.0.0.0/8", "172.16.0.0/12", - "192.168.0.0/16", "100.64.0.0/10", - "169.254.0.0/16", "127.0.0.1"])|| - host === 'localhost'; + "192.168.0.0/16", "100.64.0.0/10", + "169.254.0.0/16", "127.0.0.1"]) || + host === 'localhost'; } /** -* Return the number of parameters query strings. -* @param {String} url URL as String -* @return {int} Number of Parameters -*/ -function countFields(url) -{ + * Return the number of parameters query strings. + * @param {String} url URL as String + * @return {int} Number of Parameters + */ +function countFields(url) { return extractFileds(url).length; } /** -* Returns true if fields exists. -* @param {String} url URL as String -* @return {boolean} -*/ -function existsFields(url) -{ + * Returns true if fields exists. + * @param {String} url URL as String + * @return {boolean} + */ +function existsFields(url) { let matches = (url.match(/\?.+/i) || []); let count = matches.length; @@ -127,15 +121,14 @@ function existsFields(url) } /** -* Extract the fields from an url. -* @param {String} url URL as String -* @return {Array} Fields as array -*/ -function extractFileds(url) -{ - if(existsFields(url)) { + * Extract the fields from an url. + * @param {String} url URL as String + * @return {Array} Fields as array + */ +function extractFileds(url) { + if (existsFields(url)) { let fields = url.replace(new RegExp(".*?\\?", "i"), ""); - if(existsFragments(url)) { + if (existsFragments(url)) { fields = fields.replace(new RegExp("#.*", "i"), ""); } @@ -146,23 +139,21 @@ function extractFileds(url) } /** -* Return the number of fragments query strings. -* @param {String} url URL as String -* @return {int} Number of fragments -*/ -function countFragments(url) -{ + * Return the number of fragments query strings. + * @param {String} url URL as String + * @return {int} Number of fragments + */ +function countFragments(url) { return extractFragments(url).length; } /** -* Extract the fragments from an url. -* @param {String} url URL as String -* @return {Array} fragments as array -*/ -function extractFragments(url) -{ - if(existsFragments(url)) { + * Extract the fragments from an url. + * @param {String} url URL as String + * @return {Array} fragments as array + */ +function extractFragments(url) { + if (existsFragments(url)) { let fragments = url.replace(new RegExp(".*?#", "i"), ""); return (fragments.match(/[^&]+=?[^&]*/gi) || []); } @@ -171,12 +162,11 @@ function extractFragments(url) } /** -* Returns true if fragments exists. -* @param {String} url URL as String -* @return {boolean} -*/ -function existsFragments(url) -{ + * Returns true if fragments exists. + * @param {String} url URL as String + * @return {boolean} + */ +function existsFragments(url) { let matches = (url.match(/\#.+/i) || []); let count = matches.length; @@ -184,73 +174,69 @@ function existsFragments(url) } /** -* Load local saved data, if the browser is offline or -* some other network trouble. -*/ -function loadOldDataFromStore() -{ + * Load local saved data, if the browser is offline or + * some other network trouble. + */ +function loadOldDataFromStore() { localDataHash = storage.dataHash; } /** -* Save the hash status to the local storage (RAM). -* The status can have the following values: -* 1 "up to date" -* 2 "updated" -* 3 "update available" -* @param status_code the number for the status -*/ -function storeHashStatus(status_code) -{ - switch(status_code) - { - case 1: status_code = "hash_status_code_1"; - break; - case 2: status_code = "hash_status_code_2"; - break; - case 3: status_code = "hash_status_code_3"; - break; - default: status_code = "hash_status_code_4"; + * Save the hash status to the local storage (RAM). + * The status can have the following values: + * 1 "up to date" + * 2 "updated" + * 3 "update available" + * @param status_code the number for the status + */ +function storeHashStatus(status_code) { + switch (status_code) { + case 1: + status_code = "hash_status_code_1"; + break; + case 2: + status_code = "hash_status_code_2"; + break; + case 3: + status_code = "hash_status_code_3"; + break; + default: + status_code = "hash_status_code_4"; } storage.hashStatus = status_code; } /** -* Increase by {number} the GlobalURLCounter -* @param {int} number -*/ -function increaseGlobalURLCounter(number) -{ - if(storage.statisticsStatus) - { + * Increase by {number} the GlobalURLCounter + * @param {int} number + */ +function increaseGlobalURLCounter(number) { + if (storage.statisticsStatus) { storage.globalurlcounter += number; deferSaveOnDisk('globalurlcounter'); } } /** -* Increase by one the URLCounter -*/ -function increaseURLCounter() -{ - if(storage.statisticsStatus) - { + * Increase by one the URLCounter + */ +function increaseURLCounter() { + if (storage.statisticsStatus) { storage.globalCounter++; deferSaveOnDisk('globalCounter'); } } /** -* Change the icon. -*/ -function changeIcon() -{ + * Change the icon. + */ +function changeIcon() { checkOSAndroid().then((res) => { - if(!res) { - if(storage.globalStatus){ + if (!res) { + if (storage.globalStatus) { browser.browserAction.setIcon({path: "img/clearurls_128x128.png"}); - } else{ + } else { browser.browserAction.setIcon({path: "img/clearurls_gray_128x128.png"}); } } @@ -258,35 +244,33 @@ function changeIcon() } /** -* Get the badged status from the browser storage and put the value -* into a local variable. -* -*/ -function setBadgedStatus() -{ + * Get the badged status from the browser storage and put the value + * into a local variable. + * + */ +function setBadgedStatus() { checkOSAndroid().then((res) => { - if(!res && storage.badgedStatus) { + if (!res && storage.badgedStatus) { browser.browserAction.setBadgeBackgroundColor({ - 'color': '#'+storage.badged_color + 'color': '#' + storage.badged_color }); } }); } /** -* Returns the current URL. -* @return {String} [description] -*/ -function getCurrentURL() -{ + * Returns the current URL. + * @return {String} [description] + */ +function getCurrentURL() { return currentURL; } /** -* Check for browser. -*/ + * Check for browser. + */ function getBrowser() { - if(typeof InstallTrigger !== 'undefined') { + if (typeof InstallTrigger !== 'undefined') { return "Firefox"; } else { return "Chrome"; @@ -299,9 +283,19 @@ function getBrowser() { */ function decodeURL(url) { const rtn = decodeURIComponent(url); - if(rtn.indexOf("http://") === -1 && rtn.indexOf("https://") === -1) { + if (rtn.indexOf("http://") === -1 && rtn.indexOf("https://") === -1) { return decodeURL(rtn); } return rtn; -} \ No newline at end of file +} + +/* +* Gets the value of at `key` an object. If the resolved value is `undefined`, the `defaultValue` is returned in its place. +* +* @param {string} key the key of the object +* @param {object} defaultValue the default value +*/ +Object.prototype.getOrDefault = function (key, defaultValue) { + return this[key] === undefined ? defaultValue : this[key]; +}; \ No newline at end of file diff --git a/data/data.min.json b/data/data.min.json index 4040394..197f24e 100644 --- a/data/data.min.json +++ b/data/data.min.json @@ -36,7 +36,7 @@ "rnid" ], "referralMarketing": [ - "([\\/|\\?|#]|(&|&))+(tag=[^\\/|\\?|&]*)" + "tag" ], "exceptions": [ ".*(amazon\\.).*(\\/gp).*\\/redirector.html\\/.*", diff --git a/manifest.json b/manifest.json index a327cb9..7073921 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "ClearURLs", - "version": "1.9.1", + "version": "1.9.2", "author": "Kevin Röbert", "description": "Remove tracking elements from URLs.", "homepage_url": "https://gitlab.com/KevinRoebert/ClearUrls",