diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5432776..f2df5e1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,14 +27,14 @@ jobs: run: bun run bundle - name: Bundle source - run: git archive --format=zip ${{ github.event.release.tag_name }} --output .out/replace_maps_source.zip + run: git archive --format=zip ${{ github.event.release.tag_name }} --output .dist/replace_maps_source.zip - name: Release uses: browser-actions/release-firefox-addon@latest with: addon-id: replace_maps@nobkd.github.io - addon-path: .out/replace_maps.zip - source-path: .out/replace_maps_source.zip + addon-path: .dist/replace_maps.zip + source-path: .dist/replace_maps_source.zip approval-note: Follow the instructions in AMO-README.md release-note: ${{ github.event.release.body }} # TODO: convert md to html, h1-6 not allowed... Currently ignoring this problem, as the field is needed for publishing license: MPL-2.0 diff --git a/.gitignore b/.gitignore index 638cbb2..4329a7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ .dist/ -.out/ .version-update .env + +src/@lib/**/*.png diff --git a/AMO-README.md b/AMO-README.md index 296ada9..cd60f53 100644 --- a/AMO-README.md +++ b/AMO-README.md @@ -18,4 +18,4 @@ This uses the `package-lock.json` file for installation to avoid package changes bun run build && bun run bundle ``` -This minifies the JavaScript, copies static resources and bundles the output to `.out/replace_maps.zip`. +This minifies the JavaScript, copies static resources and bundles the output to `.dist/replace_maps.zip`. diff --git a/README.md b/README.md index 6ef5d69..dc4e975 100644 --- a/README.md +++ b/README.md @@ -27,67 +27,12 @@ As a result, the response is an extension page that contains a [Leaflet](https:/ You can turn the extension off for every hostname by using the browser action button or by using the settings page. -### Extension Flowchart - -```mermaid -flowchart TD - -subgraph action [Browser Action] -actionclick(Action Icon) -->|add / remove| storage[(Disabled Hostnames)] -settings(Settings Page) -->|add / remove| storage -end - - -subgraph reqres [Request-Response Sytem] - req([Frame Request]) --> url{Matches\nGoogle Maps\nURL?} - url -->|no| nomatch([Continue Original Request]) - - url -->|yes| match{Hostname\nDisabled?} - storage -->|provide hostnames| match - match -->|no| res([Redirect to extension map page]) - match -->|yes| nomatch - -end - -res -->|use params| params - -subgraph dec [Search-Param Decoding] - params(Search Params) -->|has q| q([readQ]) - q -->|with title| pos[Marker/s] - params -->|has z| zoom[Zoom] - - params -->|has pb| pb([readPB]) - pb -->|has| minfo[Marker Info] - pb -->|has| marea[Map Area] - - minfo -->|has\nsearch string| q - minfo -->|has\n0x...:0x...| cid[CID] - cid -.->|unknown usage| pos - minfo -->|has\nDMS coords| dms([parseDMS]) - dms --> pos - - pb -->|has| mtype[Map Type] - - marea -->|has\ncoords| mcoords[Map Coords] - marea -->|has\naltitude| mzoom([getMapZoom]) - mzoom --> zoom - - pos --> mdata[(Map Data)] - mtype --> mdata - mcoords --> mdata - zoom --> mdata -end - -mdata -->|use map data| mview([Load Leaflet Map]) -``` - ### Known issues - Sometimes the zoom level is completely wrong - Not working when a website does not use an iFrame / embed - Not working when iFrame uses only CIDs - No routes, just positions -- If insufficient information is gathered, the map stays blank [badge-license]: https://img.shields.io/github/license/nobkd/replace-maps diff --git a/build-icons.js b/build-icons.js index ec95d19..c55b145 100644 --- a/build-icons.js +++ b/build-icons.js @@ -3,7 +3,7 @@ import { join } from 'path' import { mkdirSync } from 'fs' const indir = 'icons' -const outdir = 'public/icons' +const outdir = 'src/icons' const svgs = ['icon-grey.svg', 'icon.svg'] const sizes = [48, 96] diff --git a/build.sh b/build.sh deleted file mode 100755 index 5b72320..0000000 --- a/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# clean & create dir -rm -rf '.dist' -mkdir -p '.dist' - -# copy static resources -cp -r 'public/.' '.dist' -cp 'node_modules/leaflet/dist/images/marker-icon-2x.png' 'node_modules/leaflet/dist/images/marker-shadow.png' '.dist' - -# check if dev / prod, then build -if [[ $1 == 'dev' ]]; then - echo '--watch' -else - echo '--minify' -fi | xargs bun build --experimental-css --experimental-html --outdir '.dist' 'src/bg.html' 'src/map.html' 'src/options.html' diff --git a/bump.js b/bump.js index 3e1be51..746fba3 100644 --- a/bump.js +++ b/bump.js @@ -1,6 +1,6 @@ import { writeFileSync } from 'node:fs' import { version } from './package.json' with { type: 'json' } -import manifest from './public/manifest.json' with { type: 'json' } +import manifest from './src/manifest.json' with { type: 'json' } manifest.version = version -writeFileSync('./public/manifest.json', JSON.stringify(manifest, null, 2)) +writeFileSync('./src/manifest.json', JSON.stringify(manifest, null, 2)) diff --git a/bun.lock b/bun.lock index 8ddbc54..bb3450b 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ }, "devDependencies": { "npm-run-all": "^4.1.5", + "nuekit": "^1.0.0-RC.3", "web-ext": "^8.5.0", }, }, @@ -103,6 +104,8 @@ "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], "atomically": ["atomically@2.0.3", "", { "dependencies": { "stubborn-fs": "^1.2.5", "when-exit": "^2.1.1" } }, "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw=="], @@ -135,9 +138,9 @@ "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g=="], + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - "call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="], + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], @@ -213,6 +216,8 @@ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "diff-dom": ["diff-dom@5.1.4", "", {}, "sha512-TSEaVdVGictY1KHg7VpVw2nuM02YKC9C8/qBkGiCnkiAybVbu1zQTMj2/dnVLRO7Z62UsqzHGpXweiOj5/jaZg=="], + "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -229,7 +234,7 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "entities": ["entities@6.0.0", "", {}, "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw=="], "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], @@ -307,7 +312,7 @@ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "for-each": ["for-each@0.3.3", "", { "dependencies": { "is-callable": "^1.1.3" } }, "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw=="], + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], "fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="], @@ -325,7 +330,7 @@ "get-east-asian-width": ["get-east-asian-width@1.3.0", "", {}, "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ=="], - "get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], @@ -371,7 +376,7 @@ "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], + "htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], @@ -403,11 +408,11 @@ "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - "is-async-function": ["is-async-function@2.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ=="], + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], - "is-boolean-object": ["is-boolean-object@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng=="], + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], @@ -463,7 +468,7 @@ "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], - "is-weakref": ["is-weakref@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2" } }, "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q=="], + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], @@ -555,7 +560,15 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - "object-inspect": ["object-inspect@1.13.3", "", {}, "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA=="], + "nue-glow": ["nue-glow@0.2.2", "", {}, "sha512-ua/EDUmkWswmSIAAET9MNqwG3lk92eh7H//2XZ3LGJwCojiBukEpGrRyu9Mn5EzU4fHkCvL1ObChTO8OFWMFEg=="], + + "nuejs-core": ["nuejs-core@0.5.3", "", { "dependencies": { "htmlparser2": "^10.0.0" } }, "sha512-tXEwdTSbR/c8NeRFrYawtJOKDkDUeiSX2XWW8N3qvrm6T2DS9EtV45SQya0nmou32wgoz1us/uNHBhuawV98jw=="], + + "nuekit": ["nuekit@1.0.0-RC.3", "", { "dependencies": { "diff-dom": "^5.1.4", "js-yaml": "^4.1.0", "nue-glow": "latest", "nuejs-core": "latest", "nuemark": "latest" }, "peerDependencies": { "esbuild": "^0.25", "lightningcss": "^1.29" }, "optionalPeers": ["esbuild", "lightningcss"], "bin": { "nue": "src/cli.js" } }, "sha512-kbMAGE2BU8yVn9hYmi6K/Vi5Uf4YIb7UGWex+ilBYZbrf+64dv/i438Epcdcb3yPLvzlcf9hykTnQEUu+fY3ag=="], + + "nuemark": ["nuemark@0.6.2", "", { "dependencies": { "js-yaml": "^4.1.0", "nue-glow": "latest" } }, "sha512-JHcPebtjzUYVSFXRAK5NyzyW3DoUz4OalifCdYPMT0spAjlErM1fo0ZVX1bjvUNYkp9ASsGYACX8/PZ8oYst6Q=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -615,7 +628,7 @@ "pino-std-serializers": ["pino-std-serializers@7.0.0", "", {}, "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="], - "possible-typed-array-names": ["possible-typed-array-names@1.0.0", "", {}, "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -697,7 +710,7 @@ "shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], - "shell-quote": ["shell-quote@1.8.2", "", {}, "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA=="], + "shell-quote": ["shell-quote@1.7.3", "", {}, "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw=="], "shellwords": ["shellwords@0.1.1", "", {}, "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww=="], @@ -825,7 +838,7 @@ "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], - "which-typed-array": ["which-typed-array@1.1.18", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA=="], + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], @@ -879,12 +892,16 @@ "boxen/type-fest": ["type-fest@4.40.0", "", {}, "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw=="], + "cheerio/htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], + "chrome-launcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "config-chain/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "dot-prop/type-fest": ["type-fest@4.40.0", "", {}, "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw=="], "eslint/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], @@ -903,8 +920,6 @@ "fx-runner/commander": ["commander@2.9.0", "", { "dependencies": { "graceful-readlink": ">= 1.0.0" } }, "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A=="], - "fx-runner/shell-quote": ["shell-quote@1.7.3", "", {}, "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw=="], - "fx-runner/which": ["which@1.2.4", "", { "dependencies": { "is-absolute": "^0.1.7", "isexe": "^1.1.1" }, "bin": { "which": "./bin/which" } }, "sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA=="], "global-directory/ini": ["ini@4.1.1", "", {}, "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g=="], @@ -929,6 +944,8 @@ "package-json/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "parse5/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "pino-abstract-transport/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], @@ -975,6 +992,8 @@ "boxen/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "cheerio/htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], diff --git a/src/map/utils/parseDMS.js b/map-utils/parseDMS.js similarity index 100% rename from src/map/utils/parseDMS.js rename to map-utils/parseDMS.js diff --git a/src/map/utils/parsePB.js b/map-utils/parsePB.js similarity index 100% rename from src/map/utils/parsePB.js rename to map-utils/parsePB.js diff --git a/src/map/utils/read.js b/map-utils/read.js similarity index 100% rename from src/map/utils/read.js rename to map-utils/read.js diff --git a/src/test/parseDMS.test.js b/map-utils/test/parseDMS.test.js similarity index 88% rename from src/test/parseDMS.test.js rename to map-utils/test/parseDMS.test.js index 779bcbf..be1b8ac 100644 --- a/src/test/parseDMS.test.js +++ b/map-utils/test/parseDMS.test.js @@ -1,5 +1,5 @@ import { test, expect } from 'bun:test' -import { parseDMS } from '../map/utils/parseDMS.js' +import { parseDMS } from '../parseDMS.js' test('Parse Degrees Minutes Seconds Direction: Example', () => { const res = parseDMS(`10°60'36.0"N 10°60'36.0"E`) diff --git a/src/test/parsePB.test.js b/map-utils/test/parsePB.test.js similarity index 97% rename from src/test/parsePB.test.js rename to map-utils/test/parsePB.test.js index c84387d..facb038 100644 --- a/src/test/parsePB.test.js +++ b/map-utils/test/parsePB.test.js @@ -1,5 +1,5 @@ import { test, expect } from 'bun:test' -import { parsePB, tileTypes } from '../map/utils/parsePB.js' +import { parsePB, tileTypes } from '../parsePB.js' test('Parse PB: empty list', () => { const res = parsePB([]) diff --git a/src/test/read.test.js b/map-utils/test/read.test.js similarity index 97% rename from src/test/read.test.js rename to map-utils/test/read.test.js index 7562f32..9d01fc3 100644 --- a/src/test/read.test.js +++ b/map-utils/test/read.test.js @@ -1,5 +1,5 @@ import { test, expect, mock } from 'bun:test' -import { readPB, readQ, nominatimQ } from '../map/utils/read.js' +import { readPB, readQ, nominatimQ } from '../read.js' globalThis.fetch = mock() diff --git a/src/map/utils/zoom.js b/map-utils/zoom.js similarity index 100% rename from src/map/utils/zoom.js rename to map-utils/zoom.js diff --git a/package.json b/package.json index b6564ef..ca22779 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,20 @@ "url": "git+https://github.com/nobkd/replace-maps.git" }, "scripts": { + "postinstall": "bun -e \"import {$} from 'bun'; $`cp -r 'node_modules/leaflet/dist/images' 'node_modules/leaflet-fullscreen/dist/fullscreen.png' 'node_modules/leaflet-fullscreen/dist/fullscreen@2x.png' 'src/@lib/'`\"", "icons": "bun i --no-save oslllo-svg2 && bun build-icons.js", - "dev": "run-p -rl build:dev serve", - "build": "sh build.sh", - "build:dev": "sh build.sh dev", - "serve": "web-ext run -s .dist", - "bundle": "web-ext build -s .dist -a .out --overwrite-dest -n replace_maps.zip", + "dev": "run-p -rl build:dev serve:dev", + "prod": "run-s -l build serve", + "build": "nue build -pr src", + "build:dev": "nue -r src", + "serve": "web-ext run -s src/.dist/prod", + "serve:dev": "web-ext run -s src/.dist/dev", + "bundle": "web-ext build -s src/.dist/prod -a .dist --overwrite-dest -n replace_maps.zip", "release": "bun i --no-save release-it release-it-changelogen && release-it" }, "devDependencies": { "npm-run-all": "^4.1.5", + "nuekit": "^1.0.0-RC.3", "web-ext": "^8.5.0" }, "dependencies": { diff --git a/src/@global/bootstrap.js b/src/@global/bootstrap.js new file mode 100644 index 0000000..e369713 --- /dev/null +++ b/src/@global/bootstrap.js @@ -0,0 +1,9 @@ +import { router } from '/@nue/app-router.js' + +router.configure({ + route: '/:', + url_params: ['q', 'pb', 'z'], + persistent_params: ['theme', 'disabled_hosts', 'resizable'], +}) + +router.initialize({ root: document.body }) diff --git a/src/@global/layout.html b/src/@global/layout.html new file mode 100644 index 0000000..eebcb93 --- /dev/null +++ b/src/@global/layout.html @@ -0,0 +1,8 @@ + + + + + +
+ +
diff --git a/src/bg/bg.js b/src/@lib/bg-script.js similarity index 51% rename from src/bg/bg.js rename to src/@lib/bg-script.js index fcb8599..f5b6f0d 100644 --- a/src/bg/bg.js +++ b/src/@lib/bg-script.js @@ -1,15 +1,48 @@ -import { runtime, webRequest, tabs, windows } from 'webextension-polyfill' +import { changeHostnameState, getHostnameState, nueStateChange } from '/@util/helper.js' +import { browserAction, tabs, webRequest, windows, runtime } from '/@util/webext.js' -import { disabledHosts, getHostname } from './utils/storage.js' -import { updateActiveTabIcon } from './utils/actionIcon.js' -import domainEnds from './utils/domainEnds.json' with { type: 'json' } -const gLocales = domainEnds.join('|') // TODO: collect more locales -export const matcher = new RegExp( +/*** action ***/ + +function updateIcon(url) { + browserAction.setIcon({ + path: !getHostnameState(url) ? { + 48: '/icons/48-icon.png', + 96: '/icons/96-icon.png', + } : { + 48: '/icons/48-icon-grey.png', + 96: '/icons/96-icon-grey.png', + }, + }) +} + +async function updateActiveTabIcon() { + let browserTabs = await tabs.query({ active: true, currentWindow: true }) + + let tab = browserTabs[0] + if (tab && tab.url) updateIcon(tab.url) +} + +function actionClick(tab) { + if (!tab.url || !tab.id) return + else if (tab.url.startsWith('about:')) return + + changeHostnameState(tab.url) + updateActiveTabIcon() + tabs.reload(tab.id, { bypassCache: true }) +} + + +/*** bg ***/ + +const gLocales = (await (await fetch('/domain-ends.json')).json()).join('|') + +const matcher = new RegExp( // TODO: fix regex to fit more patterns `^(https?:\/\/)?(maps\.google\.(${gLocales})\/maps.*\?.*output=embed|(www\.)?google\.(${gLocales})\/maps\/embed.*\?)` ) -export const runtimeMapUrl = runtime.getURL('map.html') + +const runtimeMapUrl = runtime.getURL('map.html') /** * Checks if `frames` send a request to Maps. @@ -21,15 +54,20 @@ export const runtimeMapUrl = runtime.getURL('map.html') function redirect(req) { // TODO: check if originUrl always matches current tab url -> e.g. in frames with subframes if (req.originUrl && req.url.match(matcher)) { - if (!disabledHosts.includes(getHostname(req.originUrl))) { + if (!getHostnameState(req.originUrl)) { return { - redirectUrl: runtimeMapUrl + '?' + req.url.split('?').pop(), + redirectUrl: `${runtimeMapUrl}?${req.url.split('?').pop()}`, } } } return {} } + +/*** onload ***/ + +browserAction.onClicked.addListener(actionClick) + // Listens to web requests from frames, redirects when fitting `matcher` webRequest.onBeforeRequest.addListener( redirect, @@ -37,7 +75,7 @@ webRequest.onBeforeRequest.addListener( urls: [''], types: ['sub_frame'], }, - ['blocking'] + ['blocking'], ) // listen to tab URL changes @@ -51,3 +89,5 @@ windows.onFocusChanged.addListener(updateActiveTabIcon) // update icon at startup updateActiveTabIcon() + +nueStateChange(updateActiveTabIcon) diff --git a/src/@lib/map.css b/src/@lib/map.css new file mode 100644 index 0000000..bebdda8 --- /dev/null +++ b/src/@lib/map.css @@ -0,0 +1,17 @@ +@import "leaflet/dist/leaflet.css"; +@import "leaflet-fullscreen/dist/leaflet.fullscreen.css"; + + +html, body, div { margin: 0; padding: 0; } + +#map { width: 100%; height: 100vh; } + +.leaflet-container { + &[data-theme="dark"] { background: #111 !important; } + @media (prefers-color-scheme: dark) { &[data-theme="system"] { background: #111 !important; } } +} + +.leaflet-layer, .leaflet-popup, .leaflet-tooltip, .leaflet-control, .leaflet-control-layers-toggle, .leaflet-attribution-flag { + [data-theme="dark"] & { filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(120%); } + @media (prefers-color-scheme: dark) { [data-theme="system"] & { filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(120%); } } +} diff --git a/src/@lib/map.dhtml b/src/@lib/map.dhtml new file mode 100644 index 0000000..27d91f3 --- /dev/null +++ b/src/@lib/map.dhtml @@ -0,0 +1,18 @@ + + +
+ + +
diff --git a/src/@lib/options.css b/src/@lib/options.css new file mode 100644 index 0000000..e8d559d --- /dev/null +++ b/src/@lib/options.css @@ -0,0 +1,61 @@ +html, body, ul, p { margin: 0; padding: 0; } + +* { box-sizing: border-box; font: inherit; } + +:root { + --text: black; + --bg: white; + + --table-even: #d3d2db; + --table-odd: #c3c2cb; + + @media (prefers-color-scheme: dark) { + --text: #ccc; + --bg: #1c1b22; + + --table-even: #53525b; + --table-odd: #33323b; + } + + font-family: system-ui, sans-serif; + font-size: 14px; +} + +body, main, main > div { width: 100%; min-height: 100vh; } + +main > div { color: var(--text); background-color: var(--bg); } + +label { font-weight: bold; } + +input, button, select { padding: 0.5rem; border-radius: 0.5rem; border-style: solid; border-color: color-mix(in srgb, var(--text), transparent); } + +section { padding: 0.5rem; display: grid; gap: 0.75rem; } + +.items, .form :is(form, li) { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + gap: 0.5rem; +} + +.form ul { + display: grid; + gap: 0.25rem; + + li { + border-radius: 0.25rem; + padding: 0.5rem; + + &:nth-child(even) { background-color: var(--table-even); } + &:nth-child(odd) { background-color: var(--table-odd); } + + button { + width: 1.75rem; + height: 1.75rem; + color: red; + display: flex; + justify-content: center; + align-items: center; + } + } +} diff --git a/src/@lib/options.dhtml b/src/@lib/options.dhtml new file mode 100644 index 0000000..7980775 --- /dev/null +++ b/src/@lib/options.dhtml @@ -0,0 +1,76 @@ + + +
+ +
+ +
+ + +
+ + + + + + +
+ + +
+
+ + +
+ +
    +
  • +

    {e}

    + +
  • +
+ + +
diff --git a/src/@util/helper.js b/src/@util/helper.js new file mode 100644 index 0000000..c77f25a --- /dev/null +++ b/src/@util/helper.js @@ -0,0 +1,31 @@ + +import { router } from '/@nue/app-router.js' + +/*** nue ***/ + +export function nueStateChange(fn) { + window.addEventListener('storage', ({ key }) => key == '$nue_state' && fn()) +} + + +/*** general ***/ + +function getHostname(url) { + url = url.replace(/^\w+:\/\//, '') + url = url.split(/[\/#\?]/, 1)[0] + return url +} + +export function changeHostnameState(url, add) { + const hostname = getHostname(url) + const hosts = new Set(router.state.disabled_hosts || []) + + if (add) hosts.add(hostname) + else hosts.has(hostname) ? hosts.delete(hostname) : hosts.add(hostname) + + router.set({ disabled_hosts: [...hosts] }) +} + +export function getHostnameState(url) { + return (router.state.disabled_hosts || []).includes(getHostname(url)) +} diff --git a/src/@util/leaflet.js b/src/@util/leaflet.js new file mode 100644 index 0000000..0ca20b2 --- /dev/null +++ b/src/@util/leaflet.js @@ -0,0 +1,65 @@ +import L from 'leaflet' +import 'leaflet-fullscreen' + +import { readPB, readQ } from '../../map-utils/read.js' + + +const tiles = { + roadmap: { + layer: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', // OpenStreetMap.Mapnik + data: { + attribution: '© OpenStreetMap', + }, + }, + satellite: { + layer: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', // Esri.WorldImagery + data: { + attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community', + }, + }, +} + +export async function init(el, { q, z, pb }) { + const data = { zoom: z ?? 15, tile: 'roadmap' } + + /* process params */ + if (pb) Object.assign(data, await readPB(pb)) + else if (q) { + const marker = await readQ(q) + if (marker) data.markers = [marker] + } + + data.tile = 'roadmap' // TODO: re-enable satellite in the future again. + + /* leaflet */ + + const map = L.map(el, { + fullscreenControl: true, + scrollWheelZoom: true, + touchZoom: true, + zoomSnap: 0.1, + zoomDelta: 0.5, + minZoom: 1, + maxZoom: 18, + center: [0, 0], + ...data, + }) + + L.control.scale().addTo(map) + L.tileLayer(tiles[data.tile].layer, tiles[data.tile].data).addTo(map) + + if (data.markers?.length == 0 && data.area) map.setView([data.area.lat, data.area.lon]) + if (data.markers) { + if (data.markers.length === 1) { + const mapMarker = data.markers[0] + map.setView([mapMarker.lat, mapMarker.lon]) + } + + data.markers.forEach((marker) => { + const mapMarker = L.marker([marker.lat, marker.lon]).addTo(map) + mapMarker.bindPopup(marker.label, { closeButton: false }).openPopup() + }) + } + + return data +} diff --git a/src/@util/webext.js b/src/@util/webext.js new file mode 100644 index 0000000..d974a86 --- /dev/null +++ b/src/@util/webext.js @@ -0,0 +1 @@ +export { runtime, browserAction, tabs, webRequest, windows } from 'webextension-polyfill' diff --git a/src/bg.html b/src/bg.html deleted file mode 100644 index ecc202a..0000000 --- a/src/bg.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/bg.md b/src/bg.md new file mode 100644 index 0000000..ebc7ac4 --- /dev/null +++ b/src/bg.md @@ -0,0 +1,4 @@ +--- +include: [bg] +title: Background & Action +--- diff --git a/src/bg/action.js b/src/bg/action.js deleted file mode 100644 index 0db2efa..0000000 --- a/src/bg/action.js +++ /dev/null @@ -1,35 +0,0 @@ -import { tabs, browserAction, /*webNavigation*/ } from 'webextension-polyfill' - -import { getHostname, invertHostState } from './utils/storage.js' -//import { matcher as mapsUrlMatcher, runtimeMapUrl } from './bg'; - -//const replacedUrlMatcher = new RegExp(`^${runtimeMapUrl}\?`); - -/** - * Async function to react to clicks on the browser action icon. - * Takes the current tab, takes its hostname and inverts the state of the hostname. - * - * Requests all frames from the current tab, filters them for extension Leaflet frames and Maps frames. - * Reloads the full tab on extension Leaflet or Maps frame match. - * @param {browser.Tabs.Tab} tab Currently active tab - */ -async function actionClick(tab) { - if (!tab.url || !tab.id) return - - let hostname = getHostname(tab.url) - await invertHostState(hostname) - - /* - // TODO: try to only reload necessary parts!!! - let frames = (await webNavigation.getAllFrames({ tabId: tab.id })) ?? []; - - if ( - frames.some((frame) => { - frame.url.match(replacedUrlMatcher) || frame.url.match(mapsUrlMatcher); - }) - ) - */ - tabs.reload(tab.id, { bypassCache: true }) -} - -browserAction.onClicked.addListener(actionClick) diff --git a/src/bg/utils/actionIcon.js b/src/bg/utils/actionIcon.js deleted file mode 100644 index 1614f4c..0000000 --- a/src/bg/utils/actionIcon.js +++ /dev/null @@ -1,33 +0,0 @@ -import { browserAction, tabs } from 'webextension-polyfill' - -import { disabledHosts, getHostname } from './storage.js' - -/** - * Updates the action icon - * @param {string} hostname Hostname - */ -export function updateIcon(hostname) { - let disabled = disabledHosts.includes(hostname) - - browserAction.setIcon({ - path: !disabled ? { - 48: '/icons/48-icon.png', - 96: '/icons/96-icon.png', - } : { - 48: '/icons/48-icon-grey.png', - 96: '/icons/96-icon-grey.png', - }, - }) -} - -/** - * Async function to update the icon of the currently active tab. Uses `updateIcon` internally - */ -export async function updateActiveTabIcon() { - let browserTabs = await tabs.query({ active: true, currentWindow: true }) - - let tab = browserTabs[0] - if (tab && tab.url) { - updateIcon(getHostname(tab.url)) - } -} diff --git a/src/bg/utils/storage.js b/src/bg/utils/storage.js deleted file mode 100644 index 0e7a374..0000000 --- a/src/bg/utils/storage.js +++ /dev/null @@ -1,77 +0,0 @@ -import { storage } from 'webextension-polyfill' - -import { updateIcon } from './actionIcon.js' - -export const KEY_DISABLED_HOSTS = 'disabled_hosts' -export const KEY_RESIZABLE_STATE = 'resizable_state' -export const KEY_THEME = 'theme' - -// Listens to changes on the storage. Updates disabled hosts list, if stored list changes -/** @type {string[]} */ -export let disabledHosts = await getDisabledHosts() -export let resizableState = await getResizableState() -export let theme = await getTheme() - -storage.local.onChanged.addListener((changes) => { - if (KEY_DISABLED_HOSTS in changes) disabledHosts = changes[KEY_DISABLED_HOSTS].newValue ?? [] - if (KEY_RESIZABLE_STATE in changes) resizableState = changes[KEY_RESIZABLE_STATE].newValue ?? false - if (KEY_THEME in changes) theme = changes[KEY_THEME].newValue ?? 'system' -}) - -/** - * Async function to get the list of disabled hostnames - * @returns {Promise} List of disabled hostnames - */ -async function getDisabledHosts() { - return (await storage.local.get(KEY_DISABLED_HOSTS))[KEY_DISABLED_HOSTS] ?? [] -} - -async function getResizableState() { - return (await storage.local.get(KEY_RESIZABLE_STATE))[KEY_RESIZABLE_STATE] ?? false -} - -async function getTheme() { - return (await storage.local.get(KEY_THEME))[KEY_THEME] ?? 'system' -} - -/** - * Async function to invert the state of a hostname. - * Adds new entry if not disabled, removes entry, if already disabled - * @param {string} hostname Hostname to invert the state of - */ -export async function invertHostState(hostname) { - if (disabledHosts.includes(hostname)) { - disabledHosts.splice(disabledHosts.indexOf(hostname), 1) - } else { - disabledHosts.push(hostname) - } - - await storage.local.set({ - [KEY_DISABLED_HOSTS]: disabledHosts, - }) - - updateIcon(hostname) -} - -export async function setResizableState(state) { - return (await storage.local.set({ - [KEY_RESIZABLE_STATE]: state - })) -} - -export async function setTheme(state) { - return (await storage.local.set({ - [KEY_THEME]: state - })) -} - -/** - * Retrieves the hostname from a URL - * @param {string} url Full URL string - * @returns {string} Hostname string - */ -export function getHostname(url) { - url = url.replace(/^\w+:\/\//, '') - url = url.split(/[\/#\?]/, 1)[0] - return url -} diff --git a/src/bg/utils/domainEnds.json b/src/domain-ends.json similarity index 100% rename from src/bg/utils/domainEnds.json rename to src/domain-ends.json diff --git a/public/icons/48-icon-grey.png b/src/icons/48-icon-grey.png similarity index 100% rename from public/icons/48-icon-grey.png rename to src/icons/48-icon-grey.png diff --git a/public/icons/48-icon.png b/src/icons/48-icon.png similarity index 100% rename from public/icons/48-icon.png rename to src/icons/48-icon.png diff --git a/public/icons/96-icon-grey.png b/src/icons/96-icon-grey.png similarity index 100% rename from public/icons/96-icon-grey.png rename to src/icons/96-icon-grey.png diff --git a/public/icons/96-icon.png b/src/icons/96-icon.png similarity index 100% rename from public/icons/96-icon.png rename to src/icons/96-icon.png diff --git a/public/manifest.json b/src/manifest.json similarity index 99% rename from public/manifest.json rename to src/manifest.json index 4634f14..5a3c0e1 100644 --- a/public/manifest.json +++ b/src/manifest.json @@ -39,4 +39,4 @@ "options_ui": { "page": "options.html" } -} \ No newline at end of file +} diff --git a/src/map.html b/src/map.html deleted file mode 100644 index 85ee737..0000000 --- a/src/map.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - Leaflet Map - - -
- - - diff --git a/src/map.md b/src/map.md new file mode 100644 index 0000000..173d6d8 --- /dev/null +++ b/src/map.md @@ -0,0 +1,6 @@ +--- +include: [map] +title: Map +--- + +[maps] diff --git a/src/map/map.js b/src/map/map.js deleted file mode 100644 index e96c59b..0000000 --- a/src/map/map.js +++ /dev/null @@ -1,87 +0,0 @@ -import L from 'leaflet' -import 'leaflet-fullscreen' - -import { theme, KEY_THEME } from '../bg/utils/storage.js' -import { readPB, readQ } from './utils/read.js' -import { tileTypes } from './utils/parsePB.js' - -import { storage } from 'webextension-polyfill' -document.body.dataset.theme = theme -storage.local.onChanged.addListener((changes) => { if (KEY_THEME in changes) document.body.dataset.theme = theme }) - -/** - * @typedef {object} Tile - * @property {string} layer - * @property {string} attr - */ - -// https://leaflet-extras.github.io/leaflet-providers/preview/ -/** @type {{TileTypes: Tile}} */ -const tileProviders = { - roadmap: { - layer: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', // OpenStreetMap.Mapnik - attr: '© OpenStreetMap', - }, - satellite: { - layer: - 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', // Esri.WorldImagery - attr: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community', - }, // TODO: add street layer etc to satellite -} - -const gPos = 'pb' -const gQuery = 'q' -const gZoom = 'z' -const params = new URLSearchParams(document.location.search) - -/** @type {MapData} */ -let mapData = {} - -if (params.has(gPos)) { - mapData = await readPB(params.get(gPos)) -} else if (params.has(gQuery)) { - let marker = await readQ(params.get(gQuery)) - - if (marker) { - mapData.markers = [marker] - } -} - -if (params.has(gZoom)) { - mapData.zoom = parseInt(params.get(gZoom)) -} - -const map = L.map('map', { - fullscreenControl: true, - scrollWheelZoom: true, - touchZoom: true, - zoom: mapData.zoom ?? 17, - zoomSnap: 0.1, - zoomDelta: 0.5, - minZoom: 0.5, -}) - -if (mapData.markers?.length == 0 && mapData.area) { - map.setView([mapData.area.lat, mapData.area.lon]) -} - -if (mapData.markers) { - if (mapData.markers.length === 1) { - let mapMarker = mapData.markers[0] - map.setView([mapMarker.lat, mapMarker.lon]) - } - - mapData.markers.forEach((marker) => { - let mapMarker = L.marker([marker.lat, marker.lon]).addTo(map) - mapMarker.bindPopup(marker.label, { closeButton: false }).openPopup() - }) -} - -L.tileLayer(tileProviders[mapData.tile || tileTypes[0]].layer, { - maxZoom: 19, - attribution: tileProviders[mapData.tile || tileTypes[0]].attr, -}).addTo(map) - -L.control.scale().addTo(map) -// TODO: fix layer loading -//L.control.layers({}, {}, { hideSingleBase: true }).addTo(map) diff --git a/src/options.html b/src/options.html deleted file mode 100644 index f2cddb5..0000000 --- a/src/options.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - Replace Maps Options - - -
- - - - -
- -
- -
-
- - -
-
-
- - diff --git a/src/options.md b/src/options.md new file mode 100644 index 0000000..da5908f --- /dev/null +++ b/src/options.md @@ -0,0 +1,6 @@ +--- +include: [options] +title: Options +--- + +[options] diff --git a/src/options/options.js b/src/options/options.js deleted file mode 100644 index 5fc9866..0000000 --- a/src/options/options.js +++ /dev/null @@ -1,113 +0,0 @@ -import { storage } from 'webextension-polyfill' - -import { - KEY_DISABLED_HOSTS, - KEY_RESIZABLE_STATE, - disabledHosts, - getHostname, - invertHostState, - resizableState, - setResizableState, - theme, - setTheme, - KEY_THEME, -} from '../bg/utils/storage.js' - -// const resizable = document.getElementById('resizable') -// resizable.addEventListener('change', (e) => { -// setResizableState(resizable.checked) -// }) - -const themeEl = document.getElementById('theme') -themeEl.addEventListener('change', (e) => { - setTheme(themeEl.selectedOptions[0].value) -}) - - -const table = document.querySelector('.table') - -function setThemeOption() { - [...themeEl.querySelectorAll('option')].filter(e => e.value == theme)?.pop()?.setAttribute('selected', true) -} - -/** - * (Re)Builds the list of diasabled hostnames - */ -function buildEntries() { - // resizable.checked = resizableState - setThemeOption() - table.innerHTML = '' - disabledHosts.forEach(createEntry) -} - -/** - * Async function to add a hostname (from the form / URL Search Params) to the displayed list and the storage list of disabled hosts - * If the entry is already present in the stored hosts, no entry is added to the display list - */ -async function addEntry() { - const search = new URLSearchParams(document.location.search) - let hostname = search.get('hostname') - if (hostname === null) return - - hostname = getHostname(hostname) - if (disabledHosts.includes(hostname)) return - - await invertHostState(hostname) - createEntry(hostname) -} - -/** - * Creates a new entry for the displayed list of disabled hostnames and appends it to the view - * @param {string} hostname Hostname to add to the list - */ -function createEntry(hostname) { - const div = document.createElement('div') - - let span = document.createElement('span') - span.innerText = hostname - - let button = document.createElement('button') - button.onclick = removeEntry - - div.append(span, button) - table.appendChild(div) -} - -/** - * Async funtion to remove an entry at click of its button. - * Takes the index in the table to remove it from the list of stored hostnames - * @param {MouseEvent} click Button click - */ -async function removeEntry(click) { - const target = click.target - if (target === null) return - - let index = getIndex(target) - if (index === -1) return - - await invertHostState(disabledHosts[index]) -} - -/** - * Gets the index of a list entry using its clicked button - * @param {HTMLButtonElement} button Button that was clicked to remove an entry - * @returns {number} Index of the list entry - */ -function getIndex(button) { - let div = button.parentElement - if (div === null) return -1 - - let index = Array.from(table.children).indexOf(div) - div.remove() - - return index -} - -storage.local.onChanged.addListener((changes) => { - if (KEY_DISABLED_HOSTS in changes) buildEntries() - // if (KEY_RESIZABLE_STATE in changes) resizable.checked = resizableState - if (KEY_THEME in changes) setThemeOption() -}) - -buildEntries() -addEntry() diff --git a/src/site.yaml b/src/site.yaml new file mode 100644 index 0000000..262132f --- /dev/null +++ b/src/site.yaml @@ -0,0 +1,5 @@ +libs: ['@lib'] +globals: ['@global'] +bundle: ['leaflet.js', 'webext.js'] + +title_template: '%s | Replace Maps' diff --git a/src/styles/map.css b/src/styles/map.css deleted file mode 100644 index 0d8aea1..0000000 --- a/src/styles/map.css +++ /dev/null @@ -1,48 +0,0 @@ -html, -body, -div { - margin: 0; - padding: 0; -} - -#map { - color-scheme: inherit; - width: calc(100vw - (100vw - 100%)); - height: 100vh; -} - -/* system */ -@media (prefers-color-scheme: dark) { - [data-theme="system"] .leaflet-container { - background: #000 !important; - } - - [data-theme="system"] - :is( - .leaflet-layer, - .leaflet-popup, - .leaflet-tooltip, - .leaflet-control, - .leaflet-control-layers-toggle, - .leaflet-attribution-flag - ) { - filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(120%); - } -} - -/* dark */ -[data-theme="dark"] .leaflet-container { - background: #000 !important; -} - -[data-theme="dark"] - :is( - .leaflet-layer, - .leaflet-popup, - .leaflet-tooltip, - .leaflet-control, - .leaflet-control-layers-toggle, - .leaflet-attribution-flag - ) { - filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(120%); -} diff --git a/src/styles/options.css b/src/styles/options.css deleted file mode 100644 index 40fc1ea..0000000 --- a/src/styles/options.css +++ /dev/null @@ -1,102 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -:root { - --text: black; - --bg: white; - - --table-even: #e3e2eb; - --table-odd: #c3c2cb; -} - -@media (prefers-color-scheme: dark) { - :root { - --text: #ccc; - --bg: #1c1b22; - - --table-even: #53525b; - --table-odd: #33323b; - } -} - -body { - width: calc(100vw - (100vw - 100%)); - min-height: 100vh; - - color: var(--text); - background-color: var(--bg); - - gap: 0.5rem; -} - -body > section { - padding: 0.5rem; - gap: 0.25rem; - display: grid; -} - -hr { - width: 100%; -} - -.items { - display: grid; - grid-template-columns: 1fr auto; -} - -.table > * { - display: flex; - align-items: center; - justify-content: space-between; - - border-radius: 0.25rem; - padding: 0.5rem 1rem; -} - -.table > :nth-child(even) { - background-color: var(--table-even); -} - -.table > :nth-child(odd) { - background-color: var(--table-odd); -} - -.table button { - padding: 0.1rem; - width: 2rem; - height: 2rem; -} - -.table button::before { - display: flex; - content: "✘"; - align-items: center; - justify-content: center; - font-size: large; - color: red; - width: 100%; - height: 100%; -} - -form { - display: flex; - box-sizing: border-box; - gap: 0.5rem; -} - -input { - padding: 0.5rem; - border-radius: 0.25rem; - border-style: solid; -} - -input[type="text"] { - width: 80%; -} -input[type="submit"] { - width: 20%; - text-align: center; -}