NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Clean URL Improved // @namespace i2p.schimon.cleanurl // @description Remove tracking parameters and redirect to original URL. This Userscript uses the URL Interface instead of RegEx. // @homepageURL https://openuserjs.org/scripts/sjehuda/Clean_URL_Improved // @supportURL https://openuserjs.org/scripts/sjehuda/Clean_URL_Improved/issues // @updateURL https://openuserjs.org/meta/sjehuda/Clean_URL_Improved.meta.js // @downloadURL https://openuserjs.org/install/sjehuda/Clean_URL_Improved.user.js // @copyright 2023, Schimon Jehudah (http://schimon.i2p) // @license MIT; https://opensource.org/licenses/MIT // @grant none // @run-at document-end // @match file:///* // @match *://*/* // @version 23.11.23 // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48dGV4dCB5PSIuOWVtIiBmb250LXNpemU9IjkwIj7wn5qlPC90ZXh0Pjwvc3ZnPgo= // ==/UserScript== // NOTE GM.getValue and GM.setValue // will be used for statistics and // custom lists /* NEWS Handle /ref= Added .replace(/\/ref=/g, '/?ref=') Coding like a Japanese TODO Delete empty parameters Statistics GM.getValue('links-total') GM.getValue('links-bad') GM.getValue('links-good') GM.getValue('parameters-total') GM.getValue('parameters-bad') GM.getValue('parameters-good') GM.getValue('parameters-urls') GM.getValue('parameters-unclassified') GM.setValue('pbl', 'hostname-parameter') GM.setValue('pwl', 'hostname-parameter') FIXME Whitelisted parameters without values are realized to purged-url */ /* Simple version of this Userscript let url = new URL(location.href); if (url.hash || url.search) { location.href = url.origin + url.pathname }; */ // https://openuserjs.org/scripts/tfr/YouTube_Link_Cleaner // Check whether HTML; otherwise, exit. //if (!document.contentType == 'text/html') if (document.doctype == null) return; //let point = []; const namespace = 'i2p.schimon.cleanurl'; // List of url parameters const urls = [ 'ap_id', 'redirect', 'ref', 'rf', 'source', 'src', 'url', 'utm_source', 'utm_term']; // List of alphabet const alphabet = 'abcdefghijklmnopqrstuvwxyz'; // List of reserved parameters const whitelist = [ 'act', // invision board 'activeTab', // npmjs 'art', // article 'artist', // bandcamp jamendo 'action', // bugzilla 'author', // git 'ap_id', // activitypub 'bill', // law 'board', // simple machines forum 'CategoryID', // id 'category', // id 'categories', // searxng 'catid', // id 'code', // code 'component', // addons.palemoon.org 'content', // id 'contributor', // lulu 'CurrentMDW', // menuetos.be 'dark', // yorik.uncreated.net 'date', // date 'days', // mediawiki 'diff', // mediawiki 'do_union', // bugzilla 'district', // house.mo.gov 'engine', // simplytranslate 'exp_time', // cdn 'expires', // cdn 'ezimgfmt', // cdn image processor 'feed', // mediawiki 'feedformat', // mediawiki 'fid', // mybb 'file_host', // cdn 'filename', // filename 'for', // cdn 'format', // file type 'from', // redmine 'guid', // guid 'group', // bugzilla 'group_id', // sourceforge 'hash', // cdn 'hidebots', // mediawiki 'hl', // language 'id', // id 'ie', // character encoding 'ip', // ip address 'item_class', // greasyfork 'item_id', // greasyfork 'iv_load_policy', // invidious 'jid', // jabber id (xmpp) 'key', // cdn 'lang', // language 'language', // searxng 'library', // oujs 'limit', // mediawiki 'list', // video 'locale', // locale 'logout', // bugzilla 'lr', // cdn 'lra', // cdn 'member', // xmb forum 'mobileaction', // mediawiki 'name', // archlinux 'news_id', // post 'node', 'oldid', // mediawiki 'order', // bugzilla 'orderBy', // oujs 'orderDir', // oujs 'outputType', // xml //'p', // search query / page number 'page', // mybb //'param', 'pid', // fluxbb 'preferencesReturnUrl', // return url 'product', // bugzilla 'profile', // copy.sh/v86/ //'q', // search query 'query', // search query 'query_format', // bugzilla 'redlink', // mediawiki 'ref_type', // gitlab //'referer', // signin NOTE provided pathname contains login (log-in) or signin (sign-in) 'resolution', // bugzilla 'requestee', // bugzilla 'requester', // bugzilla 'return_to', // signin //'s', // search query 'search', // search query 'showtopic', // invision board 'show_all_versions', // greasyfork 'showforum', // forums.duke4.net 'sign', // cdn 'signature', // cdn 'size', // rss 'sort', // greasyfork 'speed', // cdn 'st', // invision board 'start', // page 'start_time', // media playback 'subcat', // solidtorrents 'state', // cdn '__switch_theme', // theme (theanarchistlibrary.org) 'tag', // id 'template', // zapier 'tid', // mybb 'title', // send (share) links and mediawiki 'to', // gitlab 'topic', // simple machines forum 'type', // file type //'url', // url NOTE not sure whether to whitelist or blacklist 'utf8', // encoding 'urlversion', // mediawiki 'version', // greasyfork //'view', // invision board 'view-source', // git.sr.ht //'_x_tr_sl', // translate online service //'_x_tr_tl=', // translate online service //'_x_tr_hl=', // translate online service //'_x_tr_pto', // translate online service //'_x_tr_hist', // translate online service 'year' // year ]; // List of useless hash const hash = [ 'back-url', 'back_url', 'intcid', 'niche-', //'searchinput', 'src']; // List of useless parameters const blacklist = [ 'ad', 'ad_medium', 'ad_name', 'ad_pvid', 'ad_sub', //'ad_tags', 'advertising-id', //'aem_p4p_detail', 'af', 'aff', 'aff_fcid', 'aff_fsk', 'aff_platform', 'aff_trace_key', 'affparams', 'afSmartRedirect', 'afftrack', 'affparams', //'aid', 'algo_exp_id', 'algo_pvid', 'ar', //'ascsubtag', //'asc_contentid', 'asgtbndr', 'atc', 'ats', 'autostart', //'b64e', // breaks yandex 'bizType', //'block', 'bta', 'businessType', 'campaign', 'campaignId', //'__cf_chl_rt_tk', //'cid', // breaks sacred magick 'ck', //'clickid', //'client_id', //'cm_ven', //'cmd', 'content-id', 'crid', 'cst', 'cts', 'curPageLogUid', //'data', // breaks yandex //'dchild', //'dclid', 'deals-widget', 'dgcid', 'dicbo', //'dt', 'edd', 'edm_click_module', //'ei', //'embed', //'_encoding', //'etext', // breaks yandex 'eventSource', 'fbclid', 'feature', 'field-lbr_brands_browse-bin', 'forced_click', //'fr', 'frs', //'from', // breaks yandex '_ga', 'ga_order', 'ga_search_query', 'ga_search_type', 'ga_view_type', 'gatewayAdapt', //'gclid', //'gclsrc', 'gh_jid', 'gps-id', //'gs_lcp', 'gt', 'guccounter', 'hdtime', 'hosted_button_id', 'ICID', 'ico', 'ig_rid', //'idzone', //'iflsig', 'intcmp', 'irclickid', //'irgwc', //'irpid', 'is_from_webapp', 'itid', //'itok', //'katds_labels', //'keywords', 'keyno', 'l10n', 'linkCode', 'mc', 'mid', '__mk_de_DE', 'mp', 'nats', 'nci', 'obOrigUrl', 'offer_id', 'optout', 'oq', 'organic_search_click', 'pa', 'Partner', 'partner', 'partner_id', 'partner_ID', 'pcampaignid', 'pd_rd_i', 'pd_rd_r', 'pd_rd_w', 'pd_rd_wg', 'pdp_npi', 'pf_rd_i', 'pf_rd_m', 'pf_rd_p', 'pf_rd_r', 'pf_rd_s', 'pf_rd_t', 'pg', 'PHPSESSID', 'pk_campaign', 'pdp_ext_f', 'pkey', 'platform', 'plkey', 'pqr', 'pr', 'pro', 'prod', 'prom', 'promo', 'promocode', 'promoid', 'psc', 'psprogram', 'pvid', 'qid', //'r', 'realDomain', 'recruiter_id', 'redirect', 'ref', 'ref_', 'ref_src', 'refcode', 'referrer', 'refinements', 'reftag', 'rf', 'rnid', 'rowan_id1', 'rowan_msg_id', //'rss', //'sCh', 'sclient', 'scm', 'scm_id', 'scm-url', //'sd', 'sender_device', 'sh', 'shareId', 'showVariations', 'si', //'sid', // breaks whatsup.org.il '___SID', //'site_id', 'sk', 'smid', 'social_params', 'source', 'sourceId', 'sp_csd', 'spLa', 'spm', 'spreadType', //'sprefix', 'sr', 'src', '_src', 'src_cmp', 'src_player', 'src_src', 'srcSns', 'su', //'sxin_0_pb', '_t', //'tag', 'tcampaign', 'td', 'terminal_id', //'text', 'th', // Sometimes restored after page load //'title', 'tracelog', 'traffic_id', 'traffic_source', 'traffic_type', 'tt', 'uact', 'ug_edm_item_id', 'utm', //'utm1', //'utm2', //'utm3', //'utm4', //'utm5', //'utm6', //'utm7', //'utm8', //'utm9', 'utm_campaign', 'utm_content', 'utm_medium', 'utm_source', 'utm_term', 'uuid', //'utype', //'ve', //'ved', //'zone' ]; // URL Indexers const paraIDX = [ 'algo_exp_id', 'algo_pvid', 'b64e', 'cst', 'cts', 'data', 'ei', //'etext', 'from', 'iflsig', 'gbv', 'gs_lcp', 'hdtime', 'keyno', 'l10n', 'mc', 'oq', //'q', 'sei', 'sclient', 'sign', 'source', 'state', //'text', 'uact', 'uuid', 'ved']; // Market Places const paraMKT = [ '___SID', '_t', 'ad_pvid', 'af', 'aff_fsk', 'aff_platform', 'aff_trace_key', 'afSmartRedirect', 'bizType', 'businessType', 'ck', 'content-id', 'crid', 'curPageLogUid', 'deals-widget', 'edm_click_module', 'gatewayAdapt', 'gps-id', 'keywords', '__mk_de_DE', 'pd_rd_i', 'pd_rd_r', 'pd_rd_w', 'pd_rd_wg', 'pdp_npi', 'pf_rd_i', 'pf_rd_m', 'pf_rd_p', 'pf_rd_r', 'pf_rd_s', 'pf_rd_t', 'platform', 'pdp_ext_f', 'ref_', 'refinements', 'rnid', 'rowan_id1', 'rowan_msg_id', 'scm', 'scm_id', 'scm-url', 'shareId', //'showVariations', 'sk', 'smid', 'social_params', 'spLa', 'spm', 'spreadType', 'sprefix', 'sr', 'srcSns', //'sxin_0_pb', 'terminal_id', 'th', // Sometimes restored after page load 'tracelog', 'tt', 'ug_edm_item_id']; // IL const paraIL = [ 'dicbo', 'obOrigUrl']; // General const paraWWW = [ 'aff', 'promo', 'promoid', 'ref', 'utm_campaign', 'utm_content', 'utm_medium', 'utm_source', 'utm_term']; // For URL of the Address bar // Check and modify page address // TODO Add bar and ask to clean address bar (function modifyURL() { let check = [], // NOTE Marketplace website which uses /ref= instead of ?ref= // location.href.replace(/\/ref=/g, '/?ref='); url = new URL(location.href.replace('/ref=', '?ref=')); // TODO turn into boolean function for (let i = 0; i < blacklist.length; i++) { if (url.searchParams.get(blacklist[i])) { check.push(blacklist[i]); url.searchParams.delete(blacklist[i]); //newURL = url.origin + url.pathname + url.search + url.hash; } } // TODO turn into boolean function for (let i = 0; i < hash.length; i++) { if (url.hash.startsWith('#' + hash[i])) { check.push(hash[i]); //newURL = url.origin + url.pathname + url.search; } } if (check.length > 0) { let newURL = url.origin + url.pathname + url.search; window.history.pushState(null, null, newURL); //location.href = newURL; } })(); // NOTE Marketplace website which uses /ref= instead of ?ref= (function correctSlashRefURLs() { for (let i = 0; i < document.links.length; i++) { if (document.links[i].href.includes('/ref=')) { document.links[i].href = document.links[i].href.replace('/ref=', '?ref='); document.links[i].setAttribute('slash-ref', ''); } } })(); (function scanAllURLs() { for (let i = 0; i < document.links.length; i++) { let url = new URL(document.links[i].href); // NOTE Consider BitTorrent Magnet links // removing trackers would need a warning about // private torrents, if torrent is not public (dht-enabled) const allowedProtocols = [ 'finger:', 'freenet:', 'gemini:', 'gopher:', 'wap:', 'ipfs:', 'https:', 'ftps:', 'http:', 'ftp:']; if (url.search && allowedProtocols.includes(url.protocol)) { //if (url.search || url.hash) { document.links[i].setAttribute('href-data', document.links[i].href); } } })(); (function scanBadURLs() { for (let i = 0; i < document.links.length; i++) { // TODO callback, Mutation Observer, and Event Listener // TODO Count links increaseByOne('links') // NOTE To count links, add return statement to function cleanLink() // return statement will indicate that link is positive for subject // parameters and therefore should be counted. Counter will be added // by one, once detected that url is not equal to (new) url. hash.forEach(j => cleanLink(document.links[i], j, 'hash')); blacklist.forEach(j => cleanLink(document.links[i], j, 'para')); } })(); // TODO Add an Event Listener function cleanLink(link, target, type) { let url = new URL(link.href); switch (type) { case 'hash': //console.log('hash ' + i) if (url.hash.startsWith('#' + target)) { //link.setAttribute('href-data', link.href); link.href = url.origin + url.pathname + url.search; //increaseByOne('hashes') } break; case 'para': //console.log('para ' + i) if (url.searchParams.get(target)) { url.searchParams.delete(target); //link.setAttribute('href-data', link.href); link.href = url.origin + url.pathname + url.search; //increaseByOne('parameters') } break; } /* // EXTRA // For URL of hyperlinks for (const a of document.querySelectorAll('a')) { try{ let url = new URL(a.href); for (let i = 0; i < blacklist.length; i++) { if (url.searchParams.get(blacklist[i])) { url.searchParams.delete(blacklist[i]); } } a.href = url; } catch (err) { //console.warn('Found no href for element: ' + a); //console.error(err); } } */ } // TODO Hunt (for any) links within attributes using getAttributeNames()[i] // Event Listener // TODO Scan 'e.target.childNodes' until 'href-data' (link) is found document.body.addEventListener("mouseover", function(e) { // mouseover works with keyboard too //if (e.target && e.target.nodeName == "A") { let hrefData = e.target.getAttribute('href-data'); //if (e.target && hrefData && !document.getElementById(namespace)) { if (e.target && hrefData && hrefData != document.getElementById('url-original')) { if (document.getElementById(namespace)) { document.getElementById(namespace).remove(); } selectionItem = createButton(e.pageX, e.pageY, hrefData); hrefData = new URL(hrefData); selectionItem.append(purgeURL(hrefData)); let types = ['whitelist', 'blacklist', 'original']; for (let i = 0; i < types.length; i++) { let button = purgeURL(hrefData, types[i]); if (types[i] == 'original' && e.target.getAttribute('slash-ref') == '') { button.href = button.href.replace('?ref=', '/ref='); } let exist; selectionItem.childNodes.forEach( node => { if (button.href == node.href) { exist = true; } } ); if (!exist) { selectionItem.append(button); } } // Check for URLs for (let i = 0; i < urls.length; i++) { if (hrefData.searchParams.get(urls[i])) { // hrefData.includes('url=') urlParameter = hrefData.searchParams.get(urls[i]); try { urlParameter = new URL (urlParameter); } catch { if (urlParameter.includes('.')) { // NOTE It is a guess try { urlParameter = new URL ('http:' + urlParameter); } catch {} } } if (typeof urlParameter == 'object' && // confirm url object urlParameter != location.href) { // provided url isn't the same as of page newURLItem = extractURL(urlParameter); selectionItem.prepend(newURLItem); } } } /* // compare original against purged //if (selectionItem.querySelector(`#url-purged`) && // selectionItem.querySelector(`#url-original`)) { if (selectionItem.querySelector(`#url-purged`)) { //let urlOrigin = new URL (selectionItem.querySelector(`#url-original`).href); let urlPurge = new URL (selectionItem.querySelector(`#url-purged`).href); // NOTE // These "searchParams.sort" ~~may be~~ *are not* redundant. // See resUrl.searchParams.sort() urlPurge.searchParams.sort(); hrefData.searchParams.sort(); //console.log(hrefData.search); //console.log(urlPurge.search); if (hrefData.search == urlPurge.search && selectionItem.querySelector(`#url-original`)) { selectionItem.querySelector(`#url-original`).remove(); } } else // compare original against safe if (selectionItem.querySelector(`#url-known`)) { //let urlOrigin = new URL (selectionItem.querySelector(`#url-original`).href); let urlKnown = new URL (selectionItem.querySelector(`#url-known`).href); // NOTE // These "searchParams.sort" ~~may be~~ *are not* redundant. // See resUrl.searchParams.sort() urlKnown.searchParams.sort(); hrefData.searchParams.sort(); //console.log(hrefData.search); //console.log(urlKnown.search); if (hrefData.search == urlKnown.search && selectionItem.querySelector(`#url-original`)) { selectionItem.querySelector(`#url-original`).remove(); } } */ // compare original against safe and purged // NOTE on "item.href = decodeURI(resUrl)" // The solution was here. // Decode was not the issue // This is a good example to show that // smaller tasks are as important as bigger tasks let urlsToCompare = ['#url-known', '#url-purged']; for (let i = 0; i < urlsToCompare.length; i++) { if (selectionItem.querySelector(urlsToCompare[i])) { //let urlOrigin = new URL (selectionItem.querySelector(`#url-original`).href); let urlToCompare = new URL (selectionItem.querySelector(urlsToCompare[i]).href); // NOTE // These "searchParams.sort" ~~may be~~ *are not* redundant. // See resUrl.searchParams.sort() urlToCompare.searchParams.sort(); hrefData.searchParams.sort(); //console.log(hrefData.search); //console.log(urlToCompare.search); if (hrefData.search == urlToCompare.search && selectionItem.querySelector(`#url-original`)) { selectionItem.querySelector(`#url-original`).remove(); } } } // do not add element, if url has only whitelisted parameters and no potential url // add element, only if a potential url or non-whitelisted parameter was found let urlTypes = ['url-extracted', 'url-original', 'url-purged']; for (let i = 0; i < urlTypes.length; i++) { if (selectionItem.querySelector(`#${urlTypes[i]}`)) { document.body.append(selectionItem); return; } } // NOTE in case return did not reach // it means that there is no link to process e.target.removeAttribute('href-data'); //if (!e.target.getAttribute('slash-ref') == '') { // e.target.removeAttribute('href-data') //} } }); function createButton(x, y, url) { // create element let item = document.createElement(namespace); // set content item.id = namespace; // set position item.style.all = 'unset'; item.style.position = 'absolute'; //item.style.left = x+5 + 'px'; //item.style.top = y-3 + 'px'; item.style.left = x+45 + 'px'; item.style.top = y-65 + 'px'; // set appearance item.style.fontFamily = 'none'; // emoji item.style.background = '#333'; item.style.borderRadius = '5%'; item.style.padding = '3px'; item.style.direction = 'ltr'; item.style.zIndex = 10000; //item.style.opacity = 0.7; //item.style.filter = 'brightness(0.7) drop-shadow(2px 4px 6px black)'; item.style.filter = 'brightness(0.7)'; // center character item.style.justifyContent = 'center'; item.style.alignItems = 'center'; item.style.display = 'flex'; // disable selection marks item.style.userSelect = 'none'; item.style.cursor = 'default'; // set button behaviour item.onmouseover = () => { //item.style.opacity = 1; //item.style.filter = 'drop-shadow(2px 4px 6px black)'; item.style.filter = 'unset'; }; item.onmouseleave = () => { // onmouseout // TODO Wait a few seconds item.remove(); }; return item; } function extractURL(url) { let item = document.createElement('a'); item.textContent = '🔗'; // 🧧 🏷️ 🔖 item.title = 'Extracted URL'; item.id = 'url-extracted'; item.style.all = 'unset'; item.style.outline = 'none'; item.style.height = '15px'; item.style.width = '15px'; item.style.padding = '3px'; item.style.margin = '3px'; //item.style.fontSize = '0.9rem' // 90% item.style.lineHeight = 'normal'; // initial //item.style.height = 'fit-content'; item.href = url; return item; } // TODO Use icons (with shapes) for cases when color is not optimal function purgeURL(url, listType) { let orgUrl = null; let itemTitle, itemId, resUrl; let item = document.createElement('a'); item.style.all = 'unset'; switch (listType) { case 'blacklist': itemColor = 'yellow'; //itemTextContent = '🟡'; itemTitle = 'Semi-safe link'; // Purged URL itemId = 'url-purged'; resUrl = hrefDataHandler(url, blacklist); break; case 'original': // TODO dbclick (double-click) itemColor = 'orangered'; //itemTextContent = '🔴'; itemTitle = 'Unsafe link'; // Original URL itemId = 'url-original'; //resUrl = encodeURI(url); // NOTE By executing url.searchParams.sort() // we change the order of parameters // which means that we create a new and unique url // which means that it can be used to identify users that use this program // NOTE We execute url.searchParams.sort() // in order to avoid false positive for url of blacklisted parameters // but we don't apply that change on item "url-original" //url.searchParams.sort(); orgUrl = url; item.style.cursor = `not-allowed`; // no-drop item.onmouseenter = () => { item.style.filter = `drop-shadow(2px 4px 6px ${itemColor})`; }; item.onmouseout = () => { item.style.filter = 'unset'; }; break; case 'whitelist': itemColor = 'lawngreen'; //itemTextContent = '🟢'; itemTitle = 'Safe link'; // Link with whitelisted parameters itemId = 'url-known'; resUrl = hrefDataHandler(url, whitelist); break; default: itemColor = 'antiquewhite'; //itemTextContent = '⚪'; itemTitle = 'Base link'; // Link without parameters itemId = 'url-base'; resUrl = url.origin + url.pathname; resUrl = new URL(resUrl); // NOTE To avoid error in resUrl.searchParams.sort() break; } item.id = itemId; item.title = itemTitle; item.style.background = itemColor; //item.textContent = itemTextContent; item.style.borderRadius = '50%'; item.style.outline = 'none'; item.style.height = '15px'; item.style.width = '15px'; item.style.padding = '3px'; item.style.margin = '3px'; if (orgUrl){ item.href = orgUrl; } else { // NOTE Avoid duplicates by sorting parameters of all links resUrl.searchParams.sort(); // NOTE Avoid false positive by decoding // TODO decode from ?C=N%3BO%3DD to ?C=N;O=D // FIXME decodeURI doesn't appear to work // Text page https://mirror.lyrahosting.com/gnu/a2ps/ (columns raw) // SOLVED See "urlsToCompare" //item.href = decodeURI(resUrl); //item.href = decodeURIComponent(resUrl); // TODO whitespace turns into plus and // then red/white appears for the same uri item.href = resUrl; } return item; } // NOTE The URL API doesn't list parameters // without explicitly calling them, therefore // reading lengths of unknown parameters is // impossible, hence // set a loop from Aa - Zz or // add Aa - Zz to whitelist function hrefDataHandler(url, listType) { url = new URL(url.href); // NOTE Avoid duplicates by sorting parameters of all links //url.searchParams.sort(); switch (listType) { case whitelist: let newURL = new URL (url.origin + url.pathname); for (let i = 0; i < whitelist.length; i++) { if (url.searchParams.get(whitelist[i])) { newURL.searchParams.set( whitelist[i], url.searchParams.get(whitelist[i]) // catchedValue ); } } // Whitelist parameters of single character long let a2z = alphabet.split(''); for (let i = 0; i < a2z.length; i++) { if (url.searchParams.get(a2z[i])) { newURL.searchParams.set( a2z[i], url.searchParams.get(a2z[i]) ); } } let A2Z = alphabet.toUpperCase().split(''); for (let i = 0; i < A2Z.length; i++) { if (url.searchParams.get(A2Z[i])) { newURL.searchParams.set( A2Z[i], url.searchParams.get(A2Z[i]) ); } } /* let a2z = genCharArray('a', 'z'); for (let i = 0; i < a2z.length; i++) { if (url.searchParams.get(a2z[i])) { newURL.searchParams.set( a2z[i], url.searchParams.get(a2z[i]) // catchedValue ); } } let A2Z = genCharArray('A', 'Z'); for (let i = 0; i < A2Z.length; i++) { if (url.searchParams.get(A2Z[i])) { newURL.searchParams.set( A2Z[i], url.searchParams.get(A2Z[i]) // catchedValue ); } } */ url = newURL; break; case blacklist: for (let i = 0; i < blacklist.length; i++) { if (url.searchParams.get(blacklist[i])) { url.searchParams.delete(blacklist[i]); //increaseByOne('parameters') } } //increaseByOne('links') break; } return url; } // /questions/24597634/how-to-generate-an-array-of-the-alphabet function genCharArray(charA, charZ) { var a = [], i = charA.charCodeAt(0), j = charZ.charCodeAt(0); for (; i <= j; ++i) { a.push(String.fromCharCode(i)); } return a; } async function increaseByOne(key) { let currentValue = await GM.getValue(key, 0); await GM.setValue(key, currentValue + 1); console.log(key); console.log(currentValue); } // NOTE Marketplace website which uses /ref= instead of ?ref= // Check for .getAttribute('slash-ref') == '' function deleteSerialNumber(url) { let newURL = []; let pattern = /^[0-9\-]+$/; let oldUrl = url.toString().split('/'); for (const cell of oldUrl) { if (!pattern.test(cell)) { newURL.push(cell); } } newURL = newURL.join('/'); return new URL(newURL); }