From bf7cff1ab5514e8b1fbf204802d4a42a20130ecb Mon Sep 17 00:00:00 2001 From: Tom King Date: Sat, 3 Jan 2026 18:39:13 -0800 Subject: [PATCH 01/14] feat(linklighter): improve highlight uniqueness, compatibility, code size & more --- README.md | 27 +++++---- bookmarklets.json | 2 +- dist/linklighter.bookmarklet | 2 +- src/linklighter.ts | 106 ++++++++++++++++++++++++++--------- 4 files changed, 95 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 20991a6..becf23a 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ AWS. This bookmarklet works with most browsers on most platforms. + __[KillStickyHeaders] v2.0.0__: Find & delete all fixed position elements of HTML body element. Cross-platform. -+ __[Linklighter] v2.0.0__: Use the current text selection on the active web ++ __[Linklighter] v2.1.0__: Use the current text selection on the active web page to generate a URL that will highlight the selected text when opened in a modern browser. If a new URL is generated, Linklighter ask to open it in a new window to preview the highlight, and copy the new URL to the clipboard. (See @@ -118,7 +118,7 @@ followed link into a bookmark for JavaScript bookmarklet. + __Mobile Safari setup link__ -- [Setup FYI] v3.3.0 + __Mobile Safari setup link__ -- [Setup IsItAws] v1.3.4 + __Mobile Safari setup link__ -- [Setup KillStickyHeaders] v2.0.0 -+ __Mobile Safari setup link__ -- [Setup Linklighter] v2.0.0 ++ __Mobile Safari setup link__ -- [Setup Linklighter] v2.1.0 + __Mobile Safari setup link__ -- [Setup OpenInBrave] v1.1.0 + __Mobile Safari setup link__ -- [Setup OpenInFirefox] v1.6.0 + __Mobile Safari setup link__ -- [Setup OpenInFirefox-Focus] v1.1.0 @@ -194,16 +194,15 @@ using a URL protocol scheme. + __KillStickyHeaders__ - Does _not_ use a URL protocol scheme. Removes HTML child elements of `` that have a fixed position. See [Kill sticky headers][Kill sticky headers]. -+ __Linklighter__: Does _not_ use a URL protocol scheme. Uses JavaScript to - get the current selection and append a `#:~:text=…` string to the current - URL. Modern browsers interpret this and when opening such a URL, the browser - scroll to the first matching selection and highlight that text. Logic will - automatically use the full selection when its less than 80 characters, or - algorithmically split the text into a shorter "start" fragment and "end" - fragment. In that case the browser highlights from the first match of the - start fragment to the last character of the next match of the "end" - fragment. To learn more about text fragment highlighting and security - considerations, refer to [Text fragments][Text fragments]. ++ __Linklighter__: Does _not_ use a URL protocol scheme. Gets the current + selection uses it to create a "Text Fragment" URL (e.g., appends `#:~:text=…` + to the current URL. Browsers interpret this and when opening such a URL, they + scroll to the first matching selection and highlight that text. Bookmarklet + logic optimizes fragment. Depending on the selection, it will use the whole + selection, a start/end fragment pair, and/or a prefix or suffix. Browsers + will highlights the first "best" match. To learn more about text fragment + highlighting, refer to [Text fragments][Text fragments]. Works with Safari + on Apple platforms, and Google Chrome for desktops. + __OpenInBrave__ - Uses the `brave://open-url?url=` scheme for the Brave app on iOS. + __OpenInFirefox__ _and_ __OpenInFirefox-Private__ - Uses the @@ -333,7 +332,7 @@ repos I had; doesn't build yet [FYI]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "FYI" [IsItAws]: javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "IsItAws" [KillStickyHeaders]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.0' "KillStickyHeaders" -[Linklighter]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Ct=n.length%2Ci=document.URL%3Blet%20o=i%2Cc=i.indexOf('%23')%2Cl=''%3Bif(e.empty()%2Cn%26%26''!==n)%7Bif(c%3E-1%26%26(o=o.substring(0%2Cc))%2Co%2B='%23:~:text='%2C80%3Et)l=n%2Co%2B=encodeURIComponent(n)%3Belse%7Blet%20e=~~(t%2F2-2)%3Bt%3E150%3Fe=48:t%3E100%26%26(e=~~(t%2F3))%2Cl=n.substring(0%2Ce)%3Bconst%20i=%5BencodeURIComponent(l)%2CencodeURIComponent(n.slice(t-e))%5D%3Bl%2B='%E2%80%A6'%2Cc=i%5B0%5D.lastIndexOf('%2520')%2Cc%3E-1%26%26(i%5B0%5D=i%5B0%5D.substring(0%2Cc))%2Cc=i%5B1%5D.indexOf('%2520')%2Cc%3E-1%26%26(i%5B1%5D=i%5B1%5D.slice(c%2B3))%2Co%2B=i.join()%7Do=o.replace(%2F(%250A%7C%2520A)%2B%24%2F%2C'')%2Co=o.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(o!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bl%7D%22%20and%20copy%20URL%20to%20clipboard%3F%5Cn%5CnNote:%20If%20text%20isn't%20highlighted%20in%20new%20tab%2C%20you%20can%20try%20again%20with%20a%20smaller%20selection.%60))%7Bnavigator.clipboard.writeText(o)%3Bconst%20e=window.open(o%2C'%5Fblank')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.0.0' "Linklighter" +[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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" [OpenInBrave]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "OpenInBrave" [OpenInFirefox]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "OpenInFirefox" [OpenInFirefox-Focus]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "OpenInFirefox-Focus" @@ -352,7 +351,7 @@ repos I had; doesn't build yet [Setup FYI]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "Setup FYI" [Setup IsItAws]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "Setup IsItAws" [Setup KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.0' "Setup KillStickyHeaders" -[Setup Linklighter]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Ct=n.length%2Ci=document.URL%3Blet%20o=i%2Cc=i.indexOf('%23')%2Cl=''%3Bif(e.empty()%2Cn%26%26''!==n)%7Bif(c%3E-1%26%26(o=o.substring(0%2Cc))%2Co%2B='%23:~:text='%2C80%3Et)l=n%2Co%2B=encodeURIComponent(n)%3Belse%7Blet%20e=~~(t%2F2-2)%3Bt%3E150%3Fe=48:t%3E100%26%26(e=~~(t%2F3))%2Cl=n.substring(0%2Ce)%3Bconst%20i=%5BencodeURIComponent(l)%2CencodeURIComponent(n.slice(t-e))%5D%3Bl%2B='%E2%80%A6'%2Cc=i%5B0%5D.lastIndexOf('%2520')%2Cc%3E-1%26%26(i%5B0%5D=i%5B0%5D.substring(0%2Cc))%2Cc=i%5B1%5D.indexOf('%2520')%2Cc%3E-1%26%26(i%5B1%5D=i%5B1%5D.slice(c%2B3))%2Co%2B=i.join()%7Do=o.replace(%2F(%250A%7C%2520A)%2B%24%2F%2C'')%2Co=o.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(o!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bl%7D%22%20and%20copy%20URL%20to%20clipboard%3F%5Cn%5CnNote:%20If%20text%20isn't%20highlighted%20in%20new%20tab%2C%20you%20can%20try%20again%20with%20a%20smaller%20selection.%60))%7Bnavigator.clipboard.writeText(o)%3Bconst%20e=window.open(o%2C'%5Fblank')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.0.0' "Setup Linklighter" +[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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "Setup OpenInBrave" [Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "Setup OpenInFirefox" [Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "Setup OpenInFirefox-Focus" diff --git a/bookmarklets.json b/bookmarklets.json index 81be683..87c5325 100644 --- a/bookmarklets.json +++ b/bookmarklets.json @@ -18,7 +18,7 @@ { "name": "Linklighter", "file": "linklighter.bookmarklet", - "version": "2.0.0" + "version": "2.1.0" }, { "name": "OpenInBrave", diff --git a/dist/linklighter.bookmarklet b/dist/linklighter.bookmarklet index e081c60..1ebad84 100644 --- a/dist/linklighter.bookmarklet +++ b/dist/linklighter.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Ct=n.length%2Ci=document.URL%3Blet%20o=i%2Cc=i.indexOf('%23')%2Cl=''%3Bif(e.empty()%2Cn%26%26''!==n)%7Bif(c%3E-1%26%26(o=o.substring(0%2Cc))%2Co%2B='%23:~:text='%2C80%3Et)l=n%2Co%2B=encodeURIComponent(n)%3Belse%7Blet%20e=~~(t%2F2-2)%3Bt%3E150%3Fe=48:t%3E100%26%26(e=~~(t%2F3))%2Cl=n.substring(0%2Ce)%3Bconst%20i=%5BencodeURIComponent(l)%2CencodeURIComponent(n.slice(t-e))%5D%3Bl%2B='%E2%80%A6'%2Cc=i%5B0%5D.lastIndexOf('%2520')%2Cc%3E-1%26%26(i%5B0%5D=i%5B0%5D.substring(0%2Cc))%2Cc=i%5B1%5D.indexOf('%2520')%2Cc%3E-1%26%26(i%5B1%5D=i%5B1%5D.slice(c%2B3))%2Co%2B=i.join()%7Do=o.replace(%2F(%250A%7C%2520A)%2B%24%2F%2C'')%2Co=o.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(o!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bl%7D%22%20and%20copy%20URL%20to%20clipboard%3F%5Cn%5CnNote:%20If%20text%20isn't%20highlighted%20in%20new%20tab%2C%20you%20can%20try%20again%20with%20a%20smaller%20selection.%60))%7Bnavigator.clipboard.writeText(o)%3Bconst%20e=window.open(o%2C'%5Fblank')%3Be%26%26(e.opener=null)%7D%7D)()%3Bvoid'2.0.0' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' \ No newline at end of file diff --git a/src/linklighter.ts b/src/linklighter.ts index 9173e3c..f5bee8a 100644 --- a/src/linklighter.ts +++ b/src/linklighter.ts @@ -3,6 +3,7 @@ // If a new URL is generated, open it in a new window to preview the highlight (() => { + const enc: typeof encodeURIComponent = encodeURIComponent; const selection: Selection | null = window.getSelection(); if (!selection) { return; @@ -12,22 +13,65 @@ url: string = document.URL; let newUrl: string = url, strPos: number = url.indexOf('#'), - strStart: string = ''; + strStart: string = '', + prefix: string = '', + suffix: string = ''; + + // Check for duplicate text and get context if needed + if (selection.rangeCount > 0 && textFrag) { + const bodyText: string = document.body.textContent || ''; + const occurrences: number = bodyText.split(textFrag).length - 1; + + // Only add prefix/suffix if text appears multiple times + if (occurrences > 1) { + const range: Range = selection.getRangeAt(0); + const container: Node = range.commonAncestorContainer; + const fullText: string = container.textContent || ''; + const startOffset: number = range.startOffset; + const endOffset: number = range.endOffset; + + // Extract prefix (up to 20 chars before selection) + const prefixStart: number = Math.max(0, startOffset - 20); + prefix = fullText.substring(prefixStart, startOffset).trim(); + // If we cut mid-word at start, shift start forward to first complete word + if (prefixStart > 0 && fullText.charAt(prefixStart - 1) !== ' ') { + const firstSpace: number = prefix.indexOf(' '); + if (firstSpace > 0) { + prefix = prefix.substring(firstSpace + 1); + } + } + + // Extract suffix (up to 20 chars after selection) + const suffixEnd: number = Math.min(fullText.length, endOffset + 20); + suffix = fullText.substring(endOffset, suffixEnd).trim(); + // If we cut mid-word at end, shift end backward to last complete word + if (suffixEnd < fullText.length && fullText.charAt(suffixEnd) !== ' ') { + const lastSpace: number = suffix.lastIndexOf(' '); + if (lastSpace > 0) { + suffix = suffix.substring(0, lastSpace); + } + } + } + } // reset selection - selection.empty(); + selection.removeAllRanges(); // use text fragment or split it into subfragments if (textFrag && textFrag !== '') { // trim named anchor off of end of url if (strPos > -1) { newUrl = newUrl.substring(0, strPos); } - // append 1st part of suffix - newUrl += '#:~:text='; + + // Build text fragment + let fragment: string = ''; + if (prefix) { + fragment = enc(prefix) + '-,'; + } + if (textFragLen < 80) { strStart = textFrag; - // append selection as a single fragment - newUrl += encodeURIComponent(textFrag); + fragment += enc(textFrag); } else { // sub-fragment default length is < 1/2 selection length let subLen: number = ~~((textFragLen / 2) - 2); @@ -37,35 +81,45 @@ subLen = ~~(textFragLen / 3); } // create start & end subfragments of selection - strStart = textFrag.substring(0, subLen); - const subFrag: string[] = [encodeURIComponent(strStart), - encodeURIComponent(textFrag.slice(textFragLen - subLen))]; - strStart += '…'; - // trim start string- truncate at last space - strPos = subFrag[0].lastIndexOf('%20'); - if (strPos > -1) { - subFrag[0] = subFrag[0].substring(0, strPos); + let startFrag: string = textFrag.substring(0, subLen); + let endFrag: string = textFrag.slice(textFragLen - subLen); + + // Improve word boundary awareness - trim to last/first word boundary + const lastSpace: number = startFrag.lastIndexOf(' '); + if (lastSpace > subLen / 2) { + startFrag = startFrag.substring(0, lastSpace); } - // trim end string- drop text before first space - strPos = subFrag[1].indexOf('%20'); - if (strPos > -1) { - subFrag[1] = subFrag[1].slice(strPos + 3); + const firstSpace: number = endFrag.indexOf(' '); + if (firstSpace > -1 && firstSpace < subLen / 2) { + endFrag = endFrag.substring(firstSpace + 1); } - // append selection as start & end subfragments - newUrl += subFrag.join(); + + strStart = startFrag + '…'; + fragment += enc(startFrag) + ',' + enc(endFrag); } - // clean-up trailing %0A (newline) or %20 (space) - // and any leading double-hash, just in case - newUrl = newUrl.replace((/(%0A|%20A)+$/), ''); + + if (suffix) { + fragment += ',-' + enc(suffix); + } + + newUrl += '#:~:text=' + fragment; + // clean-up trailing %0A (newline), %0D (carriage return), %09 (tab), or %20 (space) + newUrl = newUrl.replace(/(%0A|%0D|%09|%20)+$/g, ''); + newUrl = newUrl.replace(/(%20){2,}/g, '%20'); newUrl = newUrl.replace(/##+:~:text=/, '#:~:text='); } if (newUrl !== url) { - if (confirm(`Open URL with highlight on "${strStart}" and copy URL to clipboard?\n\nNote: If text isn't highlighted in new tab, you can try again with a smaller selection.`)) { - // send to clipboard & open in new window - navigator.clipboard.writeText(newUrl); + if (confirm(`Open URL with highlight on "${strStart}" and copy URL to clipboard?`)) { + // Copy to clipboard with error feedback + navigator.clipboard.writeText(newUrl).catch(() => { + alert('Could not copy to clipboard. URL is in the new tab.'); + }); + const newWindow: Window | null = window.open(newUrl, '_blank'); if (newWindow) { newWindow.opener = null; + } else { + alert('Popup blocked. URL copied to clipboard.'); } } } From d9a66016ac7a769709ec93c45288d73097dcf61e Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 00:31:53 -0800 Subject: [PATCH 02/14] feat(x-man): use tighter wording in alerts (shorter bookmarklet) --- src/x-man.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/x-man.ts b/src/x-man.ts index 94cb593..f661067 100644 --- a/src/x-man.ts +++ b/src/x-man.ts @@ -44,7 +44,7 @@ newWin.opener = null; } } catch (e) { - alertMsg = `Popup window blocked. Unable to open new link with Terminal, but clipboard contains "${xman}"`; + alertMsg = `Popup window blocked. Clipboard contains "${xman}"`; if (e instanceof Error) { console.error(e.name, e.message); } @@ -59,7 +59,7 @@ } } } else if (xman !== '') { - alertMsg = `Browser doesn't look like Mac Safari. Unable to open new link with Terminal, but clipboard contains "${xman}"`; + alertMsg = `Unable to open new link with Terminal. Clipboard contains "${xman}"`; } if (alertMsg !== '' ) { alert(alertMsg); From 0fffbf31a5d03d0def1d26404296d1662dc5458c Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 00:32:34 -0800 Subject: [PATCH 03/14] chore: bump version for changed bookmarklets, update docs --- README.md | 22 +++++++++++----------- bookmarklets.json | 4 ++-- dist/linklighter.bookmarklet | 2 +- dist/unskim.bookmarklet | 2 +- dist/x-man.bookmarklet | 2 +- package-lock.json | 5 ----- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index becf23a..1c8c708 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,13 @@ Also removes Google `/amp/` suffix fromU URL path. Asks to copy the new URL to the clipboard. Finally, replaces history & reloads the page. _NOTE:_ This bookmarklet also works with Safari and Firefox on macOS. -+ __[unskim] v2.0.0__: Bypass redirect and affiliate link wrappers by ++ __[unskim] v2.0.1__: 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 from the error message. -+ __[x-man] v1.3.0__: Using the selected text in browser, create a ++ __[x-man] v1.3.1__: Using the selected text in browser, create a `x-man-page://` link to the corresponding man page and offer to place that link on the clipboard. When using Safari on Mac, the bookmarklet will use Safari to open the corresponding man page with yellow highlighting in @@ -128,8 +128,8 @@ followed link into a bookmark for JavaScript bookmarklet. + __Mobile Safari setup link__ -- [Setup OpenInWorkingCopy] v1.6.0 + __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.1.0 + __Mobile Safari setup link__ -- [Setup UtmStrip] v2.0.0 -+ __Mobile Safari setup link__ -- [Setup unskim] v2.0.0 -+ __Mobile Safari setup link__ -- [Setup x-man] v1.3.0 ++ __Mobile Safari setup link__ -- [Setup unskim] v2.0.1 ++ __Mobile Safari setup link__ -- [Setup x-man] v1.3.1 ## Requirements @@ -332,7 +332,7 @@ repos I had; doesn't build yet [FYI]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "FYI" [IsItAws]: javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "IsItAws" [KillStickyHeaders]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" +[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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" [OpenInBrave]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "OpenInBrave" [OpenInFirefox]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "OpenInFirefox" [OpenInFirefox-Focus]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "OpenInFirefox-Focus" @@ -342,8 +342,8 @@ repos I had; doesn't build yet [OpenInWorkingCopy]: javascript:'use%20strict'%3Bif(%2FiP(.d%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.0' "OpenInWorkingCopy" [OpenURLParam]: javascript:'use%20strict'%3Bconst%20c=location.search.search('url=')%3Bif(c%3E-1)%7Blet%20o=location.search.slice(4%2Bc)%3Bconst%20t=o.indexOf('%26')%3Bif(t%3E-1%26%26(o=o.slice(0%2Ct))%2Co.length%3E5)%7Bconst%20c=decodeURIComponent(o)%3Btry%7Bconst%20o=new%20URL(c%2Clocation.href)%3B'https:'===o.protocol%26%26location.replace(o.href)%7Dcatch%7B%7D%7D%7Dvoid'1.1.0' "OpenURLParam" [UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Cc=location.search%3Bif(3%3Ec.length%26%26!e.includes('%2Famp'))return%3Blet%20i=e%2Ca=c%3Bconst%20r=location.hostname%3Bif(r.includes('aliexpress.')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2Ca.includes('fb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('action%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca.includes('aff%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.toLowerCase().includes('id=')%26%26(a=a.replace(%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('ga%5F')%7C%7Ca.includes('utm%5F'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(r)%7C%7C'youtu.be'===r%7C%7C'www.youtube-nocookie.com'===r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('%5Fhsenc')%7C%7Ca.includes('%5Fhsmi'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('hmb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('cm%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('mc%5Fcid')%7C%7Ca.includes('mc%5Feid'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('iesrc')%7C%7Ca.includes('mkt%5Ftok'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('pk%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F%26%26%2B%2Fg%2C'%26')%2C'%26'===a.charAt(a.length-1)%26%26(a=a.slice(0%2C-1))%2C'%3F'!==a.charAt(0)%26%26(a='%3F'%2Ba)%2Ca.includes('%3F%26')%26%26(a='%3F'%2Ba.slice(2))%2C3%3Ea.length%26%26(a='')%2Ci=i.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(c!==a%7C%7Ce!==i)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bi%7D%24%7Ba%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20c=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bc%26%26(c.opener=null)%7D%7D)()%3Bvoid'2.0.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.0' "unskim" -[x-man]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20n=navigator.userAgent%2Co=n.includes('Chrome%2F')%7C%7Cn.includes('Firefox%2F')%7C%7Cn.includes('Brave%2F')%7C%7Cn.includes('Edg%2F')%2Ci=n.includes('Safari%2F')%2Ct=!o%26%26!i%2Ce=navigator.platform.startsWith('Mac')%26%26i%26%26!o%26%26!t%26%26!navigator.maxTouchPoints%26%262%3Enavigator.maxTouchPoints%2Ca=window.getSelection()%3Bif(!a)return%3Bconst%20l=a.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%20n=''%3Bif(navigator.clipboard%3Fnavigator.clipboard.writeText(r):(n=%60Unable%20to%20copy%20%22%24%7Br%7D%22%20to%20clipboard.%60%2Cr='')%2Ce%26%26''!==r)%7Blet%20o=null%3Btry%7Bo=window.open(r)%2Co%26%26(o.opener=null)%7Dcatch(o)%7Bn=%60Popup%20window%20blocked.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60%7Dfinally%7Bwindow.focus()%2Cnull!==o%26%26setTimeout((()=%3E%7Bo%26%26o.close()%7D)%2C3333)%7D%7Delse''!==r%26%26(n=%60Browser%20doesn't%20look%20like%20Mac%20Safari.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60)%3B''!==n%26%26alert(n)%7Da.empty()%7D%7D)()%3Bvoid'1.3.0' "x-man" +[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" +[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" @@ -351,7 +351,7 @@ repos I had; doesn't build yet [Setup FYI]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "Setup FYI" [Setup IsItAws]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "Setup IsItAws" [Setup KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Setup Linklighter" +[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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "Setup OpenInBrave" [Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "Setup OpenInFirefox" [Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "Setup OpenInFirefox-Focus" @@ -361,8 +361,8 @@ repos I had; doesn't build yet [Setup OpenInWorkingCopy]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%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.0' "Setup OpenInWorkingCopy" [Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bconst%20c=location.search.search('url=')%3Bif(c%3E-1)%7Blet%20o=location.search.slice(4%2Bc)%3Bconst%20t=o.indexOf('%26')%3Bif(t%3E-1%26%26(o=o.slice(0%2Ct))%2Co.length%3E5)%7Bconst%20c=decodeURIComponent(o)%3Btry%7Bconst%20o=new%20URL(c%2Clocation.href)%3B'https:'===o.protocol%26%26location.replace(o.href)%7Dcatch%7B%7D%7D%7Dvoid'1.1.0' "Setup OpenURLParam" [Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Cc=location.search%3Bif(3%3Ec.length%26%26!e.includes('%2Famp'))return%3Blet%20i=e%2Ca=c%3Bconst%20r=location.hostname%3Bif(r.includes('aliexpress.')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2Ca.includes('fb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('action%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca.includes('aff%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.toLowerCase().includes('id=')%26%26(a=a.replace(%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('ga%5F')%7C%7Ca.includes('utm%5F'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(r)%7C%7C'youtu.be'===r%7C%7C'www.youtube-nocookie.com'===r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('%5Fhsenc')%7C%7Ca.includes('%5Fhsmi'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('hmb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('cm%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('mc%5Fcid')%7C%7Ca.includes('mc%5Feid'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('iesrc')%7C%7Ca.includes('mkt%5Ftok'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('pk%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F%26%26%2B%2Fg%2C'%26')%2C'%26'===a.charAt(a.length-1)%26%26(a=a.slice(0%2C-1))%2C'%3F'!==a.charAt(0)%26%26(a='%3F'%2Ba)%2Ca.includes('%3F%26')%26%26(a='%3F'%2Ba.slice(2))%2C3%3Ea.length%26%26(a='')%2Ci=i.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(c!==a%7C%7Ce!==i)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bi%7D%24%7Ba%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20c=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bc%26%26(c.opener=null)%7D%7D)()%3Bvoid'2.0.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.0' "Setup unskim" -[Setup x-man]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20n=navigator.userAgent%2Co=n.includes('Chrome%2F')%7C%7Cn.includes('Firefox%2F')%7C%7Cn.includes('Brave%2F')%7C%7Cn.includes('Edg%2F')%2Ci=n.includes('Safari%2F')%2Ct=!o%26%26!i%2Ce=navigator.platform.startsWith('Mac')%26%26i%26%26!o%26%26!t%26%26!navigator.maxTouchPoints%26%262%3Enavigator.maxTouchPoints%2Ca=window.getSelection()%3Bif(!a)return%3Bconst%20l=a.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%20n=''%3Bif(navigator.clipboard%3Fnavigator.clipboard.writeText(r):(n=%60Unable%20to%20copy%20%22%24%7Br%7D%22%20to%20clipboard.%60%2Cr='')%2Ce%26%26''!==r)%7Blet%20o=null%3Btry%7Bo=window.open(r)%2Co%26%26(o.opener=null)%7Dcatch(o)%7Bn=%60Popup%20window%20blocked.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60%7Dfinally%7Bwindow.focus()%2Cnull!==o%26%26setTimeout((()=%3E%7Bo%26%26o.close()%7D)%2C3333)%7D%7Delse''!==r%26%26(n=%60Browser%20doesn't%20look%20like%20Mac%20Safari.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60)%3B''!==n%26%26alert(n)%7Da.empty()%7D%7D)()%3Bvoid'1.3.0' "Setup x-man" +[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 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" @@ -377,7 +377,7 @@ repos I had; doesn't build yet [OpenInlets page]: http://mobilemind.github.io/OpenInlets/ [GoodReader URL Scheme]: http://www.goodreader.com/gr-man-howto.html#ghttp "GoodReader:How do I save a file from Safari to GoodReader?" -[Textastic x-callback-url API]: https://www.textasticapp.com/v4/manual/x-callback-url.html#downloadusingthetextastic:scheme +[Textastic x-callback-url API]: https://www.textasticapp.com/v10/manual/integration_other_apps/x-callback-url.html#download-using-the-textastic-scheme "Download using the textastic:// scheme" [Text fragments]: https://developer.mozilla.org/en-US/docs/Web/Text_fragments [Working Copy URL Scheme]: https://workingcopyapp.com/url-schemes.html diff --git a/bookmarklets.json b/bookmarklets.json index 87c5325..51a2b75 100644 --- a/bookmarklets.json +++ b/bookmarklets.json @@ -73,12 +73,12 @@ { "name": "unskim", "file": "unskim.bookmarklet", - "version": "2.0.0" + "version": "2.0.1" }, { "name": "x-man", "file": "x-man.bookmarklet", - "version": "1.3.0" + "version": "1.3.1" } ] } diff --git a/dist/linklighter.bookmarklet b/dist/linklighter.bookmarklet index 1ebad84..8f01237 100644 --- a/dist/linklighter.bookmarklet +++ b/dist/linklighter.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch((()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D))%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' \ No newline at end of file diff --git a/dist/unskim.bookmarklet b/dist/unskim.bookmarklet index 6d24209..5294689 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.0' \ 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(''===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 diff --git a/dist/x-man.bookmarklet b/dist/x-man.bookmarklet index ec0730c..e794e2a 100644 --- a/dist/x-man.bookmarklet +++ b/dist/x-man.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20n=navigator.userAgent%2Co=n.includes('Chrome%2F')%7C%7Cn.includes('Firefox%2F')%7C%7Cn.includes('Brave%2F')%7C%7Cn.includes('Edg%2F')%2Ci=n.includes('Safari%2F')%2Ct=!o%26%26!i%2Ce=navigator.platform.startsWith('Mac')%26%26i%26%26!o%26%26!t%26%26!navigator.maxTouchPoints%26%262%3Enavigator.maxTouchPoints%2Ca=window.getSelection()%3Bif(!a)return%3Bconst%20l=a.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%20n=''%3Bif(navigator.clipboard%3Fnavigator.clipboard.writeText(r):(n=%60Unable%20to%20copy%20%22%24%7Br%7D%22%20to%20clipboard.%60%2Cr='')%2Ce%26%26''!==r)%7Blet%20o=null%3Btry%7Bo=window.open(r)%2Co%26%26(o.opener=null)%7Dcatch(o)%7Bn=%60Popup%20window%20blocked.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60%7Dfinally%7Bwindow.focus()%2Cnull!==o%26%26setTimeout((()=%3E%7Bo%26%26o.close()%7D)%2C3333)%7D%7Delse''!==r%26%26(n=%60Browser%20doesn't%20look%20like%20Mac%20Safari.%20Unable%20to%20open%20new%20link%20with%20Terminal%2C%20but%20clipboard%20contains%20%22%24%7Br%7D%22%60)%3B''!==n%26%26alert(n)%7Da.empty()%7D%7D)()%3Bvoid'1.3.0' \ No newline at end of file +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' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4fe724d..6169f61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -387,7 +387,6 @@ "integrity": "sha512-3xP4XzzDNQOIqBMWogftkwxhg5oMKApqY0BAflmLZiFYHqyhSOxv/cd/zPQLTcCXr4AkaKb25joocY0BD1WC6A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.51.0", "@typescript-eslint/types": "8.51.0", @@ -592,7 +591,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -794,7 +792,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -1571,7 +1568,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -1611,7 +1607,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From fef4a3873e69f28e46ac5913e423d1c569d1d58e Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 00:55:51 -0800 Subject: [PATCH 04/14] feat(utmstrip): refactor source for smaller dist version (<4Kb) - Removed duplicate aff_ handling of AliExpress URLs - Add strip() substitute for repeated .replace() pattern throughout the code - Merge checks using includes() with .test() for HubSpot, MailChimp, & Marketo - Optimize cleanup of orphaned params or query string marker - Change wording of alert --- src/utmstrip.ts | 81 ++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/src/utmstrip.ts b/src/utmstrip.ts index 41ef5df..96216d7 100644 --- a/src/utmstrip.ts +++ b/src/utmstrip.ts @@ -9,88 +9,73 @@ } let pathStr: string = locPath, searchStr: string = locSearch; - const hostStr: string = location.hostname; + const hostStr: string = location.hostname, + strip = (s: string, p: RegExp): string => s.replace(p, '$1'); // AliExpress trackers if (hostStr.includes('aliexpress.')) { - searchStr = searchStr.replace(/([?&])aff_(platform|trace_key)=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])algo_[ep]vid=[^&]+/g, '$1'); - searchStr = searchStr.replace(/([?&])(btsid|ws_ab_test)=[^&]+/g, '$1'); - searchStr = searchStr.replace(/([?&])s[cp]m=[^&]+/g, '$1'); + searchStr = strip(searchStr, /([?&])aff_(platform|trace_key)=[^&]+/ig); + searchStr = strip(searchStr, /([?&])algo_[ep]vid=[^&]+/g); + searchStr = strip(searchStr, /([?&])(btsid|ws_ab_test)=[^&]+/g); + searchStr = strip(searchStr, /([?&])s[cp]m=[^&]+/g); } // Amazon referrals if ((/(|\.)amazon\.com$/).test(hostStr)) { - searchStr = searchStr.replace(/([?&])(_encoding|ie|linkCode|linkId|pf|psc|ref_|tag)=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])p[df]_rd_.*?=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])(content-id|crid|cv_ct_cx|language|qid|sprefix|sr|th)=[^&]+/g, '$1'); - searchStr = searchStr.replace(/([?&])asc(_campaign|_refurl|_source|subtag)=[^&]+/g, '$1'); - searchStr = searchStr.replace(/([?&])dib(_tag)?=[^&]+/g, '$1'); + searchStr = strip(searchStr, /([?&])(_encoding|ie|linkCode|linkId|pf|psc|ref_|tag)=[^&]+/ig); + searchStr = strip(searchStr, /([?&])p[df]_rd_.*?=[^&]+/ig); + searchStr = strip(searchStr, /([?&])(content-id|crid|cv_ct_cx|language|qid|sprefix|sr|th)=[^&]+/g); + searchStr = strip(searchStr, /([?&])asc(_campaign|_refurl|_source|subtag)=[^&]+/g); + searchStr = strip(searchStr, /([?&])dib(_tag)?=[^&]+/g); } // Facebook if (searchStr.includes('fb_')) { - searchStr = searchStr.replace(/([?&])fb_(action_ids|action_types|ref|source)=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])(fbclid|hrc|refsrc)=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])fb_(action_ids|action_types|ref|source)=[^&]+/ig); + searchStr = strip(searchStr, /([?&])(fbclid|hrc|refsrc)=[^&]+/ig); } if (searchStr.includes('action_')) { - searchStr = searchStr.replace(/([?&])action_(object|ref|type)_map=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])action_(object|ref|type)_map=[^&]+/ig); } // generic/general - searchStr = searchStr.replace(/([?&])(assetType|elqTrack|mkt_tok|originalReferer|referrer|terminal_id|trk|trkCampaign|trkInfo)=[^&]+/ig, '$1'); - if (searchStr.includes('aff_')) { - searchStr = searchStr.replace(/([?&])aff_(platform|trace_key)=[^&]+/ig, '$1'); - } + searchStr = strip(searchStr, /([?&])(assetType|elqTrack|mkt_tok|originalReferer|referrer|terminal_id|trk|trkCampaign|trkInfo)=[^&]+/ig); if (searchStr.toLowerCase().includes('id=')) { - searchStr = searchStr.replace(/([?&])(an|asset|campaign|e|gcl|recipient|site)id=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])(an|asset|campaign|e|gcl|recipient|site)id=[^&]+/ig); } // Google Analytics if (searchStr.includes('ga_') || searchStr.includes('utm_')) { - searchStr = searchStr.replace(/([?&])(ga|utm)_(campaign|cid|content|design|medium|name|place|pubreferrer|reader|source|swu|term|userid|viz_id)=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])gcl(id|src)=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])(ga|utm)_(campaign|cid|content|design|medium|name|place|pubreferrer|reader|source|swu|term|userid|viz_id)=[^&]+/ig); + searchStr = strip(searchStr, /([?&])gcl(id|src)=[^&]+/ig); } // Google YouTube (handles youtube.com, youtu.be, etc.) if ((/(m|www)\.youtube\.com$/).test(hostStr) || hostStr === 'youtu.be' || hostStr === 'www.youtube-nocookie.com') { - searchStr = searchStr.replace(/([?&])(ac|annotation_id|app|feature|gclid|kw|src_vid)=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])(ac|annotation_id|app|feature|gclid|kw|src_vid)=[^&]+/ig); } // HubSpot - if (searchStr.includes('_hsenc') || searchStr.includes('_hsmi')) { - searchStr = searchStr.replace(/([?&])_hs(enc|mi)=[^&]+/ig, '$1'); + if (/[?&]_hs(enc|mi)=/.test(searchStr)) { + searchStr = strip(searchStr, /([?&])_hs(enc|mi)=[^&]+/ig); } if (searchStr.includes('hmb_')) { - searchStr = searchStr.replace(/([?&])hmb_(campaign|medium|source)=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])hmb_(campaign|medium|source)=[^&]+/ig); } // IBM Digital Analytics (Coremetrics) if (searchStr.includes('cm_')) { - searchStr = searchStr.replace(/([?&])cm_(mmc|mmca\d+|re|sp)=[^&]+/ig, '$1'); - searchStr = searchStr.replace(/([?&])manual_cm_mmc=[^&]+/ig, '$1'); + searchStr = strip(searchStr, /([?&])cm_(mmc|mmca\d+|re|sp)=[^&]+/ig); + searchStr = strip(searchStr, /([?&])manual_cm_mmc=[^&]+/ig); } // MailChimp - if (searchStr.includes('mc_cid') || searchStr.includes('mc_eid')) { - searchStr = searchStr.replace(/([?&])mc_[ce]id=[^&]+/ig, '$1'); + if (/[?&]mc_[ce]id=/.test(searchStr)) { + searchStr = strip(searchStr, /([?&])mc_[ce]id=[^&]+/ig); } // Marketo - if (searchStr.includes('iesrc') || searchStr.includes('mkt_tok')) { - searchStr = searchStr.replace(/([?&])(iesrc|mkt_tok)=[^&]+/ig, '$1'); + if (/[?&](iesrc|mkt_tok)=/.test(searchStr)) { + searchStr = strip(searchStr, /([?&])(iesrc|mkt_tok)=[^&]+/ig); } // Matomo if (searchStr.includes('pk_')) { - searchStr = searchStr.replace(/([?&])pk_(campaign|content|kwd|medium|source)=[^&]+/ig, '$1'); - } - // clean-up: reduce run of '&', conditionally remove trailing '&' - searchStr = searchStr.replace(/&&+/g, '&'); - if (searchStr.charAt(searchStr.length - 1) === '&') { - searchStr = searchStr.slice(0, -1); - } - // clean-up: restore leading '?' if necessary - if (searchStr.charAt(0) !== '?') { - searchStr = `?${searchStr}`; - } - // clean-up '?¶m=value' --> '?param=value' - if (searchStr.includes('?&')) { - searchStr = `?${searchStr.slice(2)}`; - } - // clean-up: nullify if no params assigns left (e.g. '?' or '?param') - if (searchStr.length < 3) { - searchStr = ''; + searchStr = strip(searchStr, /([?&])pk_(campaign|content|kwd|medium|source)=[^&]+/ig); } + // clean-up: reduce run of '&', remove trailing '&', fix leading '?' + searchStr = searchStr.replace(/&&+/g, '&').replace(/&$/, ''); + searchStr = searchStr[0] === '?' ? searchStr.replace(/^\?&/, '?') : `?${searchStr}`; + searchStr = searchStr.length < 3 ? '' : searchStr; // clean-up '/amp/' in pathname pathStr = pathStr.replace(/\/amp\/?$/, ''); // if changed replace location with stripped version From 938bdc94c446c2c4ea6eb06a36bb65f2e79a9513 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 01:04:12 -0800 Subject: [PATCH 05/14] chore: bump version of utmstrip, update dist & docs --- README.md | 8 ++++---- bookmarklets.json | 2 +- dist/utmstrip.bookmarklet | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1c8c708..94b9f01 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ 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.0.0__: Strips off the UTM query string elements of the ++ __[UtmStrip] v2.1.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 fromU URL path. Asks to copy the new URL to the clipboard. Finally, replaces history & reloads the page. _NOTE:_ This @@ -127,7 +127,7 @@ followed link into a bookmark for JavaScript bookmarklet. + __Mobile Safari setup link__ -- [Setup OpenInTextastic] v1.1.0 + __Mobile Safari setup link__ -- [Setup OpenInWorkingCopy] v1.6.0 + __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.1.0 -+ __Mobile Safari setup link__ -- [Setup UtmStrip] v2.0.0 ++ __Mobile Safari setup link__ -- [Setup UtmStrip] v2.1.0 + __Mobile Safari setup link__ -- [Setup unskim] v2.0.1 + __Mobile Safari setup link__ -- [Setup x-man] v1.3.1 @@ -341,7 +341,7 @@ repos I had; doesn't build yet [OpenInTextastic]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.0' "OpenInTextastic" [OpenInWorkingCopy]: javascript:'use%20strict'%3Bif(%2FiP(.d%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.0' "OpenInWorkingCopy" [OpenURLParam]: javascript:'use%20strict'%3Bconst%20c=location.search.search('url=')%3Bif(c%3E-1)%7Blet%20o=location.search.slice(4%2Bc)%3Bconst%20t=o.indexOf('%26')%3Bif(t%3E-1%26%26(o=o.slice(0%2Ct))%2Co.length%3E5)%7Bconst%20c=decodeURIComponent(o)%3Btry%7Bconst%20o=new%20URL(c%2Clocation.href)%3B'https:'===o.protocol%26%26location.replace(o.href)%7Dcatch%7B%7D%7D%7Dvoid'1.1.0' "OpenURLParam" -[UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Cc=location.search%3Bif(3%3Ec.length%26%26!e.includes('%2Famp'))return%3Blet%20i=e%2Ca=c%3Bconst%20r=location.hostname%3Bif(r.includes('aliexpress.')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2Ca.includes('fb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('action%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca.includes('aff%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.toLowerCase().includes('id=')%26%26(a=a.replace(%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('ga%5F')%7C%7Ca.includes('utm%5F'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(r)%7C%7C'youtu.be'===r%7C%7C'www.youtube-nocookie.com'===r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('%5Fhsenc')%7C%7Ca.includes('%5Fhsmi'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('hmb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('cm%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('mc%5Fcid')%7C%7Ca.includes('mc%5Feid'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('iesrc')%7C%7Ca.includes('mkt%5Ftok'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('pk%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F%26%26%2B%2Fg%2C'%26')%2C'%26'===a.charAt(a.length-1)%26%26(a=a.slice(0%2C-1))%2C'%3F'!==a.charAt(0)%26%26(a='%3F'%2Ba)%2Ca.includes('%3F%26')%26%26(a='%3F'%2Ba.slice(2))%2C3%3Ea.length%26%26(a='')%2Ci=i.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(c!==a%7C%7Ce!==i)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bi%7D%24%7Ba%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20c=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bc%26%26(c.opener=null)%7D%7D)()%3Bvoid'2.0.0' "UtmStrip" +[UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Ci=location.search%3Bif(3%3Ei.length%26%26!e.includes('%2Famp'))return%3Blet%20c=e%2Ct=i%3Bconst%20a=location.hostname%2Cn=(e%2Ci)=%3Ee.replace(i%2C'%241')%3Bif(a.includes('aliexpress.')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg))%2Ct.includes('fb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('action%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi))%2Ct=n(t%2C%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi)%2Ct.toLowerCase().includes('id=')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi))%2C(t.includes('ga%5F')%7C%7Ct.includes('utm%5F'))%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(a)%7C%7C'youtu.be'===a%7C%7C'www.youtube-nocookie.com'===a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D%5Fhs(enc%7Cmi)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('hmb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('cm%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5Dmc%5F%5Bce%5Did=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D(iesrc%7Cmkt%5Ftok)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('pk%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct=t.replace(%2F%26%26%2B%2Fg%2C'%26').replace(%2F%26%24%2F%2C'')%2Ct='%3F'===t%5B0%5D%3Ft.replace(%2F%5E%5C%3F%26%2F%2C'%3F'):'%3F'%2Bt%2Ct=3%3Et.length%3F'':t%2Cc=c.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(i!==t%7C%7Ce!==c)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bc%7D%24%7Bt%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20i=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bi%26%26(i.opener=null)%7D%7D)()%3Bvoid'2.1.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" [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" @@ -360,7 +360,7 @@ repos I had; doesn't build yet [Setup OpenInTextastic]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.0' "Setup OpenInTextastic" [Setup OpenInWorkingCopy]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%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.0' "Setup OpenInWorkingCopy" [Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bconst%20c=location.search.search('url=')%3Bif(c%3E-1)%7Blet%20o=location.search.slice(4%2Bc)%3Bconst%20t=o.indexOf('%26')%3Bif(t%3E-1%26%26(o=o.slice(0%2Ct))%2Co.length%3E5)%7Bconst%20c=decodeURIComponent(o)%3Btry%7Bconst%20o=new%20URL(c%2Clocation.href)%3B'https:'===o.protocol%26%26location.replace(o.href)%7Dcatch%7B%7D%7D%7Dvoid'1.1.0' "Setup OpenURLParam" -[Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Cc=location.search%3Bif(3%3Ec.length%26%26!e.includes('%2Famp'))return%3Blet%20i=e%2Ca=c%3Bconst%20r=location.hostname%3Bif(r.includes('aliexpress.')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2Ca.includes('fb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('action%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca.includes('aff%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.toLowerCase().includes('id=')%26%26(a=a.replace(%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('ga%5F')%7C%7Ca.includes('utm%5F'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(r)%7C%7C'youtu.be'===r%7C%7C'www.youtube-nocookie.com'===r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('%5Fhsenc')%7C%7Ca.includes('%5Fhsmi'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('hmb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('cm%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('mc%5Fcid')%7C%7Ca.includes('mc%5Feid'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('iesrc')%7C%7Ca.includes('mkt%5Ftok'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('pk%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F%26%26%2B%2Fg%2C'%26')%2C'%26'===a.charAt(a.length-1)%26%26(a=a.slice(0%2C-1))%2C'%3F'!==a.charAt(0)%26%26(a='%3F'%2Ba)%2Ca.includes('%3F%26')%26%26(a='%3F'%2Ba.slice(2))%2C3%3Ea.length%26%26(a='')%2Ci=i.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(c!==a%7C%7Ce!==i)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bi%7D%24%7Ba%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20c=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bc%26%26(c.opener=null)%7D%7D)()%3Bvoid'2.0.0' "Setup UtmStrip" +[Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Ci=location.search%3Bif(3%3Ei.length%26%26!e.includes('%2Famp'))return%3Blet%20c=e%2Ct=i%3Bconst%20a=location.hostname%2Cn=(e%2Ci)=%3Ee.replace(i%2C'%241')%3Bif(a.includes('aliexpress.')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg))%2Ct.includes('fb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('action%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi))%2Ct=n(t%2C%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi)%2Ct.toLowerCase().includes('id=')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi))%2C(t.includes('ga%5F')%7C%7Ct.includes('utm%5F'))%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(a)%7C%7C'youtu.be'===a%7C%7C'www.youtube-nocookie.com'===a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D%5Fhs(enc%7Cmi)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('hmb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('cm%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5Dmc%5F%5Bce%5Did=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D(iesrc%7Cmkt%5Ftok)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('pk%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct=t.replace(%2F%26%26%2B%2Fg%2C'%26').replace(%2F%26%24%2F%2C'')%2Ct='%3F'===t%5B0%5D%3Ft.replace(%2F%5E%5C%3F%26%2F%2C'%3F'):'%3F'%2Bt%2Ct=3%3Et.length%3F'':t%2Cc=c.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(i!==t%7C%7Ce!==c)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bc%7D%24%7Bt%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20i=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bi%26%26(i.opener=null)%7D%7D)()%3Bvoid'2.1.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 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/bookmarklets.json b/bookmarklets.json index 51a2b75..0676721 100644 --- a/bookmarklets.json +++ b/bookmarklets.json @@ -63,7 +63,7 @@ { "name": "UtmStrip", "file": "utmstrip.bookmarklet", - "version": "2.0.0" + "version": "2.1.0" }, { "name": "deLighter", diff --git a/dist/utmstrip.bookmarklet b/dist/utmstrip.bookmarklet index 6160585..454a5fc 100644 --- a/dist/utmstrip.bookmarklet +++ b/dist/utmstrip.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Cc=location.search%3Bif(3%3Ec.length%26%26!e.includes('%2Famp'))return%3Blet%20i=e%2Ca=c%3Bconst%20r=location.hostname%3Bif(r.includes('aliexpress.')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg%2C'%241'))%2Ca.includes('fb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('action%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca.includes('aff%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.toLowerCase().includes('id=')%26%26(a=a.replace(%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('ga%5F')%7C%7Ca.includes('utm%5F'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(r)%7C%7C'youtu.be'===r%7C%7C'www.youtube-nocookie.com'===r)%26%26(a=a.replace(%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('%5Fhsenc')%7C%7Ca.includes('%5Fhsmi'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('hmb%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('cm%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi%2C'%241')%2Ca=a.replace(%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('mc%5Fcid')%7C%7Ca.includes('mc%5Feid'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2C(a.includes('iesrc')%7C%7Ca.includes('mkt%5Ftok'))%26%26(a=a.replace(%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca.includes('pk%5F')%26%26(a=a.replace(%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi%2C'%241'))%2Ca=a.replace(%2F%26%26%2B%2Fg%2C'%26')%2C'%26'===a.charAt(a.length-1)%26%26(a=a.slice(0%2C-1))%2C'%3F'!==a.charAt(0)%26%26(a='%3F'%2Ba)%2Ca.includes('%3F%26')%26%26(a='%3F'%2Ba.slice(2))%2C3%3Ea.length%26%26(a='')%2Ci=i.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(c!==a%7C%7Ce!==i)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bi%7D%24%7Ba%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20c=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bc%26%26(c.opener=null)%7D%7D)()%3Bvoid'2.0.0' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=location.pathname%2Ci=location.search%3Bif(3%3Ei.length%26%26!e.includes('%2Famp'))return%3Blet%20c=e%2Ct=i%3Bconst%20a=location.hostname%2Cn=(e%2Ci)=%3Ee.replace(i%2C'%241')%3Bif(a.includes('aliexpress.')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)aff%5F(platform%7Ctrace%5Fkey)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)algo%5F%5Bep%5Dvid=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(btsid%7Cws%5Fab%5Ftest)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)s%5Bcp%5Dm=%5B%5E%26%5D%2B%2Fg))%2C%2F(%7C%5C.)amazon%5C.com%24%2F.test(a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(%5Fencoding%7Cie%7ClinkCode%7ClinkId%7Cpf%7Cpsc%7Cref%5F%7Ctag)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)p%5Bdf%5D%5Frd%5F.%2A%3F=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(content-id%7Ccrid%7Ccv%5Fct%5Fcx%7Clanguage%7Cqid%7Csprefix%7Csr%7Cth)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)asc(%5Fcampaign%7C%5Frefurl%7C%5Fsource%7Csubtag)=%5B%5E%26%5D%2B%2Fg)%2Ct=n(t%2C%2F(%5B%3F%26%5D)dib(%5Ftag)%3F=%5B%5E%26%5D%2B%2Fg))%2Ct.includes('fb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)fb%5F(action%5Fids%7Caction%5Ftypes%7Cref%7Csource)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)(fbclid%7Chrc%7Crefsrc)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('action%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)action%5F(object%7Cref%7Ctype)%5Fmap=%5B%5E%26%5D%2B%2Fgi))%2Ct=n(t%2C%2F(%5B%3F%26%5D)(assetType%7CelqTrack%7Cmkt%5Ftok%7CoriginalReferer%7Creferrer%7Cterminal%5Fid%7Ctrk%7CtrkCampaign%7CtrkInfo)=%5B%5E%26%5D%2B%2Fgi)%2Ct.toLowerCase().includes('id=')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(an%7Casset%7Ccampaign%7Ce%7Cgcl%7Crecipient%7Csite)id=%5B%5E%26%5D%2B%2Fgi))%2C(t.includes('ga%5F')%7C%7Ct.includes('utm%5F'))%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ga%7Cutm)%5F(campaign%7Ccid%7Ccontent%7Cdesign%7Cmedium%7Cname%7Cplace%7Cpubreferrer%7Creader%7Csource%7Cswu%7Cterm%7Cuserid%7Cviz%5Fid)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)gcl(id%7Csrc)=%5B%5E%26%5D%2B%2Fgi))%2C(%2F(m%7Cwww)%5C.youtube%5C.com%24%2F.test(a)%7C%7C'youtu.be'===a%7C%7C'www.youtube-nocookie.com'===a)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(ac%7Cannotation%5Fid%7Capp%7Cfeature%7Cgclid%7Ckw%7Csrc%5Fvid)=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D%5Fhs(enc%7Cmi)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)%5Fhs(enc%7Cmi)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('hmb%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)hmb%5F(campaign%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('cm%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)cm%5F(mmc%7Cmmca%5Cd%2B%7Cre%7Csp)=%5B%5E%26%5D%2B%2Fgi)%2Ct=n(t%2C%2F(%5B%3F%26%5D)manual%5Fcm%5Fmmc=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5Dmc%5F%5Bce%5Did=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)mc%5F%5Bce%5Did=%5B%5E%26%5D%2B%2Fgi))%2C%2F%5B%3F%26%5D(iesrc%7Cmkt%5Ftok)=%2F.test(t)%26%26(t=n(t%2C%2F(%5B%3F%26%5D)(iesrc%7Cmkt%5Ftok)=%5B%5E%26%5D%2B%2Fgi))%2Ct.includes('pk%5F')%26%26(t=n(t%2C%2F(%5B%3F%26%5D)pk%5F(campaign%7Ccontent%7Ckwd%7Cmedium%7Csource)=%5B%5E%26%5D%2B%2Fgi))%2Ct=t.replace(%2F%26%26%2B%2Fg%2C'%26').replace(%2F%26%24%2F%2C'')%2Ct='%3F'===t%5B0%5D%3Ft.replace(%2F%5E%5C%3F%26%2F%2C'%3F'):'%3F'%2Bt%2Ct=3%3Et.length%3F'':t%2Cc=c.replace(%2F%5C%2Famp%5C%2F%3F%24%2F%2C'')%2C(i!==t%7C%7Ce!==c)%26%26confirm('Update%20history%20and%20copy%20cleaned%20URL%20to%20clipboard%3F'))%7Bconst%20e=%60%24%7Blocation.protocol%7D%2F%2F%24%7Blocation.host%7D%24%7Bc%7D%24%7Bt%7D%24%7Blocation.hash%7D%60%3Bnavigator.clipboard.writeText(e)%2Chistory.replaceState(null%2C''%2Ce)%3Bconst%20i=window.open(e%2C'%5Fself'%2C'noreferrer')%3Bi%26%26(i.opener=null)%7D%7D)()%3Bvoid'2.1.0' \ No newline at end of file From 495c8b65a294a9a81afe3d38a5c5f3fd988ac3f1 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 01:30:32 -0800 Subject: [PATCH 06/14] fix: address CI/CD lint warnings; update dist & docs --- README.md | 4 ++-- dist/linklighter.bookmarklet | 2 +- src/linklighter.ts | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 94b9f01..5bb4e56 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ repos I had; doesn't build yet [FYI]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "FYI" [IsItAws]: javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "IsItAws" [KillStickyHeaders]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" +[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%2Fg%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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "OpenInBrave" [OpenInFirefox]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "OpenInFirefox" [OpenInFirefox-Focus]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "OpenInFirefox-Focus" @@ -351,7 +351,7 @@ repos I had; doesn't build yet [Setup FYI]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "Setup FYI" [Setup IsItAws]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "Setup IsItAws" [Setup KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Setup Linklighter" +[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%2Fg%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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "Setup OpenInBrave" [Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "Setup OpenInFirefox" [Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "Setup OpenInFirefox-Focus" diff --git a/dist/linklighter.bookmarklet b/dist/linklighter.bookmarklet index 8f01237..0324462 100644 --- a/dist/linklighter.bookmarklet +++ b/dist/linklighter.bookmarklet @@ -1 +1 @@ -javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%3Blet%20s=i%2Cr=i.indexOf('%23')%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''%2Co=t.startOffset%2Ci=t.endOffset%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)%7Br%3E-1%26%26(s=s.substring(0%2Cr))%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=t(s)%2B'%2C'%2Bt(r)%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cs%2B='%23:~:text='%2Be%2Cs=s.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2Fg%2C'')%2Cs=s.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cs=s.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(s!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(s).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(s%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' \ No newline at end of file +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%2Fg%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' \ No newline at end of file diff --git a/src/linklighter.ts b/src/linklighter.ts index f5bee8a..c591229 100644 --- a/src/linklighter.ts +++ b/src/linklighter.ts @@ -1,3 +1,4 @@ +/* eslint max-statements: ["error", 65] */ // Linklighter - use current text selection on a web page to generate a URL // that highlights the selected text when opened in a modern browser. // If a new URL is generated, open it in a new window to preview the highlight @@ -10,9 +11,9 @@ } const textFrag: string = selection.toString(), textFragLen: number = textFrag.length, - url: string = document.URL; + url: string = document.URL, + strPos: number = url.indexOf('#'); let newUrl: string = url, - strPos: number = url.indexOf('#'), strStart: string = '', prefix: string = '', suffix: string = ''; @@ -27,8 +28,7 @@ const range: Range = selection.getRangeAt(0); const container: Node = range.commonAncestorContainer; const fullText: string = container.textContent || ''; - const startOffset: number = range.startOffset; - const endOffset: number = range.endOffset; + const {startOffset, endOffset} = range; // Extract prefix (up to 20 chars before selection) const prefixStart: number = Math.max(0, startOffset - 20); @@ -66,7 +66,7 @@ // Build text fragment let fragment: string = ''; if (prefix) { - fragment = enc(prefix) + '-,'; + fragment = `${enc(prefix)}-,`; } if (textFragLen < 80) { @@ -94,15 +94,15 @@ endFrag = endFrag.substring(firstSpace + 1); } - strStart = startFrag + '…'; - fragment += enc(startFrag) + ',' + enc(endFrag); + strStart = `${startFrag}…`; + fragment += `${enc(startFrag)},${enc(endFrag)}`; } if (suffix) { - fragment += ',-' + enc(suffix); + fragment += `,-${enc(suffix)}`; } - newUrl += '#:~:text=' + fragment; + newUrl += `#:~:text=${fragment}`; // clean-up trailing %0A (newline), %0D (carriage return), %09 (tab), or %20 (space) newUrl = newUrl.replace(/(%0A|%0D|%09|%20)+$/g, ''); newUrl = newUrl.replace(/(%20){2,}/g, '%20'); From 06832111506770a3a4b02dbbfc6081ec3602a82c Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 01:34:59 -0800 Subject: [PATCH 07/14] docs(README): fix typo- missing word Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bb4e56..b3cb6f1 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ using a URL protocol scheme. child elements of `` that have a fixed position. See [Kill sticky headers][Kill sticky headers]. + __Linklighter__: Does _not_ use a URL protocol scheme. Gets the current - selection uses it to create a "Text Fragment" URL (e.g., appends `#:~:text=…` + selection and uses it to create a "Text Fragment" URL (e.g., appends `#:~:text=…` to the current URL. Browsers interpret this and when opening such a URL, they scroll to the first matching selection and highlight that text. Bookmarklet logic optimizes fragment. Depending on the selection, it will use the whole From 8c2f00d9fea37496c8bf6f41037b8c0535154002 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 01:35:20 -0800 Subject: [PATCH 08/14] docs(README): fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3cb6f1..45237cd 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ using a URL protocol scheme. scroll to the first matching selection and highlight that text. Bookmarklet logic optimizes fragment. Depending on the selection, it will use the whole selection, a start/end fragment pair, and/or a prefix or suffix. Browsers - will highlights the first "best" match. To learn more about text fragment + will highlight the first "best" match. To learn more about text fragment highlighting, refer to [Text fragments][Text fragments]. Works with Safari on Apple platforms, and Google Chrome for desktops. + __OpenInBrave__ - Uses the `brave://open-url?url=` scheme for the Brave app From f8c1c8d6299ebd8ec747dc80766a4b42907eaf82 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:27:46 -0800 Subject: [PATCH 09/14] chore(linter): allow more language types in Markdown fenced codeblocks --- .github/linters/.markdownlint.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/linters/.markdownlint.json b/.github/linters/.markdownlint.json index 69054d1..f4a1b14 100644 --- a/.github/linters/.markdownlint.json +++ b/.github/linters/.markdownlint.json @@ -1,7 +1,8 @@ { "default": true, "fenced-code-language": { - "allowed_languages": ["bash"], + "allowed_languages": ["bash", "javascript", "json", "jsonc", "markdown", + "sh", "text", "typescript", "yaml"], "language_only": true }, "line-length": { @@ -10,3 +11,4 @@ "line_length": 80 } } + From 6406b591f063e274014e3b1c87cc44509d1128e3 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:29:35 -0800 Subject: [PATCH 10/14] chore(.npmignore): add build process .temp/ directory --- .npmignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.npmignore b/.npmignore index 4971497..6c76a80 100644 --- a/.npmignore +++ b/.npmignore @@ -6,6 +6,7 @@ .github/ .gitignore .npmrc +.temp/ node_modules/ npm-debug.log pre-push From d36d0266e54a12d7693d8fe01f1b28cec797bb3f Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:30:17 -0800 Subject: [PATCH 11/14] chore: add instructions file for Copilot in case it helps --- .github/copilot-instructions.md | 346 ++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..a17dcd9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,346 @@ +# OpenInlets - Copilot Agent Instructions + +## Repository Overview + +**OpenInlets** is a collection of browser bookmarklet utilities originally +designed to open iOS apps from web pages. The project has evolved to include +bookmarklets that manipulate URLs, detect AWS hosting, and more. It's a small +(~67MB with node_modules), focused TypeScript project that compiles to +minified JavaScript bookmarklets. + +- **Language**: TypeScript (src/), JavaScript (scripts/) +- **Target Runtime**: Browser bookmarklets (ES2020) +- **Build Tools**: TypeScript compiler, Terser for minification, Node.js + scripts +- **Lines of Code**: 16 TypeScript source files in `src/`, 5 Node.js scripts + in `scripts/` +- **Node Version Required**: >=24.12.0 (strict requirement via + `engineStrict: true`) +- **npm Version Required**: >=11.6.0 +- **Primary Output**: 16 `.bookmarklet` files in `dist/` directory + +## Critical Build Instructions + +### Environment Setup + +**ALWAYS use the correct Node.js and npm versions.** The project uses +`engineStrict: true` and requires: + +- Node.js >= 24.12.0 +- npm >= 11.6.0 + +If you get version warnings during `npm ci`, the build may still work but is +not supported. + +### Installation + +**ALWAYS run this first** before any build or test operations: + +```bash +npm ci --prefer-offline +``` + +- Use `npm ci` (not `npm install`) for reproducible builds +- The `--prefer-offline` flag speeds up installation +- Takes ~2-3 seconds with cache, ~30-60 seconds without + +### Build Process + +The build has three sequential steps (automatically chained by `npm run build`): + +```bash +npm run build +``` + +This executes: + +1. **Compile**: `npm run build:compile` → TypeScript compilation (`tsc`) → + outputs to `.temp/` directory +2. **Minify**: `npm run build:minify` → Terser minification → outputs to + `dist/*.js` (temporary) +3. **Bookmarklet**: `npm run build:bookmarklet` → Wraps minified code with + `javascript:` URL prefix and version → outputs to `dist/*.bookmarklet` + +**Expected time**: 3-5 seconds total + +### Testing + +```bash +npm test +``` + +This runs `npm run build && npm run verify-build`. The verification ensures: + +- All 16 bookmarklets were built +- Calculates total size +- **Expected time**: 5-10 seconds + +### Pre-commit Validation + +**ALWAYS run before committing** (or the pre-push hook will catch it): + +```bash +./preflight +``` + +This script checks: + +1. Shell scripts with `shellcheck` (pre-push, preflight) - skipped if + shellcheck not installed +2. YAML files with `yamllint` - skipped if yamllint not installed +3. Markdown files with `markdownlint` and `cspell` +4. GitHub Actions with `actionlint` - skipped if actionlint not installed +5. TypeScript/JavaScript with `eslint` +6. npm security audit +7. Verifies pre-push hook is installed at `.git/hooks/pre-push` + +**Expected time**: 10-20 seconds with all tools, 5-10 seconds with minimal +tools + +**Pre-requisite**: If preflight fails because `.git/hooks/pre-push` doesn't +match `./pre-push`, run: + +```sh +cp -fpv pre-push .git/hooks +``` + +### Linting + +ESLint configuration is in `.github/linters/eslint.config.js`: + +```bash +npx eslint --config .github/linters/eslint.config.js . +``` + +**Expected time**: 2-5 seconds + +### Full Deployment Build + +To build and update README.md with bookmarklet links: + +```bash +npm run deploy +``` + +This runs `npm run build && npm run build:readme` and updates the README.md +file with current bookmarklet code. + +## Project Structure + +### Source Files + +- **`src/*.ts`** - 16 TypeScript bookmarklet source files (human-readable) + - Each file is a self-contained bookmarklet implementation + - Uses strict TypeScript configuration (see `tsconfig.json`) + - Target: ES2020 for modern browser compatibility + +- **`scripts/*.js`** - 5 Node.js build scripts: + - `build-bookmarklet.js` - Converts minified JS to bookmarklet URLs + - `minify.js` - Minifies compiled JavaScript with Terser + - `update-readme.js` - Updates README.md with bookmarklet links + - `verify-build.js` - Validates build output + - `utils.js` - Shared utilities + +### Build Artifacts + +- **`.temp/*.js`** - TypeScript compiler output (intermediate, gitignored) +- **`dist/*.bookmarklet`** - Final bookmarklet files (16 files, committed to git) + +**CRITICAL**: Never examine or review the contents of `dist/*.bookmarklet` +files. These are URL-encoded bookmarklets, not TypeScript or JavaScript. They +start with `javascript:` and contain percent-encoded minified code. **The only +validation needed is that for every `src/*.ts` file basename, there exists +exactly one corresponding `dist/*.bookmarklet` file.** Use `npm run +verify-build` to check this automatically. + +### Configuration Files + +- **`package.json`** - Dependencies and npm scripts +- **`tsconfig.json`** - TypeScript compiler configuration (src/) +- **`tsconfig.scripts.json`** - TypeScript configuration for scripts (unused currently) +- **`bookmarklets.json`** - Bookmarklet metadata (name, file, version) +- **`.github/linters/eslint.config.js`** - ESLint rules (strict, security-focused) +- **`.github/linters/.markdownlint.json`** - Markdown linting rules +- **`.github/linters/.yaml-lint.yml`** - YAML linting rules +- **`.github/linters/actionlint.yaml`** - GitHub Actions linting rules +- **`.cspell.jsonc`** - Spell checking configuration +- **`.cspell/dictionary-custom.txt`** - Custom word dictionary + +### GitHub Actions Workflows + +Located in `.github/workflows/`: + +1. **`ci.yml`** - NodeJS Build + - Triggers: push to main/hotfix, PRs to main + - Matrix: Node 24.x and 25.x + - Steps: Security audit → Install deps → Build & test → Generate SBOM + - **Note**: Security audit currently has `continue-on-error: true` until + Dec 18, 2025 (js-yaml issue) + - **Timeout**: 5 minutes + +2. **`linter.yml`** - Lint Code Base + - Triggers: push to main/hotfix, PRs to main, manual dispatch + - Runs: ESLint + Super-Linter (Bash, GitHub Actions, JSON, JSONC, Markdown, + YAML) + - **Timeout**: 10 minutes + - Excludes: `dist/*.bookmarklet` files from linting + +3. **`codeql-analysis.yml`** - CodeQL Security Analysis + - Triggers: push to main/hotfix, PRs to main, weekly schedule, manual + dispatch + - Language: JavaScript/TypeScript + - Config: `.github/codeql-config.yml` (excludes dist/*.bookmarklet) + - **Timeout**: 10 minutes + +4. **`release.yml`** - Release and Deploy + - Triggers: version tags (e.g., 4.0.0), manual dispatch + - Steps: Audit → Build → Create release → Deploy to gh-pages + - **Timeout**: 15 minutes (release), 8 minutes (gh-pages deploy) + +## Common Workflows + +### Making Code Changes + +1. Modify `src/*.ts` files (TypeScript source) +2. Run `npm run build` to compile and generate bookmarklets +3. Run `./preflight` to validate (or wait for pre-push hook) +4. Commit changes (both src/*.ts and dist/*.bookmarklet should be committed) + +### Adding a New Bookmarklet + +1. Create `src/newbookmarklet.ts` with TypeScript code +2. Add entry to `bookmarklets.json` with name, file + (`newbookmarklet.bookmarklet`), and version +3. Run `npm run build` - will automatically compile, minify, and create the + .bookmarklet file +4. Run `npm run deploy` to update README.md with the new bookmarklet link +5. Commit `src/newbookmarklet.ts`, `bookmarklets.json`, + `dist/newbookmarklet.bookmarklet`, and `README.md` + +### Updating Dependencies + +```sh +npm update +npm audit fix +npm test +./preflight +``` + +Check that builds still work after updates. + +## Known Issues and Workarounds + +### npm Audit Warnings (Temporary) + +**Issue**: npm audit may report moderate vulnerabilities in js-yaml transitive +dependency. + +**Workaround**: Currently set to `continue-on-error: true` in workflows until +December 18, 2025, or until js-yaml is updated. This is documented in: + +- `.github/workflows/ci.yml` (line 39) +- `.github/workflows/release.yml` (line 47) +- `pre-push` script (line 50) + +### Node Version Mismatch Warnings + +**Issue**: If you see `EBADENGINE Unsupported engine` warnings: + +```text +npm warn EBADENGINE required: { node: '>=24.12.0', npm: '>=11.6.0' } +npm warn EBADENGINE current: { node: 'v20.19.6', npm: '10.8.2' } +``` + +**Workaround**: The build may work but is not officially supported. Upgrade +Node.js and npm to meet the requirements. The project uses `engineStrict: true` +to enforce these requirements. + +### Missing Git Hooks + +**Issue**: preflight fails with "pre-push and active .git/hooks/pre-push +differ". + +**Fix**: Run the following to install the pre-push hook: + +```sh +cp -fpv pre-push .git/hooks +``` + +### Missing Optional Tools + +**Issue**: preflight skips checks if optional tools (shellcheck, yamllint, +actionlint, cspell) aren't installed. + +**Workaround**: These checks are non-blocking locally but will run in CI. +Install tools for full validation: + +```sh +# shellcheck +brew install shellcheck # or: apt install shellcheck + +# yamllint +pip install yamllint + +# actionlint +brew install actionlint # or download from https://github.com/rhysd/actionlint + +# cspell +npm install -g cspell +``` + +## Key Files Reference + +**Root Directory**: + +- `README.md` - Main documentation with bookmarklet descriptions and + installation +- `SECURITY.md` - Security policy and vulnerability reporting +- `LICENSE` - MIT License (requires current year: 2014-2026) +- `package.json` - Project metadata and scripts +- `package-lock.json` - Locked dependencies (always commit changes) +- `bookmarklets.json` - Bookmarklet registry (name, file, version) +- `preflight` - Pre-commit validation script (shell) +- `pre-push` - Git pre-push hook (shell) +- `tsconfig.json` - TypeScript compiler configuration + +**Directories**: + +- `src/` - TypeScript bookmarklet source files (16 files) +- `scripts/` - Node.js build scripts (5 files) +- `dist/` - Built bookmarklet files (16 .bookmarklet files) +- `.github/workflows/` - CI/CD workflows (4 files) +- `.github/linters/` - Linting configurations (4 files) +- `.cspell/` - Spell check dictionary +- `.temp/` - Temporary build artifacts (gitignored) +- `node_modules/` - Dependencies (gitignored) + +## Instructions for Coding Agents + +1. **Trust these instructions**. Only search for additional information if + something is incomplete or incorrect. + +2. **Always run the build** (`npm run build`) after making code changes to + TypeScript files. + +3. **Always validate** with `./preflight` or `npm test` before committing. + +4. **Never examine `dist/*.bookmarklet` contents**. They are URL-encoded + bookmarklets, not source code. Only verify that the files exist (one per + `src/*.ts` file). + +5. **Commit both source and dist**. Changes to `src/*.ts` require corresponding + commits of `dist/*.bookmarklet` files. + +6. **Use npm ci, not npm install** for reproducible builds. + +7. **Check Node/npm versions** if you encounter engine warnings or build + failures. + +8. **ESLint is strict**. The configuration enforces security rules and strict + coding standards. If eslint fails, fix the issues - don't disable rules. + +9. **Super-Linter runs in CI**. Local preflight may skip some checks if tools + are missing, but CI runs all checks. + +10. **GitHub Actions run automatically** on push/PR. Check workflow results if + CI fails. From 233794c772c47720096d303aaedc2f098cba9f14 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:41:17 -0800 Subject: [PATCH 12/14] Update README.md to fix typo mention in PR review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45237cd..976e811 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ that URL. + __[UtmStrip] v2.1.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 fromU URL path. Asks to copy the new URL +Also removes Google `/amp/` suffix from URL path. Asks to copy the new URL to the clipboard. Finally, replaces history & reloads the page. _NOTE:_ This bookmarklet also works with Safari and Firefox on macOS. From be41fa48fa6d8562ec91e6032d20406ceabc2ff9 Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:45:05 -0800 Subject: [PATCH 13/14] Update src/linklighter.ts Remove redundant/conflicting "/g" flag. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/linklighter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linklighter.ts b/src/linklighter.ts index c591229..2a02c86 100644 --- a/src/linklighter.ts +++ b/src/linklighter.ts @@ -104,7 +104,7 @@ newUrl += `#:~:text=${fragment}`; // clean-up trailing %0A (newline), %0D (carriage return), %09 (tab), or %20 (space) - newUrl = newUrl.replace(/(%0A|%0D|%09|%20)+$/g, ''); + newUrl = newUrl.replace(/(%0A|%0D|%09|%20)+$/, ''); newUrl = newUrl.replace(/(%20){2,}/g, '%20'); newUrl = newUrl.replace(/##+:~:text=/, '#:~:text='); } From 848683967461f714f2b21e74f1732a6d4516b76a Mon Sep 17 00:00:00 2001 From: Tom King Date: Sun, 4 Jan 2026 02:51:01 -0800 Subject: [PATCH 14/14] chore(linklighter): update dist & README after removing "g" flag on a replacement --- README.md | 4 ++-- dist/linklighter.bookmarklet | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 976e811..2d571df 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ repos I had; doesn't build yet [FYI]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "FYI" [IsItAws]: javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "IsItAws" [KillStickyHeaders]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%2Fg%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" +[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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "OpenInBrave" [OpenInFirefox]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "OpenInFirefox" [OpenInFirefox-Focus]: javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "OpenInFirefox-Focus" @@ -351,7 +351,7 @@ repos I had; doesn't build yet [Setup FYI]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20e=encodeURIComponent(document.title)%2Ct='%250A'%2Cn=window.getSelection()%2Co=n%26%26'Range'===n.type%26%26n.rangeCount%3E0%3Fn.getRangeAt(0).toString():''%3Blocation.href=%60mailto:%3Fsubject=fyi:%24%7Be%7D%26body=%24%7Be%7D%24%7Bt%7D%24%7BencodeURIComponent(document.URL)%7D%24%7Bt%7D---%24%7Bt%7D%24%7BencodeURIComponent(o)%7D%24%7Bt%7D%24%7Bt%7D%60%7D)()%3Bvoid'3.3.0' "Setup FYI" [Setup IsItAws]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Blocation.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.4' "Setup IsItAws" [Setup KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20o=document.querySelectorAll('body%20%2A')%3Bfor(const%20e%20of%20Array.from(o))if('fixed'===getComputedStyle(e).position)%7Bconst%20o=e.parentNode%3Bo%26%26o.removeChild(e)%7D%7D)()%3Bvoid'2.0.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%2Fg%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 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:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.0' "Setup OpenInBrave" [Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.0' "Setup OpenInFirefox" [Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3Bif(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.0' "Setup OpenInFirefox-Focus" diff --git a/dist/linklighter.bookmarklet b/dist/linklighter.bookmarklet index 0324462..c1a503a 100644 --- a/dist/linklighter.bookmarklet +++ b/dist/linklighter.bookmarklet @@ -1 +1 @@ -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%2Fg%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' \ No newline at end of file +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' \ No newline at end of file