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. 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 } } + 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 diff --git a/README.md b/README.md index 20991a6..2d571df 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 @@ -81,19 +81,19 @@ 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 +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. -+ __[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 @@ -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 @@ -127,9 +127,9 @@ 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 unskim] v2.0.0 -+ __Mobile Safari setup link__ -- [Setup x-man] v1.3.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 ## Requirements @@ -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 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 + selection, a start/end fragment pair, and/or a prefix or suffix. Browsers + 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 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%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" @@ -342,9 +341,9 @@ 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" -[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" +[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,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%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" @@ -361,9 +360,9 @@ 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 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 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" @@ -378,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 81be683..0676721 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", @@ -63,7 +63,7 @@ { "name": "UtmStrip", "file": "utmstrip.bookmarklet", - "version": "2.0.0" + "version": "2.1.0" }, { "name": "deLighter", @@ -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 e081c60..c1a503a 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%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 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/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 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" diff --git a/src/linklighter.ts b/src/linklighter.ts index 9173e3c..2a02c86 100644 --- a/src/linklighter.ts +++ b/src/linklighter.ts @@ -1,33 +1,77 @@ +/* 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 (() => { + const enc: typeof encodeURIComponent = encodeURIComponent; const selection: Selection | null = window.getSelection(); if (!selection) { return; } 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 = ''; + 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, endOffset} = range; + + // 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)+$/, ''); + 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.'); } } } 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 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);