diff --git a/README.md b/README.md index 5f44266..a5928ea 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ URL of the original page. + __[IsItAws] v1.3.5__: Check the current page host to determine if it runs on AWS. This bookmarklet works with most browsers on most platforms. -+ __[KillStickyHeaders] v2.0.0__: Find & delete all fixed position elements of ++ __[KillStickyHeaders] v2.1.0__: Find & delete all fixed position elements of HTML body element. Cross-platform. + __[Linklighter] v2.1.0__: Use the current text selection on the active web @@ -53,30 +53,30 @@ deLighter above, as a companion bookmarklet that un-highlights the newly opened window.) Linklighter works with recent releases of Safari on iPhone, iPad, and Mac, and also works with Google Chrome on desktops. -+ __[OpenInBrave] v1.1.1__: Open the current web page in the Brave app on iOS. ++ __[OpenInBrave] v1.1.2__: Open the current web page in the Brave app on iOS. -+ __[OpenInFirefox] v1.6.1__: Open the current web page in the Firefox app for ++ __[OpenInFirefox] v1.6.2__: Open the current web page in the Firefox app for iOS. -+ __[OpenInFirefox-Focus] v1.1.1__: Open the current web page in the Firefox ++ __[OpenInFirefox-Focus] v1.1.2__: Open the current web page in the Firefox Focus app for iOS. -+ __[OpenInFirefox-Private] v1.1.1__: Open the current web page in private ++ __[OpenInFirefox-Private] v1.1.2__: Open the current web page in private mode of the Firefox app for iOS. -+ __[OpenInGoodReader] v1.5.1__: When viewing a PDF in Mobile Safari, open or ++ __[OpenInGoodReader] v1.5.2__: When viewing a PDF in Mobile Safari, open or download the same PDF in GoodReader. -+ __[OpenInTextastic] v1.1.1__: Open the current web page in the Textastic app ++ __[OpenInTextastic] v1.1.2__: Open the current web page in the Textastic app on iOS. Download the server response of the current HTTP URL, save it in the root directory of the local (Textastic) file system, and then open it in the editor. Handy to view the source code of a web page or download a raw file. -+ __[OpenInWorkingCopy] v1.6.1__: When viewing a BitBucket _or_ Github ++ __[OpenInWorkingCopy] v1.6.2__: When viewing a BitBucket _or_ Github repository in Mobile Safari, show the same repo in the Working Copy iOS app (cloning the repo locally if necessary). -+ __[OpenURLParam] v1.1.1__: Work-around for blocked navigation from certain ++ __[OpenURLParam] v1.2.0__: Work-around for blocked navigation from certain ad or tracking blockers. If the current URL contains a parameter in the form of `url=...` this bookmarklet will parse the `url` parameter and navigate to that URL. @@ -117,16 +117,16 @@ followed link into a bookmark for JavaScript bookmarklet. + __Mobile Safari setup link__ -- [Setup deLighter] v1.3.1 + __Mobile Safari setup link__ -- [Setup FYI] v3.3.0 + __Mobile Safari setup link__ -- [Setup IsItAws] v1.3.5 -+ __Mobile Safari setup link__ -- [Setup KillStickyHeaders] v2.0.0 ++ __Mobile Safari setup link__ -- [Setup KillStickyHeaders] v2.1.0 + __Mobile Safari setup link__ -- [Setup Linklighter] v2.1.0 -+ __Mobile Safari setup link__ -- [Setup OpenInBrave] v1.1.1 -+ __Mobile Safari setup link__ -- [Setup OpenInFirefox] v1.6.1 -+ __Mobile Safari setup link__ -- [Setup OpenInFirefox-Focus] v1.1.1 -+ __Mobile Safari setup link__ -- [Setup OpenInFirefox-Private] v1.1.1 -+ __Mobile Safari setup link__ -- [Setup OpenInGoodReader] v1.5.1 -+ __Mobile Safari setup link__ -- [Setup OpenInTextastic] v1.1.1 -+ __Mobile Safari setup link__ -- [Setup OpenInWorkingCopy] v1.6.1 -+ __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.1.1 ++ __Mobile Safari setup link__ -- [Setup OpenInBrave] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInFirefox] v1.6.2 ++ __Mobile Safari setup link__ -- [Setup OpenInFirefox-Focus] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInFirefox-Private] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInGoodReader] v1.5.2 ++ __Mobile Safari setup link__ -- [Setup OpenInTextastic] v1.1.2 ++ __Mobile Safari setup link__ -- [Setup OpenInWorkingCopy] v1.6.2 ++ __Mobile Safari setup link__ -- [Setup OpenURLParam] v1.2.0 + __Mobile Safari setup link__ -- [Setup UtmStrip] v2.1.0 + __Mobile Safari setup link__ -- [Setup unskim] v2.0.1 + __Mobile Safari setup link__ -- [Setup x-man] v1.3.1 @@ -236,6 +236,10 @@ using a URL protocol scheme. ## Version Notes +4.1.0 Revise 11 bookmarklets- streamline simple ones, modernize KillStickHeaders + and OpenURLParam. Update scripts/build-bookmarklets.js to compact bookmarklets + that don't use vars. + 4.0.2 No functional changes. Hardened install, build, & ci 4.0.0 MAJOR RELEASE - Migrated to TypeScript for type safety & more, updated @@ -333,16 +337,16 @@ repos I had; doesn't build yet [deLighter]: javascript:document.location.reload()%3Bvoid'1.3.1' "deLighter" [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:location.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.5' "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" +[KillStickyHeaders]: javascript:Array.from(document.querySelectorAll('body%20%2A')).filter(e=%3E%5B'fixed'%2C'sticky'%5D.includes(getComputedStyle(e).position)).forEach(e=%3Ee.remove())%3Bvoid'2.1.0' "KillStickyHeaders" [Linklighter]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%2Cs=i.indexOf('%23')%3Blet%20r=i%2Cc=''%2Cl=''%2Ca=''%3Bif(e.rangeCount%3E0%26%26n%26%26(document.body.textContent%7C%7C'').split(n).length-1%3E1)%7Bconst%20t=e.getRangeAt(0)%2Cn=t.commonAncestorContainer.textContent%7C%7C''%2C%7BstartOffset:o%2CendOffset:i%7D=t%2Cs=Math.max(0%2Co-20)%3Bif(l=n.substring(s%2Co).trim()%2Cs%3E0%26%26'%20'!==n.charAt(s-1))%7Bconst%20t=l.indexOf('%20')%3Bt%3E0%26%26(l=l.substring(t%2B1))%7Dconst%20r=Math.min(n.length%2Ci%2B20)%3Bif(a=n.substring(i%2Cr).trim()%2Cr%3Cn.length%26%26'%20'!==n.charAt(r))%7Bconst%20t=a.lastIndexOf('%20')%3Bt%3E0%26%26(a=a.substring(0%2Ct))%7D%7Dif(e.removeAllRanges()%2Cn%26%26''!==n)%7Bs%3E-1%26%26(r=r.substring(0%2Cs))%3Blet%20e=''%3Bif(l%26%26(e=t(l)%2B'-%2C')%2C80%3Eo)c=n%2Ce%2B=t(n)%3Belse%7Blet%20i=~~(o%2F2-2)%3Bo%3E150%3Fi=48:o%3E100%26%26(i=~~(o%2F3))%3Blet%20s=n.substring(0%2Ci)%2Cr=n.slice(o-i)%3Bconst%20l=s.lastIndexOf('%20')%3Bl%3Ei%2F2%26%26(s=s.substring(0%2Cl))%3Bconst%20a=r.indexOf('%20')%3Ba%3E-1%26%26i%2F2%3Ea%26%26(r=r.substring(a%2B1))%2Cc=s%2B'%E2%80%A6'%2Ce%2B=%60%24%7Bt(s)%7D%2C%24%7Bt(r)%7D%60%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cr%2B='%23:~:text='%2Be%2Cr=r.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2F%2C'')%2Cr=r.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cr=r.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(r!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(r).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(r%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Linklighter" -[OpenInBrave]: javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.1' "OpenInBrave" -[OpenInFirefox]: javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.1' "OpenInFirefox" -[OpenInFirefox-Focus]: javascript:if(%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.1' "OpenInFirefox-Focus" -[OpenInFirefox-Private]: javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.1' "OpenInFirefox-Private" -[OpenInGoodReader]: javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.1' "OpenInGoodReader" -[OpenInTextastic]: javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.1' "OpenInTextastic" -[OpenInWorkingCopy]: javascript:if(%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.1' "OpenInWorkingCopy" -[OpenURLParam]: javascript:const%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.1' "OpenURLParam" +[OpenInBrave]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.2' "OpenInBrave" +[OpenInFirefox]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.2' "OpenInFirefox" +[OpenInFirefox-Focus]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "OpenInFirefox-Focus" +[OpenInFirefox-Private]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "OpenInFirefox-Private" +[OpenInGoodReader]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.2' "OpenInGoodReader" +[OpenInTextastic]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.2' "OpenInTextastic" +[OpenInWorkingCopy]: javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26('bitbucket.org'===location.host%7C%7C'github.com'===location.host))location.href=%60working-copy:%2F%2Fshow%3Fremote=%24%7BencodeURIComponent(location.href.split('%2F').slice(0%2C5).join('%2F'))%7D.git%60%3Bvoid'1.6.2' "OpenInWorkingCopy" +[OpenURLParam]: javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' "OpenURLParam" [UtmStrip]: javascript:'use%20strict'%3B(()=%3E%7Bconst%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" @@ -352,16 +356,16 @@ repos I had; doesn't build yet [Setup deLighter]: https://mobilemind.github.io/OpenInlets/x/#javascript:document.location.reload()%3Bvoid'1.3.1' "Setup deLighter" [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:location.href='https:%2F%2Fisitonaws.com%2Fdiscover%3Fname='%2Blocation.host%3Bvoid'1.3.5' "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 KillStickyHeaders]: https://mobilemind.github.io/OpenInlets/x/#javascript:Array.from(document.querySelectorAll('body%20%2A')).filter(e=%3E%5B'fixed'%2C'sticky'%5D.includes(getComputedStyle(e).position)).forEach(e=%3Ee.remove())%3Bvoid'2.1.0' "Setup KillStickyHeaders" [Setup Linklighter]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=encodeURIComponent%2Ce=window.getSelection()%3Bif(!e)return%3Bconst%20n=e.toString()%2Co=n.length%2Ci=document.URL%2Cs=i.indexOf('%23')%3Blet%20r=i%2Cc=''%2Cl=''%2Ca=''%3Bif(e.rangeCount%3E0%26%26n%26%26(document.body.textContent%7C%7C'').split(n).length-1%3E1)%7Bconst%20t=e.getRangeAt(0)%2Cn=t.commonAncestorContainer.textContent%7C%7C''%2C%7BstartOffset:o%2CendOffset:i%7D=t%2Cs=Math.max(0%2Co-20)%3Bif(l=n.substring(s%2Co).trim()%2Cs%3E0%26%26'%20'!==n.charAt(s-1))%7Bconst%20t=l.indexOf('%20')%3Bt%3E0%26%26(l=l.substring(t%2B1))%7Dconst%20r=Math.min(n.length%2Ci%2B20)%3Bif(a=n.substring(i%2Cr).trim()%2Cr%3Cn.length%26%26'%20'!==n.charAt(r))%7Bconst%20t=a.lastIndexOf('%20')%3Bt%3E0%26%26(a=a.substring(0%2Ct))%7D%7Dif(e.removeAllRanges()%2Cn%26%26''!==n)%7Bs%3E-1%26%26(r=r.substring(0%2Cs))%3Blet%20e=''%3Bif(l%26%26(e=t(l)%2B'-%2C')%2C80%3Eo)c=n%2Ce%2B=t(n)%3Belse%7Blet%20i=~~(o%2F2-2)%3Bo%3E150%3Fi=48:o%3E100%26%26(i=~~(o%2F3))%3Blet%20s=n.substring(0%2Ci)%2Cr=n.slice(o-i)%3Bconst%20l=s.lastIndexOf('%20')%3Bl%3Ei%2F2%26%26(s=s.substring(0%2Cl))%3Bconst%20a=r.indexOf('%20')%3Ba%3E-1%26%26i%2F2%3Ea%26%26(r=r.substring(a%2B1))%2Cc=s%2B'%E2%80%A6'%2Ce%2B=%60%24%7Bt(s)%7D%2C%24%7Bt(r)%7D%60%7Da%26%26(e%2B='%2C-'%2Bt(a))%2Cr%2B='%23:~:text='%2Be%2Cr=r.replace(%2F(%250A%7C%250D%7C%2509%7C%2520)%2B%24%2F%2C'')%2Cr=r.replace(%2F(%2520)%7B2%2C%7D%2Fg%2C'%2520')%2Cr=r.replace(%2F%23%23%2B:~:text=%2F%2C'%23:~:text=')%7Dif(r!==i%26%26confirm(%60Open%20URL%20with%20highlight%20on%20%22%24%7Bc%7D%22%20and%20copy%20URL%20to%20clipboard%3F%60))%7Bnavigator.clipboard.writeText(r).catch(()=%3E%7Balert('Could%20not%20copy%20to%20clipboard.%20URL%20is%20in%20the%20new%20tab.')%7D)%3Bconst%20t=window.open(r%2C'%5Fblank')%3Bt%3Ft.opener=null:alert('Popup%20blocked.%20URL%20copied%20to%20clipboard.')%7D%7D)()%3Bvoid'2.1.0' "Setup Linklighter" -[Setup OpenInBrave]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.1' "Setup OpenInBrave" -[Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.1' "Setup OpenInFirefox" -[Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%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.1' "Setup OpenInFirefox-Focus" -[Setup OpenInFirefox-Private]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.1' "Setup OpenInFirefox-Private" -[Setup OpenInGoodReader]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.1' "Setup OpenInGoodReader" -[Setup OpenInTextastic]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.1' "Setup OpenInTextastic" -[Setup OpenInWorkingCopy]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%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.1' "Setup OpenInWorkingCopy" -[Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:const%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.1' "Setup OpenURLParam" +[Setup OpenInBrave]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.2' "Setup OpenInBrave" +[Setup OpenInFirefox]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.2' "Setup OpenInFirefox" +[Setup OpenInFirefox-Focus]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "Setup OpenInFirefox-Focus" +[Setup OpenInFirefox-Private]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' "Setup OpenInFirefox-Private" +[Setup OpenInGoodReader]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.2' "Setup OpenInGoodReader" +[Setup OpenInTextastic]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.2' "Setup OpenInTextastic" +[Setup OpenInWorkingCopy]: https://mobilemind.github.io/OpenInlets/x/#javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26('bitbucket.org'===location.host%7C%7C'github.com'===location.host))location.href=%60working-copy:%2F%2Fshow%3Fremote=%24%7BencodeURIComponent(location.href.split('%2F').slice(0%2C5).join('%2F'))%7D.git%60%3Bvoid'1.6.2' "Setup OpenInWorkingCopy" +[Setup OpenURLParam]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' "Setup OpenURLParam" [Setup UtmStrip]: https://mobilemind.github.io/OpenInlets/x/#javascript:'use%20strict'%3B(()=%3E%7Bconst%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/SECURITY.md b/SECURITY.md index 0b4a9cb..edf5f2f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ updates: | Version | Supported | | ------- | ------------------ | -| 4.0.x | :white_check_mark: | +| 4.1.x | :white_check_mark: | ## Reporting a Vulnerability @@ -64,8 +64,8 @@ npm version patch # or minor, or major git push --follow-tags # Or manually create and push a signed tag -git tag -s 4.0.0 -m "Release version 4.0.0" -git push origin 4.0.0 +git tag -s 4.1.0 -m "Release version 4.0.0" +git push origin 4.1.0 ``` ## Release Verification @@ -76,10 +76,10 @@ All releases should be signed with GPG/SSH signatures for verification: ```bash # Verify the signature on a release tag -git verify-tag 4.0.0 +git verify-tag 4.1.0 # Show tag details with signature -git tag -v 4.0.0 +git tag -v 4.1.0 ``` ### Verifying Signed Commits diff --git a/bookmarklets.json b/bookmarklets.json index 22758ed..b582370 100644 --- a/bookmarklets.json +++ b/bookmarklets.json @@ -13,7 +13,7 @@ { "name": "KillStickyHeaders", "file": "killStickyHeaders.bookmarklet", - "version": "2.0.0" + "version": "2.1.0" }, { "name": "Linklighter", @@ -23,42 +23,42 @@ { "name": "OpenInBrave", "file": "openinbrave.bookmarklet", - "version": "1.1.1" + "version": "1.1.2" }, { "name": "OpenInFirefox", "file": "openinfirefox.bookmarklet", - "version": "1.6.1" + "version": "1.6.2" }, { "name": "OpenInFirefox-Focus", "file": "openinfirefox-focus.bookmarklet", - "version": "1.1.1" + "version": "1.1.2" }, { "name": "OpenInFirefox-Private", "file": "openinfirefox-private.bookmarklet", - "version": "1.1.1" + "version": "1.1.2" }, { "name": "OpenInGoodReader", "file": "openingoodreader.bookmarklet", - "version": "1.5.1" + "version": "1.5.2" }, { "name": "OpenInTextastic", "file": "openintextastic.bookmarklet", - "version": "1.1.1" + "version": "1.1.2" }, { "name": "OpenInWorkingCopy", "file": "openinworkingcopy.bookmarklet", - "version": "1.6.1" + "version": "1.6.2" }, { "name": "OpenURLParam", "file": "openurlparam.bookmarklet", - "version": "1.1.1" + "version": "1.2.0" }, { "name": "UtmStrip", diff --git a/dist/killStickyHeaders.bookmarklet b/dist/killStickyHeaders.bookmarklet index ad01a40..9a2a3ba 100644 --- a/dist/killStickyHeaders.bookmarklet +++ b/dist/killStickyHeaders.bookmarklet @@ -1 +1 @@ -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' \ No newline at end of file +javascript:Array.from(document.querySelectorAll('body%20%2A')).filter(e=%3E%5B'fixed'%2C'sticky'%5D.includes(getComputedStyle(e).position)).forEach(e=%3Ee.remove())%3Bvoid'2.1.0' \ No newline at end of file diff --git a/dist/openinbrave.bookmarklet b/dist/openinbrave.bookmarklet index 8dfc69e..4c6e7cc 100644 --- a/dist/openinbrave.bookmarklet +++ b/dist/openinbrave.bookmarklet @@ -1 +1 @@ -javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='brave:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.1.2' \ No newline at end of file diff --git a/dist/openinfirefox-focus.bookmarklet b/dist/openinfirefox-focus.bookmarklet index a2c7e7c..20ab4a2 100644 --- a/dist/openinfirefox-focus.bookmarklet +++ b/dist/openinfirefox-focus.bookmarklet @@ -1 +1 @@ -javascript:if(%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.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox-focus:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' \ No newline at end of file diff --git a/dist/openinfirefox-private.bookmarklet b/dist/openinfirefox-private.bookmarklet index 09f85f4..f6b39d7 100644 --- a/dist/openinfirefox-private.bookmarklet +++ b/dist/openinfirefox-private.bookmarklet @@ -1 +1 @@ -javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=%60firefox:%2F%2Fopen-url%3Furl=%24%7BencodeURIComponent(location.href)%7D%26private=true%60%3Bvoid'1.1.2' \ No newline at end of file diff --git a/dist/openinfirefox.bookmarklet b/dist/openinfirefox.bookmarklet index 06186bd..f12db2f 100644 --- a/dist/openinfirefox.bookmarklet +++ b/dist/openinfirefox.bookmarklet @@ -1 +1 @@ -javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href='firefox:%2F%2Fopen-url%3Furl='%2BencodeURIComponent(location.href)%3Bvoid'1.6.2' \ No newline at end of file diff --git a/dist/openingoodreader.bookmarklet b/dist/openingoodreader.bookmarklet index 9654bca..0ced424 100644 --- a/dist/openingoodreader.bookmarklet +++ b/dist/openingoodreader.bookmarklet @@ -1 +1 @@ -javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26%2F%5C.pdf(%24%7C%5C%3F)%2F.test(location.href))location.href='gr'%2Blocation.href%3Bvoid'1.5.2' \ No newline at end of file diff --git a/dist/openintextastic.bookmarklet b/dist/openintextastic.bookmarklet index 4364dd7..ae92b1d 100644 --- a/dist/openintextastic.bookmarklet +++ b/dist/openintextastic.bookmarklet @@ -1 +1 @@ -javascript:if(%2FiP(.d%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent))location.href=location.href.replace(%2F%5Ehttps%3F%2F%2C'textastic')%3Bvoid'1.1.2' \ No newline at end of file diff --git a/dist/openinworkingcopy.bookmarklet b/dist/openinworkingcopy.bookmarklet index 33fc58f..5ee25f1 100644 --- a/dist/openinworkingcopy.bookmarklet +++ b/dist/openinworkingcopy.bookmarklet @@ -1 +1 @@ -javascript:if(%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.1' \ No newline at end of file +javascript:if(%2FiP(ad%7Chone)%2F.test(navigator.userAgent)%26%26('bitbucket.org'===location.host%7C%7C'github.com'===location.host))location.href=%60working-copy:%2F%2Fshow%3Fremote=%24%7BencodeURIComponent(location.href.split('%2F').slice(0%2C5).join('%2F'))%7D.git%60%3Bvoid'1.6.2' \ No newline at end of file diff --git a/dist/openurlparam.bookmarklet b/dist/openurlparam.bookmarklet index 65d710b..e0b389c 100644 --- a/dist/openurlparam.bookmarklet +++ b/dist/openurlparam.bookmarklet @@ -1 +1 @@ -javascript:const%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.1' \ No newline at end of file +javascript:'use%20strict'%3B(()=%3E%7Bconst%20t=new%20URL(document.location.href)%3Bif(11%3Et.search.length)return%3Bconst%20e=new%20URLSearchParams(t.search)%2Cn=%5B'url'%2C'destination'%2C'redirect'%2C'target'%2C'goto'%2C'u'%2C'dest'%2C'link'%2C'out'%5D.filter(t=%3Ee.has(t)).map(t=%3E%7Bconst%20n=e.get(t)%3Breturn%20n%3FdecodeURIComponent(n):''%7D).find(t=%3Et.match(%2F%5Ehttps:%2F))%3Bn%26%26window.location.replace(new%20URL(n))%7D)()%3Bvoid'1.2.0' \ No newline at end of file diff --git a/package.json b/package.json index bbfd3be..7b281e6 100644 --- a/package.json +++ b/package.json @@ -59,5 +59,5 @@ "test": "npm run build && npm run verify-build", "verify-build": "node scripts/verify-build.js" }, - "version": "4.0.2" + "version": "4.1.0" } diff --git a/scripts/build-bookmarklet.js b/scripts/build-bookmarklet.js index 889c023..9023f6d 100755 --- a/scripts/build-bookmarklet.js +++ b/scripts/build-bookmarklet.js @@ -21,15 +21,9 @@ function buildBookmarklet(bookmarklet) { // Read minified code from dist/ directory let theCode = readFileOrFail(distPath); - // "unstrict" theCode in simplest cases - switch(bookmarklet.file) { - default: - break; - case 'delighter.bookmarklet': - case 'isitaws.bookmarklet': - case bookmarklet.file.startsWith('openin') ? bookmarklet.file : false : - case 'openurlparam.bookmarklet': - theCode = theCode.replace(/^'use strict';/,''); + // "unstrict" theCode in simplest cases (no vars or const in bookmarklet) + if (! /(const|let|var) /.test(theCode)) { + theCode = theCode.replace(/^'use strict';/,''); } // Append version string diff --git a/src/killStickyHeaders.ts b/src/killStickyHeaders.ts index eb3241d..d6ce4d0 100644 --- a/src/killStickyHeaders.ts +++ b/src/killStickyHeaders.ts @@ -1,12 +1,5 @@ -// use querySelectorAll() to find & delete all fixed position elements of body -(() => { - const elements: NodeListOf = document.querySelectorAll('body *'); - for (const element of Array.from(elements)) { - if (getComputedStyle(element).position === 'fixed') { - const parent: Node | null = element.parentNode; - if (parent) { - parent.removeChild(element); - } - } - } -})(); +// find & delete all fixed/sticky position elements of body + +Array.from(document.querySelectorAll('body *')) + .filter(el => ['fixed', 'sticky'].includes(getComputedStyle(el).position)) + .forEach(el => el.remove()); diff --git a/src/openinbrave.ts b/src/openinbrave.ts index ae2cb15..cd69918 100644 --- a/src/openinbrave.ts +++ b/src/openinbrave.ts @@ -1,4 +1,4 @@ // verify iOS UserAgent & then prefix URL with protocol of Brave iOS app -if (/iP(.d|hone)/.test(navigator.userAgent)) { +if (/iP(ad|hone)/.test(navigator.userAgent)) { location.href = `brave://open-url?url=${encodeURIComponent(location.href)}`; } diff --git a/src/openinfirefox-focus.ts b/src/openinfirefox-focus.ts index d83d9fd..67b15c1 100644 --- a/src/openinfirefox-focus.ts +++ b/src/openinfirefox-focus.ts @@ -1,4 +1,4 @@ // verify iOS UserAgent, prefix URL w/ Firefox Focus protocol, & add private suffix -if (/iP(.d|hone)/.test(navigator.userAgent)) { +if (/iP(ad|hone)/.test(navigator.userAgent)) { location.href = `firefox-focus://open-url?url=${encodeURIComponent(location.href)}&private=true`; } diff --git a/src/openinfirefox-private.ts b/src/openinfirefox-private.ts index 1227164..292f965 100644 --- a/src/openinfirefox-private.ts +++ b/src/openinfirefox-private.ts @@ -1,4 +1,4 @@ // verify iOS UserAgent, prefix URL w/ Firefox iOS protocol, & add private suffix -if (/iP(.d|hone)/.test(navigator.userAgent)) { +if (/iP(ad|hone)/.test(navigator.userAgent)) { location.href = `firefox://open-url?url=${encodeURIComponent(location.href)}&private=true`; } diff --git a/src/openinfirefox.ts b/src/openinfirefox.ts index e3b0510..0279328 100644 --- a/src/openinfirefox.ts +++ b/src/openinfirefox.ts @@ -1,4 +1,4 @@ // verify iOS UserAgent & then prefix URL with protocol of Firefox iOS app -if (/iP(.d|hone)/.test(navigator.userAgent)) { +if (/iP(ad|hone)/.test(navigator.userAgent)) { location.href = `firefox://open-url?url=${encodeURIComponent(location.href)}`; } diff --git a/src/openingoodreader.ts b/src/openingoodreader.ts index cbed339..5475563 100644 --- a/src/openingoodreader.ts +++ b/src/openingoodreader.ts @@ -1,6 +1,6 @@ // verify iOS UserAgent *and* file is PDF // then prefix URL with protocol of GoodReader iOS app -if (/iP(.d|hone)/.test(navigator.userAgent) && +if (/iP(ad|hone)/.test(navigator.userAgent) && /\.pdf($|\?)/.test(location.href)) { location.href = `gr${location.href}`; } diff --git a/src/openintextastic.ts b/src/openintextastic.ts index 4957d39..bc3ebbf 100644 --- a/src/openintextastic.ts +++ b/src/openintextastic.ts @@ -1,4 +1,4 @@ // verify on an iOS device, then swap in URL protocol of Textastic iOS app -if (/iP(.d|hone)/.test(navigator.userAgent)) { +if (/iP(ad|hone)/.test(navigator.userAgent)) { location.href = location.href.replace(/^https?/, 'textastic'); } diff --git a/src/openinworkingcopy.ts b/src/openinworkingcopy.ts index fb97a2c..bcfd25a 100644 --- a/src/openinworkingcopy.ts +++ b/src/openinworkingcopy.ts @@ -1,7 +1,7 @@ // verify host is 'github.com' (or 'bitbucket.org') & on an iOS device // then use URL protocol of Working Copy with "show" and top-level repo URL // Working Copy "show" will open to the repo, performing a clone if needed -if (/iP(.d|hone)/.test(navigator.userAgent) && +if (/iP(ad|hone)/.test(navigator.userAgent) && (location.host === 'bitbucket.org' || location.host === 'github.com')) { location.href = `working-copy://show?remote=${encodeURIComponent(location.href.split('/').slice(0, 5).join('/'))}.git`; } diff --git a/src/openurlparam.ts b/src/openurlparam.ts index 6a88538..fc9f70e 100644 --- a/src/openurlparam.ts +++ b/src/openurlparam.ts @@ -1,25 +1,29 @@ -// grab the URL param from the current URL query string (location.search) -const u: number = location.search.search("url="); -if (u > -1) { - let t: string = location.search.slice(4 + u); - // check if there are params after it (i.e., an "&") - const p: number = t.indexOf("&"); - // lop off trailing params - if (p > -1) { - t = t.slice(0, p); - } - if (t.length > 5) { - // decode URL and validate it before navigating - const decodedUrl: string = decodeURIComponent(t); - try { - // Parse URL to validate it's well-formed - const urlObj: URL = new URL(decodedUrl, location.href); - // Only allow https scheme to prevent XSS and enforce secure connections - if (urlObj.protocol === "https:") { - location.replace(urlObj.href); - } - } catch { - // Invalid URL - do nothing - } - } -} +// grab the (target) "url" param from the current URL query string (location.search) + +(() => { + // default to getting the current address & making it a URL object to work with + const origUrl: URL = new URL(document.location.href); + + // exit if the current URL object searchParam is less than 11 chars ('https://x.x) + if (origUrl.search.length < 11) { + return; + } + + // create array of common param keys used for URLs (in priority order) + const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], + origParams: URLSearchParams = new URLSearchParams(origUrl.search); + + // find 1st param key match that's a valid looking URL, starting w/ 'https:' + const urlCandidate: string | undefined = urlKey + .filter((param: string) => origParams.has(param)) + .map((param: string) => { + const value: string | null = origParams.get(param); + return value ? decodeURIComponent(value) : ''; + }) + .find((url: string) => url.match(/^https:/)); + + // navigate to new URL if found + if (urlCandidate) { + window.location.replace(new URL(urlCandidate)); + } +})(); diff --git a/src/unskim.ts b/src/unskim.ts index 8f3ca3a..f41f7a0 100644 --- a/src/unskim.ts +++ b/src/unskim.ts @@ -5,40 +5,39 @@ // wrap everything in an anon function for isolation (() => { - -// default to getting the current address & making it a URL object to work with -let origUrl: URL = new URL(document.location.href); - -// handle special case of Safari error page (DNS block of redirection service) -if (origUrl.href === 'safari-resource:/ErrorPage.html') { - // DO NOT "UNCURL" the apostrophe or quotes in the match regex, that formatting IS CRITICAL - const urlStr: string | undefined = document.querySelector('p.error-message')?.textContent?.match(/Safari can't open the page "(https?:[^"]+)"/)?.[1]; - if (urlStr) { - origUrl = new URL(urlStr); - } -} - -// if the current URL object doesn't even have a searchParam, exit now -if (origUrl.search === '') { - return; -} - -// create array of common param keys used for URLs (in priority order) -const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], - origParams: URLSearchParams = new URLSearchParams(origUrl.search); - -// find 1st param key match that's a valid looking URL -const urlCandidate: string | undefined = urlKey - .filter((param: string) => origParams.has(param)) - .map((param: string) => { - const value: string | null = origParams.get(param); - return value ? decodeURIComponent(value) : ''; - }) - .find((url: string) => url.match(/^https?:/)); - -// navigate to new URL if found -if (urlCandidate) { - window.location.replace(new URL(urlCandidate)); -} + // default to getting the current address & making it a URL object to work with + let origUrl: URL = new URL(document.location.href); + + // handle special case of Safari error page (DNS block of redirection service) + if (origUrl.href === 'safari-resource:/ErrorPage.html') { + // DO NOT "UNCURL" the apostrophe or quotes in the match regex, that formatting IS CRITICAL + const urlStr: string | undefined = document.querySelector('p.error-message')?.textContent?.match(/Safari can't open the page "(https?:[^"]+)"/)?.[1]; + if (urlStr) { + origUrl = new URL(urlStr); + } + } + + // if the current URL object doesn't even have a searchParam, exit now + if (origUrl.search === '') { + return; + } + + // create array of common param keys used for URLs (in priority order) + const urlKey: string[] = ['url', 'destination', 'redirect', 'target', 'goto', 'u', 'dest', 'link', 'out'], + origParams: URLSearchParams = new URLSearchParams(origUrl.search); + + // find 1st param key match that's a valid looking URL + const urlCandidate: string | undefined = urlKey + .filter((param: string) => origParams.has(param)) + .map((param: string) => { + const value: string | null = origParams.get(param); + return value ? decodeURIComponent(value) : ''; + }) + .find((url: string) => url.match(/^https?:/)); + + // navigate to new URL if found + if (urlCandidate) { + window.location.replace(new URL(urlCandidate)); + } })();