diff --git a/.cspell/dictionary-custom.txt b/.cspell/dictionary-custom.txt index f45e924..9c448ab 100644 --- a/.cspell/dictionary-custom.txt +++ b/.cspell/dictionary-custom.txt @@ -2,6 +2,7 @@ Blogsy Bronto Codacy cyclonedx +DDG delighter Ghostery Klaviyo @@ -9,6 +10,7 @@ Listrak Matomo Octocat Omeda +openurlparam smushing Temu Textastic @@ -16,3 +18,4 @@ uglifyjs unskim utmstrip Vero +x-man diff --git a/.github/linters/eslint.config.js b/.github/linters/eslint.config.js index 1b74f68..8fdcac0 100644 --- a/.github/linters/eslint.config.js +++ b/.github/linters/eslint.config.js @@ -1,6 +1,6 @@ const globals = require("globals"); const js = require("@eslint/js"); -const security = require("eslint-plugin-security"); +const noUnsanitized = require("eslint-plugin-no-unsanitized"); const tsParser = require("@typescript-eslint/parser"); const tsPlugin = require("@typescript-eslint/eslint-plugin"); @@ -101,8 +101,7 @@ const baseRules = { "symbol-description": "error", "unicode-bom": "error", "vars-on-top": "error", - ...security.configs['recommended'].rules, - "security/detect-non-literal-regexp": "off" + ...noUnsanitized.configs.recommended.rules }; module.exports = [ @@ -110,14 +109,16 @@ module.exports = [ files: ["src/*.js"], ignores: [".cspell.json","*.json", "**/*{.,-}min.js", "node_modules/*", "dist/*.bookmarklet"], languageOptions: { - ecmaVersion: 2020, + ecmaVersion: 2023, globals: {...globals.browser, ...globals.node}, parserOptions: { ecmaFeatures: {globalReturn: false, impliedStrict: true} }, sourceType: "script" }, - plugins: {security}, + plugins: { + "no-unsanitized": noUnsanitized + }, rules: { ...baseRules, "init-declarations": "error", @@ -141,7 +142,7 @@ module.exports = [ }, plugins: { "@typescript-eslint": tsPlugin, - security + "no-unsanitized": noUnsanitized }, rules: { ...baseRules, @@ -160,24 +161,48 @@ module.exports = [ } }, { - files: ["scripts/*.js", ".github/linters/*.js"], + files: [".temp/*.js"], ignores: [".cspell.json","*.json", "**/*{.,-}min.js", "node_modules/*", "dist/*.bookmarklet"], languageOptions: { ecmaVersion: 2020, + globals: {...globals.browser, ...globals.node}, + parserOptions: { + ecmaFeatures: {globalReturn: false, impliedStrict: true} + }, + sourceType: "script" + }, + plugins: { + "no-unsanitized": noUnsanitized + }, + rules: { + ...baseRules, + "init-declarations": "error", + "no-await-in-loop": "error", + "no-console": "error", + "no-inline-comments": "error", + "no-param-reassign": "error" + } + }, + { + files: ["scripts/*.js", ".github/linters/*.js"], + ignores: [".cspell.json","*.json", "**/*{.,-}min.js", "node_modules/*", "dist/*.bookmarklet"], + languageOptions: { + ecmaVersion: 2023, globals: globals.node, parserOptions: { ecmaFeatures: {globalReturn: false, impliedStrict: true} }, sourceType: "script" }, - plugins: {security}, + plugins: { + "no-unsanitized": noUnsanitized + }, rules: { ...baseRules, "no-await-in-loop": "off", "no-console": "off", "no-inline-comments": "off", - "no-param-reassign": "off", - "security/detect-non-literal-fs-filename": "off" + "no-param-reassign": "off" } } ]; diff --git a/.npmrc b/.npmrc index 58b115e..4161fe9 100644 --- a/.npmrc +++ b/.npmrc @@ -3,7 +3,7 @@ # enforce engine compatibility - fail if Node.js/npm version doesn't match engine-strict=true -# save exact versions (no ^ or ~ ranges) for better reproducibility +# save exact versions (avoid ^ or ~ ranges) for better reproducibility save-exact=true # use public registry @@ -18,9 +18,6 @@ audit-level=low # security: block automatic lifecycle scripts (preinstall, postinstall, & prepare) ignore-scripts=true -# prefer offline - use cache when possible for faster installs -prefer-offline=true - # disable fund messages for cleaner CI logs fund=false diff --git a/README.md b/README.md index 3970dd3..bc32ea6 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,15 @@ iPad, and Mac, and also works with Google Chrome on desktops. + __[OpenInBrave] v1.1.2__: Open the current web page in the Brave app on iOS. ++ __[OpenInChrome] v1.0.0__: Open the current web page in the Google Chrome app +on iOS. + ++ __[OpenInDDG] v1.0.0__: Open the current web page in the DuckDuckGo browser +app on iOS. + ++ __[OpenInEdge] v1.0.0__: Open the current web page in the Microsoft Edge app +on iOS. + + __[OpenInFirefox] v1.6.2__: Open the current web page in the Firefox app for iOS. @@ -67,6 +76,10 @@ mode of the Firefox app for iOS. + __[OpenInGoodReader] v1.5.2__: When viewing a PDF in Mobile Safari, open or download the same PDF in GoodReader. ++ __[OpenInOpera] v1.0.0__: Open the current web page in the Opera app on iOS. + ++ __[OpenInOrion] v1.0.0__: Open the current web page in the Opera app on iOS. + + __[OpenInTextastic] v1.1.2__: Open the current web page in the Textastic app on iOS. Download the server response of the current HTTP URL, save it in the root directory of the local (Textastic) file system, and then open it in the @@ -76,19 +89,19 @@ editor. Handy to view the source code of a web page or download a raw file. repository in Mobile Safari, show the same repo in the Working Copy iOS app (cloning the repo locally if necessary). -+ __[OpenURLParam] v1.2.0__: Work-around for blocked navigation from certain ++ __[OpenURLParam] v1.2.1__: Work-around for blocked navigation from certain ad or tracking blockers. If the current URL contains a parameter in the form of `url=...` this bookmarklet will parse the `url` parameter and navigate to that URL. -+ __[UtmStrip] v2.2.0__: Strips off the UTM query string elements of the ++ __[UtmStrip] v2.3.0__: Strips off the UTM query string elements of the current URL to remove common "urchin" tracking information from youtube, etc. Also removes Google `/amp/` suffix from URL path. Asks to copy the new URL to the clipboard. Finally, replaces history & reloads the page. Now works to remove host-specific trackers for common online retailers and Google Search. _NOTE:_ This bookmarklet also works with Safari and Firefox on macOS. -+ __[unskim] v2.0.1__: Bypass redirect and affiliate link wrappers by ++ __[unskim] v2.0.2__: Bypass redirect and affiliate link wrappers by detecting common URL parameters (like `url=`, `destination=`, `redirect=`, etc.) and navigating directly to the target URL. Also handles Safari DNS error pages when a redirect service is blocked, extracting the intended destination @@ -121,15 +134,20 @@ followed link into a bookmark for JavaScript bookmarklet. + __Mobile Safari setup link__ -- [Setup KillStickyHeaders] v2.1.0 + __Mobile Safari setup link__ -- [Setup Linklighter] v2.1.0 + __Mobile Safari setup link__ -- [Setup OpenInBrave] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInChrome] v1.0.0 ++ __Mobile Safari setup link__ -- [Setup OpenInDDG] v1.0.0 ++ __Mobile Safari setup link__ -- [Setup OpenInEdge] v1.0.0 + __Mobile Safari setup link__ -- [Setup OpenInFirefox] v1.6.2 + __Mobile Safari setup link__ -- [Setup OpenInFirefox-Focus] v1.1.2 + __Mobile Safari setup link__ -- [Setup OpenInFirefox-Private] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInOpera] v1.0.0 ++ __Mobile Safari setup link__ -- [Setup OpenInOrion] v1.0.0 + __Mobile Safari setup link__ -- [Setup OpenInGoodReader] v1.5.2 + __Mobile Safari setup link__ -- [Setup OpenInTextastic] v1.1.2 + __Mobile Safari setup link__ -- [Setup OpenInWorkingCopy] v1.6.2 -+ __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.2.0 -+ __Mobile Safari setup link__ -- [Setup UtmStrip] v2.2.0 -+ __Mobile Safari setup link__ -- [Setup unskim] v2.0.1 ++ __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.2.1 ++ __Mobile Safari setup link__ -- [Setup UtmStrip] v2.3.0 ++ __Mobile Safari setup link__ -- [Setup unskim] v2.0.2 + __Mobile Safari setup link__ -- [Setup x-man] v1.3.1 ## Requirements @@ -206,6 +224,12 @@ using a URL protocol scheme. on Apple platforms, and Google Chrome for desktops. + __OpenInBrave__ - Uses the `brave://open-url?url=` scheme for the Brave app on iOS. ++ __OpenInChrome__ - Uses the `googlechrome://` or `googlechromes://` scheme for + the Google Chrome app on iOS. ++ __OpenInDDG__ - Uses the `ddgQuickLink://` scheme for the DuckDuckGo browser + app on iOS. ++ __OpenInEdge__ - Uses the `microsoft-edge-http://` or `microsoft-edge-https://` + scheme for the Google Chrome app on iOS. + __OpenInFirefox__ _and_ __OpenInFirefox-Private__ - Uses the `firefox://open-url?url=` scheme for the Firefox app on iOS. The "Private" version appends `&private=true` after the target url for private browsing. @@ -214,6 +238,10 @@ using a URL protocol scheme. target url for private browsing. + __OpenInGoodReader__ - Uses the `grhttp://` or `grhttps://` URL protocol scheme for GoodReader. See [GoodReader URL Scheme][GoodReader URL Scheme]. ++ __OpenInOpera__ - Uses the `touch-http://` or `touch-https://` scheme for the + Opera app on iOS. ++ __OpenInOrion__ - Uses the `orion://open-url?url=` scheme for the Orion app + on iOS. + __OpenInTextastic__ - Uses the `textastic://` protocol scheme of the Textastic app on iOS. For details, see [Textastic x-callback-url API][Textastic x-callback-url API]. @@ -238,6 +266,10 @@ using a URL protocol scheme. ## Version Notes +4.2.0 Major update to browser coverage with addition of OpenInChrome, OpenInDDG, + OpenInEdge, OpenInOpera, & OpenInOrion. Improvements to unskim, UtmStrip, + & OpenURLParam. Refinements build process with updated dependencies. + 4.1.1 Major update to UtmStrip with 26 new universal exact-match static keys, 4 new universal prefixes, 57 new host-specific keys/prefixes across 7 new hosts, and host-specific tracking & decluttering for Google Search. @@ -346,15 +378,20 @@ repos I had; doesn't build yet [KillStickyHeaders]: javascript:Array.from(document.querySelectorAll('body%20%2A')).filter(e=%3E%5B'fixed'%2C'sticky'%5D.includes(getComputedStyle(e).position)).forEach(e=%3Ee.remove())%3Bvoid'2.1.0' "KillStickyHeaders" [Linklighter]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%2Cs=i.indexOf('%23')%3Blet%20r=i%2Cc=''%2Cl=''%2Ca=''%3Bif(e.rangeCount%3E0%26%26n%26%26(document.body.textContent%7C%7C'').split(n).length-1%3E1)%7Bconst%20t=e.getRangeAt(0)%2Cn=t.commonAncestorContainer.textContent%7C%7C''%2C%7BstartOffset:o%2CendOffset:i%7D=t%2Cs=Math.max(0%2Co-20)%3Bif(l=n.substring(s%2Co).trim()%2Cs%3E0%26%26'%20'!==n.charAt(s-1))%7Bconst%20t=l.indexOf('%20')%3Bt%3E0%26%26(l=l.substring(t%2B1))%7Dconst%20r=Math.min(n.length%2Ci%2B20)%3Bif(a=n.substring(i%2Cr).trim()%2Cr%3Cn.length%26%26'%20'!==n.charAt(r))%7Bconst%20t=a.lastIndexOf('%20')%3Bt%3E0%26%26(a=a.substring(0%2Ct))%7D%7Dif(e.removeAllRanges()%2Cn%26%26''!==n)%7Bs%3E-1%26%26(r=r.substring(0%2Cs))%3Blet%20e=''%3Bif(l%26%26(e=t(l)%2B'-%2C')%2C80%3Eo)c=n%2Ce%2B=t(n)%3Belse%7Blet%20i=~~(o%2F2-2)%3Bo%3E150%3Fi=48:o%3E100%26%26(i=~~(o%2F3))%3Blet%20s=n.substring(0%2Ci)%2Cr=n.slice(o-i)%3Bconst%20l=s.lastIndexOf('%20')%3Bl%3Ei%2F2%26%26(s=s.substring(0%2Cl))%3Bconst%20a=r.indexOf('%20')%3Ba%3E-1%26%26i%2F2%3Ea%26%26(r=r.substring(a%2B1))%2Cc=s%2B'%E2%80%A6'%2Ce%2B=%60%24%7Bt(s)%7D%2C%24%7Bt(r)%7D%60%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cr%2B='%23:~:text='%2Be%2Cr=r.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2F%2C'')%2Cr=r.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cr=r.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(r!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(r).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(r%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" [OpenInBrave]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.2' "OpenInBrave" +[OpenInChrome]: javascript:'use%20strict'%3Bif(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))%7Bconst%20o=%60googlechrome%24%7B'https:'===location.protocol%3F's':''%7D:%2F%2F%60%3Blocation.href=%60%24%7Bo%7D%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%7Dvoid'1.0.0' "OpenInChrome" +[OpenInDDG]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60ddgQuickLink:%2F%2F%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%3Bvoid'1.0.0' "OpenInDDG" +[OpenInEdge]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='microsoft-edge-'%2Blocation.href%3Bvoid'1.0.0' "OpenInEdge" [OpenInFirefox]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.2' "OpenInFirefox" [OpenInFirefox-Focus]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "OpenInFirefox-Focus" [OpenInFirefox-Private]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "OpenInFirefox-Private" +[OpenInOpera]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='touch-'%2Blocation.href%3Bvoid'1.0.0' "OpenInOpera" +[OpenInOrion]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='orion:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.0.0' "OpenInOrion" [OpenInGoodReader]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.2' "OpenInGoodReader" [OpenInTextastic]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.2' "OpenInTextastic" [OpenInWorkingCopy]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26('bitbucket.org'===location.host%7C%7C'github.com'===location.host))location.href=%60working-copy:%2F%2Fshow%3Fremote=%24%7BencodeURIComponent(location.href.split('%2F').slice(0%2C5).join('%2F'))%7D.git%60%3Bvoid'1.6.2' "OpenInWorkingCopy" -[OpenURLParam]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' "OpenURLParam" -[UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%3Bif((e!==d%7C%7Ct!==o)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20t=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(t)%2Chistory.replaceState(null%2C''%2Ct)%3Bconst%20e=window.open(t%2C'%5Fself'%2C'noreferrer')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.2.0' "UtmStrip" -[unskim]: javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(''===e.search)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.1' "unskim" +[OpenURLParam]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif('https:'!==t.protocol%7C%7C13%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.1' "OpenURLParam" +[UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2Cn=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bif(confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F')%26%26(navigator.clipboard.writeText(n)%2Ce!==d%7C%7Ct!==o))%7Bhistory.replaceState(null%2C''%2Cn)%3Bconst%20t=window.open(n%2C'%5Fself'%2C'noreferrer')%3Bt%26%26(t.opener=null)%7D%7D)()%3Bvoid'2.3.0' "UtmStrip" +[unskim]: javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(13%3Ee.search.length)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.2' "unskim" [x-man]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20i=navigator.userAgent%2Cn=i.includes('Chrome%2F')%7C%7Ci.includes('Firefox%2F')%7C%7Ci.includes('Brave%2F')%7C%7Ci.includes('Edg%2F')%2Co=i.includes('Safari%2F')%2Ct=!n%26%26!o%2Ca=navigator.platform.startsWith('Mac')%26%26o%26%26!n%26%26!t%26%26!navigator.maxTouchPoints%26%262%3Enavigator.maxTouchPoints%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20l=e.toString().split('%5Cn')%5B0%5D.trim()%3Blet%20r='x-man-page:%2F%2F'%3Bconst%20c=l.match(%2F%5E(.%2A%3F)%5C((%5Cd)%5C)%24%2F)%3Bif(r%2B=c%3F'1'===c%5B2%5D%3Fc%5B1%5D:%60%24%7Bc%5B2%5D%7D%2F%24%7Bc%5B1%5D%7D%60:l%2Cl)%7Bif(confirm(%60Link%20for%20%22%24%7Bl%7D%22%20is:%20%24%7Br%7D%5Cn%5CnCopy%20to%20clipboard%3F%60))%7Blet%20i=''%3Bif(navigator.clipboard%3Fnavigator.clipboard.writeText(r):(i=%60Unable%20to%20copy%20%22%24%7Br%7D%22%20to%20clipboard.%60%2Cr='')%2Ca%26%26''!==r)%7Blet%20n=null%3Btry%7Bn=window.open(r)%2Cn%26%26(n.opener=null)%7Dcatch(n)%7Bi=%60Popup%20window%20blocked.%20Clipboard%20contains%20%22%24%7Br%7D%22%60%7Dfinally%7Bwindow.focus()%2Cnull!==n%26%26setTimeout(()=%3E%7Bn%26%26n.close()%7D%2C3333)%7D%7Delse''!==r%26%26(i=%60Unable%20to%20open%20new%20link%20with%20Terminal.%20Clipboard%20contains%20%22%24%7Br%7D%22%60)%3B''!==i%26%26alert(i)%7De.empty()%7D%7D)()%3Bvoid'1.3.1' "x-man" @@ -365,15 +402,20 @@ repos I had; doesn't build yet [Setup KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:Array.from(document.querySelectorAll('body%20%2A')).filter(e=%3E%5B'fixed'%2C'sticky'%5D.includes(getComputedStyle(e).position)).forEach(e=%3Ee.remove())%3Bvoid'2.1.0' "Setup KillStickyHeaders" [Setup Linklighter]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%2Cs=i.indexOf('%23')%3Blet%20r=i%2Cc=''%2Cl=''%2Ca=''%3Bif(e.rangeCount%3E0%26%26n%26%26(document.body.textContent%7C%7C'').split(n).length-1%3E1)%7Bconst%20t=e.getRangeAt(0)%2Cn=t.commonAncestorContainer.textContent%7C%7C''%2C%7BstartOffset:o%2CendOffset:i%7D=t%2Cs=Math.max(0%2Co-20)%3Bif(l=n.substring(s%2Co).trim()%2Cs%3E0%26%26'%20'!==n.charAt(s-1))%7Bconst%20t=l.indexOf('%20')%3Bt%3E0%26%26(l=l.substring(t%2B1))%7Dconst%20r=Math.min(n.length%2Ci%2B20)%3Bif(a=n.substring(i%2Cr).trim()%2Cr%3Cn.length%26%26'%20'!==n.charAt(r))%7Bconst%20t=a.lastIndexOf('%20')%3Bt%3E0%26%26(a=a.substring(0%2Ct))%7D%7Dif(e.removeAllRanges()%2Cn%26%26''!==n)%7Bs%3E-1%26%26(r=r.substring(0%2Cs))%3Blet%20e=''%3Bif(l%26%26(e=t(l)%2B'-%2C')%2C80%3Eo)c=n%2Ce%2B=t(n)%3Belse%7Blet%20i=~~(o%2F2-2)%3Bo%3E150%3Fi=48:o%3E100%26%26(i=~~(o%2F3))%3Blet%20s=n.substring(0%2Ci)%2Cr=n.slice(o-i)%3Bconst%20l=s.lastIndexOf('%20')%3Bl%3Ei%2F2%26%26(s=s.substring(0%2Cl))%3Bconst%20a=r.indexOf('%20')%3Ba%3E-1%26%26i%2F2%3Ea%26%26(r=r.substring(a%2B1))%2Cc=s%2B'%E2%80%A6'%2Ce%2B=%60%24%7Bt(s)%7D%2C%24%7Bt(r)%7D%60%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cr%2B='%23:~:text='%2Be%2Cr=r.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2F%2C'')%2Cr=r.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cr=r.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(r!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(r).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(r%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Setup Linklighter" [Setup OpenInBrave]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.2' "Setup OpenInBrave" +[Setup OpenInChrome]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))%7Bconst%20o=%60googlechrome%24%7B'https:'===location.protocol%3F's':''%7D:%2F%2F%60%3Blocation.href=%60%24%7Bo%7D%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%7Dvoid'1.0.0' "Setup OpenInChrome" +[Setup OpenInDDG]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60ddgQuickLink:%2F%2F%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%3Bvoid'1.0.0' "Setup OpenInDDG" +[Setup OpenInEdge]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='microsoft-edge-'%2Blocation.href%3Bvoid'1.0.0' "Setup OpenInEdge" [Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.2' "Setup OpenInFirefox" [Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "Setup OpenInFirefox-Focus" [Setup OpenInFirefox-Private]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "Setup OpenInFirefox-Private" [Setup OpenInGoodReader]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.2' "Setup OpenInGoodReader" +[Setup OpenInOpera]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='touch-'%2Blocation.href%3Bvoid'1.0.0' "Setup OpenInOpera" +[Setup OpenInOrion]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='orion:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.0.0' "Setup OpenInOrion" [Setup OpenInTextastic]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.2' "Setup OpenInTextastic" [Setup OpenInWorkingCopy]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26('bitbucket.org'===location.host%7C%7C'github.com'===location.host))location.href=%60working-copy:%2F%2Fshow%3Fremote=%24%7BencodeURIComponent(location.href.split('%2F').slice(0%2C5).join('%2F'))%7D.git%60%3Bvoid'1.6.2' "Setup OpenInWorkingCopy" -[Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' "Setup OpenURLParam" -[Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%3Bif((e!==d%7C%7Ct!==o)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20t=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(t)%2Chistory.replaceState(null%2C''%2Ct)%3Bconst%20e=window.open(t%2C'%5Fself'%2C'noreferrer')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.2.0' "Setup UtmStrip" -[Setup unskim]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(''===e.search)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.1' "Setup unskim" +[Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif('https:'!==t.protocol%7C%7C13%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.1' "Setup OpenURLParam" +[Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2Cn=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bif(confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F')%26%26(navigator.clipboard.writeText(n)%2Ce!==d%7C%7Ct!==o))%7Bhistory.replaceState(null%2C''%2Cn)%3Bconst%20t=window.open(n%2C'%5Fself'%2C'noreferrer')%3Bt%26%26(t.opener=null)%7D%7D)()%3Bvoid'2.3.0' "Setup UtmStrip" +[Setup unskim]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(13%3Ee.search.length)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.2' "Setup unskim" [Setup x-man]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20i=navigator.userAgent%2Cn=i.includes('Chrome%2F')%7C%7Ci.includes('Firefox%2F')%7C%7Ci.includes('Brave%2F')%7C%7Ci.includes('Edg%2F')%2Co=i.includes('Safari%2F')%2Ct=!n%26%26!o%2Ca=navigator.platform.startsWith('Mac')%26%26o%26%26!n%26%26!t%26%26!navigator.maxTouchPoints%26%262%3Enavigator.maxTouchPoints%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20l=e.toString().split('%5Cn')%5B0%5D.trim()%3Blet%20r='x-man-page:%2F%2F'%3Bconst%20c=l.match(%2F%5E(.%2A%3F)%5C((%5Cd)%5C)%24%2F)%3Bif(r%2B=c%3F'1'===c%5B2%5D%3Fc%5B1%5D:%60%24%7Bc%5B2%5D%7D%2F%24%7Bc%5B1%5D%7D%60:l%2Cl)%7Bif(confirm(%60Link%20for%20%22%24%7Bl%7D%22%20is:%20%24%7Br%7D%5Cn%5CnCopy%20to%20clipboard%3F%60))%7Blet%20i=''%3Bif(navigator.clipboard%3Fnavigator.clipboard.writeText(r):(i=%60Unable%20to%20copy%20%22%24%7Br%7D%22%20to%20clipboard.%60%2Cr='')%2Ca%26%26''!==r)%7Blet%20n=null%3Btry%7Bn=window.open(r)%2Cn%26%26(n.opener=null)%7Dcatch(n)%7Bi=%60Popup%20window%20blocked.%20Clipboard%20contains%20%22%24%7Br%7D%22%60%7Dfinally%7Bwindow.focus()%2Cnull!==n%26%26setTimeout(()=%3E%7Bn%26%26n.close()%7D%2C3333)%7D%7Delse''!==r%26%26(i=%60Unable%20to%20open%20new%20link%20with%20Terminal.%20Clipboard%20contains%20%22%24%7Br%7D%22%60)%3B''!==i%26%26alert(i)%7De.empty()%7D%7D)()%3Bvoid'1.3.1' "Setup x-man" diff --git a/SECURITY.md b/SECURITY.md index 31a5a8c..07cbebc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ updates: | Version | Supported | | ------- | ------------------ | -| 4.1.x | :white_check_mark: | +| 4.2.x | :white_check_mark: | ## Reporting a Vulnerability @@ -64,8 +64,8 @@ npm version patch # or minor, or major git push --follow-tags # Or manually create and push a signed tag -git tag -s 4.1.1 -m "Release version 4.1.1" -git push origin 4.1.1 +git tag -s 4.2.0 -m "Release version 4.2.0" +git push origin 4.2.0 ``` ## Release Verification @@ -76,10 +76,10 @@ All releases should be signed with GPG/SSH signatures for verification: ```bash # Verify the signature on a release tag -git verify-tag 4.1.1 +git verify-tag 4.2.0 # Show tag details with signature -git tag -v 4.1.1 +git tag -v 4.2.0 ``` ### Verifying Signed Commits diff --git a/bookmarklets.json b/bookmarklets.json index 4b06a98..ee6e5e9 100644 --- a/bookmarklets.json +++ b/bookmarklets.json @@ -25,6 +25,21 @@ "file": "openinbrave.bookmarklet", "version": "1.1.2" }, + { + "name": "OpenInChrome", + "file": "openinchrome.bookmarklet", + "version": "1.0.0" + }, + { + "name": "OpenInDDG", + "file": "openinddg.bookmarklet", + "version": "1.0.0" + }, + { + "name": "OpenInEdge", + "file": "openinedge.bookmarklet", + "version": "1.0.0" + }, { "name": "OpenInFirefox", "file": "openinfirefox.bookmarklet", @@ -45,6 +60,16 @@ "file": "openingoodreader.bookmarklet", "version": "1.5.2" }, + { + "name": "OpenInOpera", + "file": "openinopera.bookmarklet", + "version": "1.0.0" + }, + { + "name": "OpenInOrion", + "file": "openinorion.bookmarklet", + "version": "1.0.0" + }, { "name": "OpenInTextastic", "file": "openintextastic.bookmarklet", @@ -58,12 +83,12 @@ { "name": "OpenURLParam", "file": "openurlparam.bookmarklet", - "version": "1.2.0" + "version": "1.2.1" }, { "name": "UtmStrip", "file": "utmstrip.bookmarklet", - "version": "2.2.0" + "version": "2.3.0" }, { "name": "deLighter", @@ -73,7 +98,7 @@ { "name": "unskim", "file": "unskim.bookmarklet", - "version": "2.0.1" + "version": "2.0.2" }, { "name": "x-man", diff --git a/dist/openinchrome.bookmarklet b/dist/openinchrome.bookmarklet new file mode 100644 index 0000000..16f4c53 --- /dev/null +++ b/dist/openinchrome.bookmarklet @@ -0,0 +1 @@ +javascript:'use%20strict'%3Bif(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))%7Bconst%20o=%60googlechrome%24%7B'https:'===location.protocol%3F's':''%7D:%2F%2F%60%3Blocation.href=%60%24%7Bo%7D%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%7Dvoid'1.0.0' \ No newline at end of file diff --git a/dist/openinddg.bookmarklet b/dist/openinddg.bookmarklet new file mode 100644 index 0000000..eb6e6ba --- /dev/null +++ b/dist/openinddg.bookmarklet @@ -0,0 +1 @@ +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60ddgQuickLink:%2F%2F%24%7Blocation.host%7D%24%7Blocation.pathname%7D%24%7Blocation.search%7D%24%7Blocation.hash%7D%60%3Bvoid'1.0.0' \ No newline at end of file diff --git a/dist/openinedge.bookmarklet b/dist/openinedge.bookmarklet new file mode 100644 index 0000000..a0cde22 --- /dev/null +++ b/dist/openinedge.bookmarklet @@ -0,0 +1 @@ +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='microsoft-edge-'%2Blocation.href%3Bvoid'1.0.0' \ No newline at end of file diff --git a/dist/openinopera.bookmarklet b/dist/openinopera.bookmarklet new file mode 100644 index 0000000..c16822e --- /dev/null +++ b/dist/openinopera.bookmarklet @@ -0,0 +1 @@ +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='touch-'%2Blocation.href%3Bvoid'1.0.0' \ No newline at end of file diff --git a/dist/openinorion.bookmarklet b/dist/openinorion.bookmarklet new file mode 100644 index 0000000..0c252a1 --- /dev/null +++ b/dist/openinorion.bookmarklet @@ -0,0 +1 @@ +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='orion:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.0.0' \ No newline at end of file diff --git a/dist/openurlparam.bookmarklet b/dist/openurlparam.bookmarklet index e0b389c..23544b1 100644 --- a/dist/openurlparam.bookmarklet +++ b/dist/openurlparam.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif('https:'!==t.protocol%7C%7C13%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.1' \ No newline at end of file diff --git a/dist/unskim.bookmarklet b/dist/unskim.bookmarklet index 5294689..a8753a5 100644 --- a/dist/unskim.bookmarklet +++ b/dist/unskim.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(''===e.search)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.1' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Blet%20e=new%20URL(document.location.href)%3Bif('safari-resource:%2FErrorPage.html'===e.href)%7Bconst%20t=document.querySelector('p.error-message')%3F.textContent%3F.match(%2FSafari%20can't%20open%20the%20page%20%22(https%3F:%5B%5E%22%5D%2B)%22%2F)%3F.%5B1%5D%3Bt%26%26(e=new%20URL(t))%7Dif(13%3Ee.search.length)return%3Bconst%20t=new%20URLSearchParams(e.search)%2Cr=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(e=%3Et.has(e)).map(e=%3E%7Bconst%20r=t.get(e)%3Breturn%20r%3FdecodeURIComponent(r):''%7D).find(e=%3Ee.match(%2F%5Ehttps%3F:%2F))%3Br%26%26window.location.replace(new%20URL(r))%7D)()%3Bvoid'2.0.2' \ No newline at end of file diff --git a/dist/utmstrip.bookmarklet b/dist/utmstrip.bookmarklet index 02b2a94..a6ee80d 100644 --- a/dist/utmstrip.bookmarklet +++ b/dist/utmstrip.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%3Bif((e!==d%7C%7Ct!==o)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20t=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(t)%2Chistory.replaceState(null%2C''%2Ct)%3Bconst%20e=window.open(t%2C'%5Fself'%2C'noreferrer')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.2.0' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=location.pathname%2Ce=location.search%3Bif(3%3Ee.length%26%26!t.includes('%2Famp'))return%3Bconst%20i=location.hostname%2Cs=new%20URLSearchParams(e)%2Ca=%5B'assetType'%2C'elqTrack'%2C'mkt%5Ftok'%2C'originalReferer'%2C'referrer'%2C'terminal%5Fid'%2C'trk'%2C'trkCampaign'%2C'trkInfo'%2C'anid'%2C'assetid'%2C'campaignid'%2C'eid'%2C'gclid'%2C'recipientid'%2C'siteid'%2C'sib%5Fcuid'%2C'sib%5Fsid'%2C'%5Fbta%5Ftid'%2C'%5Fbta%5Fc'%2C'%5F%5Fs'%2C'fbclid'%2C'hrc'%2C'igsh'%2C'igshid'%2C'refsrc'%2C'%5Fgl'%2C'gclsrc'%2C'srsltid'%2C'%5Fhsenc'%2C'%5Fhsmi'%2C'%5F%5Fhsfp'%2C'%5F%5Fhssc'%2C'%5F%5Fhstc'%2C'cm%5Fmmc'%2C'cm%5Fre'%2C'cm%5Fsp'%2C'manual%5Fcm%5Fmmc'%2C'%5Fke'%2C'%5Fkx'%2C'trk%5Fcontact'%2C'trk%5Fmsg'%2C'trk%5Fmodule'%2C'trk%5Fsid'%2C'mc%5Fcid'%2C'mc%5Feid'%2C'iesrc'%2C'msclkid'%2C'dclid'%2C'twclid'%2C'ttclid'%2C'oly%5Fenc%5Fid'%2C'oly%5Fanon%5Fid'%2C'epik'%2C'vero%5Fid'%5D%2C%5F=%5B'fb%5F'%2C'action%5F'%2C'ga%5F'%2C'utm%5F'%2C'hmb%5F'%2C'hsa%5F'%2C'mtm%5F'%2C'pk%5F'%2C'oly%5F'%2C'stm%5F'%5D%3B%2F%5C.aliexpress%5C.%5Ba-z%5D%7B2%2C3%7D%24%2F.test(i)%3F(a.push('algo%5Fevid'%2C'algo%5Fpvid'%2C'btsid'%2C'spm'%2C'scm'%2C'ws%5Fab%5Ftest')%2C%5F.push('aff%5F')):%2F(%7C%5C.)amazon%5C.com%24%2F.test(i)%3Fa.push('%5Fencoding'%2C'asc%5Fcampaign'%2C'asc%5Frefurl'%2C'asc%5Fsource'%2C'ascsubtag'%2C'content-id'%2C'crid'%2C'cv%5Fct%5Fcx'%2C'dib%5Ftag'%2C'dib'%2C'ie'%2C'language'%2C'linkCode'%2C'linkId'%2C'pd%5Frd%5Fi'%2C'pd%5Frd%5Fr'%2C'pd%5Frd%5Fw'%2C'pd%5Frd%5Fwg'%2C'pf%5Frd%5Fi'%2C'pf%5Frd%5Fm'%2C'pf%5Frd%5Fp'%2C'pf%5Frd%5Fr'%2C'pf%5Frd%5Fs'%2C'pf%5Frd%5Ft'%2C'pf'%2C'psc'%2C'qid'%2C'ref%5F'%2C'sprefix'%2C'sr'%2C'tag'%2C'th'):i.endsWith('.ebay.com')%7C%7C%2F%5C.ebay%5C.co%5C.%5Ba-z%5D%7B2%7D%24%2F.test(i)%3Fa.push('mkevt'%2C'mkcid'%2C'mkrid'%2C'campid'%2C'toolid'%2C'customid'%2C'norover'%2C'itm'%2C'amdata'):%2F(%5E%7C%5C.)google%5C.(com%7C%5Ba-z%5D%7B2%7D%7Ccom%3F%5C.%5Ba-z%5D%7B2%7D)%24%2F.test(i)%26%26t.startsWith('%2Fsearch')%3Fa.push('aqs'%2C'ei'%2C'gs%5Flp'%2C'gs%5Fssp'%2C'iflsig'%2C'sca%5Fesv'%2C'ved'%2C'oq'%2C'sa'%2C'uact'%2C'rlz'%2C'sxsrf'%2C'bih'%2C'biw'%2C'client'%2C'prmd'%2C'sclient'%2C'source'%2C'sourceid'%2C'ie'%2C'oe'):i.endsWith('.linkedin.com')%3Fa.push('li%5Ffat%5Fid'%2C'licu'%2C'lipi'%2C'midSig'%2C'midToken'%2C'refId'):i.endsWith('.target.com')%3Fa.push('afid'%2C'clkid'%2C'lnm'%2C'preselect'%2C'tref'):i.endsWith('.temu.com')%3F(a.push('%5Fbg%5Ffs'%2C'%5Fp%5Fjump%5Fid'%2C'%5Fp%5Frfs'%2C'refer%5Fpage%5Fid'%2C'refer%5Fpage%5Fname'%2C'refer%5Fpage%5Fsn')%2C%5F.push('%5Fx%5F')):i.endsWith('.tiktok.com')%7C%7C'tiktok.com'===i%3Fa.push('%5Fd'%2C'%5Fr'%2C'%5Ft'%2C'is%5Ffrom%5Fwebapp'%2C'preview%5Fpb'%2C'share%5Fapp%5Fname'%2C'share%5Fitem%5Fid'%2C'tt4d%5Ft'%2C'timestamp'%2C'u%5Fcode'%2C'user%5Fid'):%2F%5C.(twitter%7Cx)%5C.com%24%2F.test(i)%7C%7C%2F%5E(twitter%7Cx)%5C.com%24%2F.test(i)%3Fa.push('cn'%2C'ref%5Fsrc'%2C'ref%5Furl'%2C's'%2C't'):i.endsWith('.walmart.com')%3Fa.push('adsredirect'%2C'affiliates%5Fad%5Fid'%2C'athcpid'%2C'athpgid'%2C'athcgid'%2C'athmtid'%2C'athstid'%2C'athznid'%2C'athiession'%2C'athancid'%2C'athposb'%2C'athena'%2C'campaign%5Fid'%2C'wmlspartner'):(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(i)%7C%7C'youtu.be'===i%7C%7C'www.youtube-nocookie.com'===i)%26%26a.push('ac'%2C'annotation%5Fid'%2C'app'%2C'feature'%2C'gclid'%2C'kw'%2C'src%5Fvid')%3Bconst%20c=new%20Set(a.map(t=%3Et.toLowerCase()))%2Cr=t=%3E%7Bconst%20e=t.toLowerCase()%3Breturn%20c.has(e)%7C%7C%5F.some(t=%3Ee.startsWith(t))%7C%7C%2F%5Ecm%5Fmmca%5Cd%2B%24%2Fi.test(t)%7D%3Bfor(const%20t%20of%5B...s.keys()%5D)r(t)%26%26s.delete(t)%3Blet%20d=s.toString()%3Bd=d%3F'%3F'%2Bd:''%3Bconst%20o=t.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2Cn=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bo%7D%24%7Bd%7D%24%7Blocation.hash%7D%60%3Bif(confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F')%26%26(navigator.clipboard.writeText(n)%2Ce!==d%7C%7Ct!==o))%7Bhistory.replaceState(null%2C''%2Cn)%3Bconst%20t=window.open(n%2C'%5Fself'%2C'noreferrer')%3Bt%26%26(t.opener=null)%7D%7D)()%3Bvoid'2.3.0' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ce5c289..4f5b0f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "OpenInlets", - "version": "4.1.1", + "version": "4.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "OpenInlets", - "version": "4.1.1", + "version": "4.2.0", "license": "MIT", "devDependencies": { "@eslint/js": "9.39.2", - "@types/node": "25.2.0", + "@types/node": "25.2.2", "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "eslint": "9.39.2", - "eslint-plugin-security": "3.0.1", + "eslint-plugin-no-unsanitized": "~4.1.4", "globals": "17.3.0", "terser": "5.46.0", "typescript": "5.9.3" @@ -343,9 +343,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", - "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", + "version": "25.2.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.2.tgz", + "integrity": "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -846,20 +846,14 @@ } } }, - "node_modules/eslint-plugin-security": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-3.0.1.tgz", - "integrity": "sha512-XjVGBhtDZJfyuhIxnQ/WMm385RbX3DBu7H1J7HNNhmB2tnGxMeqVSnYv79oAj992ayvIBZghsymwkYFS6cGH4Q==", + "node_modules/eslint-plugin-no-unsanitized": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.4.tgz", + "integrity": "sha512-cjAoZoq3J+5KJuycYYOWrc0/OpZ7pl2Z3ypfFq4GtaAgheg+L7YGxUo2YS3avIvo/dYU5/zR2hXu3v81M9NxhQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-regex": "^2.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "license": "MPL-2.0", + "peerDependencies": { + "eslint": "^8 || ^9" } }, "node_modules/eslint-scope": { @@ -1436,16 +1430,6 @@ "node": ">=6" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1456,20 +1440,10 @@ "node": ">=4" } }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "regexp-tree": "~0.1.1" - } - }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index c93ecf2..03ae0a6 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "description": "Project that makes Safari bookmarklets to automagically jump to the corresponding app and more.", "devDependencies": { "@eslint/js": "9.39.2", - "@types/node": "25.2.0", + "@types/node": "25.2.2", "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "eslint": "9.39.2", - "eslint-plugin-security": "3.0.1", + "eslint-plugin-no-unsanitized" : "~4.1.4", "globals": "17.3.0", "terser": "5.46.0", "typescript": "5.9.3" @@ -52,12 +52,12 @@ "scripts": { "build": "npm run build:compile && npm run build:minify && npm run build:bookmarklet", "build:bookmarklet": "node scripts/build-bookmarklet.js", - "build:compile": "tsc", + "build:compile": "tsc && eslint --config .github/linters/eslint.config.js ./.temp/*.js", "build:minify": "node scripts/minify.js", "build:readme": "node scripts/update-readme.js", "deploy": "npm run build && npm run build:readme", "test": "npm run build && npm run verify-build", "verify-build": "node scripts/verify-build.js" }, - "version": "4.1.1" + "version": "4.2.0" } diff --git a/scripts/minify.js b/scripts/minify.js index b4f3995..2507502 100755 --- a/scripts/minify.js +++ b/scripts/minify.js @@ -18,7 +18,7 @@ const {minify} = require('terser'); // Security note: These settings do not introduce vulnerabilities, but can // change code semantics. See SECURITY.md for build verification procedures. const terserOptions = { - ecma: 2020, // Target ES2020 - fully supported by Safari 26.1+ + ecma: 2023, // Target ES2023 - fully supported by Safari 26.2+ module: false, // Not ES modules compress: { // Aggressive unsafe optimizations (matching UglifyJS config) diff --git a/scripts/utils.js b/scripts/utils.js index 0def41f..2bd4259 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -38,12 +38,10 @@ function readFileOrFail(filePath) { function validateBookmarklet(bookmarklet, index) { const required = ['name', 'file', 'version']; for (const field of required) { - // eslint-disable-next-line security/detect-object-injection if (!(field in bookmarklet) || bookmarklet[field] === null) { console.error(`Invalid config: bookmarklet at index ${index} missing required field '${field}'`); process.exit(1); } - // eslint-disable-next-line security/detect-object-injection const value = bookmarklet[field]; if (typeof value === 'string' && value.trim().length === 0) { console.error(`Invalid config: bookmarklet at index ${index} has empty string for field '${field}'`); diff --git a/src/killStickyHeaders.ts b/src/killStickyHeaders.ts index d6ce4d0..20cfa3c 100644 --- a/src/killStickyHeaders.ts +++ b/src/killStickyHeaders.ts @@ -1,5 +1,5 @@ // find & delete all fixed/sticky position elements of body Array.from(document.querySelectorAll('body *')) - .filter(el => ['fixed', 'sticky'].includes(getComputedStyle(el).position)) - .forEach(el => el.remove()); + .filter(el => ['fixed', 'sticky'].includes(getComputedStyle(el).position)) + .forEach(el => el.remove()); diff --git a/src/openinchrome.ts b/src/openinchrome.ts new file mode 100644 index 0000000..5e651d2 --- /dev/null +++ b/src/openinchrome.ts @@ -0,0 +1,8 @@ +// verify iOS UserAgent & then prefix URL with protocol for Google Chrome iOS app +// googlechrome://URL_WITHOUT_SCHEME or googlechromes://URL_WITHOUT_SCHEME + +if (/iP(ad|hone)/.test(navigator.userAgent)) { + type ChromeScheme = 'googlechrome://' | 'googlechromes://'; + const chromeScheme: ChromeScheme = `googlechrome${location.protocol === 'https:' ? 's' : ''}://`; + location.href = `${chromeScheme}${location.host}${location.pathname}${location.search}${location.hash}`; +} diff --git a/src/openinddg.ts b/src/openinddg.ts new file mode 100644 index 0000000..96cb220 --- /dev/null +++ b/src/openinddg.ts @@ -0,0 +1,5 @@ +// verify iOS UserAgent & then prefix URL with protocol for DuckDuckGo Browser + +if (/iP(ad|hone)/.test(navigator.userAgent)) { + location.href = `ddgQuickLink://${location.host}${location.pathname}${location.search}${location.hash}`; +} diff --git a/src/openinedge.ts b/src/openinedge.ts new file mode 100644 index 0000000..815421b --- /dev/null +++ b/src/openinedge.ts @@ -0,0 +1,6 @@ +// verify iOS UserAgent & then prefix URL with protocol for Microsoft Edge iOS app +// microsoft-edge-http://URL_WITHOUT_SCHEME or microsoft-edge-https://URL_WITHOUT_SCHEME + +if (/iP(ad|hone)/.test(navigator.userAgent)) { + location.href = `microsoft-edge-${location.href}`; +} diff --git a/src/openinopera.ts b/src/openinopera.ts new file mode 100644 index 0000000..4349a8e --- /dev/null +++ b/src/openinopera.ts @@ -0,0 +1,6 @@ +// verify iOS UserAgent & then prefix URL with protocol for Opera iOS app +// touch-http://URL_WITHOUT_SCHEME or touch-https://URL_WITHOUT_SCHEME + +if (/iP(ad|hone)/.test(navigator.userAgent)) { + location.href = `touch-${location.href}`; +} diff --git a/src/openinorion.ts b/src/openinorion.ts new file mode 100644 index 0000000..bd1e9d2 --- /dev/null +++ b/src/openinorion.ts @@ -0,0 +1,4 @@ +// verify iOS UserAgent & then prefix URL with protocol of Orion iOS app +if (/iP(ad|hone)/.test(navigator.userAgent)) { + location.href = `orion://open-url?url=${encodeURIComponent(location.href)}`; +} diff --git a/src/openurlparam.ts b/src/openurlparam.ts index fc9f70e..a242605 100644 --- a/src/openurlparam.ts +++ b/src/openurlparam.ts @@ -1,29 +1,29 @@ // grab the (target) "url" param from the current URL query string (location.search) (() => { - // default to getting the current address & making it a URL object to work with - const origUrl: URL = new URL(document.location.href); + // default to getting the current address & making it a URL object to work with + const origUrl: URL = new URL(document.location.href); - // exit if the current URL object searchParam is less than 11 chars ('https://x.x) - if (origUrl.search.length < 11) { - return; - } + // exit if URL isn't https or if searchParam is less than 13 chars ('u=https://x.y') + if (origUrl.protocol !== 'https:' || origUrl.search.length < 13) { + return; + } - // create array of common param keys used for URLs (in priority order) - const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], - origParams: URLSearchParams = new URLSearchParams(origUrl.search); + // create array of common param keys used for URLs (in priority order) + const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], + origParams: URLSearchParams = new URLSearchParams(origUrl.search); - // find 1st param key match that's a valid looking URL, starting w/ 'https:' - const urlCandidate: string | undefined = urlKey - .filter((param: string) => origParams.has(param)) - .map((param: string) => { - const value: string | null = origParams.get(param); - return value ? decodeURIComponent(value) : ''; - }) - .find((url: string) => url.match(/^https:/)); + // find 1st param key match that's a valid looking URL, starting w/ 'https:' + const urlCandidate: string | undefined = urlKey + .filter((param: string) => origParams.has(param)) + .map((param: string) => { + const value: string | null = origParams.get(param); + return value ? decodeURIComponent(value) : ''; + }) + .find((url: string) => url.match(/^https:/)); - // navigate to new URL if found - if (urlCandidate) { - window.location.replace(new URL(urlCandidate)); - } + // navigate to new URL if found + if (urlCandidate) { + window.location.replace(new URL(urlCandidate)); + } })(); diff --git a/src/unskim.ts b/src/unskim.ts index f41f7a0..a0425e0 100644 --- a/src/unskim.ts +++ b/src/unskim.ts @@ -5,39 +5,39 @@ // wrap everything in an anon function for isolation (() => { - // default to getting the current address & making it a URL object to work with - let origUrl: URL = new URL(document.location.href); - - // handle special case of Safari error page (DNS block of redirection service) - if (origUrl.href === 'safari-resource:/ErrorPage.html') { - // DO NOT "UNCURL" the apostrophe or quotes in the match regex, that formatting IS CRITICAL - const urlStr: string | undefined = document.querySelector('p.error-message')?.textContent?.match(/Safari can't open the page "(https?:[^"]+)"/)?.[1]; - if (urlStr) { - origUrl = new URL(urlStr); - } - } - - // if the current URL object doesn't even have a searchParam, exit now - if (origUrl.search === '') { - return; - } - - // create array of common param keys used for URLs (in priority order) - const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], - origParams: URLSearchParams = new URLSearchParams(origUrl.search); - - // find 1st param key match that's a valid looking URL - const urlCandidate: string | undefined = urlKey - .filter((param: string) => origParams.has(param)) - .map((param: string) => { - const value: string | null = origParams.get(param); - return value ? decodeURIComponent(value) : ''; - }) - .find((url: string) => url.match(/^https?:/)); - - // navigate to new URL if found - if (urlCandidate) { - window.location.replace(new URL(urlCandidate)); - } + // default to getting the current address & making it a URL object to work with + let origUrl: URL = new URL(document.location.href); + + // handle special case of Safari error page (DNS block of redirection service) + if (origUrl.href === 'safari-resource:/ErrorPage.html') { + // DO NOT "UNCURL" the apostrophe or quotes in the match regex, that formatting IS CRITICAL + const urlStr: string | undefined = document.querySelector('p.error-message')?.textContent?.match(/Safari can't open the page "(https?:[^"]+)"/)?.[1]; + if (urlStr) { + origUrl = new URL(urlStr); + } + } + + // exit if searchParam is less than 13 chars ('u=https://x.y) + if (origUrl.search.length < 13) { + return; + } + + // create array of common param keys used for URLs (in priority order) + const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], + origParams: URLSearchParams = new URLSearchParams(origUrl.search); + + // find 1st param key match that's a valid looking URL + const urlCandidate: string | undefined = urlKey + .filter((param: string) => origParams.has(param)) + .map((param: string) => { + const value: string | null = origParams.get(param); + return value ? decodeURIComponent(value) : ''; + }) + .find((url: string) => url.match(/^https?:/)); + + // navigate to new URL if found + if (urlCandidate) { + window.location.replace(new URL(urlCandidate)); + } })(); diff --git a/src/utmstrip.ts b/src/utmstrip.ts index abad76f..36ab7ce 100644 --- a/src/utmstrip.ts +++ b/src/utmstrip.ts @@ -168,8 +168,8 @@ // Iterate and delete matching params for (const key of [...params.keys()]) { - if (shouldDelete(key)) { - params.delete(key); + if (shouldDelete(key)) { + params.delete(key); } } @@ -177,14 +177,15 @@ let searchStr: string = params.toString(); searchStr = searchStr ? `?${searchStr}` : ''; - // Clean up '/amp/' in pathname - const pathStr: string = locPath.replace(/\/amp\/?$/, ''); + // Clean up '/amp/' in pathname & assign newURL + const pathStr: string = locPath.replace(/\/amp\/?$/, ''), + newURL: string = `${location.protocol}//${location.host}${pathStr}${searchStr}${location.hash}`; - // If changed, replace location with stripped version - if (locSearch !== searchStr || locPath !== pathStr) { - if (confirm('Update history and copy cleaned URL to clipboard?')) { - const newURL: string = `${location.protocol}//${location.host}${pathStr}${searchStr}${location.hash}`; - navigator.clipboard.writeText(newURL); + // always _offer_ to copy the "cleaned" newURL + if (confirm('Update history and copy cleaned URL to clipboard?')) { + navigator.clipboard.writeText(newURL); + // If changed (truly cleaned), replace location with stripped version + if (locSearch !== searchStr || locPath !== pathStr) { history.replaceState(null, '', newURL); const newWindow: Window | null = window.open(newURL, '_self', 'noreferrer'); if (newWindow) { diff --git a/tsconfig.json b/tsconfig.json index 2be0caa..6f00d59 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "ES2020", + "target": "ES2023", "module": "none", - "lib": ["ES2020", "DOM"], + "lib": ["ES2023", "DOM"], "outDir": "./.temp", "strict": true, "noEmit": false, diff --git a/tsconfig.scripts.json b/tsconfig.scripts.json index d1f2e0b..1f3b961 100644 --- a/tsconfig.scripts.json +++ b/tsconfig.scripts.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "commonjs", - "lib": ["ES2020"], + "lib": ["ES2023"], "outDir": "./.temp-scripts", "types": ["node"] },