├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── extensions.json ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── addon ├── _locales │ ├── ar │ │ └── messages.json │ ├── ca │ │ └── messages.json │ ├── de │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── eo │ │ └── messages.json │ ├── es │ │ └── messages.json │ ├── fi │ │ └── messages.json │ ├── fr │ │ └── messages.json │ ├── he │ │ └── messages.json │ ├── it │ │ └── messages.json │ ├── nb_NO │ │ └── messages.json │ ├── nl │ │ └── messages.json │ ├── pt │ │ └── messages.json │ ├── pt_BR │ │ └── messages.json │ ├── ru │ │ └── messages.json │ ├── uk │ │ └── messages.json │ └── zh_TW │ │ └── messages.json ├── icons │ ├── LICENSE.txt │ ├── README.md │ ├── drag_indicator-black.svg │ ├── osm.svg │ ├── osm128x128.png │ ├── osm16x16.png │ ├── osm48x48.png │ └── osm96x96.png ├── manifest.json ├── options │ ├── index.html │ └── style.css └── popup │ ├── index.html │ └── style.css ├── jest.config.js ├── package-lock.json ├── package.json ├── src ├── background-listeners-setup.ts ├── injectable-content-script.ts ├── options │ ├── App.svelte │ ├── components │ │ ├── ConfigurableLine.svelte │ │ └── UrlTemplateForm.svelte │ ├── main.ts │ └── utils.ts ├── popup │ ├── App.svelte │ ├── components │ │ ├── BasicLinkCreationDialog.svelte │ │ ├── ConfigurationLink.svelte │ │ ├── ErrorMessage.svelte │ │ ├── InfoBox.svelte │ │ ├── LinkList.svelte │ │ └── ShowEnabledLinksButton.svelte │ ├── main.ts │ ├── sites-manipulation-helper.test.ts │ ├── sites-manipulation-helper.ts │ └── utils.ts ├── sites-configuration.ts └── storage │ ├── config-handler.ts │ └── migrations.ts ├── tsconfig.json └── webpack.config.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v2 12 | with: 13 | node-version: 16 14 | - run: npm ci 15 | - run: npm test 16 | - run: npm run build 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | addon/**/*.js 3 | 4 | # from firefox's web-ext tool 5 | web-ext-artifacts/ 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "svelte.svelte-vscode" 8 | ], 9 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 10 | "unwantedRecommendations": [ 11 | 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Design principles 4 | 1. Focus on the OpenStreetMap Community 5 | 2. User autonomy 6 | 3. Inclusivity 7 | 8 | ## Initial setup 9 | Install a recent version of Node.js and run `npm install`. 10 | 11 | ## Build 12 | Run `npm run build`. 13 | A file will be created in `./web-ext-artifacts/`. 14 | 15 | ## Test 16 | Run automated tests with `npm test`. 17 | 18 | To test manually, first compile the TypeScript code with `npm run tscompile`, and then load the folder `./addon/` in [Firefox][firefox-load] and [Chrome][chrome-load]. 19 | 20 | [firefox-load]: https://extensionworkshop.com/documentation/develop/temporary-installation-in-firefox/ 21 | [chrome-load]: https://developer.chrome.com/extensions/getstarted 22 | 23 | ## Develop 24 | The main code is at `./src/`, and it is compiled to `./addon` (where the other assets are). 25 | 26 | Take a look at `./addon/manifest.json` to find all entrypoints. 27 | 28 | The VS Code IDE is recommended for this repository. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OSM Smart Menu 2 | A browser extension to help the OpenStreetMap community easily access different maps and tools to analyze OSM data. 3 | 4 | It's officially supported for [Google Chrome][chrome-desktop] and Mozilla Firefox (for [Desktop][firefox-desktop] and [Android][firefox-android]). 5 | 6 | [chrome-desktop]: https://chrome.google.com/webstore/detail/osm-smart-menu/icipmdhgbkejfideagkhdebiaeohfijk 7 | [firefox-desktop]: https://addons.mozilla.org/firefox/addon/osm-smart-menu/ 8 | [firefox-android]: https://addons.mozilla.org/android/addon/osm-smart-menu/ 9 | 10 | User documentation is available at the [OpenStreetMap Wiki](https://wiki.openstreetmap.org/wiki/OSM_Smart_Menu). 11 | 12 | ## Contributing 13 | 14 | > See technical details in [CONTRIBUTING.md](./CONTRIBUTING.md) 15 | 16 | ### Report bugs or request features 17 | You can [open a new issue](https://github.com/jgpacker/osm-smart-menu/issues/new) in Github or [send a message](https://www.openstreetmap.org/message/new/jgpacker) to the author's OpenStreetMap account. 18 | 19 | ### Translations 20 | Translate this extensions' interface using [Weblate](https://hosted.weblate.org/engage/osm-smart-menu/). 21 | -------------------------------------------------------------------------------- /addon/_locales/ar/messages.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /addon/_locales/ca/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "OSM Smart Menu" 4 | }, 5 | "loading": { 6 | "message": "Carregant...\nSi no para de carregar, recarregueu la pàgina i torneu-ho a provar.", 7 | "description": "Shown in popup shortly before menu appears." 8 | }, 9 | "newOptionDetected_notice": { 10 | "message": "Voleu afegir aquest lloc web al menú?", 11 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 12 | }, 13 | "newOptionDetected_buttonText": { 14 | "message": "Afegeix enllaç", 15 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 16 | }, 17 | "newOptionDetected_added": { 18 | "message": "Afegit com a $PAGE_TITLE$", 19 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 20 | "placeholders": { 21 | "PAGE_TITLE": { 22 | "content": "$1", 23 | "example": "OpenStreetMap" 24 | } 25 | } 26 | }, 27 | "configurationLink": { 28 | "message": "Configuració", 29 | "description": "Link to configuration page inside main popup." 30 | }, 31 | "config_editButton": { 32 | "message": "Editar", 33 | "description": "Text inside 'edit button' in configuration page" 34 | }, 35 | "config_saveButton": { 36 | "message": "Guardar", 37 | "description": "Text inside 'save button' in configuration page" 38 | }, 39 | "config_deleteButton": { 40 | "message": "Esborrar", 41 | "description": "Text inside 'delete button' in configuration page" 42 | }, 43 | "config_linkDeleted": { 44 | "message": "Enllaç $LINK_NAME$ esborrat. Feu clic aquí per desfer.", 45 | "description": "Shown inline when a link is deleted inside configuration page", 46 | "placeholders": { 47 | "LINK_NAME": { 48 | "content": "$1", 49 | "example": "OpenStreetMap" 50 | } 51 | } 52 | }, 53 | "config_pattern_formTitle": { 54 | "message": "Creació d’opcions avançades (experimental)", 55 | "description": "Form Legend/Title" 56 | }, 57 | "config_pattern_name": { 58 | "message": "Nom", 59 | "description": "Input label" 60 | }, 61 | "config_pattern_urlTemplate": { 62 | "message": "Plantilla d'URL", 63 | "description": "Input label" 64 | }, 65 | "config_pattern_createOption": { 66 | "message": "Crear opció", 67 | "description": "Button" 68 | }, 69 | "error_noAccess": { 70 | "message": "ERROR: No s'ha pogut trobar o accedir a la pàgina actual.\nSi aquesta pàgina encara s'està carregant, torneu-ho a provar.\nSi això és inesperat, informeu-ne a $EXTENSION_SITE$.", 71 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 72 | "placeholders": { 73 | "EXTENSION_SITE": { 74 | "content": "$1", 75 | "example": "github.com/jgpacker/osm-smart-menu/" 76 | } 77 | } 78 | }, 79 | "error_incompatibleWebsite": { 80 | "message": "No s'ha trobat cap mapa ni paràmetre d'OpenStreetMap.\nSi això és inesperat, podeu informar-ne a $EXTENSION_SITE$.", 81 | "description": "Error shown inside popup when appropriate", 82 | "placeholders": { 83 | "EXTENSION_SITE": { 84 | "content": "$1", 85 | "example": "github.com/jgpacker/osm-smart-menu/" 86 | } 87 | } 88 | }, 89 | "error_noInformationExtracted": { 90 | "message": "El lloc web ha estat reconegut, però no s'han trobat paràmetres.\nSi es tracta d'un mapa, intenteu moure'l una mica per inicialitzar els seus paràmetres.\nSi sembla un error, informeu-ne a $EXTENSION_SITE$.", 91 | "description": "Error shown inside popup when appropriate", 92 | "placeholders": { 93 | "EXTENSION_SITE": { 94 | "content": "$1", 95 | "example": "github.com/jgpacker/osm-smart-menu/" 96 | } 97 | } 98 | }, 99 | "noEnabledCompatibleLinksFound": { 100 | "message": "Cap dels enllaços activats és compatible amb els paràmetres trobats en aquesta pàgina.", 101 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 102 | }, 103 | "button_showEnabledLinks": { 104 | "message": "Mostra enllaços activats mitjançant paràmetres addicionals", 105 | "description": "Shown in popup below some errors to allow users to see enabled links." 106 | }, 107 | "site_openstreetmap": { 108 | "message": "OpenStreetMap" 109 | }, 110 | "site_bingmaps": { 111 | "message": "Bing Maps" 112 | }, 113 | "site_googlemaps": { 114 | "message": "Google Maps" 115 | }, 116 | "site_openmapsurfer": { 117 | "message": "OpenMapSurfer" 118 | }, 119 | "site_opencyclemap": { 120 | "message": "OpenCycleMap" 121 | }, 122 | "site_opensnowmap": { 123 | "message": "OpenSnowMap" 124 | }, 125 | "site_historicmap": { 126 | "message": "Objectes històrics" 127 | }, 128 | "site_opnvkarte": { 129 | "message": "ÖPNVKarte" 130 | }, 131 | "site_stamen": { 132 | "message": "Stamen Maps" 133 | }, 134 | "site_mapillary": { 135 | "message": "Mapillary" 136 | }, 137 | "site_umap": { 138 | "message": "uMap" 139 | }, 140 | "site_osmhistoryviewer": { 141 | "message": "Visualitzador d'historial de l'OSM" 142 | }, 143 | "site_pewuosmhistory": { 144 | "message": "Visualitzador d'historial de l'OSM (PeWu)" 145 | }, 146 | "site_osmroutemanager": { 147 | "message": "Gestor de rutes d'OSM" 148 | }, 149 | "site_osmose": { 150 | "message": "Osmose" 151 | }, 152 | "site_osmchangetiles": { 153 | "message": "Últimes modificacions d'OSM (Neis)" 154 | }, 155 | "site_waze": { 156 | "message": "Waze" 157 | }, 158 | "site_osmcha": { 159 | "message": "OSMCha" 160 | }, 161 | "site_opentopomap": { 162 | "message": "OpenTopoMap" 163 | }, 164 | "site_openseamap": { 165 | "message": "OpenSeaMap" 166 | }, 167 | "site_mapcompare": { 168 | "message": "Compara el mapa" 169 | }, 170 | "extensionDescription": { 171 | "message": "Ajuda els contribuïdors d’OpenStreetMap a canviar fàcilment entre diferents mapes i eines d’anàlisi de la comunitat." 172 | }, 173 | "extensionShortName": { 174 | "message": "OSMenu" 175 | }, 176 | "extensionStoreDescription": { 177 | "message": "Quan feu clic al botó del menú, aquesta extensió detectarà els paràmetres d'OpenStreetMap de la pàgina actual i generarà una llista de mapes i eines en relació amb aquests paràmetres.\n\nEls paràmetres reconeguts inclouen:\n\nSi no esteu segur de quin lloc web es reconeixerà, proveu de visitar https://www.openstreetmap.org/ abans de fer clic en el menú.\n\nEl logotip d'OpenStreetMap és una marca registrada d'OpenStreetMap Foundation i s'utilitza amb el seu permís. Aquest projecte no és sostingut ni està afiliat amb l'OpenStreetMap Foundation.", 178 | "description": "Description shown in Chrome and Firefox's extension download page" 179 | }, 180 | "button_showOtherEnabledLinks": { 181 | "message": "Mostra la resta d'enllaços activats mitjançant paràmetres addicionals", 182 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 183 | }, 184 | "site_osmrelationanalyzer": { 185 | "message": "Analitzador de relacions d'OSM" 186 | }, 187 | "site_howdidyoucontribute": { 188 | "message": "Com heu contribuït a OSM?" 189 | }, 190 | "site_osmwiki": { 191 | "message": "Wiki d'OpenStreetMap" 192 | }, 193 | "site_openstreetbrowser": { 194 | "message": "OpenStreetBrowser" 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /addon/_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "noEnabledCompatibleLinksFound": { 3 | "message": "Keine der aktivierten Verknüpfungen sind mit den Parametern der Seite kompatibel.", 4 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 5 | }, 6 | "error_incompatibleWebsite": { 7 | "message": "Keine OpenStreerMap Parameter gefunden.\nWenn dies unerwartet ist, können sie es unter $EXTENSION_SITE$ melden", 8 | "description": "Error shown inside popup when appropriate", 9 | "placeholders": { 10 | "EXTENSION_SITE": { 11 | "content": "$1", 12 | "example": "github.com/jgpacker/osm-smart-menu/" 13 | } 14 | } 15 | }, 16 | "config_pattern_urlTemplate": { 17 | "message": "URL Vorlage", 18 | "description": "Input label" 19 | }, 20 | "config_pattern_name": { 21 | "message": "Name", 22 | "description": "Input label" 23 | }, 24 | "config_pattern_formTitle": { 25 | "message": "Erweiterte Optionen hinzufügen (experimentell)", 26 | "description": "Form Legend/Title" 27 | }, 28 | "config_deleteButton": { 29 | "message": "Löschen", 30 | "description": "Text inside 'delete button' in configuration page" 31 | }, 32 | "config_saveButton": { 33 | "message": "Speichern", 34 | "description": "Text inside 'save button' in configuration page" 35 | }, 36 | "config_editButton": { 37 | "message": "Bearbeiten", 38 | "description": "Text inside 'edit button' in configuration page" 39 | }, 40 | "configurationLink": { 41 | "message": "Konfiguration", 42 | "description": "Link to configuration page inside main popup." 43 | }, 44 | "newOptionDetected_added": { 45 | "message": "Hinzugefügt als $PAGE_TITLE$", 46 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 47 | "placeholders": { 48 | "PAGE_TITLE": { 49 | "content": "$1", 50 | "example": "OpenStreetMap" 51 | } 52 | } 53 | }, 54 | "newOptionDetected_buttonText": { 55 | "message": "Link hinzufügen", 56 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 57 | }, 58 | "newOptionDetected_notice": { 59 | "message": "Möchten sie diese Website ins Menü hinzufügen?", 60 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 61 | }, 62 | "extensionDescription": { 63 | "message": "Hilft OpenStreetMap-Mitwirkenden, einfach zwischen verschiedenen Karten und Analysewerkzeugen aus der Community zu wechseln." 64 | }, 65 | "config_pattern_createOption": { 66 | "message": "Option erstellen", 67 | "description": "Button" 68 | }, 69 | "loading": { 70 | "message": "Laden...\nWenn dies das Laden nicht stoppt, lade die Seite neu und versuche es erneut.", 71 | "description": "Shown in popup shortly before menu appears." 72 | }, 73 | "extensionStoreDescription": { 74 | "message": "Wenn Sie auf die Schaltfläche des Menüs klicken, erkennt diese Erweiterung die OpenStreetMap-Parameter innerhalb der aktuellen Seite und generiert eine Liste von verwandten Karten und Tools mit diesen Parametern.\n\nZu den erkannten Parametern gehören:\n\nWenn Sie sich nicht sicher sind, welche Website erkannt wird, versuchen Sie, https://www.openstreetmap.org/ aufzurufen, bevor Sie auf das Menü klicken.\n\nDas OpenStreetMap-Logo ist eine Marke der OpenStreetMap Foundation und wird mit deren Genehmigung verwendet. Dieses Projekt wird nicht von der OpenStreetMap Foundation unterstützt oder ist mit ihr verbunden.", 75 | "description": "Description shown in Chrome and Firefox's extension download page" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /addon/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "OSM Smart Menu" 4 | }, 5 | "extensionShortName": { 6 | "message": "OSMenu" 7 | }, 8 | "extensionDescription": { 9 | "message": "Helps OpenStreetMap contributors to easily switch between different maps and analysis tools from the community." 10 | }, 11 | "extensionStoreDescription": { 12 | "message": "When you click the menu's button, this extension will detect OpenStreetMap parameters inside the current page and will generate a list of related maps and tools with these parameters.\n\nRecognized parameters include:\n\nIf you are not sure which website will be recognized, try going to https://www.openstreetmap.org/ before clicking on the menu.\n\nThe OpenStreetMap logo is a trademark of the OpenStreetMap Foundation, and is used with their permission. This project is not endorsed by or affiliated with the OpenStreetMap Foundation.", 13 | "description": "Description shown in Chrome and Firefox's extension download page" 14 | }, 15 | "loading": { 16 | "message": "Loading...\nIf this does not stop loading, reload the page and try again.", 17 | "description": "Shown in popup shortly before menu appears." 18 | }, 19 | "newOptionDetected_notice": { 20 | "message": "Would you like to add this website in the menu?", 21 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 22 | }, 23 | "newOptionDetected_buttonText": { 24 | "message": "Add link", 25 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 26 | }, 27 | "newOptionDetected_added": { 28 | "message": "Added as $PAGE_TITLE$", 29 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 30 | "placeholders": { 31 | "PAGE_TITLE": { 32 | "content": "$1", 33 | "example": "OpenStreetMap" 34 | } 35 | } 36 | }, 37 | "configurationLink": { 38 | "message": "Configuration", 39 | "description": "Link to configuration page inside main popup." 40 | }, 41 | "config_editButton": { 42 | "message": "Edit", 43 | "description": "Text inside 'edit button' in configuration page" 44 | }, 45 | "config_saveButton": { 46 | "message": "Save", 47 | "description": "Text inside 'save button' in configuration page" 48 | }, 49 | "config_deleteButton": { 50 | "message": "Delete", 51 | "description": "Text inside 'delete button' in configuration page" 52 | }, 53 | "config_linkDeleted": { 54 | "message": "Link $LINK_NAME$ deleted. Click here to undo.", 55 | "description": "Shown inline when a link is deleted inside configuration page", 56 | "placeholders": { 57 | "LINK_NAME": { 58 | "content": "$1", 59 | "example": "OpenStreetMap" 60 | } 61 | } 62 | }, 63 | "config_pattern_formTitle": { 64 | "message": "Advanced Option Creation (experimental)", 65 | "description": "Form Legend/Title" 66 | }, 67 | "config_pattern_name": { 68 | "message": "Name", 69 | "description": "Input label" 70 | }, 71 | "config_pattern_urlTemplate": { 72 | "message": "URL Template", 73 | "description": "Input label" 74 | }, 75 | "config_pattern_createOption": { 76 | "message": "Create option", 77 | "description": "Button" 78 | }, 79 | "error_noAccess": { 80 | "message": "ERROR: Could not find or access current page.\nIf this page is still loading, try again.\nIf this is unexpected, please report it in $EXTENSION_SITE$.", 81 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 82 | "placeholders": { 83 | "EXTENSION_SITE": { 84 | "content": "$1", 85 | "example": "github.com/jgpacker/osm-smart-menu/" 86 | } 87 | } 88 | }, 89 | "error_incompatibleWebsite": { 90 | "message": "No map or OpenStreetMap parameters were found.\nIf this is unexpected, you can report it in $EXTENSION_SITE$.", 91 | "description": "Error shown inside popup when appropriate", 92 | "placeholders": { 93 | "EXTENSION_SITE": { 94 | "content": "$1", 95 | "example": "github.com/jgpacker/osm-smart-menu/" 96 | } 97 | } 98 | }, 99 | "error_noInformationExtracted": { 100 | "message": "The website was recognized, but no parameters were found.\nIf this is a map, try to move it a little to initialize it's parameters.\nIf this looks like a bug, please report it in $EXTENSION_SITE$.", 101 | "description": "Error shown inside popup when appropriate", 102 | "placeholders": { 103 | "EXTENSION_SITE": { 104 | "content": "$1", 105 | "example": "github.com/jgpacker/osm-smart-menu/" 106 | } 107 | } 108 | }, 109 | "noEnabledCompatibleLinksFound": { 110 | "message": "None of the enabled links are compatible with the parameters found in this page.", 111 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 112 | }, 113 | "button_showEnabledLinks": { 114 | "message": "Show enabled links using additional parameters", 115 | "description": "Shown in popup below some errors to allow users to see enabled links." 116 | }, 117 | "button_showOtherEnabledLinks": { 118 | "message": "Show the rest of the enabled links using additional parameters", 119 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 120 | }, 121 | "site_openstreetmap": { 122 | "message": "OpenStreetMap" 123 | }, 124 | "site_bingmaps": { 125 | "message": "Bing Maps" 126 | }, 127 | "site_googlemaps": { 128 | "message": "Google Maps" 129 | }, 130 | "site_sentinelhub": { 131 | "message": "Sentinel Hub" 132 | }, 133 | "site_openmapsurfer": { 134 | "message": "OpenMapSurfer" 135 | }, 136 | "site_opencyclemap": { 137 | "message": "OpenCycleMap" 138 | }, 139 | "site_openseamap": { 140 | "message": "OpenSeaMap" 141 | }, 142 | "site_opensnowmap": { 143 | "message": "OpenSnowMap" 144 | }, 145 | "site_opentopomap": { 146 | "message": "OpenTopoMap" 147 | }, 148 | "site_historicmap": { 149 | "message": "Historical Objects" 150 | }, 151 | "site_openinframap": { 152 | "message": "Open Infrastructure Map " 153 | }, 154 | "site_openptmap": { 155 | "message": "Open Public Transport Map" 156 | }, 157 | "site_opnvkarte": { 158 | "message": "ÖPNVKarte" 159 | }, 160 | "site_stamen": { 161 | "message": "Stamen Maps" 162 | }, 163 | "site_f4map": { 164 | "message": "F4 Map (3D demo)" 165 | }, 166 | "site_hotmap": { 167 | "message": "Humanitarian OSM Team Map" 168 | }, 169 | "site_openstreetcam": { 170 | "message": "KartaView" 171 | }, 172 | "site_mapillary": { 173 | "message": "Mapillary" 174 | }, 175 | "site_level0": { 176 | "message": "Level0 Editor" 177 | }, 178 | "site_ideditor": { 179 | "message": "iD Editor" 180 | }, 181 | "site_umap": { 182 | "message": "uMap" 183 | }, 184 | "site_osmdeephistory": { 185 | "message": "OSM Deep History" 186 | }, 187 | "site_deepdiff": { 188 | "message": "Deep Diff" 189 | }, 190 | "site_osmhistoryviewer": { 191 | "message": "OSM History Viewer" 192 | }, 193 | "site_pewuosmhistory": { 194 | "message": "OSM History Viewer (PeWu)" 195 | }, 196 | "site_whodidit": { 197 | "message": "WHODIDIT (Zverik)" 198 | }, 199 | "site_overpassapi": { 200 | "message": "Augmented OSM Change Viewer" 201 | }, 202 | "site_osmrelationanalyzer": { 203 | "message": "OSM Relation Analyzer" 204 | }, 205 | "site_osmroutemanager": { 206 | "message": "OSM Route Manager" 207 | }, 208 | "site_osmose": { 209 | "message": "Osmose" 210 | }, 211 | "site_osminspector": { 212 | "message": "OSM Inspector" 213 | }, 214 | "site_mapcompare": { 215 | "message": "Map Compare" 216 | }, 217 | "site_keepright": { 218 | "message": "keep right!" 219 | }, 220 | "site_howdidyoucontribute": { 221 | "message": "How did you contribute to OSM?" 222 | }, 223 | "site_osmwiki": { 224 | "message": "OpenStreetMap wiki" 225 | }, 226 | "site_osmchangeviz": { 227 | "message": "Changeset by Comparison (Neis)" 228 | }, 229 | "site_osmchangetiles": { 230 | "message": "Latest OSM Edits (Neis)" 231 | }, 232 | "site_waze": { 233 | "message": "Waze" 234 | }, 235 | "site_openstreetbrowser": { 236 | "message": "OpenStreetBrowser" 237 | }, 238 | "site_osmcha": { 239 | "message": "OSMCha" 240 | }, 241 | "site_osmbuildings": { 242 | "message": "OSM Buildings" 243 | }, 244 | "site_openlevelup": { 245 | "message": "OpenLevelUp!" 246 | }, 247 | "site_indoorequal": { 248 | "message": "indoor=" 249 | }, 250 | "site_missingmaps": { 251 | "message": "Missing Maps" 252 | }, 253 | "site_osmlanevisualizer": { 254 | "message": "OSM Lane Visualizer" 255 | }, 256 | "site_stravaglobal": { 257 | "message": "Strava Global Heatmap" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/eo/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "config_pattern_formTitle": { 3 | "message": "Kreo de Altnivelaj Opcioj (eksperimenta)", 4 | "description": "Form Legend/Title" 5 | }, 6 | "site_osmchangetiles": { 7 | "message": "Lastaj OSM-Redaktoj (Neis)" 8 | }, 9 | "site_missingmaps": { 10 | "message": "Mankantaj Mapoj" 11 | }, 12 | "site_ideditor": { 13 | "message": "iD-Redaktilo" 14 | }, 15 | "site_mapcompare": { 16 | "message": "Mapo-Komparo" 17 | }, 18 | "site_osmbuildings": { 19 | "message": "OSM-Konstruaĵoj" 20 | }, 21 | "site_indoorequal": { 22 | "message": "indoor=" 23 | }, 24 | "site_openlevelup": { 25 | "message": "OpenLevelUp!" 26 | }, 27 | "site_osmcha": { 28 | "message": "OSMCha" 29 | }, 30 | "site_osmroutemanager": { 31 | "message": "OSM-Vojo-Administrilo" 32 | }, 33 | "site_osmrelationanalyzer": { 34 | "message": "OSM-Rilato-Analizilo" 35 | }, 36 | "site_pewuosmhistory": { 37 | "message": "Historio-Vidigilo de OSM (PeWu)" 38 | }, 39 | "site_osmhistoryviewer": { 40 | "message": "Historio-Vidigilo de OSM" 41 | }, 42 | "site_deepdiff": { 43 | "message": "Profunda Diferenco" 44 | }, 45 | "site_osmdeephistory": { 46 | "message": "Profunda Historio OSM" 47 | }, 48 | "site_openinframap": { 49 | "message": "Open Infrastructure Map " 50 | }, 51 | "extensionShortName": { 52 | "message": "OSMenu" 53 | }, 54 | "extensionName": { 55 | "message": "OSM-Saĝmenuo" 56 | }, 57 | "site_openmapsurfer": { 58 | "message": "OpenMapSurfer" 59 | }, 60 | "site_howdidyoucontribute": { 61 | "message": "Kiel vi kontribuis al OSM?" 62 | }, 63 | "site_keepright": { 64 | "message": "restu dekstre!" 65 | }, 66 | "site_osminspector": { 67 | "message": "OSM-Inspektilo" 68 | }, 69 | "site_osmose": { 70 | "message": "Osmose" 71 | }, 72 | "site_umap": { 73 | "message": "uMap" 74 | }, 75 | "site_mapillary": { 76 | "message": "Mapillary" 77 | }, 78 | "site_openstreetcam": { 79 | "message": "OpenStreetCam" 80 | }, 81 | "site_stamen": { 82 | "message": "Stamen-Mapoj" 83 | }, 84 | "site_opnvkarte": { 85 | "message": "ÖPNVKarte" 86 | }, 87 | "site_openptmap": { 88 | "message": "Open Public Transport Map" 89 | }, 90 | "site_historicmap": { 91 | "message": "Historiaĵoj" 92 | }, 93 | "site_opentopomap": { 94 | "message": "OpenTopoMap" 95 | }, 96 | "site_opensnowmap": { 97 | "message": "OpenSnowMap" 98 | }, 99 | "site_openseamap": { 100 | "message": "OpenSeaMap" 101 | }, 102 | "site_opencyclemap": { 103 | "message": "OpenCycleMap" 104 | }, 105 | "config_linkDeleted": { 106 | "message": "Ligilo $LINK_NAME$ forviŝita. Alklaku ĉi tie por malfari.", 107 | "description": "Shown inline when a link is deleted inside configuration page", 108 | "placeholders": { 109 | "LINK_NAME": { 110 | "content": "$1", 111 | "example": "OpenStreetMap" 112 | } 113 | } 114 | }, 115 | "newOptionDetected_notice": { 116 | "message": "Ĉu vi ŝatus aldoni ĉi tiun retejon en la menuon?", 117 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 118 | }, 119 | "site_googlemaps": { 120 | "message": "Google-Mapoj" 121 | }, 122 | "site_bingmaps": { 123 | "message": "Bing-Mapoj" 124 | }, 125 | "config_pattern_urlTemplate": { 126 | "message": "Ŝablono de retpaĝa adreso", 127 | "description": "Input label" 128 | }, 129 | "site_osmwiki": { 130 | "message": "vikio de OpenStreetMap" 131 | }, 132 | "site_openstreetbrowser": { 133 | "message": "OpenStreetBrowser" 134 | }, 135 | "site_whodidit": { 136 | "message": "WHODIDIT (Zverik)" 137 | }, 138 | "site_waze": { 139 | "message": "Waze" 140 | }, 141 | "site_openstreetmap": { 142 | "message": "OpenStreetMap" 143 | }, 144 | "newOptionDetected_added": { 145 | "message": "Aldonite kiel $PAGE_TITLE$", 146 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 147 | "placeholders": { 148 | "PAGE_TITLE": { 149 | "content": "$1", 150 | "example": "OpenStreetMap" 151 | } 152 | } 153 | }, 154 | "newOptionDetected_buttonText": { 155 | "message": "Aldoni ligilon", 156 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 157 | }, 158 | "configurationLink": { 159 | "message": "Agordo", 160 | "description": "Link to configuration page inside main popup." 161 | }, 162 | "config_pattern_createOption": { 163 | "message": "Krei opcion", 164 | "description": "Button" 165 | }, 166 | "config_pattern_name": { 167 | "message": "Nomo", 168 | "description": "Input label" 169 | }, 170 | "config_deleteButton": { 171 | "message": "Forviŝi", 172 | "description": "Text inside 'delete button' in configuration page" 173 | }, 174 | "config_saveButton": { 175 | "message": "Konservi", 176 | "description": "Text inside 'save button' in configuration page" 177 | }, 178 | "config_editButton": { 179 | "message": "Redakti", 180 | "description": "Text inside 'edit button' in configuration page" 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /addon/_locales/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionStoreDescription": { 3 | "message": "Cuando se pulsa en el botón del menú, esta extensión detectará los parámetros de OpenStreetMap de la página actual y generará una lista de mapas y herramientas en relación con esos parámetros.\n\nEntre los parámetros reconocidos se incluyen:\n\nSi no tiene certeza de cuáles sitios web se reconocen, pruebe a visitar https://www.openstreetmap.org/ antes de pulsar en el menú.\n\nEl logotipo de OpenStreetMap es una marca registrada de OpenStreetMap Foundation y se utiliza con su permiso. Este proyecto no es respaldado ni está afiliado con la OpenStreetMap Foundation.", 4 | "description": "Description shown in Chrome and Firefox's extension download page" 5 | }, 6 | "site_waze": { 7 | "message": "Waze" 8 | }, 9 | "site_osmwiki": { 10 | "message": "Wiki de OpenStreetMap" 11 | }, 12 | "site_opentopomap": { 13 | "message": "OpenTopoMap" 14 | }, 15 | "site_opensnowmap": { 16 | "message": "OpenSnowMap" 17 | }, 18 | "site_openseamap": { 19 | "message": "OpenSeaMap" 20 | }, 21 | "site_opencyclemap": { 22 | "message": "OpenCycleMap" 23 | }, 24 | "site_openmapsurfer": { 25 | "message": "OpenMapSurfer" 26 | }, 27 | "site_sentinelhub": { 28 | "message": "Sentinel Hub" 29 | }, 30 | "site_googlemaps": { 31 | "message": "Google Maps" 32 | }, 33 | "site_bingmaps": { 34 | "message": "Bing Maps" 35 | }, 36 | "site_openstreetmap": { 37 | "message": "OpenStreetMap" 38 | }, 39 | "button_showOtherEnabledLinks": { 40 | "message": "Mostrar el resto de los enlaces activos mediante parámetros adicionales", 41 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 42 | }, 43 | "button_showEnabledLinks": { 44 | "message": "Mostrar enlaces activados mediante parámetros adicionales", 45 | "description": "Shown in popup below some errors to allow users to see enabled links." 46 | }, 47 | "noEnabledCompatibleLinksFound": { 48 | "message": "Ninguno de los enlaces activados es compatible con los parámetros encontrados en esta página.", 49 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 50 | }, 51 | "error_noInformationExtracted": { 52 | "message": "Se reconoció el sitio web pero no se encontró ningún parámetro.\nSi este es un mapa, pruebe a moverlo un poco para inicializar sus parámetros.\nSi parece que se trata de un defecto, informe al respecto en $EXTENSION_SITE$.", 53 | "description": "Error shown inside popup when appropriate", 54 | "placeholders": { 55 | "EXTENSION_SITE": { 56 | "content": "$1", 57 | "example": "github.com/jgpacker/osm-smart-menu/" 58 | } 59 | } 60 | }, 61 | "error_incompatibleWebsite": { 62 | "message": "No se encontró ningún mapa ni parámetro de OpenStreetMap.\nSi el problema es inesperado, informe al respecto en $EXTENSION_SITE$.", 63 | "description": "Error shown inside popup when appropriate", 64 | "placeholders": { 65 | "EXTENSION_SITE": { 66 | "content": "$1", 67 | "example": "github.com/jgpacker/osm-smart-menu/" 68 | } 69 | } 70 | }, 71 | "error_noAccess": { 72 | "message": "ERROR: no se pudo encontrar, o acceder a, la página actual.\nSi la página sigue cargándose, inténtelo de nuevo.\nSi el problema es inesperado, informe al respecto en $EXTENSION_SITE$.", 73 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 74 | "placeholders": { 75 | "EXTENSION_SITE": { 76 | "content": "$1", 77 | "example": "github.com/jgpacker/osm-smart-menu/" 78 | } 79 | } 80 | }, 81 | "config_pattern_createOption": { 82 | "message": "Crear opción", 83 | "description": "Button" 84 | }, 85 | "config_pattern_urlTemplate": { 86 | "message": "Plantilla de URL", 87 | "description": "Input label" 88 | }, 89 | "config_pattern_name": { 90 | "message": "Nombre", 91 | "description": "Input label" 92 | }, 93 | "config_pattern_formTitle": { 94 | "message": "Creación de opciones avanzada (experimental)", 95 | "description": "Form Legend/Title" 96 | }, 97 | "config_linkDeleted": { 98 | "message": "Se eliminó el enlace $LINK_NAME$. Pulse aquí para deshacer.", 99 | "description": "Shown inline when a link is deleted inside configuration page", 100 | "placeholders": { 101 | "LINK_NAME": { 102 | "content": "$1", 103 | "example": "OpenStreetMap" 104 | } 105 | } 106 | }, 107 | "config_deleteButton": { 108 | "message": "Eliminar", 109 | "description": "Text inside 'delete button' in configuration page" 110 | }, 111 | "config_saveButton": { 112 | "message": "Guardar", 113 | "description": "Text inside 'save button' in configuration page" 114 | }, 115 | "config_editButton": { 116 | "message": "Editar", 117 | "description": "Text inside 'edit button' in configuration page" 118 | }, 119 | "configurationLink": { 120 | "message": "Configuración", 121 | "description": "Link to configuration page inside main popup." 122 | }, 123 | "newOptionDetected_added": { 124 | "message": "Se añadió como $PAGE_TITLE$", 125 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 126 | "placeholders": { 127 | "PAGE_TITLE": { 128 | "content": "$1", 129 | "example": "OpenStreetMap" 130 | } 131 | } 132 | }, 133 | "newOptionDetected_buttonText": { 134 | "message": "Añadir enlace", 135 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 136 | }, 137 | "newOptionDetected_notice": { 138 | "message": "¿Quiere añadir este sitio web al menú?", 139 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 140 | }, 141 | "loading": { 142 | "message": "Cargando…\nSi no para de cargar, recargue la página e inténtelo de nuevo.", 143 | "description": "Shown in popup shortly before menu appears." 144 | }, 145 | "extensionDescription": { 146 | "message": "Ayuda a los contribuidores de OpenStreetMap a cambiar fácilmente entre los distintos mapas y herramientas de análisis comunitarias." 147 | }, 148 | "extensionShortName": { 149 | "message": "OSMenu" 150 | }, 151 | "extensionName": { 152 | "message": "Menú inteligente de OSM" 153 | }, 154 | "site_osminspector": { 155 | "message": "Inspector de OSM" 156 | }, 157 | "site_missingmaps": { 158 | "message": "Mapas faltantes" 159 | }, 160 | "site_historicmap": { 161 | "message": "Objetos históricos" 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /addon/_locales/fi/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionStoreDescription": { 3 | "message": "Kun klikkaat valikon painiketta, tämä laajennus havaitsee OpenStreetMap-parametrit nykyisellä sivulla ja luo luettelon näihin parametreihin liittyvistä kartoista ja työkaluista.\n\nTunnistettuja parametreja ovat muun muassa:\n\nJos et ole varma, mikä verkkosivusto tunnistetaan, kokeile siirtyä osoitteeseen https://www.openstreetmap.org/ ennen valikon napsauttamista.\n\nOpenStreetMap-logo on OpenStreetMap-säätiön tavaramerkki, ja sitä käytetään sen luvalla. Tämä projekti ei ole OpenStreetMap-säätiön tukema tai siihen sidoksissa.", 4 | "description": "Description shown in Chrome and Firefox's extension download page" 5 | }, 6 | "site_pewuosmhistory": { 7 | "message": "OSM-historiakatselin (PeWu)" 8 | }, 9 | "site_osmhistoryviewer": { 10 | "message": "OSM-historiakatselin" 11 | }, 12 | "site_osmdeephistory": { 13 | "message": "OSM-syvä historia" 14 | }, 15 | "site_openptmap": { 16 | "message": "Avoin Joukkoliikennekartta" 17 | }, 18 | "site_openinframap": { 19 | "message": "Avoin Infrastruktuurikartta " 20 | }, 21 | "site_osmlanevisualizer": { 22 | "message": "OSM-kaistavisualisoija" 23 | }, 24 | "site_missingmaps": { 25 | "message": "Puuttuvat Kartat" 26 | }, 27 | "site_osmbuildings": { 28 | "message": "OSM-rakennukset" 29 | }, 30 | "site_osmchangetiles": { 31 | "message": "Viimeisimmät OSM-muutokset (Neis)" 32 | }, 33 | "site_osmchangeviz": { 34 | "message": "Muutosjoukko vertailun mukaan (Neis)" 35 | }, 36 | "site_howdidyoucontribute": { 37 | "message": "Kuinka osallistuit OSM:ään?" 38 | }, 39 | "site_keepright": { 40 | "message": "Pysy Oikealla!" 41 | }, 42 | "site_mapcompare": { 43 | "message": "Map Compare" 44 | }, 45 | "site_osminspector": { 46 | "message": "OSM-tarkastaja" 47 | }, 48 | "site_osmose": { 49 | "message": "Osmose" 50 | }, 51 | "site_osmroutemanager": { 52 | "message": "OSM-reittimanageri" 53 | }, 54 | "site_osmrelationanalyzer": { 55 | "message": "OSM-suhdeanalysaattori" 56 | }, 57 | "site_overpassapi": { 58 | "message": "Lisätty OSM-muutosten katselija" 59 | }, 60 | "site_deepdiff": { 61 | "message": "Deep Diff" 62 | }, 63 | "site_level0": { 64 | "message": "Level0-editori" 65 | }, 66 | "site_ideditor": { 67 | "message": "iD-editori" 68 | }, 69 | "site_openstreetcam": { 70 | "message": "KartaView" 71 | }, 72 | "site_hotmap": { 73 | "message": "Humanitaarisen OSM-tiimin kartta" 74 | }, 75 | "site_f4map": { 76 | "message": "F4 Map (3D-demo)" 77 | }, 78 | "site_historicmap": { 79 | "message": "Historialliset esineet" 80 | }, 81 | "extensionShortName": { 82 | "message": "OSValikko" 83 | }, 84 | "extensionName": { 85 | "message": "OSM Älyvalikko" 86 | }, 87 | "site_osmwiki": { 88 | "message": "OpenStreetMap-wiki" 89 | }, 90 | "site_stravaglobal": { 91 | "message": "Strava Global Heatmap" 92 | }, 93 | "site_indoorequal": { 94 | "message": "indoor=" 95 | }, 96 | "site_openlevelup": { 97 | "message": "OpenLevelUp!" 98 | }, 99 | "site_osmcha": { 100 | "message": "OSMCha" 101 | }, 102 | "site_openstreetbrowser": { 103 | "message": "OpenStreetBrowser" 104 | }, 105 | "site_waze": { 106 | "message": "Waze" 107 | }, 108 | "site_whodidit": { 109 | "message": "WHODIDIT (Zverik)" 110 | }, 111 | "site_umap": { 112 | "message": "uMap" 113 | }, 114 | "site_mapillary": { 115 | "message": "Mapillary" 116 | }, 117 | "site_stamen": { 118 | "message": "Stamen kartat" 119 | }, 120 | "site_opnvkarte": { 121 | "message": "ÖPNVKarte" 122 | }, 123 | "site_opentopomap": { 124 | "message": "OpenTopoMap" 125 | }, 126 | "site_opensnowmap": { 127 | "message": "OpenSnowMap" 128 | }, 129 | "site_openseamap": { 130 | "message": "OpenSeaMap" 131 | }, 132 | "site_opencyclemap": { 133 | "message": "OpenCycleMap" 134 | }, 135 | "site_openmapsurfer": { 136 | "message": "OpenMapSurfer" 137 | }, 138 | "site_sentinelhub": { 139 | "message": "Sentinel Hub" 140 | }, 141 | "site_googlemaps": { 142 | "message": "Google Maps" 143 | }, 144 | "site_bingmaps": { 145 | "message": "Bing Kartat" 146 | }, 147 | "site_openstreetmap": { 148 | "message": "OpenStreetMap" 149 | }, 150 | "button_showOtherEnabledLinks": { 151 | "message": "Näytä loput käytössä olevat linkit lisäparametreilla", 152 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 153 | }, 154 | "button_showEnabledLinks": { 155 | "message": "Näytä käytössä olevat linkit lisäparametreilla", 156 | "description": "Shown in popup below some errors to allow users to see enabled links." 157 | }, 158 | "error_incompatibleWebsite": { 159 | "message": "Kartta- tai OpenStreetMap-parametreja ei löytynyt.\nJos tämä on odottamatonta, voit ilmoittaa siitä verkkosivulla $EXTENSION_SITE$.", 160 | "description": "Error shown inside popup when appropriate", 161 | "placeholders": { 162 | "EXTENSION_SITE": { 163 | "content": "$1", 164 | "example": "github.com/jgpacker/osm-smart-menu/" 165 | } 166 | } 167 | }, 168 | "error_noAccess": { 169 | "message": "VIRHE: Nykyistä sivua ei löytynyt tai sitä ei voitu käyttää.\nJos tämä sivu latautuu edelleen, yritä uudelleen.\nJos tämä on odottamatonta, ilmoita siitä verkkosivulla $EXTENSION_SITE$.", 170 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 171 | "placeholders": { 172 | "EXTENSION_SITE": { 173 | "content": "$1", 174 | "example": "github.com/jgpacker/osm-smart-menu/" 175 | } 176 | } 177 | }, 178 | "noEnabledCompatibleLinksFound": { 179 | "message": "Mikään käytössä olevista linkeistä ei ole yhteensopiva tällä sivulla olevien parametrien kanssa.", 180 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 181 | }, 182 | "error_noInformationExtracted": { 183 | "message": "Verkkosivusto tunnistettiin, mutta parametreja ei löytynyt.\nJos tämä on kartta, yritä siirtää sitä hieman alustaaksesi sen parametrit.\nJos tämä näyttää virheeltä, ilmoita siitä verkkosivulla $EXTENSION_SITE$.", 184 | "description": "Error shown inside popup when appropriate", 185 | "placeholders": { 186 | "EXTENSION_SITE": { 187 | "content": "$1", 188 | "example": "github.com/jgpacker/osm-smart-menu/" 189 | } 190 | } 191 | }, 192 | "config_pattern_createOption": { 193 | "message": "Luo vaihtoehto", 194 | "description": "Button" 195 | }, 196 | "config_pattern_urlTemplate": { 197 | "message": "URL-malli", 198 | "description": "Input label" 199 | }, 200 | "config_pattern_formTitle": { 201 | "message": "Edistynyt vaihtoehtojen luominen (kokeellinen)", 202 | "description": "Form Legend/Title" 203 | }, 204 | "config_linkDeleted": { 205 | "message": "Linkki $LINK_NAME$ poistettu. Kumoa klikkaamalla tästä.", 206 | "description": "Shown inline when a link is deleted inside configuration page", 207 | "placeholders": { 208 | "LINK_NAME": { 209 | "content": "$1", 210 | "example": "OpenStreetMap" 211 | } 212 | } 213 | }, 214 | "newOptionDetected_added": { 215 | "message": "Lisätty nimellä $PAGE_TITLE$", 216 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 217 | "placeholders": { 218 | "PAGE_TITLE": { 219 | "content": "$1", 220 | "example": "OpenStreetMap" 221 | } 222 | } 223 | }, 224 | "newOptionDetected_buttonText": { 225 | "message": "Lisää linkki", 226 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 227 | }, 228 | "newOptionDetected_notice": { 229 | "message": "Haluatko lisätä tämän verkkosivun valikkoon?", 230 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 231 | }, 232 | "loading": { 233 | "message": "Ladataan…\nJos lataaminen ei lopu, lataa sivu uudelleen ja yritä uudelleen.", 234 | "description": "Shown in popup shortly before menu appears." 235 | }, 236 | "extensionDescription": { 237 | "message": "Auttaa OpenStreetMapin tekijöitä vaihtamaan helposti yhteisön eri karttojen ja analyysityökalujen välillä." 238 | }, 239 | "config_pattern_name": { 240 | "message": "Nimi", 241 | "description": "Input label" 242 | }, 243 | "config_deleteButton": { 244 | "message": "Poista", 245 | "description": "Text inside 'delete button' in configuration page" 246 | }, 247 | "config_saveButton": { 248 | "message": "Tallenna", 249 | "description": "Text inside 'save button' in configuration page" 250 | }, 251 | "config_editButton": { 252 | "message": "Muokkaa", 253 | "description": "Text inside 'edit button' in configuration page" 254 | }, 255 | "configurationLink": { 256 | "message": "Asetukset", 257 | "description": "Link to configuration page inside main popup." 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "config_pattern_createOption": { 3 | "message": "Créer une option", 4 | "description": "Button" 5 | }, 6 | "config_pattern_urlTemplate": { 7 | "message": "Modèle d'URL", 8 | "description": "Input label" 9 | }, 10 | "config_pattern_name": { 11 | "message": "Nom", 12 | "description": "Input label" 13 | }, 14 | "config_deleteButton": { 15 | "message": "Supprimer", 16 | "description": "Text inside 'delete button' in configuration page" 17 | }, 18 | "config_saveButton": { 19 | "message": "Enregistrer", 20 | "description": "Text inside 'save button' in configuration page" 21 | }, 22 | "config_editButton": { 23 | "message": "Modifier", 24 | "description": "Text inside 'edit button' in configuration page" 25 | }, 26 | "configurationLink": { 27 | "message": "Configuration", 28 | "description": "Link to configuration page inside main popup." 29 | }, 30 | "newOptionDetected_added": { 31 | "message": "Ajouté comme $PAGE_TITLE$", 32 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 33 | "placeholders": { 34 | "PAGE_TITLE": { 35 | "content": "$1", 36 | "example": "OpenStreetMap" 37 | } 38 | } 39 | }, 40 | "newOptionDetected_buttonText": { 41 | "message": "Ajouter un lien", 42 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 43 | }, 44 | "extensionShortName": { 45 | "message": "OSMenu" 46 | }, 47 | "extensionName": { 48 | "message": "Menu Intelligent OSM" 49 | }, 50 | "config_linkDeleted": { 51 | "message": "Lien $LINK_NAME$ supprimé. Cliquez ici pour annuler.", 52 | "description": "Shown inline when a link is deleted inside configuration page", 53 | "placeholders": { 54 | "LINK_NAME": { 55 | "content": "$1", 56 | "example": "OpenStreetMap" 57 | } 58 | } 59 | }, 60 | "newOptionDetected_notice": { 61 | "message": "Souhaitez-vous ajouter ce site Web au menu ?", 62 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 63 | }, 64 | "loading": { 65 | "message": "Chargement…\nSi le chargement ne s'arrête pas, rechargez la page et réessayez.", 66 | "description": "Shown in popup shortly before menu appears." 67 | }, 68 | "extensionDescription": { 69 | "message": "Aide les contributeurs d'OpenStreetMap à jongler entre les cartes et outils d'analyse de la communauté." 70 | }, 71 | "noEnabledCompatibleLinksFound": { 72 | "message": "Aucun des liens activés n'est compatible avec les paramètres trouvés sur cette page.", 73 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 74 | }, 75 | "error_incompatibleWebsite": { 76 | "message": "Aucune carte ou paramètre OpenStreetMap n'a été trouvé.\nSi ceci était inattendu, vous pouvez le signaler dans $EXTENSION_SITE$.", 77 | "description": "Error shown inside popup when appropriate", 78 | "placeholders": { 79 | "EXTENSION_SITE": { 80 | "content": "$1", 81 | "example": "github.com/jgpacker/osm-smart-menu/" 82 | } 83 | } 84 | }, 85 | "button_showOtherEnabledLinks": { 86 | "message": "Afficher le reste des liens activés en utilisant des paramètres supplémentaires", 87 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 88 | }, 89 | "button_showEnabledLinks": { 90 | "message": "Afficher les liens activés en utilisant des paramètres supplémentaires", 91 | "description": "Shown in popup below some errors to allow users to see enabled links." 92 | }, 93 | "error_noAccess": { 94 | "message": "ERREUR : Impossible de trouver ou d'accéder à cette page.\nSi cette page se charge encore, réessayez.\nSi c'était inattendu, merci de le signaler dans $EXTENSION_SITE$.", 95 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 96 | "placeholders": { 97 | "EXTENSION_SITE": { 98 | "content": "$1", 99 | "example": "github.com/jgpacker/osm-smart-menu/" 100 | } 101 | } 102 | }, 103 | "site_osmlanevisualizer": { 104 | "message": "OSM Lane Visualizer" 105 | }, 106 | "site_missingmaps": { 107 | "message": "Missing Maps" 108 | }, 109 | "site_indoorequal": { 110 | "message": "indoor=" 111 | }, 112 | "site_openlevelup": { 113 | "message": "OpenLevelUp!" 114 | }, 115 | "site_osmbuildings": { 116 | "message": "OSM Buildings" 117 | }, 118 | "site_osmcha": { 119 | "message": "OSMCha" 120 | }, 121 | "site_openstreetbrowser": { 122 | "message": "OpenStreetBrowser" 123 | }, 124 | "site_waze": { 125 | "message": "Waze" 126 | }, 127 | "site_osmchangetiles": { 128 | "message": "Latest OSM Edits (Neis)" 129 | }, 130 | "site_osmchangeviz": { 131 | "message": "Changeset by Comparison (Neis)" 132 | }, 133 | "site_howdidyoucontribute": { 134 | "message": "How did you contribute to OSM?" 135 | }, 136 | "site_keepright": { 137 | "message": "keep right!" 138 | }, 139 | "site_mapcompare": { 140 | "message": "Map Compare" 141 | }, 142 | "site_osminspector": { 143 | "message": "Inspecteur OSM" 144 | }, 145 | "site_osmose": { 146 | "message": "Osmose" 147 | }, 148 | "site_osmroutemanager": { 149 | "message": "OSM Route Manager" 150 | }, 151 | "site_osmrelationanalyzer": { 152 | "message": "OSM Relation Analyzer" 153 | }, 154 | "site_whodidit": { 155 | "message": "WHODIDIT (Zverik)" 156 | }, 157 | "site_pewuosmhistory": { 158 | "message": "OSM History Viewer (PeWu)" 159 | }, 160 | "site_osmhistoryviewer": { 161 | "message": "OSM History Viewer" 162 | }, 163 | "site_deepdiff": { 164 | "message": "Deep Diff" 165 | }, 166 | "site_osmdeephistory": { 167 | "message": "OSM Deep History" 168 | }, 169 | "site_umap": { 170 | "message": "uMap" 171 | }, 172 | "site_mapillary": { 173 | "message": "Mapillary" 174 | }, 175 | "site_openstreetcam": { 176 | "message": "OpenStreetCam" 177 | }, 178 | "site_opnvkarte": { 179 | "message": "ÖPNVKarte" 180 | }, 181 | "site_openptmap": { 182 | "message": "Open Public Transport Map" 183 | }, 184 | "site_historicmap": { 185 | "message": "Historical Objects" 186 | }, 187 | "site_opentopomap": { 188 | "message": "OpenTopoMap" 189 | }, 190 | "site_opensnowmap": { 191 | "message": "OpenSnowMap" 192 | }, 193 | "site_openseamap": { 194 | "message": "OpenSeaMap" 195 | }, 196 | "site_opencyclemap": { 197 | "message": "OpenCycleMap" 198 | }, 199 | "site_openmapsurfer": { 200 | "message": "OpenMapSurfer" 201 | }, 202 | "site_sentinelhub": { 203 | "message": "Sentinel Hub" 204 | }, 205 | "site_googlemaps": { 206 | "message": "Google Maps" 207 | }, 208 | "site_openstreetmap": { 209 | "message": "OpenStreetMap" 210 | }, 211 | "site_stravaglobal": { 212 | "message": "Carte d’activité Globale Strava" 213 | }, 214 | "site_osmwiki": { 215 | "message": "Wiki OpenStreetMap" 216 | }, 217 | "site_overpassapi": { 218 | "message": "Achavi" 219 | }, 220 | "site_ideditor": { 221 | "message": "Éditeur iD" 222 | }, 223 | "site_level0": { 224 | "message": "Éditeur Level0" 225 | }, 226 | "site_hotmap": { 227 | "message": "Carte HOT" 228 | }, 229 | "site_f4map": { 230 | "message": "Carte F4 (demo 3D)" 231 | }, 232 | "site_stamen": { 233 | "message": "Cartes Stamen" 234 | }, 235 | "site_openinframap": { 236 | "message": "Open Infrastructure Map " 237 | }, 238 | "site_bingmaps": { 239 | "message": "Bing Cartes" 240 | }, 241 | "error_noInformationExtracted": { 242 | "message": "Le site a été reconnu mais aucun paramètre n’a été trouvé.\nPour une carte, essayer de la déplacer pour générer des paramètres.\nSi c’est un bug, merci de le signaler dans $EXTENSION_SITE$.", 243 | "description": "Error shown inside popup when appropriate", 244 | "placeholders": { 245 | "EXTENSION_SITE": { 246 | "content": "$1", 247 | "example": "github.com/jgpacker/osm-smart-menu/" 248 | } 249 | } 250 | }, 251 | "config_pattern_formTitle": { 252 | "message": "Option de Création Avancée (Expérimental)", 253 | "description": "Form Legend/Title" 254 | }, 255 | "extensionStoreDescription": { 256 | "message": "En cliquant sur l’icône dans le menu, l’extension détectera les paramètres OpenStreetMap de la page actuelle pour générer une liste de cartes et outils les utilisant.\n\nParmi les paramètres pris en charge sont inclus :\n\nPour une liste complète des sites reconnus, vérifier à partir de https://www.openstreetmap.org/.\n\nLe logo OpenStreetMap est une marque déposée de la Fondation OpenStreetMap et est utilisée avec son accord. Ce projet n’est ni approuvé ni affilié ni financé à la Fondation OpenStreetMap.", 257 | "description": "Description shown in Chrome and Firefox's extension download page" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/he/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionStoreDescription": { 3 | "message": "בלחיצה על כפתור התפריט, הרחבה זו תזהה את המשתנים של OpenStreetMap בתוך העמוד הנוכחי ותייצר רשימה של מפות וכלים קשורים עם המשתנים האלו.\n\nבין המשתנים שמזוהים:\n\nאם לא ברור לך איזה אתר יזוהה, כדאי לנסות לפנות אל https://www.openstreetmap.org/‎ בטרם לחיצה על התפריט.\n\nהלוגו של OpenStreetMap הוא סימן מסחר של OpenStreetMap Foundation והשימוש בו הוא בהסכמתם. מיזם זה אינו בעידוד או משתייך ל־OpenStreetMap Foundation.", 4 | "description": "Description shown in Chrome and Firefox's extension download page" 5 | }, 6 | "site_osmbuildings": { 7 | "message": "OSM Buildings" 8 | }, 9 | "site_osmcha": { 10 | "message": "OSMCha" 11 | }, 12 | "site_mapcompare": { 13 | "message": "Map Compare" 14 | }, 15 | "site_osminspector": { 16 | "message": "OSM Inspector" 17 | }, 18 | "site_osmose": { 19 | "message": "Osmose" 20 | }, 21 | "site_osmroutemanager": { 22 | "message": "OSM Route Manager" 23 | }, 24 | "site_osmrelationanalyzer": { 25 | "message": "OSM Relation Analyzer" 26 | }, 27 | "site_openstreetbrowser": { 28 | "message": "OpenStreetBrowser" 29 | }, 30 | "site_osmchangeviz": { 31 | "message": "Changeset by Comparison (Neis)" 32 | }, 33 | "site_keepright": { 34 | "message": "keep right!‎" 35 | }, 36 | "site_howdidyoucontribute": { 37 | "message": "איך תרמת ל־OSM?" 38 | }, 39 | "site_waze": { 40 | "message": "Waze" 41 | }, 42 | "site_osmchangetiles": { 43 | "message": "Latest OSM Edits (Neis)" 44 | }, 45 | "site_openlevelup": { 46 | "message": "OpenLevelUp!‎" 47 | }, 48 | "site_indoorequal": { 49 | "message": "indoor=‎" 50 | }, 51 | "site_osmlanevisualizer": { 52 | "message": "OSM Lane Visualizer" 53 | }, 54 | "site_overpassapi": { 55 | "message": "Augmented OSM Change Viewer" 56 | }, 57 | "site_osmhistoryviewer": { 58 | "message": "OSM History Viewer" 59 | }, 60 | "site_deepdiff": { 61 | "message": "Deep Diff" 62 | }, 63 | "site_level0": { 64 | "message": "Level0 Editor" 65 | }, 66 | "site_mapillary": { 67 | "message": "Mapillary" 68 | }, 69 | "site_hotmap": { 70 | "message": "Humanitarian OSM Team Map" 71 | }, 72 | "site_f4map": { 73 | "message": "F4 Map (3D demo)" 74 | }, 75 | "site_stamen": { 76 | "message": "Stamen Maps" 77 | }, 78 | "site_opnvkarte": { 79 | "message": "ÖPNVKarte" 80 | }, 81 | "site_openinframap": { 82 | "message": "Open Infrastructure Map " 83 | }, 84 | "site_historicmap": { 85 | "message": "Historical Objects" 86 | }, 87 | "site_opentopomap": { 88 | "message": "OpenTopoMap" 89 | }, 90 | "site_openptmap": { 91 | "message": "Open Public Transport Map" 92 | }, 93 | "site_openstreetcam": { 94 | "message": "KartaView" 95 | }, 96 | "site_osmdeephistory": { 97 | "message": "OSM Deep History" 98 | }, 99 | "site_umap": { 100 | "message": "uMap" 101 | }, 102 | "site_whodidit": { 103 | "message": "WHODIDIT (Zverik)" 104 | }, 105 | "site_osmwiki": { 106 | "message": "הוויקי של OpenStreetMap" 107 | }, 108 | "site_missingmaps": { 109 | "message": "Missing Maps" 110 | }, 111 | "site_opensnowmap": { 112 | "message": "OpenSnowMap" 113 | }, 114 | "site_openseamap": { 115 | "message": "OpenSeaMap" 116 | }, 117 | "site_opencyclemap": { 118 | "message": "OpenCycleMap" 119 | }, 120 | "site_openmapsurfer": { 121 | "message": "OpenMapSurfer" 122 | }, 123 | "site_sentinelhub": { 124 | "message": "Sentinel Hub" 125 | }, 126 | "site_googlemaps": { 127 | "message": "מפות Google" 128 | }, 129 | "site_bingmaps": { 130 | "message": "מפות Bing" 131 | }, 132 | "site_openstreetmap": { 133 | "message": "OpenStreetMap" 134 | }, 135 | "button_showOtherEnabledLinks": { 136 | "message": "להציג את שאר הקישורים הפעילים באמצעות משתנים נוספים", 137 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 138 | }, 139 | "button_showEnabledLinks": { 140 | "message": "הצגת קישורים פעילים באמצעות משתנים נוספים", 141 | "description": "Shown in popup below some errors to allow users to see enabled links." 142 | }, 143 | "noEnabledCompatibleLinksFound": { 144 | "message": "אף אחד מהקישורים הפעילים לא תואם למשתנים שנמצאו בעמוד הזה.", 145 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 146 | }, 147 | "error_noInformationExtracted": { 148 | "message": "האתר זוהה, אך לא נמצאו משתניפ.\nאם זו מפה, כדאי לנסות לזוז בה מעט כדי לאתחל את המשתנים שלה.\nאם זאת נראית כמו תקלה, נא לדווח עליה אל $EXTENSION_SITE$.", 149 | "description": "Error shown inside popup when appropriate", 150 | "placeholders": { 151 | "EXTENSION_SITE": { 152 | "content": "$1", 153 | "example": "github.com/jgpacker/osm-smart-menu/" 154 | } 155 | } 156 | }, 157 | "error_noAccess": { 158 | "message": "שגיאה: לא ניתן לאתר או לגשת לעמוד הנוכחי.\nאם העמוד הזה עדיין נטען, נא לנסות שוב.\nאם זו תופעה לא צפויה, נא לדווח עליה אל $EXTENSION_SITE$.", 159 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 160 | "placeholders": { 161 | "EXTENSION_SITE": { 162 | "content": "$1", 163 | "example": "github.com/jgpacker/osm-smart-menu/" 164 | } 165 | } 166 | }, 167 | "error_incompatibleWebsite": { 168 | "message": "לא נמצאו מפה או משתני OpenStreetMap.\nאם זו תופעה בלתי צפויה, ניתן לדווח עליה אל $EXTENSION_SITE$.", 169 | "description": "Error shown inside popup when appropriate", 170 | "placeholders": { 171 | "EXTENSION_SITE": { 172 | "content": "$1", 173 | "example": "github.com/jgpacker/osm-smart-menu/" 174 | } 175 | } 176 | }, 177 | "config_pattern_createOption": { 178 | "message": "יצירת אפשרות", 179 | "description": "Button" 180 | }, 181 | "config_pattern_urlTemplate": { 182 | "message": "תבנית כתובת", 183 | "description": "Input label" 184 | }, 185 | "config_pattern_name": { 186 | "message": "שם", 187 | "description": "Input label" 188 | }, 189 | "config_pattern_formTitle": { 190 | "message": "יצירת אפשרויות מתקדמות (ניסיוני)", 191 | "description": "Form Legend/Title" 192 | }, 193 | "config_linkDeleted": { 194 | "message": "הקישור $LINK_NAME$ נמחק. ישוחזר בלחיצה כאן.", 195 | "description": "Shown inline when a link is deleted inside configuration page", 196 | "placeholders": { 197 | "LINK_NAME": { 198 | "content": "$1", 199 | "example": "OpenStreetMap" 200 | } 201 | } 202 | }, 203 | "config_deleteButton": { 204 | "message": "מחיקה", 205 | "description": "Text inside 'delete button' in configuration page" 206 | }, 207 | "config_saveButton": { 208 | "message": "שמירה", 209 | "description": "Text inside 'save button' in configuration page" 210 | }, 211 | "config_editButton": { 212 | "message": "עריכה", 213 | "description": "Text inside 'edit button' in configuration page" 214 | }, 215 | "configurationLink": { 216 | "message": "הגדרות", 217 | "description": "Link to configuration page inside main popup." 218 | }, 219 | "newOptionDetected_added": { 220 | "message": "נוסף בתור $PAGE_TITLE$", 221 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 222 | "placeholders": { 223 | "PAGE_TITLE": { 224 | "content": "$1", 225 | "example": "OpenStreetMap" 226 | } 227 | } 228 | }, 229 | "newOptionDetected_buttonText": { 230 | "message": "הוספת קישור", 231 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 232 | }, 233 | "newOptionDetected_notice": { 234 | "message": "להוסיף את האתר הזה לתפריט?", 235 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 236 | }, 237 | "loading": { 238 | "message": "בטעינה…\nאם התהליך הזה לא נשלם, יש לרענן את העמוד ולנסות שוב.", 239 | "description": "Shown in popup shortly before menu appears." 240 | }, 241 | "extensionDescription": { 242 | "message": "מסייע למתנדבי OpenStreetMap לעבור בקלות בין מפות וכלי ניתוח שונים מתוצרת הקהילה." 243 | }, 244 | "extensionShortName": { 245 | "message": "OSMenu" 246 | }, 247 | "extensionName": { 248 | "message": "תפריט חכם ל־OSM" 249 | }, 250 | "site_pewuosmhistory": { 251 | "message": "מציג ההיסטוריה של OSM‏ (PeWu)" 252 | }, 253 | "site_ideditor": { 254 | "message": "העורך iD" 255 | }, 256 | "site_stravaglobal": { 257 | "message": "Strava Global Heatmap" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_osmhistoryviewer": { 3 | "message": "OSM History Viewer" 4 | }, 5 | "site_osmlanevisualizer": { 6 | "message": "OSM Lane Visualizer" 7 | }, 8 | "site_missingmaps": { 9 | "message": "Missing Maps" 10 | }, 11 | "site_indoorequal": { 12 | "message": "indoor=" 13 | }, 14 | "site_openlevelup": { 15 | "message": "OpenLevelUp!" 16 | }, 17 | "site_osmbuildings": { 18 | "message": "OSM Buildings" 19 | }, 20 | "site_osmcha": { 21 | "message": "OSMCha" 22 | }, 23 | "site_openstreetbrowser": { 24 | "message": "OpenStreetBrowser" 25 | }, 26 | "site_waze": { 27 | "message": "Waze" 28 | }, 29 | "site_osmchangetiles": { 30 | "message": "Latest OSM Edits (Neis)" 31 | }, 32 | "site_osmchangeviz": { 33 | "message": "Changeset by Comparison (Neis)" 34 | }, 35 | "site_osmwiki": { 36 | "message": "OpenStreetMap wiki" 37 | }, 38 | "site_howdidyoucontribute": { 39 | "message": "How did you contribute to OSM?" 40 | }, 41 | "site_keepright": { 42 | "message": "keep right!" 43 | }, 44 | "site_mapcompare": { 45 | "message": "Map Compare" 46 | }, 47 | "site_osminspector": { 48 | "message": "OSM Inspector" 49 | }, 50 | "site_osmose": { 51 | "message": "Osmose" 52 | }, 53 | "site_osmroutemanager": { 54 | "message": "OSM Route Manager" 55 | }, 56 | "site_osmrelationanalyzer": { 57 | "message": "OSM Relation Analyzer" 58 | }, 59 | "site_overpassapi": { 60 | "message": "AChaVi" 61 | }, 62 | "site_whodidit": { 63 | "message": "WHODIDIT (Zverik)" 64 | }, 65 | "site_deepdiff": { 66 | "message": "Deep Diff" 67 | }, 68 | "site_osmdeephistory": { 69 | "message": "OSM Deep History" 70 | }, 71 | "site_umap": { 72 | "message": "uMap" 73 | }, 74 | "site_level0": { 75 | "message": "Level0 Editor" 76 | }, 77 | "site_mapillary": { 78 | "message": "Mapillary" 79 | }, 80 | "site_openstreetcam": { 81 | "message": "KartaView" 82 | }, 83 | "site_hotmap": { 84 | "message": "Humanitarian OSM Team Map" 85 | }, 86 | "site_f4map": { 87 | "message": "F4 Map (3D demo)" 88 | }, 89 | "site_stamen": { 90 | "message": "Stamen Maps" 91 | }, 92 | "site_opnvkarte": { 93 | "message": "ÖPNVKarte" 94 | }, 95 | "site_openptmap": { 96 | "message": "Open Public Transport Map" 97 | }, 98 | "site_openinframap": { 99 | "message": "Open Infrastructure Map " 100 | }, 101 | "site_historicmap": { 102 | "message": "Historical Objects" 103 | }, 104 | "site_opentopomap": { 105 | "message": "OpenTopoMap" 106 | }, 107 | "site_opensnowmap": { 108 | "message": "OpenSnowMap" 109 | }, 110 | "site_openseamap": { 111 | "message": "OpenSeaMap" 112 | }, 113 | "site_opencyclemap": { 114 | "message": "OpenCycleMap" 115 | }, 116 | "site_openmapsurfer": { 117 | "message": "OpenMapSurfer" 118 | }, 119 | "site_sentinelhub": { 120 | "message": "Sentinel Hub" 121 | }, 122 | "site_googlemaps": { 123 | "message": "Google Maps" 124 | }, 125 | "site_bingmaps": { 126 | "message": "Bing Maps" 127 | }, 128 | "site_openstreetmap": { 129 | "message": "OpenStreetMap" 130 | }, 131 | "button_showOtherEnabledLinks": { 132 | "message": "Mostra il resto dei collegamenti abilitati utilizzando parametri aggiuntivi", 133 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 134 | }, 135 | "button_showEnabledLinks": { 136 | "message": "Mostra i link abilitati utilizzando parametri aggiuntivi", 137 | "description": "Shown in popup below some errors to allow users to see enabled links." 138 | }, 139 | "noEnabledCompatibleLinksFound": { 140 | "message": "Nessuno dei link abilitati è compatibile con i parametri che si trovano in questa pagina.", 141 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 142 | }, 143 | "error_noInformationExtracted": { 144 | "message": "Il sito web è stato riconosciuto, ma non è stato trovato alcun parametro.\nSe si tratta di una mappa, provate a spostarla un po' per inizializzare i suoi parametri.\nSe sembra un bug, segnalatelo in $EXTENSION_SITE$.", 145 | "description": "Error shown inside popup when appropriate", 146 | "placeholders": { 147 | "EXTENSION_SITE": { 148 | "content": "$1", 149 | "example": "github.com/jgpacker/osm-smart-menu/" 150 | } 151 | } 152 | }, 153 | "error_incompatibleWebsite": { 154 | "message": "Non sono stati trovati né mappa né parametri OpenStreetMap.\nSe questo è inaspettato, potete segnalarlo in $EXTENSION_SITE$.", 155 | "description": "Error shown inside popup when appropriate", 156 | "placeholders": { 157 | "EXTENSION_SITE": { 158 | "content": "$1", 159 | "example": "github.com/jgpacker/osm-smart-menu/" 160 | } 161 | } 162 | }, 163 | "error_noAccess": { 164 | "message": "ERRORE: impossibile trovare o accedere alla pagina corrente.\nSe questa pagina è ancora in fase di caricamento, riprova.\nSe ciò è imprevisto, segnalalo in $EXTENSION_SITE$.", 165 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 166 | "placeholders": { 167 | "EXTENSION_SITE": { 168 | "content": "$1", 169 | "example": "github.com/jgpacker/osm-smart-menu/" 170 | } 171 | } 172 | }, 173 | "config_pattern_createOption": { 174 | "message": "Crea opzione", 175 | "description": "Button" 176 | }, 177 | "config_pattern_urlTemplate": { 178 | "message": "URL modello", 179 | "description": "Input label" 180 | }, 181 | "config_pattern_name": { 182 | "message": "Nome", 183 | "description": "Input label" 184 | }, 185 | "config_pattern_formTitle": { 186 | "message": "Creazione di opzioni avanzate (sperimentale)", 187 | "description": "Form Legend/Title" 188 | }, 189 | "config_linkDeleted": { 190 | "message": "Collegamento $LINK_NAME$ eliminato. Clicca qui per annullare.", 191 | "description": "Shown inline when a link is deleted inside configuration page", 192 | "placeholders": { 193 | "LINK_NAME": { 194 | "content": "$1", 195 | "example": "OpenStreetMap" 196 | } 197 | } 198 | }, 199 | "config_deleteButton": { 200 | "message": "Elimina", 201 | "description": "Text inside 'delete button' in configuration page" 202 | }, 203 | "config_saveButton": { 204 | "message": "Salva", 205 | "description": "Text inside 'save button' in configuration page" 206 | }, 207 | "config_editButton": { 208 | "message": "Modifica", 209 | "description": "Text inside 'edit button' in configuration page" 210 | }, 211 | "configurationLink": { 212 | "message": "Configurazione", 213 | "description": "Link to configuration page inside main popup." 214 | }, 215 | "newOptionDetected_added": { 216 | "message": "Aggiunto come $PAGE_TITLE$", 217 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 218 | "placeholders": { 219 | "PAGE_TITLE": { 220 | "content": "$1", 221 | "example": "OpenStreetMap" 222 | } 223 | } 224 | }, 225 | "newOptionDetected_buttonText": { 226 | "message": "Aggiungi link", 227 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 228 | }, 229 | "newOptionDetected_notice": { 230 | "message": "Vuoi aggiungere questo sito web al menu?", 231 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 232 | }, 233 | "loading": { 234 | "message": "Caricamento in corso...\nSe il caricamento non si interrompe, ricarica la pagina e riprova.", 235 | "description": "Shown in popup shortly before menu appears." 236 | }, 237 | "extensionStoreDescription": { 238 | "message": "Quando clicchi sul pulsante del menu, questa estensione riconoscerà i parametri OpenStreetMap all'interno della pagina corrente e genererà una lista di mappe e strumenti correlati con questi parametri.\n\nI parametri riconosciuti includono:\n\nSe non sei sicuro quale sito verrà riconosciuto, vai su https://www.openstreetmap.org/ e clicca sul menu.\n\nIl logo OpenStreetMap è un marchio registrato dalla OpenStreetMap Foundation ed è utilizzato con il loro permesso. Questo progetto non è supportato o affiliato alla OpenStreetMap Foundation.", 239 | "description": "Description shown in Chrome and Firefox's extension download page" 240 | }, 241 | "extensionDescription": { 242 | "message": "Aiuta gli utenti OpenStreetMap a passare facilmente tra diverse mappe e strumenti di analisi della comunità." 243 | }, 244 | "extensionShortName": { 245 | "message": "OSMenu" 246 | }, 247 | "extensionName": { 248 | "message": "OSM Smart Menu" 249 | }, 250 | "site_stravaglobal": { 251 | "message": "Strava Global Heatmap" 252 | }, 253 | "site_pewuosmhistory": { 254 | "message": "OSM History Viewer (PeWu)" 255 | }, 256 | "site_ideditor": { 257 | "message": "iD Editor" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/nb_NO/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_osmwiki": { 3 | "message": "OpenStreetMap-wiki" 4 | }, 5 | "site_keepright": { 6 | "message": "keep right!" 7 | }, 8 | "site_indoorequal": { 9 | "message": "indoor=" 10 | }, 11 | "site_missingmaps": { 12 | "message": "Missing Maps" 13 | }, 14 | "site_osmlanevisualizer": { 15 | "message": "OSM-kjørefeltsvisualisator" 16 | }, 17 | "site_openlevelup": { 18 | "message": "OpenLevelUp!" 19 | }, 20 | "site_osmbuildings": { 21 | "message": "OSM-bygninger" 22 | }, 23 | "site_osmcha": { 24 | "message": "OSMCha" 25 | }, 26 | "site_openstreetbrowser": { 27 | "message": "OpenStreetBrowser" 28 | }, 29 | "site_waze": { 30 | "message": "Waze" 31 | }, 32 | "site_osmchangetiles": { 33 | "message": "Siste OSM-redigeringer (Neis)" 34 | }, 35 | "site_osmchangeviz": { 36 | "message": "Endringsett ved sammenligning (Neis)" 37 | }, 38 | "site_howdidyoucontribute": { 39 | "message": "Hvordan bidro du til OSM?" 40 | }, 41 | "site_mapcompare": { 42 | "message": "Kartsammenligning" 43 | }, 44 | "site_osminspector": { 45 | "message": "OSM-inspektøren" 46 | }, 47 | "site_osmose": { 48 | "message": "Osmose" 49 | }, 50 | "site_osmroutemanager": { 51 | "message": "OSM-rutehåndterer" 52 | }, 53 | "site_osmrelationanalyzer": { 54 | "message": "OSM-relasjonsanalysator" 55 | }, 56 | "site_overpassapi": { 57 | "message": "Augmentert OSM-endringsviser" 58 | }, 59 | "site_whodidit": { 60 | "message": "WHODIDIT (Zverik)" 61 | }, 62 | "site_osmhistoryviewer": { 63 | "message": "OSM-historikkviser" 64 | }, 65 | "site_deepdiff": { 66 | "message": "Deep-diff" 67 | }, 68 | "site_osmdeephistory": { 69 | "message": "OSM-dyphistorikk" 70 | }, 71 | "site_umap": { 72 | "message": "uMap" 73 | }, 74 | "site_level0": { 75 | "message": "Level0-redigerer" 76 | }, 77 | "site_mapillary": { 78 | "message": "Mapillary" 79 | }, 80 | "site_openstreetcam": { 81 | "message": "KartaView" 82 | }, 83 | "site_hotmap": { 84 | "message": "Humanitarian OSM-lagkart" 85 | }, 86 | "site_f4map": { 87 | "message": "F4 Map (3D-demo)" 88 | }, 89 | "site_stamen": { 90 | "message": "Stamen-kart" 91 | }, 92 | "site_opnvkarte": { 93 | "message": "ÖPNVKarte" 94 | }, 95 | "site_openptmap": { 96 | "message": "Open Public Transport-kart" 97 | }, 98 | "site_openinframap": { 99 | "message": "Open Infrastructure-kart " 100 | }, 101 | "site_historicmap": { 102 | "message": "Historiske objekter" 103 | }, 104 | "site_opentopomap": { 105 | "message": "OpenTopoMap" 106 | }, 107 | "site_opensnowmap": { 108 | "message": "OpenSnowMap" 109 | }, 110 | "site_openseamap": { 111 | "message": "OpenSeaMap" 112 | }, 113 | "site_opencyclemap": { 114 | "message": "OpenCycleMap" 115 | }, 116 | "site_openmapsurfer": { 117 | "message": "OpenMapSurfer" 118 | }, 119 | "site_sentinelhub": { 120 | "message": "Sentinel Hub" 121 | }, 122 | "site_googlemaps": { 123 | "message": "Google Maps" 124 | }, 125 | "site_bingmaps": { 126 | "message": "Bing-kart" 127 | }, 128 | "site_openstreetmap": { 129 | "message": "OpenStreetMap" 130 | }, 131 | "button_showOtherEnabledLinks": { 132 | "message": "Vis resten av de påskrudde lenkene ved bruk av ytterligere parametre", 133 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 134 | }, 135 | "button_showEnabledLinks": { 136 | "message": "Vis påskrudde lenker ved bruk av ytterligere parametre", 137 | "description": "Shown in popup below some errors to allow users to see enabled links." 138 | }, 139 | "noEnabledCompatibleLinksFound": { 140 | "message": "Ingen av de påskrudde lenkene er kompatible med parameterne på siden.", 141 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 142 | }, 143 | "error_noInformationExtracted": { 144 | "message": "Nettsiden ble gjenkjent, men ingen parametre ble funnet.\nHvis dette er et kart, prøv å flytt det litt for å igangsette dets parametre.\nHvis dette ser ut til å være en feil, innrapporter det på $EXTENSION_SITE$.", 145 | "description": "Error shown inside popup when appropriate", 146 | "placeholders": { 147 | "EXTENSION_SITE": { 148 | "content": "$1", 149 | "example": "github.com/jgpacker/osm-smart-menu/" 150 | } 151 | } 152 | }, 153 | "error_incompatibleWebsite": { 154 | "message": "Fant ingen kart eller OpenStreetMap-parametre.\nHvis dette er uventet, innrapporter det på $EXTENSION_SITE$.", 155 | "description": "Error shown inside popup when appropriate", 156 | "placeholders": { 157 | "EXTENSION_SITE": { 158 | "content": "$1", 159 | "example": "github.com/jgpacker/osm-smart-menu/" 160 | } 161 | } 162 | }, 163 | "error_noAccess": { 164 | "message": "Feil: Fikk ikke tilgang til, eller fant ikke nåværende side.\nHvis siden fremdeles lastes inn, prøv igjen.\nHvis dette er uventet, innrapporter det til $EXTENSION_SITE$.", 165 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 166 | "placeholders": { 167 | "EXTENSION_SITE": { 168 | "content": "$1", 169 | "example": "github.com/jgpacker/osm-smart-menu/" 170 | } 171 | } 172 | }, 173 | "config_pattern_createOption": { 174 | "message": "Opprett alternativ", 175 | "description": "Button" 176 | }, 177 | "config_pattern_urlTemplate": { 178 | "message": "Nettadressemal", 179 | "description": "Input label" 180 | }, 181 | "config_pattern_name": { 182 | "message": "Navn", 183 | "description": "Input label" 184 | }, 185 | "config_pattern_formTitle": { 186 | "message": "Avansert alternativsopprettelse (eksperimentelt)", 187 | "description": "Form Legend/Title" 188 | }, 189 | "config_linkDeleted": { 190 | "message": "Lenken $LINK_NAME$ ble slettet. Klikk her for å angre.", 191 | "description": "Shown inline when a link is deleted inside configuration page", 192 | "placeholders": { 193 | "LINK_NAME": { 194 | "content": "$1", 195 | "example": "OpenStreetMap" 196 | } 197 | } 198 | }, 199 | "config_deleteButton": { 200 | "message": "Slett", 201 | "description": "Text inside 'delete button' in configuration page" 202 | }, 203 | "config_saveButton": { 204 | "message": "Lagre", 205 | "description": "Text inside 'save button' in configuration page" 206 | }, 207 | "config_editButton": { 208 | "message": "Rediger", 209 | "description": "Text inside 'edit button' in configuration page" 210 | }, 211 | "configurationLink": { 212 | "message": "Oppsett", 213 | "description": "Link to configuration page inside main popup." 214 | }, 215 | "newOptionDetected_added": { 216 | "message": "Lagt til som $PAGE_TITLE$", 217 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 218 | "placeholders": { 219 | "PAGE_TITLE": { 220 | "content": "$1", 221 | "example": "OpenStreetMap" 222 | } 223 | } 224 | }, 225 | "newOptionDetected_buttonText": { 226 | "message": "Legg til lenke", 227 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 228 | }, 229 | "newOptionDetected_notice": { 230 | "message": "Ønsker du å legge denne nettsiden til i menyen?", 231 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 232 | }, 233 | "loading": { 234 | "message": "Laster inn …\nHvis innlastingen ikke fullføres, gjeninnlast siden og prøv igjen.", 235 | "description": "Shown in popup shortly before menu appears." 236 | }, 237 | "extensionDescription": { 238 | "message": "Hjelper OpenStreetMap-bidragsytere å bytte mellom forskjellige kart og analyseverktøy fra gemenskapen" 239 | }, 240 | "extensionShortName": { 241 | "message": "OSMenu" 242 | }, 243 | "extensionName": { 244 | "message": "OSM-smartmeny" 245 | }, 246 | "extensionStoreDescription": { 247 | "message": "Når du klikker på menyknappen, vil denne utvidelsen oppdage OpenStreetMap-parametre i nåværende side og generere en liste over relaterte kart og verktøy med disse parameterne.\n\nDisse parameterne gjenkjennes:\n\nHvis du ikke er sikker på hvilken nettside som vil bli gjenkjent, prøv å gå til https://www.openstreetmap.org/ før du klikker på menyen.\n\nOpenStreetMap-logoen er et varemerke som tilhører OpenStreetMap-stiftelsen, og brukes med tillatelse. Dette prosjektet er ikke støttet av eller tilknyttet OpenStreetMap-stiftelsen.", 248 | "description": "Description shown in Chrome and Firefox's extension download page" 249 | }, 250 | "site_pewuosmhistory": { 251 | "message": "OSM-historikkviser (PeWu)" 252 | }, 253 | "site_ideditor": { 254 | "message": "iD-redigereren" 255 | }, 256 | "site_stravaglobal": { 257 | "message": "Bruksfargediagram for Strava" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/nl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_stravaglobal": { 3 | "message": "Strava Global Heatmap" 4 | }, 5 | "site_osmlanevisualizer": { 6 | "message": "OSM Lane Visualizer" 7 | }, 8 | "site_missingmaps": { 9 | "message": "Missing Maps" 10 | }, 11 | "site_indoorequal": { 12 | "message": "indoor=" 13 | }, 14 | "site_openlevelup": { 15 | "message": "OpenLevelUp!" 16 | }, 17 | "site_osmbuildings": { 18 | "message": "OSM Buildings" 19 | }, 20 | "site_osmcha": { 21 | "message": "OSMCha" 22 | }, 23 | "site_openstreetbrowser": { 24 | "message": "OpenStreetBrowser" 25 | }, 26 | "site_waze": { 27 | "message": "Waze" 28 | }, 29 | "site_osmchangetiles": { 30 | "message": "Latest OSM Edits (Neis)" 31 | }, 32 | "site_osmchangeviz": { 33 | "message": "Changeset by Comparison (Neis)" 34 | }, 35 | "site_osmwiki": { 36 | "message": "OpenStreetMap-wiki" 37 | }, 38 | "site_howdidyoucontribute": { 39 | "message": "How did you contribute to OSM?" 40 | }, 41 | "site_keepright": { 42 | "message": "keep right!" 43 | }, 44 | "site_mapcompare": { 45 | "message": "Map Compare" 46 | }, 47 | "site_osminspector": { 48 | "message": "OSM Inspector" 49 | }, 50 | "site_osmose": { 51 | "message": "Osmose" 52 | }, 53 | "site_osmroutemanager": { 54 | "message": "OSM Route Manager" 55 | }, 56 | "site_osmrelationanalyzer": { 57 | "message": "OSM Relation Analyzer" 58 | }, 59 | "site_overpassapi": { 60 | "message": "Augmented OSM Change Viewer" 61 | }, 62 | "site_whodidit": { 63 | "message": "WHODIDIT (Zverik)" 64 | }, 65 | "site_pewuosmhistory": { 66 | "message": "OSM History Viewer (PeWu)" 67 | }, 68 | "site_osmhistoryviewer": { 69 | "message": "OSM History Viewer" 70 | }, 71 | "site_deepdiff": { 72 | "message": "Deep Diff" 73 | }, 74 | "site_osmdeephistory": { 75 | "message": "OSM Deep History" 76 | }, 77 | "site_umap": { 78 | "message": "uMap" 79 | }, 80 | "site_ideditor": { 81 | "message": "iD-bewerker" 82 | }, 83 | "site_level0": { 84 | "message": "Level0-bewerker" 85 | }, 86 | "site_mapillary": { 87 | "message": "Mapillary" 88 | }, 89 | "site_openstreetcam": { 90 | "message": "KartaView" 91 | }, 92 | "site_hotmap": { 93 | "message": "Humanitarian OSM Team Map" 94 | }, 95 | "site_f4map": { 96 | "message": "F4 Map (3D-demo)" 97 | }, 98 | "site_stamen": { 99 | "message": "Stamen Maps" 100 | }, 101 | "site_opnvkarte": { 102 | "message": "ÖPNVKarte" 103 | }, 104 | "site_openptmap": { 105 | "message": "Open Public Transport Map" 106 | }, 107 | "site_openinframap": { 108 | "message": "Open Infrastructure Map " 109 | }, 110 | "site_historicmap": { 111 | "message": "Historical Objects" 112 | }, 113 | "site_opentopomap": { 114 | "message": "OpenTopoMap" 115 | }, 116 | "site_opensnowmap": { 117 | "message": "OpenSnowMap" 118 | }, 119 | "site_openseamap": { 120 | "message": "OpenSeaMap" 121 | }, 122 | "site_opencyclemap": { 123 | "message": "OpenCycleMap" 124 | }, 125 | "site_openmapsurfer": { 126 | "message": "OpenMapSurfer" 127 | }, 128 | "site_sentinelhub": { 129 | "message": "Sentinel Hub" 130 | }, 131 | "site_googlemaps": { 132 | "message": "Google Maps" 133 | }, 134 | "site_bingmaps": { 135 | "message": "Bing Kaarten" 136 | }, 137 | "site_openstreetmap": { 138 | "message": "OpenStreetMap" 139 | }, 140 | "button_showOtherEnabledLinks": { 141 | "message": "Overige ingeschakelde links met aanvullende opties tonen", 142 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 143 | }, 144 | "button_showEnabledLinks": { 145 | "message": "Ingeschakelde links met aanvullende opties tonen", 146 | "description": "Shown in popup below some errors to allow users to see enabled links." 147 | }, 148 | "noEnabledCompatibleLinksFound": { 149 | "message": "Geen van de ingeschakelde links zijn compatibel met de op deze pagina aangetroffen opties.", 150 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 151 | }, 152 | "error_noInformationExtracted": { 153 | "message": "De website werd herkend, maar er zijn geen opties aangetroffen.\nAls het om een kaart gaat, probeer hem dan een stukje te verschuiven.\nAls dit onverwachts was, meld de fout dan op $EXTENSION_SITE$.", 154 | "description": "Error shown inside popup when appropriate", 155 | "placeholders": { 156 | "EXTENSION_SITE": { 157 | "content": "$1", 158 | "example": "github.com/jgpacker/osm-smart-menu/" 159 | } 160 | } 161 | }, 162 | "error_incompatibleWebsite": { 163 | "message": "Geen kaart of OpenStreetMap-opties aangetroffen.\nAls dit onverwachts was, meld deze fout dan op $EXTENSION_SITE$.", 164 | "description": "Error shown inside popup when appropriate", 165 | "placeholders": { 166 | "EXTENSION_SITE": { 167 | "content": "$1", 168 | "example": "github.com/jgpacker/osm-smart-menu/" 169 | } 170 | } 171 | }, 172 | "error_noAccess": { 173 | "message": "FOUTMELDING: de huidige pagina is niet beschikbaar.\nAls de pagina nog wordt geladen, probeer het dan opnieuw.\nAls dit onverwachts was, meld de fout dan op $EXTENSION_SITE$.", 174 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 175 | "placeholders": { 176 | "EXTENSION_SITE": { 177 | "content": "$1", 178 | "example": "github.com/jgpacker/osm-smart-menu/" 179 | } 180 | } 181 | }, 182 | "config_pattern_createOption": { 183 | "message": "Optie aanmaken", 184 | "description": "Button" 185 | }, 186 | "config_pattern_urlTemplate": { 187 | "message": "URL-sjabloon", 188 | "description": "Input label" 189 | }, 190 | "config_pattern_name": { 191 | "message": "Naam", 192 | "description": "Input label" 193 | }, 194 | "config_pattern_formTitle": { 195 | "message": "Geavanceerde opties (experimenteel)", 196 | "description": "Form Legend/Title" 197 | }, 198 | "config_linkDeleted": { 199 | "message": "$LINK_NAME$ is verwijderd - klik om ongedaan te maken.", 200 | "description": "Shown inline when a link is deleted inside configuration page", 201 | "placeholders": { 202 | "LINK_NAME": { 203 | "content": "$1", 204 | "example": "OpenStreetMap" 205 | } 206 | } 207 | }, 208 | "config_deleteButton": { 209 | "message": "Verwijderen", 210 | "description": "Text inside 'delete button' in configuration page" 211 | }, 212 | "config_saveButton": { 213 | "message": "Opslaan", 214 | "description": "Text inside 'save button' in configuration page" 215 | }, 216 | "config_editButton": { 217 | "message": "Bewerken", 218 | "description": "Text inside 'edit button' in configuration page" 219 | }, 220 | "configurationLink": { 221 | "message": "Instellingen", 222 | "description": "Link to configuration page inside main popup." 223 | }, 224 | "newOptionDetected_added": { 225 | "message": "Toegevoegd als $PAGE_TITLE$", 226 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 227 | "placeholders": { 228 | "PAGE_TITLE": { 229 | "content": "$1", 230 | "example": "OpenStreetMap" 231 | } 232 | } 233 | }, 234 | "newOptionDetected_buttonText": { 235 | "message": "Link toevoegen", 236 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 237 | }, 238 | "newOptionDetected_notice": { 239 | "message": "Wilt u deze website toevoegen aan het menu?", 240 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 241 | }, 242 | "loading": { 243 | "message": "Bezig met laden...\nAls er niks wordt geladen, herlaad dan de pagina en probeer het opnieuw.", 244 | "description": "Shown in popup shortly before menu appears." 245 | }, 246 | "extensionStoreDescription": { 247 | "message": "Als u de menuknop aanklikt, detecteert deze extensie automatisch OpenStreetMap-opties op de huidige pagina en genereert op basis hiervan een lijst met vergelijkbare kaarten en hulpmiddelen.\n\nOndersteunde opties:\n\nAls u niet zeker weet welke website herkend gaat worden, ga dan op voorhand naar https://www.openstreetmap.org/.\n\nHet OpenStreetMap-logo is een handelsmerk van de OpenStreetMap Foundation en wordt met hun instemming gebruikt. Dit project is niet afkomstig van of betrokken bij de OpenStreetMap Foundation.", 248 | "description": "Description shown in Chrome and Firefox's extension download page" 249 | }, 250 | "extensionDescription": { 251 | "message": "Hiermee kunnen OpenStreetMap-bijdragers eenvoudig schakelen tussen kaartsoorten en analysehulpmiddelen." 252 | }, 253 | "extensionShortName": { 254 | "message": "OSMenu" 255 | }, 256 | "extensionName": { 257 | "message": "OSM - slim menu" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/pt/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionStoreDescription": { 3 | "message": "Quando clicar no botão do menu, esta extensão irá detetar parâmetros do OpenStreetMap dentro da sua página atual e irá gerar uma lista de mapas e ferramentas relacionadas que utilizam esses parâmetros.\n\nOs parâmetros reconhecidos incluem:\n\nSe não tem certeza qual site será reconhecido, vá a https://www.openstreetmap.org/ antes de clicar no menu.\n\nO logotipo OpenStreetMap é uma marca registada da OpenStreetMap Foundation e é utilizado com a permissão desta. Este projeto não é endossado ou afiliado com a OpenStreetMap Foundation.", 4 | "description": "Description shown in Chrome and Firefox's extension download page" 5 | }, 6 | "site_osmchangetiles": { 7 | "message": "Últimas edições no OSM (Neis)" 8 | }, 9 | "site_osmchangeviz": { 10 | "message": "Comparação de alterações (Neis)" 11 | }, 12 | "site_howdidyoucontribute": { 13 | "message": "Como contribuiu para o OSM?" 14 | }, 15 | "site_historicmap": { 16 | "message": "Objetos históricos" 17 | }, 18 | "button_showOtherEnabledLinks": { 19 | "message": "Mostrar o resto das hiperligações ativadas utilizando parâmetros adicionais", 20 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 21 | }, 22 | "button_showEnabledLinks": { 23 | "message": "Mostrar hiperligações ativadas utilizando parâmetros adicionais", 24 | "description": "Shown in popup below some errors to allow users to see enabled links." 25 | }, 26 | "noEnabledCompatibleLinksFound": { 27 | "message": "Nenhuma das hiperligações ativadas são compatíveis com os parâmetros encontrados nesta página.", 28 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 29 | }, 30 | "error_noInformationExtracted": { 31 | "message": "O site foi reconhecido, mas não foram encontrados nenhuns parâmetros.\nSe isto é um mapa, tente mexer nele um pouco para inicializar os parâmetros dele.\nSe isto parece ser um erro, por favor reporte-o em $EXTENSION_SITE$.", 32 | "description": "Error shown inside popup when appropriate", 33 | "placeholders": { 34 | "EXTENSION_SITE": { 35 | "content": "$1", 36 | "example": "github.com/jgpacker/osm-smart-menu/" 37 | } 38 | } 39 | }, 40 | "error_incompatibleWebsite": { 41 | "message": "Não foram encontrados parâmetros de mapa ou do OpenStreetMap.\nSe isto parece ser um erro, por favor reporte-o em $EXTENSION_SITE$.", 42 | "description": "Error shown inside popup when appropriate", 43 | "placeholders": { 44 | "EXTENSION_SITE": { 45 | "content": "$1", 46 | "example": "github.com/jgpacker/osm-smart-menu/" 47 | } 48 | } 49 | }, 50 | "error_noAccess": { 51 | "message": "ERRO: não foi possível encontrar ou aceder à página atual.\nSe a página ainda está a carregar, tente novamente.\nSe isto for inesperado, por favor reporte isto em $EXTENSION_SITE$.", 52 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 53 | "placeholders": { 54 | "EXTENSION_SITE": { 55 | "content": "$1", 56 | "example": "github.com/jgpacker/osm-smart-menu/" 57 | } 58 | } 59 | }, 60 | "config_pattern_createOption": { 61 | "message": "Criar opção", 62 | "description": "Button" 63 | }, 64 | "config_pattern_urlTemplate": { 65 | "message": "Modelo de URL", 66 | "description": "Input label" 67 | }, 68 | "config_pattern_name": { 69 | "message": "Nome", 70 | "description": "Input label" 71 | }, 72 | "config_pattern_formTitle": { 73 | "message": "Criação avançada de opção (experimental)", 74 | "description": "Form Legend/Title" 75 | }, 76 | "config_linkDeleted": { 77 | "message": "Hiperligação $LINK_NAME$ eliminada. Clique aqui para restaurá-la.", 78 | "description": "Shown inline when a link is deleted inside configuration page", 79 | "placeholders": { 80 | "LINK_NAME": { 81 | "content": "$1", 82 | "example": "OpenStreetMap" 83 | } 84 | } 85 | }, 86 | "config_deleteButton": { 87 | "message": "Eliminar", 88 | "description": "Text inside 'delete button' in configuration page" 89 | }, 90 | "config_saveButton": { 91 | "message": "Guardar", 92 | "description": "Text inside 'save button' in configuration page" 93 | }, 94 | "config_editButton": { 95 | "message": "Editar", 96 | "description": "Text inside 'edit button' in configuration page" 97 | }, 98 | "configurationLink": { 99 | "message": "Configurações", 100 | "description": "Link to configuration page inside main popup." 101 | }, 102 | "newOptionDetected_added": { 103 | "message": "Adicionado como $PAGE_TITLE$", 104 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 105 | "placeholders": { 106 | "PAGE_TITLE": { 107 | "content": "$1", 108 | "example": "OpenStreetMap" 109 | } 110 | } 111 | }, 112 | "newOptionDetected_buttonText": { 113 | "message": "Adicionar hiperligação", 114 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 115 | }, 116 | "newOptionDetected_notice": { 117 | "message": "Gostaria de adicionar esse site no menu?", 118 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 119 | }, 120 | "loading": { 121 | "message": "A carregar...\nSe não parar de carregar, recarregue a página e tente novamente.", 122 | "description": "Shown in popup shortly before menu appears." 123 | }, 124 | "extensionDescription": { 125 | "message": "Facilita o acesso de contribuidores do OpenStreetMap entre diferentes mapas e ferramentas de análise da comunidade." 126 | }, 127 | "extensionShortName": { 128 | "message": "Menu OSM" 129 | }, 130 | "extensionName": { 131 | "message": "Menu Inteligente OSM" 132 | }, 133 | "site_stravaglobal": { 134 | "message": "Strava Mapa de Calor Global" 135 | }, 136 | "site_osmlanevisualizer": { 137 | "message": "OSM Lane Visualizer" 138 | }, 139 | "site_missingmaps": { 140 | "message": "Missing Maps" 141 | }, 142 | "site_indoorequal": { 143 | "message": "indoor=" 144 | }, 145 | "site_openlevelup": { 146 | "message": "OpenLevelUp!" 147 | }, 148 | "site_osmbuildings": { 149 | "message": "OSM Buildings" 150 | }, 151 | "site_osmcha": { 152 | "message": "OSMCha" 153 | }, 154 | "site_openstreetbrowser": { 155 | "message": "OpenStreetBrowser" 156 | }, 157 | "site_waze": { 158 | "message": "Waze" 159 | }, 160 | "site_osmwiki": { 161 | "message": "Wiki do OpenStreetMap" 162 | }, 163 | "site_keepright": { 164 | "message": "keep right!" 165 | }, 166 | "site_mapcompare": { 167 | "message": "Map Compare" 168 | }, 169 | "site_osminspector": { 170 | "message": "OSM Inspector" 171 | }, 172 | "site_osmose": { 173 | "message": "Osmose" 174 | }, 175 | "site_osmroutemanager": { 176 | "message": "OSM Route Manager" 177 | }, 178 | "site_osmrelationanalyzer": { 179 | "message": "OSM Relation Analyzer" 180 | }, 181 | "site_overpassapi": { 182 | "message": "Augmented OSM Change Viewer" 183 | }, 184 | "site_whodidit": { 185 | "message": "WHODIDIT (Zverik)" 186 | }, 187 | "site_pewuosmhistory": { 188 | "message": "OSM History Viewer (PeWu)" 189 | }, 190 | "site_osmhistoryviewer": { 191 | "message": "OSM History Viewer" 192 | }, 193 | "site_deepdiff": { 194 | "message": "Deep Diff" 195 | }, 196 | "site_osmdeephistory": { 197 | "message": "OSM Deep History" 198 | }, 199 | "site_umap": { 200 | "message": "uMap" 201 | }, 202 | "site_ideditor": { 203 | "message": "iD Editor" 204 | }, 205 | "site_level0": { 206 | "message": "Level0 Editor" 207 | }, 208 | "site_mapillary": { 209 | "message": "Mapillary" 210 | }, 211 | "site_openstreetcam": { 212 | "message": "KartaView" 213 | }, 214 | "site_hotmap": { 215 | "message": "Humanitarian OSM Team Map" 216 | }, 217 | "site_f4map": { 218 | "message": "F4 Map (3D demo)" 219 | }, 220 | "site_stamen": { 221 | "message": "Stamen Maps" 222 | }, 223 | "site_opnvkarte": { 224 | "message": "ÖPNVKarte" 225 | }, 226 | "site_openptmap": { 227 | "message": "Open Public Transport Map" 228 | }, 229 | "site_openinframap": { 230 | "message": "Open Infrastructure Map " 231 | }, 232 | "site_opentopomap": { 233 | "message": "OpenTopoMap" 234 | }, 235 | "site_opensnowmap": { 236 | "message": "OpenSnowMap" 237 | }, 238 | "site_openseamap": { 239 | "message": "OpenSeaMap" 240 | }, 241 | "site_opencyclemap": { 242 | "message": "OpenCycleMap" 243 | }, 244 | "site_openmapsurfer": { 245 | "message": "OpenMapSurfer" 246 | }, 247 | "site_sentinelhub": { 248 | "message": "Sentinel Hub" 249 | }, 250 | "site_googlemaps": { 251 | "message": "Google Maps" 252 | }, 253 | "site_bingmaps": { 254 | "message": "Bing Maps" 255 | }, 256 | "site_openstreetmap": { 257 | "message": "OpenStreetMap" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/pt_BR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "Menu Inteligente OSM" 4 | }, 5 | "extensionShortName": { 6 | "message": "Menu OSM" 7 | }, 8 | "extensionStoreDescription": { 9 | "message": "Quando você clicar no botão do menu, esta extensão irá detectar parâmetros OpenStreetMap dentro da sua página atual e irá gerar uma lista de mapas e ferramentas relacionadas que utilizam esses parâmetros.\n\nParâmetros reconhecidos incluem:\n\nSe você não tem certeza qual site será reconhecido, vá para https://www.openstreetmap.org/ antes de clicar no menu.\n\nO logotipo OpenStreetMap é uma marca registrada da OpenStreetMap Foundation, e é utilizado com sua permissão. Este projeto não é endossado ou afiliado com a OpenStreetMap Foundation." 10 | }, 11 | "extensionDescription": { 12 | "message": "Facilita o acesso de contribuidores do OpenStreetMap entre diferentes mapas e ferramentas de análise da comunidade." 13 | }, 14 | "loading": { 15 | "message": "Carregando...\nSe não parar de carregar, recarregue a página e tente novamente." 16 | }, 17 | "newOptionDetected_notice": { 18 | "message": "Gostaria de adicionar esse site no menu?" 19 | }, 20 | "newOptionDetected_buttonText": { 21 | "message": "Adicionar link" 22 | }, 23 | "newOptionDetected_added": { 24 | "message": "Adicionado como $PAGE_TITLE$", 25 | "placeholders": { 26 | "PAGE_TITLE": { 27 | "content": "$1", 28 | "example": "OpenStreetMap" 29 | } 30 | } 31 | }, 32 | "configurationLink": { 33 | "message": "Configurações" 34 | }, 35 | "config_editButton": { 36 | "message": "Editar" 37 | }, 38 | "config_saveButton": { 39 | "message": "Salvar" 40 | }, 41 | "config_deleteButton": { 42 | "message": "Excluir" 43 | }, 44 | "config_linkDeleted": { 45 | "message": "Link $LINK_NAME$ excluído. Clique aqui para restaurá-lo.", 46 | "placeholders": { 47 | "LINK_NAME": { 48 | "content": "$1", 49 | "example": "OpenStreetMap" 50 | } 51 | } 52 | }, 53 | "config_pattern_formTitle": { 54 | "message": "Criação avançada de opção (experimental)" 55 | }, 56 | "config_pattern_name": { 57 | "message": "Nome" 58 | }, 59 | "config_pattern_urlTemplate": { 60 | "message": "Padrão de URL" 61 | }, 62 | "config_pattern_createOption": { 63 | "message": "Criar opção" 64 | }, 65 | "error_noAccess": { 66 | "message": "ERRO: Não foi possível encontrar ou acessar a página atual.\nSe a página ainda está carregando, tente novamente.\nSe isto for inesperado, por favor relate isto em $EXTENSION_SITE$.", 67 | "placeholders": { 68 | "EXTENSION_SITE": { 69 | "content": "$1" 70 | } 71 | } 72 | }, 73 | "error_incompatibleWebsite": { 74 | "message": "Não foram encontrados parâmetros de mapa ou do OpenStreetMap.\nSe isto parece ser um bug, por favor relate em $EXTENSION_SITE$.", 75 | "placeholders": { 76 | "EXTENSION_SITE": { 77 | "content": "$1" 78 | } 79 | } 80 | }, 81 | "error_noInformationExtracted": { 82 | "message": "O site foi reconhecido, mas seus parâmetros não foram encontrados.\nSe isto é um mapa, tente mexer nele um pouco para inicializar seus parâmetros.\nSe isto parece ser um bug, por favor relate em $EXTENSION_SITE$.", 83 | "placeholders": { 84 | "EXTENSION_SITE": { 85 | "content": "$1" 86 | } 87 | } 88 | }, 89 | "noEnabledCompatibleLinksFound": { 90 | "message": "Nenhum link ativado é compatível com os parâmetros encontrados nesta página." 91 | }, 92 | "button_showEnabledLinks": { 93 | "message": "Exibir links ativados usando parâmetros adicionais" 94 | }, 95 | "button_showOtherEnabledLinks": { 96 | "message": "Exibir o resto dos links ativados usando parâmetros adicionais" 97 | }, 98 | "site_historicmap": { 99 | "message": "Objetos históricos" 100 | }, 101 | "site_osmwiki": { 102 | "message": "Wiki do OpenStreetMap" 103 | }, 104 | "site_howdidyoucontribute": { 105 | "message": "Como você contribuiu no OSM?" 106 | }, 107 | "site_osmchangeviz": { 108 | "message": "Comparação de alterações (Neis)" 109 | }, 110 | "site_osmchangetiles": { 111 | "message": "Últimas Edições OSM (Neis)" 112 | }, 113 | "site_stravaglobal": { 114 | "message": "Strava Mapa de Calor Global" 115 | }, 116 | "site_osmlanevisualizer": { 117 | "message": "OSM Lane Visualizer" 118 | }, 119 | "site_missingmaps": { 120 | "message": "Missing Maps" 121 | }, 122 | "site_indoorequal": { 123 | "message": "indoor=" 124 | }, 125 | "site_openlevelup": { 126 | "message": "OpenLevelUp!" 127 | }, 128 | "site_osmbuildings": { 129 | "message": "OSM Buildings" 130 | }, 131 | "site_osmcha": { 132 | "message": "OSMCha" 133 | }, 134 | "site_openstreetbrowser": { 135 | "message": "OpenStreetBrowser" 136 | }, 137 | "site_waze": { 138 | "message": "Waze" 139 | }, 140 | "site_keepright": { 141 | "message": "keep right!" 142 | }, 143 | "site_mapcompare": { 144 | "message": "Map Compare" 145 | }, 146 | "site_osminspector": { 147 | "message": "OSM Inspector" 148 | }, 149 | "site_osmose": { 150 | "message": "Osmose" 151 | }, 152 | "site_osmroutemanager": { 153 | "message": "OSM Route Manager" 154 | }, 155 | "site_osmrelationanalyzer": { 156 | "message": "OSM Relation Analyzer" 157 | }, 158 | "site_overpassapi": { 159 | "message": "Augmented OSM Change Viewer" 160 | }, 161 | "site_whodidit": { 162 | "message": "WHODIDIT (Zverik)" 163 | }, 164 | "site_pewuosmhistory": { 165 | "message": "OSM History Viewer (PeWu)" 166 | }, 167 | "site_osmhistoryviewer": { 168 | "message": "OSM History Viewer" 169 | }, 170 | "site_deepdiff": { 171 | "message": "Deep Diff" 172 | }, 173 | "site_osmdeephistory": { 174 | "message": "OSM Deep History" 175 | }, 176 | "site_umap": { 177 | "message": "uMap" 178 | }, 179 | "site_ideditor": { 180 | "message": "iD Editor" 181 | }, 182 | "site_level0": { 183 | "message": "Level0 Editor" 184 | }, 185 | "site_mapillary": { 186 | "message": "Mapillary" 187 | }, 188 | "site_openstreetcam": { 189 | "message": "KartaView" 190 | }, 191 | "site_hotmap": { 192 | "message": "Humanitarian OSM Team Map" 193 | }, 194 | "site_f4map": { 195 | "message": "F4 Map (3D demo)" 196 | }, 197 | "site_stamen": { 198 | "message": "Stamen Maps" 199 | }, 200 | "site_opnvkarte": { 201 | "message": "ÖPNVKarte" 202 | }, 203 | "site_openptmap": { 204 | "message": "Open Public Transport Map" 205 | }, 206 | "site_openinframap": { 207 | "message": "Open Infrastructure Map " 208 | }, 209 | "site_opentopomap": { 210 | "message": "OpenTopoMap" 211 | }, 212 | "site_opensnowmap": { 213 | "message": "OpenSnowMap" 214 | }, 215 | "site_openseamap": { 216 | "message": "OpenSeaMap" 217 | }, 218 | "site_opencyclemap": { 219 | "message": "OpenCycleMap" 220 | }, 221 | "site_openmapsurfer": { 222 | "message": "OpenMapSurfer" 223 | }, 224 | "site_sentinelhub": { 225 | "message": "Sentinel Hub" 226 | }, 227 | "site_googlemaps": { 228 | "message": "Google Maps" 229 | }, 230 | "site_bingmaps": { 231 | "message": "Bing Maps" 232 | }, 233 | "site_openstreetmap": { 234 | "message": "OpenStreetMap" 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /addon/_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_f4map": { 3 | "message": "F4 Map (3D демо)" 4 | }, 5 | "site_historicmap": { 6 | "message": "Исторические объекты" 7 | }, 8 | "newOptionDetected_notice": { 9 | "message": "Хотите добавить этот сайт в меню?", 10 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 11 | }, 12 | "site_osmcha": { 13 | "message": "OSMCha" 14 | }, 15 | "site_openstreetbrowser": { 16 | "message": "OpenStreetBrowser" 17 | }, 18 | "site_waze": { 19 | "message": "Waze" 20 | }, 21 | "site_osmwiki": { 22 | "message": "OpenStreetMap вики" 23 | }, 24 | "site_umap": { 25 | "message": "uMap" 26 | }, 27 | "site_level0": { 28 | "message": "Редактор Level0" 29 | }, 30 | "site_mapillary": { 31 | "message": "Mapillary" 32 | }, 33 | "site_openstreetcam": { 34 | "message": "KartaView" 35 | }, 36 | "site_stamen": { 37 | "message": "Stamen Maps" 38 | }, 39 | "site_opnvkarte": { 40 | "message": "ÖPNVKarte" 41 | }, 42 | "site_opentopomap": { 43 | "message": "OpenTopoMap" 44 | }, 45 | "site_opensnowmap": { 46 | "message": "OpenSnowMap" 47 | }, 48 | "site_openseamap": { 49 | "message": "OpenSeaMap" 50 | }, 51 | "site_opencyclemap": { 52 | "message": "OpenCycleMap" 53 | }, 54 | "site_openmapsurfer": { 55 | "message": "OpenMapSurfer" 56 | }, 57 | "site_sentinelhub": { 58 | "message": "Sentinel Hub" 59 | }, 60 | "site_googlemaps": { 61 | "message": "Карты Google" 62 | }, 63 | "site_bingmaps": { 64 | "message": "Bing Maps" 65 | }, 66 | "site_openstreetmap": { 67 | "message": "OpenStreetMap" 68 | }, 69 | "config_pattern_urlTemplate": { 70 | "message": "Шаблон URL-адреса", 71 | "description": "Input label" 72 | }, 73 | "config_pattern_name": { 74 | "message": "Название", 75 | "description": "Input label" 76 | }, 77 | "config_deleteButton": { 78 | "message": "Удалить", 79 | "description": "Text inside 'delete button' in configuration page" 80 | }, 81 | "config_saveButton": { 82 | "message": "Сохранить", 83 | "description": "Text inside 'save button' in configuration page" 84 | }, 85 | "config_editButton": { 86 | "message": "Редактировать", 87 | "description": "Text inside 'edit button' in configuration page" 88 | }, 89 | "configurationLink": { 90 | "message": "Конфигурация", 91 | "description": "Link to configuration page inside main popup." 92 | }, 93 | "newOptionDetected_added": { 94 | "message": "Добавлено как $PAGE_TITLE$", 95 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 96 | "placeholders": { 97 | "PAGE_TITLE": { 98 | "content": "$1", 99 | "example": "OpenStreetMap" 100 | } 101 | } 102 | }, 103 | "newOptionDetected_buttonText": { 104 | "message": "Добавить ссылку", 105 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 106 | }, 107 | "extensionShortName": { 108 | "message": "OSMenu" 109 | }, 110 | "extensionName": { 111 | "message": "OSM Smart Menu" 112 | }, 113 | "site_openlevelup": { 114 | "message": "OpenLevelUp!" 115 | }, 116 | "site_osminspector": { 117 | "message": "OSM Inspector" 118 | }, 119 | "site_osmose": { 120 | "message": "Osmose" 121 | }, 122 | "site_ideditor": { 123 | "message": "Редактор iD" 124 | }, 125 | "site_hotmap": { 126 | "message": "Humanitarian OSM Team Map" 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /addon/_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_osmlanevisualizer": { 3 | "message": "OSM Lane Visualizer" 4 | }, 5 | "site_openlevelup": { 6 | "message": "OpenLevelUp!" 7 | }, 8 | "site_osmcha": { 9 | "message": "OSMCha" 10 | }, 11 | "site_openstreetbrowser": { 12 | "message": "OpenStreetBrowser" 13 | }, 14 | "site_waze": { 15 | "message": "Waze" 16 | }, 17 | "site_osmchangetiles": { 18 | "message": "Останні зміни OSM (Neis)" 19 | }, 20 | "site_howdidyoucontribute": { 21 | "message": "Який ви зробили внесок в OSM?" 22 | }, 23 | "site_keepright": { 24 | "message": "keep right!" 25 | }, 26 | "site_mapcompare": { 27 | "message": "Map Compare" 28 | }, 29 | "site_osminspector": { 30 | "message": "OSM Inspector" 31 | }, 32 | "site_osmose": { 33 | "message": "Osmose" 34 | }, 35 | "site_osmroutemanager": { 36 | "message": "OSM Route Manager" 37 | }, 38 | "site_osmrelationanalyzer": { 39 | "message": "OSM Relation Analyzer" 40 | }, 41 | "site_overpassapi": { 42 | "message": "Augmented OSM Change Viewer" 43 | }, 44 | "site_whodidit": { 45 | "message": "WHODIDIT (Zverik)" 46 | }, 47 | "extensionStoreDescription": { 48 | "message": "Коли ви натискаєте кнопку меню, це розширення виявляє параметри OpenStreetMap всередині поточної сторінки й утворює список відповідних мап та інструментів з цими параметрами.\n\nДо розпізнаваних параметрів належать:\n\nЯкщо ви не впевнені, який вебсайт буде розпізнаний, спробуйте перейти на https://www.openstreetmap.org/, перш ніж натиснути на меню.\n\nЛоготип OpenStreetMap є товарним знаком Фонду OpenStreetMap і використовується з їх дозволу. Цей проєкт не схвалений та не пов'язаний з OpenStreetMap Foundation.", 49 | "description": "Description shown in Chrome and Firefox's extension download page" 50 | }, 51 | "extensionDescription": { 52 | "message": "Допомагає учасникам OpenStreetMap легко перемикатися між різними мапами та інструментами аналізу від спільноти." 53 | }, 54 | "extensionShortName": { 55 | "message": "OSMenu" 56 | }, 57 | "site_stravaglobal": { 58 | "message": "Strava Global Heatmap" 59 | }, 60 | "site_missingmaps": { 61 | "message": "Missing Maps" 62 | }, 63 | "site_indoorequal": { 64 | "message": "indoor=" 65 | }, 66 | "site_osmbuildings": { 67 | "message": "OSM Buildings" 68 | }, 69 | "site_osmchangeviz": { 70 | "message": "Changeset by Comparison (Neis)" 71 | }, 72 | "site_osmwiki": { 73 | "message": "Вікі OpenStreetMap" 74 | }, 75 | "site_pewuosmhistory": { 76 | "message": "OSM History Viewer (PeWu)" 77 | }, 78 | "site_osmhistoryviewer": { 79 | "message": "OSM History Viewer" 80 | }, 81 | "site_deepdiff": { 82 | "message": "Deep Diff" 83 | }, 84 | "site_osmdeephistory": { 85 | "message": "OSM Deep History" 86 | }, 87 | "site_umap": { 88 | "message": "uMap" 89 | }, 90 | "site_ideditor": { 91 | "message": "Редактор iD" 92 | }, 93 | "site_level0": { 94 | "message": "Редактор Level0" 95 | }, 96 | "site_mapillary": { 97 | "message": "Mapillary" 98 | }, 99 | "site_openstreetcam": { 100 | "message": "KartaView" 101 | }, 102 | "site_hotmap": { 103 | "message": "Humanitarian OSM Team Map" 104 | }, 105 | "site_f4map": { 106 | "message": "F4 Map (3D демо)" 107 | }, 108 | "site_stamen": { 109 | "message": "Stamen Maps" 110 | }, 111 | "site_opnvkarte": { 112 | "message": "ÖPNVKarte" 113 | }, 114 | "site_openptmap": { 115 | "message": "Відкрити мапу громадського транспорту" 116 | }, 117 | "site_openinframap": { 118 | "message": "Відкрити мапу інфраструктури " 119 | }, 120 | "site_historicmap": { 121 | "message": "Історичні об'єкти" 122 | }, 123 | "site_opentopomap": { 124 | "message": "OpenTopoMap" 125 | }, 126 | "site_opensnowmap": { 127 | "message": "OpenSnowMap" 128 | }, 129 | "site_openseamap": { 130 | "message": "OpenSeaMap" 131 | }, 132 | "site_opencyclemap": { 133 | "message": "OpenCycleMap" 134 | }, 135 | "site_openmapsurfer": { 136 | "message": "OpenMapSurfer" 137 | }, 138 | "site_sentinelhub": { 139 | "message": "Sentinel Hub" 140 | }, 141 | "site_googlemaps": { 142 | "message": "Карти Google" 143 | }, 144 | "site_bingmaps": { 145 | "message": "Bing Maps" 146 | }, 147 | "site_openstreetmap": { 148 | "message": "OpenStreetMap" 149 | }, 150 | "button_showOtherEnabledLinks": { 151 | "message": "Показати решту увімкнених посилань за допомогою додаткових параметрів", 152 | "description": "Shown in popup below the list of links; to see other enabled links with additional parameters." 153 | }, 154 | "button_showEnabledLinks": { 155 | "message": "Показувати увімкнені посилання за допомогою додаткових параметрів", 156 | "description": "Shown in popup below some errors to allow users to see enabled links." 157 | }, 158 | "noEnabledCompatibleLinksFound": { 159 | "message": "Жодне з увімкнених посилань не сумісне з параметрами, знайденими на цій сторінці.", 160 | "description": "Shown in popup when there aren't any enabled links that support the parameters found in the current page." 161 | }, 162 | "error_noInformationExtracted": { 163 | "message": "Вебсайт розпізнано, але параметри не знайдено.\nЯкщо це мапа, спробуйте трохи перемістити її, щоб ініціалізувати її параметри.\nЯкщо це виглядає як помилка, будь ласка, повідомте про це в $EXTENSION_SITE$.", 164 | "description": "Error shown inside popup when appropriate", 165 | "placeholders": { 166 | "EXTENSION_SITE": { 167 | "content": "$1", 168 | "example": "github.com/jgpacker/osm-smart-menu/" 169 | } 170 | } 171 | }, 172 | "error_incompatibleWebsite": { 173 | "message": "Не знайдено жодної мапи або параметрів OpenStreetMap.\nЯкщо це неочікувано, ви можете повідомити про це в $EXTENSION_SITE$.", 174 | "description": "Error shown inside popup when appropriate", 175 | "placeholders": { 176 | "EXTENSION_SITE": { 177 | "content": "$1", 178 | "example": "github.com/jgpacker/osm-smart-menu/" 179 | } 180 | } 181 | }, 182 | "error_noAccess": { 183 | "message": "ПОМИЛКА: Не вдалося знайти поточну сторінку або отримати до неї доступ.\nЯкщо ця сторінка все ще завантажується, повторіть спробу.\nЯкщо це неочікувано, будь ласка, повідомте про це в $EXTENSION_SITE$.", 184 | "description": "Error shown inside popup when the current page cannot be accessed (technical exception)", 185 | "placeholders": { 186 | "EXTENSION_SITE": { 187 | "content": "$1", 188 | "example": "github.com/jgpacker/osm-smart-menu/" 189 | } 190 | } 191 | }, 192 | "config_pattern_createOption": { 193 | "message": "Створити опцію", 194 | "description": "Button" 195 | }, 196 | "config_pattern_urlTemplate": { 197 | "message": "Шаблон URL-адреси", 198 | "description": "Input label" 199 | }, 200 | "config_pattern_name": { 201 | "message": "Назва", 202 | "description": "Input label" 203 | }, 204 | "config_pattern_formTitle": { 205 | "message": "Розширене створення опцій (експериментально)", 206 | "description": "Form Legend/Title" 207 | }, 208 | "config_linkDeleted": { 209 | "message": "Посилання $LINK_NAME$ видалено. Клацніть тут, щоб скасувати дію.", 210 | "description": "Shown inline when a link is deleted inside configuration page", 211 | "placeholders": { 212 | "LINK_NAME": { 213 | "content": "$1", 214 | "example": "OpenStreetMap" 215 | } 216 | } 217 | }, 218 | "config_deleteButton": { 219 | "message": "Видалити", 220 | "description": "Text inside 'delete button' in configuration page" 221 | }, 222 | "config_saveButton": { 223 | "message": "Зберегти", 224 | "description": "Text inside 'save button' in configuration page" 225 | }, 226 | "config_editButton": { 227 | "message": "Редагувати", 228 | "description": "Text inside 'edit button' in configuration page" 229 | }, 230 | "configurationLink": { 231 | "message": "Конфігурація", 232 | "description": "Link to configuration page inside main popup." 233 | }, 234 | "newOptionDetected_added": { 235 | "message": "Додано як $PAGE_TITLE$", 236 | "description": "Shown in popup when an unknown website seems compatible with OpenStreetMap. After the button is clicked.", 237 | "placeholders": { 238 | "PAGE_TITLE": { 239 | "content": "$1", 240 | "example": "OpenStreetMap" 241 | } 242 | } 243 | }, 244 | "newOptionDetected_buttonText": { 245 | "message": "Додати посилання", 246 | "description": "Shown in popup (inside the button) when an unknown website seems compatible with OpenStreetMap" 247 | }, 248 | "newOptionDetected_notice": { 249 | "message": "Додати цей вебсайт до меню?", 250 | "description": "Shown in popup (before the button) when an unknown website seems compatible with OpenStreetMap" 251 | }, 252 | "loading": { 253 | "message": "Завантаження...\nЯкщо завантаження не припиняється, перезавантажте сторінку та повторіть спробу.", 254 | "description": "Shown in popup shortly before menu appears." 255 | }, 256 | "extensionName": { 257 | "message": "OSM Smart Menu" 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /addon/_locales/zh_TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "OSM 智慧選單" 4 | }, 5 | "extensionShortName": { 6 | "message": "OSMenu" 7 | }, 8 | "extensionDescription": { 9 | "message": "幫助開放街圖貢獻者簡單切換社群當中不同的地圖與分析工具。" 10 | }, 11 | "extensionStoreDescription": { 12 | "message": "當你點選單按鈕時,這個套件會偵測目前頁面的開放街圖參數,並且會產生相關地圖與對應參數清單。\n\n認可的參數包括:\n\n如果你不確定那些網站能被認可,在點選單之前請瀏覽 https://www.openstreetmap.org/ 網站。\n\n開放街圖的 logo 是開放街圖基金會的註冊商標,已經經由他們同意來使用。這個專案並沒有被開放街圖基金會認可或是有相關聯。\n" 13 | }, 14 | "loading": { 15 | "message": "載入中...\n如果沒有停止載入,請試著重新整理再試一次。", 16 | "description": "在選單出現前很快顯示跳出視窗" 17 | }, 18 | "newOptionDetected_notice": { 19 | "message": "你想要新增這個網站到選單當中嗎?", 20 | "description": "當未知網站與開放街圖相容時跳出視窗。在按鈕之前" 21 | }, 22 | "newOptionDetected_buttonText": { 23 | "message": "新增選項", 24 | "description": "當未知網站與開放街圖相容時跳出視窗。在按鈕之中" 25 | }, 26 | "newOptionDetected_added": { 27 | "message": "新增 $PAGE_TITLE$", 28 | "description": "當未知網站與開放街圖相容時跳出視窗。在按鈕按下之後", 29 | "placeholders": { 30 | "PAGE_TITLE": { 31 | "content": "$1", 32 | "example": "OpenStreetMap" 33 | } 34 | } 35 | }, 36 | "configurationLink": { 37 | "message": "設定", 38 | "description": "在主跳出視窗的設定頁面連結。" 39 | }, 40 | "config_editButton": { 41 | "message": "編輯", 42 | "description": "在設定頁面'編輯按鈕'中的文字" 43 | }, 44 | "config_saveButton": { 45 | "message": "儲存", 46 | "description": "在設定頁面'儲存按鈕'中的文字" 47 | }, 48 | "config_deleteButton": { 49 | "message": "刪除", 50 | "description": "在設定頁面'刪除按鈕'中的文字" 51 | }, 52 | "config_linkDeleted": { 53 | "message": "連結 $LINK_NAME$ 已經刪除。點這邊來復原。", 54 | "description": "當線內的連結在設定頁面已經刪除", 55 | "placeholders": { 56 | "LINK_NAME": { 57 | "content": "$1", 58 | "example": "OpenStreetMap" 59 | } 60 | } 61 | }, 62 | "config_pattern_formTitle": { 63 | "message": "創建進階選項 (實驗性)", 64 | "description": "從圖徵/標籤" 65 | }, 66 | "config_pattern_name": { 67 | "message": "名稱", 68 | "description": "輸入標籤" 69 | }, 70 | "config_pattern_urlTemplate": { 71 | "message": "網址模版", 72 | "description": "輸入標籤" 73 | }, 74 | "config_pattern_createOption": { 75 | "message": "創建選項", 76 | "description": "按鈕" 77 | }, 78 | "error_noAccess": { 79 | "message": "錯誤,無法找到或連線到目前頁面。\n如果這是非預期狀況,請到 $EXTENSION_SITE$ 回報。", 80 | "description": "目前頁面無法連線時,錯誤訊息顯示在跳出視窗當中 (技術問題)", 81 | "placeholders": { 82 | "EXTENSION_SITE": { 83 | "content": "$1", 84 | "example": "github.com/jgpacker/osm-smart-menu/" 85 | } 86 | } 87 | }, 88 | "error_incompatibleWebsite": { 89 | "message": "錯誤:目前的網站與開放街圖並不相容。\n如果這並不是預期的狀況,請到 $EXTENSION_SITE$ 回報狀況。", 90 | "description": "如果適當錯誤訊息顯示在跳出視窗當中", 91 | "placeholders": { 92 | "EXTENSION_SITE": { 93 | "content": "$1", 94 | "example": "github.com/jgpacker/osm-smart-menu/" 95 | } 96 | } 97 | }, 98 | "error_noInformationExtracted": { 99 | "message": "錯誤:如果網站被認可,但是無法適別參數時。\n如果你認為這是臭蟲的話,請到 $EXTENSION_SITE$ 回報狀況。", 100 | "description": "如果適當錯誤訊息顯示在跳出視窗當中", 101 | "placeholders": { 102 | "EXTENSION_SITE": { 103 | "content": "$1", 104 | "example": "github.com/jgpacker/osm-smart-menu/" 105 | } 106 | } 107 | }, 108 | "noEnabledCompatibleLinksFound": { 109 | "message": "目前有的連結都沒有與目前頁面的參數相容。", 110 | "description": "當目前頁面中的參數都沒有支援的連結時,顯示在跳出視窗當中。" 111 | }, 112 | "button_showEnabledLinks": { 113 | "message": "使用額外參數來顯示可用連結", 114 | "description": "允許使用者看到可用連結,顯示在錯誤下面跳出視窗當中。" 115 | }, 116 | "button_showOtherEnabledLinks": { 117 | "message": "使用額外參數顯示其餘可用的連結", 118 | "description": "看到其他額外參數的可用連結,顯示在連結清單下的跳出視窗當中。" 119 | }, 120 | "site_openstreetmap": { 121 | "message": "開放街圖" 122 | }, 123 | "site_bingmaps": { 124 | "message": "Bing 地圖" 125 | }, 126 | "site_googlemaps": { 127 | "message": "Google 地圖" 128 | }, 129 | "site_sentinelhub": { 130 | "message": "Sentinel Hub" 131 | }, 132 | "site_openmapsurfer": { 133 | "message": "OpenMapSurfer" 134 | }, 135 | "site_opencyclemap": { 136 | "message": "開放單車地圖" 137 | }, 138 | "site_openseamap": { 139 | "message": "開放海洋地圖" 140 | }, 141 | "site_opensnowmap": { 142 | "message": "開放雪地地圖" 143 | }, 144 | "site_opentopomap": { 145 | "message": "OpenTopoMap" 146 | }, 147 | "site_historicmap": { 148 | "message": "歷史物件" 149 | }, 150 | "site_openinframap": { 151 | "message": "開放設施地圖" 152 | }, 153 | "site_openptmap": { 154 | "message": "開放大眾運輸地圖" 155 | }, 156 | "site_opnvkarte": { 157 | "message": "ÖPNVKarte" 158 | }, 159 | "site_stamen": { 160 | "message": "Stamen 地圖" 161 | }, 162 | "site_f4map": { 163 | "message": "F4 Map (3D 展示)" 164 | }, 165 | "site_hotmap": { 166 | "message": "人道救援開放街圖小組" 167 | }, 168 | "site_openstreetcam": { 169 | "message": "Karta景" 170 | }, 171 | "site_mapillary": { 172 | "message": "Mapillary" 173 | }, 174 | "site_level0": { 175 | "message": "Level0 編輯器" 176 | }, 177 | "site_umap": { 178 | "message": "uMap" 179 | }, 180 | "site_osmdeephistory": { 181 | "message": "OSM 深度歷史" 182 | }, 183 | "site_deepdiff": { 184 | "message": "Deep Diff" 185 | }, 186 | "site_osmhistoryviewer": { 187 | "message": "OSM 歷史檢視器" 188 | }, 189 | "site_whodidit": { 190 | "message": "WHODIDIT (Zverik)" 191 | }, 192 | "site_overpassapi": { 193 | "message": "Augmented OSM 變動檢視器" 194 | }, 195 | "site_osmrelationanalyzer": { 196 | "message": "OSM 關聯分析器" 197 | }, 198 | "site_osmroutemanager": { 199 | "message": "OSM 導航管理器" 200 | }, 201 | "site_osmose": { 202 | "message": "Osmose" 203 | }, 204 | "site_osminspector": { 205 | "message": "OSM 偵側器" 206 | }, 207 | "site_mapcompare": { 208 | "message": "地圖比較" 209 | }, 210 | "site_keepright": { 211 | "message": "keep right!" 212 | }, 213 | "site_howdidyoucontribute": { 214 | "message": "你如何貢獻 OSM?" 215 | }, 216 | "site_osmwiki": { 217 | "message": "開放街圖 Wiki" 218 | }, 219 | "site_osmchangeviz": { 220 | "message": "變更集的比較 (Neis)" 221 | }, 222 | "site_osmchangetiles": { 223 | "message": "最新 OSM 編輯 (Neis)" 224 | }, 225 | "site_waze": { 226 | "message": "Waze" 227 | }, 228 | "site_openstreetbrowser": { 229 | "message": "開放街道瀏覽器" 230 | }, 231 | "site_osmcha": { 232 | "message": "OSMCha" 233 | }, 234 | "site_osmbuildings": { 235 | "message": "OSM 建築" 236 | }, 237 | "site_openlevelup": { 238 | "message": "OpenLevelUp!" 239 | }, 240 | "site_indoorequal": { 241 | "message": "indoor=" 242 | }, 243 | "site_missingmaps": { 244 | "message": "遺失地圖" 245 | }, 246 | "site_osmlanevisualizer": { 247 | "message": "OSM 車道視覺化" 248 | }, 249 | "site_stravaglobal": { 250 | "message": "Strava 全球熱力圖" 251 | }, 252 | "site_pewuosmhistory": { 253 | "message": "OSM 歷史檢視器 (PeWu)" 254 | }, 255 | "site_ideditor": { 256 | "message": "iD 編輯器" 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /addon/icons/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The images in this folder are licensed under the Creative Commons Attribution-Share Alike 2.0 Generic license. 2 | Source: https://commons.wikimedia.org/wiki/File:Openstreetmap_logo.svg 3 | -------------------------------------------------------------------------------- /addon/icons/README.md: -------------------------------------------------------------------------------- 1 | To easily generate a PNG file from a SVG, install Inkscape and run (changing the resolution as needed): 2 | 3 | export RESOLUTION=96; inkscape -z -w "$RESOLUTION" -h "$RESOLUTION" osm.svg -e "osm${RESOLUTION}x${RESOLUTION}.png" -------------------------------------------------------------------------------- /addon/icons/drag_indicator-black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addon/icons/osm128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgpacker/osm-smart-menu/5f5a9ee493ff472cf2dbb7683ab58457273e5a07/addon/icons/osm128x128.png -------------------------------------------------------------------------------- /addon/icons/osm16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgpacker/osm-smart-menu/5f5a9ee493ff472cf2dbb7683ab58457273e5a07/addon/icons/osm16x16.png -------------------------------------------------------------------------------- /addon/icons/osm48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgpacker/osm-smart-menu/5f5a9ee493ff472cf2dbb7683ab58457273e5a07/addon/icons/osm48x48.png -------------------------------------------------------------------------------- /addon/icons/osm96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jgpacker/osm-smart-menu/5f5a9ee493ff472cf2dbb7683ab58457273e5a07/addon/icons/osm96x96.png -------------------------------------------------------------------------------- /addon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_extensionName__", 3 | "short_name": "__MSG_extensionShortName__", 4 | "description": "__MSG_extensionDescription__", 5 | "default_locale": "en", 6 | 7 | "minimum_chrome_version": "45", 8 | 9 | "author": "João Guilherme Packer", 10 | "homepage_url": "https://github.com/jgpacker/osm-smart-menu", 11 | "version": "0.17.2", 12 | 13 | "browser_specific_settings": { 14 | "gecko": { 15 | "id": "osm-smart-menu@jgpacker.me.br" 16 | } 17 | }, 18 | 19 | "permissions": [ 20 | "storage", 21 | "activeTab" 22 | ], 23 | 24 | "icons": { 25 | "48": "icons/osm48x48.png", 26 | "96": "icons/osm96x96.png", 27 | "128": "icons/osm128x128.png" 28 | }, 29 | 30 | "background": { 31 | "scripts": ["background-listeners-setup.js"] 32 | }, 33 | 34 | "browser_action": { 35 | "browser_style": true, 36 | "default_icon": { 37 | "16": "icons/osm16x16.png", 38 | "48": "icons/osm48x48.png", 39 | "96": "icons/osm96x96.png" 40 | }, 41 | "default_title": "__MSG_extensionName__", 42 | "default_popup": "popup/index.html" 43 | }, 44 | 45 | "options_ui": { 46 | "page": "options/index.html" 47 | }, 48 | 49 | "commands": { "_execute_browser_action": {} }, 50 | 51 | "manifest_version": 2 52 | } 53 | -------------------------------------------------------------------------------- /addon/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /addon/options/style.css: -------------------------------------------------------------------------------- 1 | 2 | /* drag-and-drop style https://github.com/bevacqua/dragula/blob/b0e5c4fc1017e1e33e2013ec8e47d43a46164c86/dist/dragula.css */ 3 | .gu-mirror { 4 | position: fixed !important; 5 | margin: 0 !important; 6 | z-index: 9999 !important; 7 | opacity: 0.8; 8 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; 9 | filter: alpha(opacity=80); 10 | } 11 | .gu-hide { 12 | display: none !important; 13 | } 14 | .gu-unselectable { 15 | -webkit-user-select: none !important; 16 | -moz-user-select: none !important; 17 | -ms-user-select: none !important; 18 | user-select: none !important; 19 | } 20 | .gu-transit { 21 | opacity: 0.2; 22 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"; 23 | filter: alpha(opacity=20); 24 | } 25 | -------------------------------------------------------------------------------- /addon/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /addon/popup/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | font-size: 15px; 5 | overflow-x: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osm-smart-menu", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "test": "jest", 6 | "tscompile": "svelte-check && webpack --mode=production", 7 | "watch": "npm run tscompile -- --watch", 8 | "build": "npm run tscompile && web-ext build --source-dir addon" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/jgpacker/osm-smart-menu.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jgpacker/osm-smart-menu/issues" 16 | }, 17 | "dependencies": { 18 | "@types/dragula": "^3.7.0", 19 | "dragula": "^3.7.2", 20 | "@types/lodash.escaperegexp": "^4.1.6", 21 | "lodash.escaperegexp": "^4.1.2", 22 | "webextension-polyfill-ts": "^0.22.0" 23 | }, 24 | "devDependencies": { 25 | "@types/jest": "27", 26 | "jest": "27", 27 | "svelte": "^3.37.0", 28 | "svelte-check": "^1.3.0", 29 | "svelte-loader": "^2.13.6", 30 | "svelte-preprocess": "^4.6.1", 31 | "ts-jest": "27", 32 | "ts-loader": "^8.0.3", 33 | "typescript": "^4.1.2", 34 | "web-ext": "^6.5.0", 35 | "webpack": "^5.30.0", 36 | "webpack-cli": "^4.6.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/background-listeners-setup.ts: -------------------------------------------------------------------------------- 1 | import { idempotentMigrations } from './storage/migrations'; 2 | import { browser } from 'webextension-polyfill-ts'; 3 | 4 | browser.runtime.onInstalled.addListener(idempotentMigrations); 5 | -------------------------------------------------------------------------------- /src/injectable-content-script.ts: -------------------------------------------------------------------------------- 1 | import { browser } from 'webextension-polyfill-ts'; 2 | import { Sites, OsmAttribute } from './sites-configuration'; 3 | 4 | browser.runtime.onMessage.addListener(async (message: ContentScriptInputMessage): Promise => 5 | message.candidateSiteIds.length !== 0 ? 6 | message.candidateSiteIds.map(extractData) : 7 | [ lookForPermalink() ] // helps to get parameters from unknown websites 8 | ); 9 | 10 | export type ContentScriptOutputMessage = ExtractedData[] 11 | 12 | export type ContentScriptInputMessage = { 13 | candidateSiteIds: string[]; 14 | } 15 | 16 | type ExtractedData = { 17 | siteId?: string; 18 | permalink?: string; 19 | additionalAttributes?: Partial>; 20 | } 21 | 22 | function extractData(siteId: string): ExtractedData { 23 | const site = Sites[siteId]; 24 | if (!site) { // custom user sites are not in the `Sites` object 25 | return { 26 | siteId, 27 | ...lookForPermalink(), 28 | } 29 | } 30 | const extr = site.extractors; 31 | if (extr) { 32 | const permalink = extr.getPermalink && extr.getPermalink(document); 33 | const additionalAttributes = extr.getAttributesFromPage && extr.getAttributesFromPage(window); 34 | return { siteId, permalink, additionalAttributes }; 35 | } else { 36 | return { siteId }; 37 | } 38 | } 39 | 40 | function lookForPermalink(): ExtractedData { 41 | let permalink: Element | null | undefined = 42 | document.querySelector('[id*=permalink i]') || 43 | document.querySelector('[class*=permalink i]'); 44 | if (permalink && !(permalink instanceof HTMLAnchorElement)) { 45 | permalink = permalink.querySelector('a'); 46 | } 47 | if (!permalink) { 48 | permalink = [...document.querySelectorAll('a')] 49 | .find(a => /permalink/i.test(a.textContent || '')); 50 | } 51 | 52 | let url: string; 53 | if (permalink && permalink instanceof HTMLAnchorElement) { 54 | url = permalink.href; 55 | } else { 56 | url = window.document.location.href; 57 | } 58 | 59 | return { 60 | permalink: url, 61 | }; 62 | } -------------------------------------------------------------------------------- /src/options/App.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {#each sitesConfig as siteConfig (siteConfig.id)} 13 | 14 | {/each} 15 |
16 | 17 | -------------------------------------------------------------------------------- /src/options/components/ConfigurableLine.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 | 93 | 94 | {#if siteConfig.customPattern || siteConfig.defaultConfiguration} 95 | {#if !deleted} 96 | 122 | {:else} 123 |
124 | {browser.i18n.getMessage('config_linkDeleted', getSiteTitle(siteConfig))} 125 |
126 | {/if} 127 | {/if} 128 | -------------------------------------------------------------------------------- /src/options/components/UrlTemplateForm.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 | 34 | 35 |
36 |
37 | {browser.i18n.getMessage('config_pattern_formTitle')} 38 | 42 | 51 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /src/options/main.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getSitesConfiguration, 3 | SiteConfiguration, 4 | } from "../storage/config-handler"; 5 | import { setupDragAndDrop } from "./utils"; 6 | // @ts-expect-error 7 | import App from "./App.svelte"; 8 | 9 | getSitesConfiguration().then((sitesConfig: SiteConfiguration[]) => { 10 | new App({ 11 | target: document.body, 12 | props: { 13 | sitesConfig, 14 | }, 15 | }); 16 | 17 | setupDragAndDrop(document); 18 | }); 19 | -------------------------------------------------------------------------------- /src/options/utils.ts: -------------------------------------------------------------------------------- 1 | import { setOrderedSiteIds } from "../storage/config-handler"; 2 | import dragula from "dragula"; 3 | 4 | export const dragHandleClass = "drag-handle"; 5 | //TODO: Migrate to svelte-dnd-action after https://github.com/isaacHagoel/svelte-dnd-action/issues/86 6 | export function setupDragAndDrop(d: Document): HTMLElement { 7 | const div: HTMLDivElement = d.querySelector("div") as HTMLDivElement; 8 | 9 | const dragAndDropHandler = dragula([div], { 10 | moves: (_el, _container, handle) => 11 | Boolean(handle && handle.classList.contains(dragHandleClass)), 12 | }); 13 | dragAndDropHandler.on("drop", updateSiteIdsOrder); 14 | 15 | async function updateSiteIdsOrder() { 16 | const orderedSitesWithId = [ 17 | ...d.querySelectorAll("div input[type=checkbox]"), 18 | ].map((input: Element) => (input as HTMLInputElement).name); 19 | await setOrderedSiteIds(orderedSitesWithId); 20 | } 21 | 22 | return div; 23 | } 24 | -------------------------------------------------------------------------------- /src/popup/App.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 22 |
23 | {#await eventualSitesOrError} 24 | 25 |
{browser.i18n.getMessage('loading')}
26 |
27 | {:then sitesListOrError} 28 | {#if 'sitesList' in sitesListOrError} 29 | 31 | 32 | 37 | {:else} 38 | 39 | {#if sitesListOrError.error === KnownError.INCOMPATIBLE_WEBSITE || sitesListOrError.error === KnownError.NO_INFORMATION_EXTRACTED} 40 | 41 | {/if} 42 | {/if} 43 | {/await} 44 |
45 | -------------------------------------------------------------------------------- /src/popup/components/BasicLinkCreationDialog.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 25 | 26 | {#if customUserOption} 27 | 28 | {#if !optionCreated} 29 | {browser.i18n.getMessage('newOptionDetected_notice')} 30 | 33 | {:else}{browser.i18n.getMessage('newOptionDetected_added', customUserOption && customUserOption.defaultName)}{/if} 34 | 35 | {/if} 36 | -------------------------------------------------------------------------------- /src/popup/components/ConfigurationLink.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | browser.runtime.openOptionsPage()}> 20 | {browser.i18n.getMessage('configurationLink')} 21 | 22 | -------------------------------------------------------------------------------- /src/popup/components/ErrorMessage.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | {errorMessage.firstPart} 22 | openLink(errorMessage.linkHref)}> 23 | {errorMessage.linkText} 24 | 25 | {errorMessage.lastPart} 26 | 27 | -------------------------------------------------------------------------------- /src/popup/components/InfoBox.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
19 | 20 |
21 | -------------------------------------------------------------------------------- /src/popup/components/LinkList.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | 28 | {#each siteLinks as site} 29 | openLink(site.url)}> 30 | {site.customName || browser.i18n.getMessage(`site_${site.id}`) || '???'} 31 | 32 | {:else} 33 | {browser.i18n.getMessage('noEnabledCompatibleLinksFound')} 34 | {/each} 35 | -------------------------------------------------------------------------------- /src/popup/components/ShowEnabledLinksButton.svelte: -------------------------------------------------------------------------------- 1 | 48 | 49 | 74 | 75 | 87 | 88 | {#if restOfEnabledLinks.size > 0} 89 | {#if currentlyShownLinks.length > 0} 90 |
91 | {/if} 92 | {#if enabledLinks} 93 | 94 | {:else} 95 |
96 | 100 |
101 | {/if} 102 | {/if} 103 | -------------------------------------------------------------------------------- /src/popup/main.ts: -------------------------------------------------------------------------------- 1 | import { browser } from 'webextension-polyfill-ts' 2 | import { ContentScriptOutputMessage, ContentScriptInputMessage } from '../injectable-content-script'; 3 | import { KnownError, CustomUserOption } from './utils'; 4 | import { findSiteCandidates, pickWinningCandidate, getRelevantSites, SiteLink } from './sites-manipulation-helper'; 5 | import { getSitesConfiguration, SiteConfiguration } from '../storage/config-handler'; 6 | // @ts-expect-error 7 | import App from './App.svelte'; 8 | import { OsmAttribute } from "../sites-configuration"; 9 | 10 | export type EventualSitesOrError = Promise< 11 | | { 12 | config: SiteConfiguration[]; 13 | currentSiteId: string | undefined; 14 | sitesList: SiteLink[]; 15 | extractedParameters: Partial>; 16 | customUserOption?: CustomUserOption; 17 | } 18 | | { 19 | config: SiteConfiguration[]; 20 | error: KnownError; 21 | } 22 | >; 23 | 24 | const eventualSitesOrError: EventualSitesOrError = getSitesOrError(); 25 | new App({ 26 | target: document.body, 27 | props: { eventualSitesOrError }, 28 | }); 29 | 30 | async function getSitesOrError(): EventualSitesOrError { 31 | const config = await getSitesConfiguration(); 32 | const tabs = await browser.tabs.query({ active: true, currentWindow: true }); 33 | const currentTab = tabs[0]; 34 | if (!currentTab || !currentTab.url || !currentTab.id) { 35 | const error = KnownError.NO_ACCESS; 36 | return { config, error }; 37 | } 38 | 39 | const candidateSiteIds = findSiteCandidates(config, currentTab.url); 40 | const contentScriptResult = await getDataFromContentScript(currentTab.id, candidateSiteIds) ; 41 | const currentSite = contentScriptResult && pickWinningCandidate(config, contentScriptResult, currentTab.url); 42 | if (!currentSite) { 43 | const error = 44 | candidateSiteIds.length === 0 45 | ? KnownError.INCOMPATIBLE_WEBSITE 46 | : KnownError.NO_INFORMATION_EXTRACTED; 47 | return { config, error }; 48 | } 49 | 50 | let customUserOption: CustomUserOption | undefined; 51 | if (currentSite.detectedPattern) { 52 | customUserOption = { 53 | urlPattern: currentSite.detectedPattern, 54 | defaultName: currentTab.title || '???', 55 | }; 56 | } 57 | 58 | const sitesList = getRelevantSites(config, currentSite.siteId, currentSite.attributes); 59 | return { 60 | config, 61 | currentSiteId: currentSite.siteId, 62 | sitesList, 63 | customUserOption, 64 | extractedParameters: currentSite.attributes, 65 | }; 66 | } 67 | 68 | async function getDataFromContentScript(tabId: number, candidateSiteIds: string[]): Promise { 69 | try { 70 | await browser.tabs.executeScript(tabId, { file: "/injectable-content-script.js" }); 71 | 72 | const message: ContentScriptInputMessage = { candidateSiteIds }; 73 | return (await browser.tabs.sendMessage(tabId, message)) as ContentScriptOutputMessage; 74 | } catch (e) { 75 | logUnexpectedError(e); 76 | return; 77 | } 78 | } 79 | 80 | function logUnexpectedError(e: any): void { 81 | const errorPrefix = 'OSM WebExtension ERROR'; 82 | if(e instanceof Error) { 83 | console.error(errorPrefix, e.message, e.stack); 84 | } else { 85 | console.error(errorPrefix, JSON.stringify(e)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/popup/sites-manipulation-helper.test.ts: -------------------------------------------------------------------------------- 1 | import { findSiteCandidates, getRelevantSites, pickWinningCandidate } from "./sites-manipulation-helper"; 2 | import { SiteConfiguration } from "../storage/config-handler"; 3 | import { Sites, OsmAttribute } from "../sites-configuration"; 4 | 5 | const aDefaultSiteConfig: SiteConfiguration = { 6 | id: 'test1', 7 | isEnabled: true, 8 | defaultConfiguration: { 9 | link: 'example.com', 10 | paramOpts: [{ ordered: "/#map={zoom}/{lat}/{lon}" }], 11 | } 12 | }; 13 | 14 | describe(findSiteCandidates.name, () => { 15 | test('empty input gives empty output', () => { 16 | expect(findSiteCandidates([aDefaultSiteConfig], '')).toEqual([]); 17 | }); 18 | test('empty url finds nothing', () => { 19 | expect(findSiteCandidates([aDefaultSiteConfig], '')).toEqual([]); 20 | }); 21 | test('chrome://home url finds nothing', () => { 22 | expect(findSiteCandidates([aDefaultSiteConfig], 'chrome://home/')).toEqual([]); 23 | }); 24 | test('finds a site with same domain', () => { 25 | expect(findSiteCandidates([aDefaultSiteConfig], 'http://example.com/map?zoom=1&lat=2&lon=3')).toEqual([aDefaultSiteConfig.id]); 26 | }); 27 | test('finds a site with domainRegexp', () => { 28 | const siteConfigWithAdjustment: SiteConfiguration = { 29 | ...aDefaultSiteConfig, 30 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, domainRegexp: /example\.com\.[a-z]{2}$/ }, 31 | }; 32 | expect(findSiteCandidates([siteConfigWithAdjustment], 'http://example.com.br/map?zoom=1&lat=2&lon=3')).toEqual([aDefaultSiteConfig.id]); 33 | }); 34 | test('does not get a site that doesn\'t match domainRegexp', () => { 35 | const siteConfigWithAdjustment: SiteConfiguration = { 36 | ...aDefaultSiteConfig, 37 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, domainRegexp: /www\.example\.com$/ }, 38 | }; 39 | expect(findSiteCandidates([siteConfigWithAdjustment], 'http://example.com/map?zoom=1&lat=2&lon=3')).toEqual([]); 40 | }); 41 | test('get user url template that that includes domain', () => { 42 | const configUrlPat: SiteConfiguration = { 43 | id: 'url-pat', 44 | isEnabled: true, 45 | customPattern: { tag: 'user-v1', url: 'https://www.example.com/map?zoom={zoom}&lat={latitude}&lon={longitude}' }, 46 | }; 47 | expect(findSiteCandidates([configUrlPat], 'http://example.com/map?zoom=1&lat=2&lon=3')).toEqual([configUrlPat.id]); 48 | }); 49 | const knownGoogleDomains = [ 50 | '.com', '.ad', '.ae', '.com.af', '.com.ag', '.com.ai', '.am', '.co.ao', '.com.ar', '.as', '.com.uy', '.com.na', 51 | '.at', '.com.au', '.az', '.ba', '.com.bd', '.com.my', '.co.mz', '.be', '.bf', '.bg', '.com.bh', '.bi', '.bj', 52 | '.com.bn', '.com.bo', '.com.br', '.bs', '.co.bw', '.by', '.com.bz', '.ca', '.cd', '.cf', '.cg', '.ch', '.com.ph', 53 | '.ci', '.co.ck', '.cl', '.cm', '.cn', '.com.co', '.co.cr', '.com.cu', '.cv', '.com.cy', '.cz', '.de', '.dj', '.dk', 54 | '.dm', '.com.do', '.dz', '.com.ec', '.ee', '.com.eg', '.es', '.com.et', '.fi', '.com.fj', '.fm', '.fr', '.ga', 55 | '.ge', '.gg', '.com.gh', '.com.gi', '.gl', '.gm', '.gp', '.gr', '.com.gt', '.gy', '.com.hk', '.hn', '.hr', '.ht', 56 | '.hu', '.co.id', '.ie', '.co.il', '.im', '.co.in', '.iq', '.is', '.it', '.je', '.com.jm', '.jo', '.co.jp', '.co.ke', 57 | '.com.kh', '.ki', '.kg', '.co.kr', '.com.kw', '.kz', '.la', '.com.lb', '.li', '.lk', '.co.ls', '.lt', '.lu', '.lv', 58 | '.com.ly', '.co.ma', '.md', '.me', '.mg', '.mk', '.ml', '.mn', '.ms', '.com.mt', '.mu', '.mv', '.mw', '.com.mx', 59 | '.com.nf', '.com.ng', '.com.ni', '.ne', '.nl', '.no', '.com.np', '.nr', '.nu', '.co.nz', '.com.om', '.com.pa', '.com.pe', 60 | '.com.pk', '.pl', '.pn', '.com.pr', '.ps', '.pt', '.com.py', '.com.qa', '.ro', '.ru', '.rw', '.com.sa', '.com.sb', '.sc', 61 | '.se', '.com.sg', '.sh', '.si', '.sk', '.com.sl', '.sn', '.so', '.sm', '.st', '.com.sv', '.td', '.tg', '.co.th', 62 | '.com.tj', '.tk', '.tl', '.tm', '.tn', '.to', '.com.tr', '.tt', '.com.tw', '.co.tz', '.com.ua', '.co.ug', '.co.uk', 63 | '.co.uz', '.com.vc', '.co.ve', '.vg', '.co.vi', '.com.vn', '.vu', '.ws', '.rs', '.co.za', '.co.zm', '.co.zw', '.cat', '.xxx', 64 | ]; 65 | test('known google domains properly match current google configuration', () => { 66 | knownGoogleDomains.forEach(domain => { 67 | const inputUrl = `https://www.google${domain}/maps`; 68 | const googleConfiguration = { id: 'googlemaps', isEnabled: true, defaultConfiguration: Sites['googlemaps'] }; 69 | expect(findSiteCandidates([googleConfiguration], inputUrl)).toEqual(['googlemaps']); 70 | }); 71 | }); 72 | }); 73 | 74 | describe(pickWinningCandidate.name, () => { 75 | test('empty input gives empty output', () => { 76 | expect(pickWinningCandidate([], [], '')).toBeUndefined(); 77 | }); 78 | test('picks a site', () => { 79 | const inputAttributes = [{ siteId: aDefaultSiteConfig.id, additionalAttributes: { zoom: '1', lat: '2', lon: '3' } }]; 80 | const expectedOutput = { siteId: aDefaultSiteConfig.id, attributes: inputAttributes[0].additionalAttributes }; 81 | expect(pickWinningCandidate([aDefaultSiteConfig], inputAttributes, 'https://example.com/')).toEqual(expectedOutput); 82 | }); 83 | const userUrlTemplateTests: { exampleUrl: string; urlTemplate: string; expectedAttrs: Partial>; }[] = [ 84 | { 85 | exampleUrl: 'https://geohack.toolforge.org/geohack.php?params=23.00_N_24.43_E', 86 | urlTemplate: 'https://geohack.toolforge.org/geohack.php?params={latitude}_N_{longitude}_E', 87 | expectedAttrs: { lon: '24.43', lat: '23.00' }, 88 | }, 89 | { 90 | exampleUrl: 'https://hiking.waymarkedtrails.org/#route?id=2966504&map=3!23.0!24.1', 91 | urlTemplate: 'https://hiking.waymarkedtrails.org/#route?id={osm_relation_id}', 92 | expectedAttrs: { relationId: '2966504' }, 93 | }, 94 | { 95 | exampleUrl: 'https://hiking.waymarkedtrails.org/#route?id=10534456&map=14!58.4593!11.4308', 96 | urlTemplate: 'https://hiking.waymarkedtrails.org/#route?id={osm_relation_id}&map={zoom}!{latitude}!{longitude}', 97 | expectedAttrs: { relationId: '10534456', zoom: '14', lat: '58.4593', lon: '11.4308' }, 98 | }, 99 | { 100 | exampleUrl: 'https://disfactory.tw/#map=16.00/120.1/23.23400000000001', 101 | urlTemplate: 'https://disfactory.tw/#map={zoom}/{longitude}/{latitude}', 102 | expectedAttrs: { zoom: '16.00', lon: '120.1', lat: '23.23400000000001' }, 103 | }, 104 | { 105 | exampleUrl: 'https://taginfo.openstreetmap.org/keys/ref:isil', 106 | urlTemplate: 'https://taginfo.openstreetmap.org/keys/{osm_tag_key}', 107 | expectedAttrs: { key: 'ref:isil' }, 108 | }, 109 | { 110 | exampleUrl: 'https://taginfo.openstreetmap.org/tags/parking=surface', 111 | urlTemplate: 'https://taginfo.openstreetmap.org/tags/{osm_tag_key}={osm_tag_value}', 112 | expectedAttrs: { key: 'parking', value: 'surface' }, 113 | }, 114 | { 115 | exampleUrl: 'https://wiki.openstreetmap.org/wiki/Key:amenity', 116 | urlTemplate: 'https://wiki.openstreetmap.org/wiki/Key:{osm_tag_key}', 117 | expectedAttrs: { key: 'amenity' }, 118 | }, 119 | { 120 | exampleUrl: 'https://wiki.openstreetmap.org/wiki/Tag:amenity%3Ddrinking_water', 121 | urlTemplate: 'https://wiki.openstreetmap.org/wiki/Tag:{osm_tag_key}%3D{osm_tag_value}', 122 | expectedAttrs: { key: 'amenity', value: 'drinking_water' }, 123 | }, 124 | ]; 125 | userUrlTemplateTests.forEach((testParams) => { 126 | test(`get parameters from url ${testParams.exampleUrl}`, () => { 127 | const inputConfig: SiteConfiguration = { id: 'user-pattern', isEnabled: true, customPattern: { tag: 'user-v1', url: testParams.urlTemplate} }; 128 | const expectedOutput = { siteId: inputConfig.id, attributes: testParams.expectedAttrs }; 129 | expect(pickWinningCandidate([inputConfig], [{ siteId: inputConfig.id }], testParams.exampleUrl)).toEqual(expectedOutput); 130 | }); 131 | }); 132 | test('get osmchangetiles from weird url', () => { 133 | const expectedSiteId = 'osmchangetiles'; 134 | const inputConfig = { isEnabled: true, id: expectedSiteId, defaultConfiguration: Sites[expectedSiteId]} 135 | const inputUrl = 'https://resultmaps.neis-one.org/osm-change-tiles?quadkey=1202200110303320#16/48.7537/2.3536'; 136 | const expectedOutput = { siteId: expectedSiteId, attributes:{ zoom: '16', lat: '48.7537', lon: '2.3536' } }; 137 | expect(pickWinningCandidate([inputConfig], [{ siteId: expectedSiteId }], inputUrl)).toEqual(expectedOutput); 138 | }); 139 | test('get google from weird url', () => { 140 | const expectedSiteId = 'googlemaps'; 141 | const inputConfig = { isEnabled: true, id: expectedSiteId, defaultConfiguration: Sites[expectedSiteId]} 142 | const inputUrl = "https://www.google.com.tw/maps/place/24%C2%B010'54.1%22N+120%C2%B051'58.2%22E/@24.18169,120.86617,17z/data=!3m1!4b1!4m5!3m4!1s0x0:0x0!8m2!3d24.18169!4d120.86617"; 143 | const expectedOutput = { siteId: expectedSiteId, attributes: { zoom: '17', lat: '24.18169', lon: '120.86617' } }; 144 | expect(pickWinningCandidate([inputConfig], [{ siteId: expectedSiteId }], inputUrl)).toEqual(expectedOutput); 145 | }); 146 | test(`get parameters from a url and not the permalink`, () => { 147 | const inputConfig: SiteConfiguration = { id: 'url-not-permalink', isEnabled: true, customPattern: { tag: 'user-v1', url: 'https://wiki.openstreetmap.org/wiki/Key:{osm_tag_key}' } }; 148 | const pageInput = [{ siteId: inputConfig.id, permalink: 'https://wiki.openstreetmap.org/w/index.php?title=Key:name&oldid=2013483' }]; 149 | const expectedOutput = { siteId: inputConfig.id, attributes: { key: 'name'} }; 150 | expect(pickWinningCandidate([inputConfig], pageInput, 'https://wiki.openstreetmap.org/wiki/Key:name')).toEqual(expectedOutput); 151 | }); 152 | test(`recognize parameters from 'osmose'`, () => { 153 | const id = 'osmose' 154 | const inputConfig: SiteConfiguration = { id, isEnabled: true, defaultConfiguration: Sites[id] }; 155 | const expectedOutputAttributes = { zoom: '18', lat: '48.439383', lon: '-4.416006'}; 156 | expect(pickWinningCandidate([inputConfig], [{}], 'http://osmose.openstreetmap.fr/en/map/#item=7130&zoom=18&lat=48.439383&lon=-4.416006&level=1%2C2%2C3&tags=&fixable=')!.attributes).toEqual(expectedOutputAttributes); 157 | }); 158 | 159 | describe('zoom', () => { 160 | test('with zoomAdjustment=1', () => { 161 | const inputAttributes = [{ siteId: aDefaultSiteConfig.id, additionalAttributes: { zoom: '1', lat: '2', lon: '3' } }]; 162 | const siteConfigWithAdjustment: SiteConfiguration = { 163 | ...aDefaultSiteConfig, 164 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, zoomAdjustment: 1 }, 165 | }; 166 | const expectedOutput = { 167 | siteId: aDefaultSiteConfig.id, 168 | attributes: { ...inputAttributes[0].additionalAttributes, zoom: '2' }, 169 | }; 170 | expect(pickWinningCandidate([siteConfigWithAdjustment], inputAttributes, 'https://example.com/')).toEqual(expectedOutput); 171 | }); 172 | test('with zoomAdjustment=12', () => { 173 | const inputAttributes = [{ siteId: aDefaultSiteConfig.id, additionalAttributes: { zoom: '5', lat: '6', lon: '7' } }]; 174 | const siteConfigWithAdjustment: SiteConfiguration = { 175 | ...aDefaultSiteConfig, 176 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, zoomAdjustment: 12 }, 177 | }; 178 | const expectedOutput = { 179 | siteId: aDefaultSiteConfig.id, 180 | attributes: { ...inputAttributes[0].additionalAttributes, zoom: '17' }, 181 | }; 182 | expect(pickWinningCandidate([siteConfigWithAdjustment], inputAttributes, 'https://example.com/')).toEqual(expectedOutput); 183 | }); 184 | }); 185 | }); 186 | 187 | describe(getRelevantSites.name, () => { 188 | const zll567_attributes = { zoom: '5', lat: '6', lon: '7' }; 189 | 190 | test('empty input gives empty output', () => { 191 | expect(getRelevantSites([], '', {})).toEqual([]); 192 | }); 193 | test('applies zoom,lat,lon to a site', () => { 194 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=5/6/7' }]; 195 | expect(getRelevantSites([aDefaultSiteConfig], '', zll567_attributes)).toEqual(expectedOutput); 196 | }); 197 | test('applies changeset id to osm site', () => { 198 | const basicPattern: SiteConfiguration = 199 | {id: 'an-id', isEnabled: true, customName: 'a-name', defaultConfiguration: Sites.openstreetmap}; 200 | const expectedOutput = [{ id: basicPattern.id, customName: basicPattern.customName, url: 'https://www.openstreetmap.org/changeset/83729'}]; 201 | expect(getRelevantSites([basicPattern], '', { changesetId: '83729'})).toEqual(expectedOutput); 202 | }); 203 | test('applies zoom,lat,lon to a user-v1 pattern', () => { 204 | const basicPattern: SiteConfiguration = 205 | {id: 'an-id', isEnabled: true, customName: 'a-name', customPattern: 206 | {tag: 'user-v1', url: 'https://www.openstreetmap.org/#map={zoom}/{latitude}/{longitude}'}}; 207 | const expectedOutput = [{ id: basicPattern.id, customName: basicPattern.customName, url: 'https://www.openstreetmap.org/#map=5/6/7'}]; 208 | expect(getRelevantSites([basicPattern], '', zll567_attributes)).toEqual(expectedOutput); 209 | }); 210 | test('user-v1 pattern is case-sensitive', () => { 211 | const basicPattern: SiteConfiguration = 212 | {id: 'an-id', isEnabled: true, customName: 'a-name', customPattern: 213 | {tag: 'user-v1', url: 'https://www.waze.com/pt-BR/editor?env=row&lon={longitude}&lat={latitude}&zoom=7'}}; 214 | const expectedOutput = [{ id: basicPattern.id, customName: basicPattern.customName, url: 'https://www.waze.com/pt-BR/editor?env=row&lon=7&lat=6&zoom=7'}]; 215 | expect(getRelevantSites([basicPattern], '', zll567_attributes)).toEqual(expectedOutput); 216 | }); 217 | 218 | describe('zoom', () => { 219 | test('with zoomAdjustment=1', () => { 220 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=4/6/7' }]; 221 | const siteConfigWithAdjustment: SiteConfiguration = { 222 | ...aDefaultSiteConfig, 223 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, zoomAdjustment: 1 }, 224 | }; 225 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 226 | }); 227 | test('with zoomAdjustment=3', () => { 228 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=2/6/7' }]; 229 | const siteConfigWithAdjustment: SiteConfiguration = { 230 | ...aDefaultSiteConfig, 231 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, zoomAdjustment: 3 }, 232 | }; 233 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 234 | }); 235 | test('with maxZoom smaller than zoom', () => { 236 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=3/6/7' }]; 237 | const siteConfigWithAdjustment: SiteConfiguration = { 238 | ...aDefaultSiteConfig, 239 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, maxZoom: 3 }, 240 | }; 241 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 242 | }); 243 | test('with maxZoom greater than zoom', () => { 244 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=5/6/7' }]; 245 | const siteConfigWithAdjustment: SiteConfiguration = { 246 | ...aDefaultSiteConfig, 247 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, maxZoom: 6 }, 248 | }; 249 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 250 | }); 251 | test('with maxZoom greater than a decimal zoom', () => { 252 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=9.2109/6/7' }]; 253 | const siteConfigWithAdjustment: SiteConfiguration = { 254 | ...aDefaultSiteConfig, 255 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, maxZoom: 10 }, 256 | }; 257 | const input = { ...zll567_attributes, zoom: '9.2109' }; 258 | expect(getRelevantSites([siteConfigWithAdjustment], '', input)).toEqual(expectedOutput); 259 | }); 260 | 261 | test('with maxZoom equal to zoom', () => { 262 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=5/6/7' }]; 263 | const siteConfigWithAdjustment: SiteConfiguration = { 264 | ...aDefaultSiteConfig, 265 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, maxZoom: 5 }, 266 | }; 267 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 268 | }); 269 | test('with maxZoom greater than zoom AND zoomAdjustment', () => { 270 | const expectedOutput = [{ id: aDefaultSiteConfig.id, url: 'https://example.com/#map=2/6/7' }]; 271 | const siteConfigWithAdjustment: SiteConfiguration = { 272 | ...aDefaultSiteConfig, 273 | defaultConfiguration: { ...aDefaultSiteConfig.defaultConfiguration!, maxZoom: 3, zoomAdjustment: 3 }, 274 | }; 275 | expect(getRelevantSites([siteConfigWithAdjustment], '', zll567_attributes)).toEqual(expectedOutput); 276 | }); 277 | }); 278 | }); 279 | -------------------------------------------------------------------------------- /src/popup/sites-manipulation-helper.ts: -------------------------------------------------------------------------------- 1 | import { DefaultSiteConfiguration, ParamOpt, OsmAttribute } from "../sites-configuration"; 2 | import { ContentScriptOutputMessage } from "../injectable-content-script"; 3 | import { SiteConfiguration } from "../storage/config-handler"; 4 | import escaperegexp from 'lodash.escaperegexp'; 5 | 6 | const naturalNumberRegExp = "[0-9]+"; 7 | const decimalNumberRegExp = "[0-9.-]+"; 8 | const positiveDecimalNumberRegExp = "[0-9.]+"; 9 | 10 | const InfoRegExp: Record = { 11 | nodeId: naturalNumberRegExp, 12 | wayId: naturalNumberRegExp, 13 | relationId: naturalNumberRegExp, 14 | tracesId: naturalNumberRegExp, 15 | userName: "[^#?\/]+", // any character except URL separator characters 16 | changesetId: naturalNumberRegExp, 17 | zoom: positiveDecimalNumberRegExp, //believe it or not, some websites accept a decimal zoom. TODO: verify if any site has a problem having a decimal zoom as a parameter 18 | lat: decimalNumberRegExp, 19 | lon: decimalNumberRegExp, 20 | key: "[^#?\/=]+", 21 | value: "[^#?\/]+", 22 | }; 23 | //TODO: should I add support for route information? (start, intermediary and end points, and maybe transport mode) 24 | 25 | export function findSiteCandidates(sitesConfiguration: SiteConfiguration[], url: string): string[] { 26 | if (!url) return []; 27 | const hostname = (new URL(url).hostname); 28 | if (!hostname) return []; 29 | 30 | return sitesConfiguration 31 | .filter((site) => { 32 | if (site.customPattern && site.customPattern.url) { 33 | return site.customPattern.url.includes(hostname); 34 | } 35 | else if (site.defaultConfiguration) { 36 | if (site.defaultConfiguration.domainRegexp) { 37 | return site.defaultConfiguration.domainRegexp.test(hostname); 38 | } else { 39 | return site.defaultConfiguration.link.includes(hostname); 40 | } 41 | } 42 | else return false; 43 | }) 44 | .map(site => site.id); 45 | } 46 | 47 | export function pickWinningCandidate( 48 | sitesConfiguration: SiteConfiguration[], 49 | results: ContentScriptOutputMessage, 50 | currentTabUrl: string, 51 | ): { siteId?: string, attributes: Record, detectedPattern?: UrlPattern } | undefined { 52 | if (results.length === 0) { 53 | return undefined; 54 | } 55 | const [head, ...tail] = results; 56 | let extractedAttributes: Record = {}; 57 | let detectedPattern: UrlPattern | undefined; 58 | let site: SiteConfiguration | undefined; 59 | if (head.siteId) { 60 | site = sitesConfiguration.find((site) => head.siteId === site.id); 61 | } else site = undefined; 62 | 63 | if (site) { 64 | if (head.permalink) { 65 | extractedAttributes = extractAttributesFromUrl(head.permalink, site); 66 | } 67 | if (Object.keys(extractedAttributes).length === 0) { 68 | extractedAttributes = extractAttributesFromUrl(currentTabUrl, site); 69 | } 70 | } else { 71 | [detectedPattern, extractedAttributes] = detectAndExtractAttributesFromUrl(head.permalink || currentTabUrl); 72 | } 73 | const allExtractedAttributes = Object.assign(extractedAttributes, head.additionalAttributes); 74 | 75 | if (Object.values(allExtractedAttributes).length === 0) { 76 | return pickWinningCandidate(sitesConfiguration, tail, currentTabUrl); 77 | } else { 78 | return { 79 | siteId: head.siteId, 80 | attributes: withAdjustedZoom(site, allExtractedAttributes), 81 | detectedPattern, 82 | }; 83 | } 84 | } 85 | 86 | function withAdjustedZoom(config: SiteConfiguration | undefined, extractedAttributes: Record): Record { 87 | if (extractedAttributes.zoom && config && config.defaultConfiguration && config.defaultConfiguration.zoomAdjustment) { 88 | const nZoom = Number(extractedAttributes.zoom); 89 | return { 90 | ...extractedAttributes, 91 | zoom: (nZoom + config.defaultConfiguration.zoomAdjustment).toString(), 92 | } 93 | } else { 94 | return extractedAttributes; 95 | } 96 | } 97 | 98 | export type SiteLink = { 99 | id: string; 100 | url: string; 101 | customName: string | undefined; 102 | } 103 | 104 | export function getRelevantSites( 105 | sitesConfiguration: SiteConfiguration[], 106 | currentSiteId: string | undefined, 107 | retrievedAttributes: Record, 108 | ): SiteLink[] { 109 | return sitesConfiguration.map(function (site): SiteLink | undefined { 110 | if (site.id == currentSiteId || !site.isEnabled) return undefined; 111 | 112 | const { id, customName } = site; 113 | if (site.defaultConfiguration) { 114 | const chosenOption = site.defaultConfiguration.paramOpts.find(function (paramOpt) { 115 | const [orderedParameters, unorderedParameters] = extractParametersFromParamOpt(paramOpt); 116 | const necessaryParameters = orderedParameters.concat(unorderedParameters); 117 | return necessaryParameters.every(param => retrievedAttributes[param] !== undefined); 118 | }); 119 | 120 | let attributes = retrievedAttributes; 121 | if (retrievedAttributes.zoom) { 122 | attributes = { ...retrievedAttributes, zoom: reviewZoom(site.defaultConfiguration, retrievedAttributes.zoom) } 123 | } 124 | 125 | if (chosenOption) { 126 | const path = applyParametersToUrl(chosenOption, attributes); 127 | const protocol = site.defaultConfiguration.httpOnly ? 'http' : 'https'; 128 | return { 129 | id, 130 | customName, 131 | url: `${protocol}://${site.defaultConfiguration.link}${path}`, 132 | }; 133 | } 134 | } else { 135 | if (site.customPattern) { 136 | const url = applyParametersToUrlPattern(site.customPattern, retrievedAttributes); 137 | if (url) { 138 | return { id, customName, url }; 139 | } 140 | } 141 | } 142 | return undefined; 143 | }).filter((s): s is SiteLink => Boolean(s)); 144 | } 145 | 146 | const userUrlParametersMap: Record = { 147 | zoom: 'zoom', latitude: 'lat', longitude: 'lon', osm_changeset_id: 'changesetId', osm_user_name: 'userName', 148 | osm_tag_key: 'key', osm_tag_value: 'value', osm_node_id: 'nodeId', osm_way_id: 'wayId', osm_relation_id: 'relationId', 149 | }; 150 | 151 | function applyParametersToUrlPattern(urlPattern: UrlPattern, retrievedAttributes: Record): string | undefined { 152 | const { zoom, lat, lon } = retrievedAttributes; 153 | if (urlPattern.tag === 'user-v1') { 154 | let { url } = urlPattern; 155 | const parameters = extractBracetParameters(url.toLowerCase()); 156 | const hasNecessaryParameters = parameters.every(key => userUrlParametersMap[key] in retrievedAttributes); 157 | if (!hasNecessaryParameters) return undefined; 158 | 159 | parameters.forEach(function (key: string) { 160 | url = url.replace('{' + key + '}', retrievedAttributes[userUrlParametersMap[key]]); 161 | }); 162 | return url; 163 | } 164 | else if (!zoom || !lat || !lon) { 165 | return undefined; 166 | } 167 | 168 | const urlObj = new URL(urlPattern.url); 169 | if (urlPattern.tag === 'hash-2') { 170 | const matchArray = urlObj.hash.match(/(#[a-z=]*)([0-9.]+)(\/)([0-9.-]+)(\/)([0-9.-]+)(.*)/); 171 | if (!matchArray) return undefined; 172 | const [, prefix, /*zoom*/, separator1, /*lat*/, separator2, /*lon*/, suffix] = matchArray; 173 | urlObj.hash = `${prefix}${retrievedAttributes.zoom}${separator1}${retrievedAttributes.lat}${separator2}${retrievedAttributes.lon}${suffix}`; 174 | return urlObj.toString(); 175 | } 176 | else if (urlPattern.tag === 'qs') { 177 | Object.entries(urlPattern.querystringSubst).forEach(([osmAttribute, querystringParameter]): void => { 178 | urlObj.searchParams.set(querystringParameter, retrievedAttributes[osmAttribute]); 179 | }) 180 | return urlObj.toString(); 181 | } 182 | else if (urlPattern.tag === 'hash-1') { 183 | const auxUrl = new URL(urlObj.toString()); 184 | auxUrl.search = '?' + auxUrl.hash.substring(1); 185 | 186 | Object.entries(urlPattern.hashParametersSubst).forEach(([osmAttribute, hashParameter]): void => { 187 | auxUrl.searchParams.set(hashParameter, retrievedAttributes[osmAttribute]); 188 | }) 189 | urlObj.hash = '#' + auxUrl.search.substring(1); 190 | return urlObj.toString(); 191 | } 192 | const _exhaustivenessCheck: never = urlPattern; 193 | return _exhaustivenessCheck; 194 | } 195 | 196 | export type UrlPattern = SimpleQuerystringPattern | HashWithNamedParametersPattern | OsmLikePattern | UserUrlPattern; 197 | 198 | type SimpleQuerystringPattern = { // example https://apps.sentinel-hub.com/eo-browser/?lat=41.718&lng=12.014&zoom=8 199 | tag: 'qs'; 200 | querystringSubst: NamedMapParameters; 201 | url: string; 202 | }; 203 | 204 | type HashWithNamedParametersPattern = { // example https://www.osmhydrant.org/en/#zoom=14&lat=48.20168&lon=16.48777 205 | tag: 'hash-1'; 206 | hashParametersSubst: NamedMapParameters; 207 | url: string; 208 | }; 209 | 210 | type NamedMapParameters = { 211 | zoom: 'zoom' | 'z'; 212 | lat: 'lat' | 'y'; 213 | lon: 'lon' | 'lng' | 'x'; 214 | }; 215 | 216 | type OsmLikePattern = { // example https://www.opengeofiction.net/#map=4/-16.51/-46.93 217 | tag: 'hash-2'; 218 | url: string; 219 | }; 220 | 221 | type UserUrlPattern = { 222 | tag: 'user-v1'; 223 | url: string; // template with bracet parameters 224 | }; 225 | 226 | function detectAndExtractAttributesFromUrl(url: string): [UrlPattern | undefined, Record] { 227 | const urlObj = new URL(url); 228 | const osmLikePatternExtraction = extractOsmPatternExtraction(urlObj); 229 | if (osmLikePatternExtraction) { 230 | const osmLikePattern: OsmLikePattern = { 231 | tag: 'hash-2', 232 | url: url, 233 | } 234 | return [osmLikePattern, osmLikePatternExtraction]; 235 | } 236 | 237 | let { zoom, lat, lon }: Partial = {}; 238 | const sp = urlObj.searchParams; 239 | if ( 240 | (sp.has(zoom = 'zoom') || sp.has(zoom = 'z')) && 241 | (sp.has(lat = 'lat') || sp.has(lat = 'y')) && 242 | (sp.has(lon = 'lon') || sp.has(lon = 'lng') || sp.has(lon = 'x')) 243 | ) { 244 | const qsPattern: SimpleQuerystringPattern = { 245 | tag: 'qs', 246 | querystringSubst: { zoom, lat, lon }, 247 | url, 248 | }; 249 | return [qsPattern, extractAttributesFromUrlPattern(urlObj, qsPattern)]; 250 | } 251 | 252 | const auxUrl = new URL(urlObj.toString()); 253 | auxUrl.search = '?' + auxUrl.hash.substring(1); 254 | const auxSp = auxUrl.searchParams; 255 | if ( 256 | (auxSp.has(zoom = 'zoom') || auxSp.has(zoom = 'z')) && 257 | (auxSp.has(lat = 'lat') || auxSp.has(lat = 'y')) && 258 | (auxSp.has(lon = 'lon') || auxSp.has(lon = 'lng') || auxSp.has(lon = 'x')) 259 | ) { 260 | const hashPattern: HashWithNamedParametersPattern = { 261 | tag: 'hash-1', 262 | hashParametersSubst: { zoom, lat, lon }, 263 | url, 264 | }; 265 | return [hashPattern, extractAttributesFromUrlPattern(urlObj, hashPattern)]; 266 | } 267 | 268 | return [, {}]; 269 | } 270 | 271 | function extractAttributesFromUrlPattern(url: URL, urlPattern: UrlPattern): Record { 272 | if (urlPattern.tag === 'hash-2') { 273 | const attributes = extractOsmPatternExtraction(url); 274 | return attributes || {}; 275 | } 276 | else if (urlPattern.tag === 'qs') { 277 | const zoom = url.searchParams.get(urlPattern.querystringSubst.zoom); 278 | const lat = url.searchParams.get(urlPattern.querystringSubst.lat); 279 | const lon = url.searchParams.get(urlPattern.querystringSubst.lon); 280 | if (zoom && lat && lon) { 281 | return { zoom, lat, lon }; 282 | } else return {}; 283 | } 284 | else if (urlPattern.tag === 'hash-1') { 285 | const auxUrl = new URL(url.toString()); 286 | auxUrl.search = '?' + auxUrl.hash.substring(1); 287 | const zoom = auxUrl.searchParams.get(urlPattern.hashParametersSubst.zoom); 288 | const lat = auxUrl.searchParams.get(urlPattern.hashParametersSubst.lat); 289 | const lon = auxUrl.searchParams.get(urlPattern.hashParametersSubst.lon); 290 | if (zoom && lat && lon) { 291 | return { zoom, lat, lon }; 292 | } else return {}; 293 | } 294 | else if (urlPattern.tag === 'user-v1') { 295 | const parameters = extractBracetParameters(urlPattern.url); 296 | let wipRegexp = escaperegexp(urlPattern.url); 297 | parameters.forEach((parameter) => { 298 | const targetRegexp = InfoRegExp[userUrlParametersMap[parameter]]; 299 | wipRegexp = wipRegexp.replace(`\\{${parameter}\\}`, `(${targetRegexp})`); 300 | }); 301 | const regexp = new RegExp(wipRegexp); 302 | const match = regexp.exec(url.toString()); 303 | if (match) { 304 | const extractedAttributes: Record = {}; 305 | parameters.forEach(function (parameter, index) { 306 | extractedAttributes[userUrlParametersMap[parameter]] = match[index + 1]; 307 | }); 308 | return extractedAttributes; 309 | } 310 | return {}; 311 | } 312 | 313 | const _exhaustivenessCheck: never = urlPattern; 314 | return _exhaustivenessCheck; 315 | } 316 | 317 | function extractAttributesFromUrl(url: string, siteConfig: SiteConfiguration): Record { 318 | if (siteConfig.customPattern) { 319 | return extractAttributesFromUrlPattern(new URL(url), siteConfig.customPattern); 320 | } 321 | else if (siteConfig.defaultConfiguration) { 322 | const config = siteConfig.defaultConfiguration; 323 | for (let i = 0; i < config.paramOpts.length; i++) { 324 | let extractedAttributes: Record = {}; 325 | const [orderedParameters] = extractParametersFromParamOpt(config.paramOpts[i]); 326 | 327 | let partialUrl = config.paramOpts[i].ordered; 328 | partialUrl = partialUrl.replace(/([.?^$])/g, '\\$1'); // escape regex special characters TODO: add more and review location in code 329 | orderedParameters.forEach(function (parameter) { 330 | partialUrl = partialUrl.replace(`{${parameter}}`, `(${InfoRegExp[parameter]})`); 331 | }); 332 | const orderedPartRegExp = new RegExp(partialUrl); 333 | 334 | const orderedMatch = orderedPartRegExp.exec(url); 335 | if (orderedMatch) { 336 | const unorderedParametersMap = config.paramOpts[i].unordered || {}; 337 | const matchesUnordered = Object.entries(unorderedParametersMap).every(function ([key, value]: [string, string?]) { 338 | const unorderedPartXRegExp = new RegExp( 339 | `${value}=(${InfoRegExp[key]})` 340 | ) 341 | const unorderedMatch = unorderedPartXRegExp.exec(url) 342 | if (!unorderedMatch) { 343 | return false; 344 | } else { 345 | extractedAttributes[key] = unorderedMatch[1]; 346 | return true; 347 | } 348 | }) 349 | if (matchesUnordered) { 350 | orderedParameters.forEach(function (orderedParameter, index) { 351 | extractedAttributes[orderedParameter] = orderedMatch[index + 1]; 352 | }); 353 | return extractedAttributes; 354 | } else { 355 | extractedAttributes = {}; 356 | } 357 | } 358 | } 359 | } 360 | return {}; 361 | } 362 | 363 | function extractOsmPatternExtraction(url: URL): Record | undefined { 364 | const matchArray = url.hash.match(/#[a-z=]*([0-9.]+)\/([0-9.-]+)\/([0-9.-]+)/); 365 | if (matchArray) { 366 | const [, zoom, lat, lon] = matchArray; 367 | if (zoom && lat && lon) { 368 | return { zoom, lat, lon }; 369 | } 370 | } 371 | return undefined; 372 | } 373 | 374 | function extractParametersFromParamOpt(paramOpt: ParamOpt): [string[], string[]] { 375 | return [ 376 | extractBracetParameters(paramOpt.ordered), 377 | paramOpt.unordered ? Object.keys(paramOpt.unordered): [], 378 | ]; 379 | } 380 | 381 | function extractBracetParameters(s: string): string[] { 382 | const fieldGetterRegExp = /\{([^\}]+)\}/g; 383 | const parametersFound = []; 384 | let match: RegExpExecArray | null; 385 | while (match = fieldGetterRegExp.exec(s)) { 386 | const attributeNameWithoutBraces = match[1]; 387 | parametersFound.push(attributeNameWithoutBraces); 388 | } 389 | return parametersFound; 390 | } 391 | 392 | /* gets an "interpolable" string and applies the parameters from an object into it, returning a new string */ 393 | function applyParametersToUrl(option: ParamOpt, retrievedAttributes: Record): string { 394 | let url = option.ordered; 395 | const encodedAttributes: Record = {}; 396 | 397 | Object.keys(retrievedAttributes).forEach(function (key) { 398 | encodedAttributes[key] = encodeURIComponent(retrievedAttributes[key]); 399 | url = url.replace('{' + key + '}', encodedAttributes[key]); 400 | }); 401 | 402 | if (option.unordered) { 403 | const urlQueryParameters = 404 | Object.entries(option.unordered).map(function ([key, value]) { 405 | return value + '=' + encodedAttributes[key]; 406 | }); 407 | url += '?' + urlQueryParameters.join('&'); // TODO: be mindful of whether there is an '?' or '#' already 408 | } 409 | 410 | return url; 411 | } 412 | 413 | function reviewZoom(site: DefaultSiteConfiguration, zoom: string): string { 414 | if (site) { 415 | const { maxZoom, zoomAdjustment } = site; 416 | let nZoom = Number(zoom); 417 | if (zoomAdjustment) { 418 | nZoom = nZoom - zoomAdjustment; 419 | } 420 | if (maxZoom) { 421 | nZoom = Math.min(nZoom, maxZoom); 422 | } 423 | return nZoom.toString(); 424 | } else { 425 | return zoom; 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /src/popup/utils.ts: -------------------------------------------------------------------------------- 1 | import { browser } from "webextension-polyfill-ts"; 2 | import { UrlPattern } from "./sites-manipulation-helper"; 3 | 4 | export type CustomUserOption = { 5 | defaultName: string; 6 | urlPattern: UrlPattern; 7 | } 8 | 9 | export enum KnownError { 10 | NO_ACCESS = 'noAccess', 11 | INCOMPATIBLE_WEBSITE = 'incompatibleWebsite', 12 | NO_INFORMATION_EXTRACTED = 'noInformationExtracted', 13 | } 14 | 15 | export function openLink(url: string): void { 16 | browser.tabs.create({ url }); 17 | } 18 | -------------------------------------------------------------------------------- /src/storage/config-handler.ts: -------------------------------------------------------------------------------- 1 | import { browser, Storage } from "webextension-polyfill-ts"; 2 | import { Sites, DefaultSiteConfiguration } from "../sites-configuration"; 3 | import { UrlPattern } from "../popup/sites-manipulation-helper"; 4 | 5 | export type StoredConfiguration = { 6 | isEnabled: boolean; 7 | customName?: string; 8 | customPattern?: UrlPattern; 9 | } 10 | 11 | // needs fallback https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync#Browser_compatibility 12 | const storage: Storage.StorageArea = browser.storage.sync || browser.storage.local; 13 | 14 | const getConfigKey = (siteId: string) => `site_${siteId}`; 15 | 16 | function getDefaultEnabledAttribute(siteId: string) { 17 | if (Sites[siteId] && Sites[siteId].disabledByDefault) { 18 | return false; 19 | } else { 20 | return true; 21 | } 22 | } 23 | 24 | async function getStoredConfig(siteId: string): Promise { 25 | const key = getConfigKey(siteId); 26 | const storedObject = await storage.get(key); 27 | if (typeof storedObject === "object" && storedObject && 28 | typeof storedObject[key] === "object" && storedObject[key] 29 | ) { 30 | const s = storedObject[key]; 31 | const siteConfig: StoredConfiguration = { 32 | isEnabled: typeof s.isEnabled !== "undefined"? s.isEnabled: getDefaultEnabledAttribute(siteId), 33 | customName: s.customName, 34 | customPattern: s.customPattern, 35 | }; 36 | return siteConfig 37 | } else { 38 | return { 39 | isEnabled: getDefaultEnabledAttribute(siteId), 40 | }; 41 | } 42 | } 43 | 44 | export async function updateStoredConfig(siteId: string, config: Partial): Promise { 45 | const oldConfig = await getStoredConfig(siteId); 46 | await setStoredConfig(siteId, { 47 | ...oldConfig, 48 | ...config 49 | }); 50 | } 51 | 52 | export async function setStoredConfig(siteId: string, config: StoredConfiguration): Promise { 53 | const newConfig = { 54 | [getConfigKey(siteId)]: config, 55 | }; 56 | await storage.set(newConfig); 57 | } 58 | 59 | export async function addNewUrlPattern(name: string, urlPattern: UrlPattern, isEnabled: boolean = true): Promise { 60 | const timestamp = Date.now(); 61 | const siteId = encodeURIComponent(`${timestamp}_${urlPattern.url}`); 62 | 63 | await setStoredConfig(siteId, { 64 | isEnabled, 65 | customName: name, 66 | customPattern: urlPattern, 67 | }); 68 | 69 | await setOrderedSiteIds( 70 | [siteId].concat(await getOrderedSiteIds()) 71 | ); 72 | }; 73 | 74 | export async function deleteUrlPattern(siteId: string): Promise { 75 | await setOrderedSiteIds( 76 | (await getOrderedSiteIds()).filter(id => id !== siteId) 77 | ); 78 | await storage.remove(getConfigKey(siteId)); 79 | } 80 | 81 | const siteIdsOrderKey = 'sites-order'; 82 | const defaultSiteIdsOrder = Object.keys(Sites); 83 | export async function getOrderedSiteIds(): Promise { 84 | const storedObject = await storage.get(siteIdsOrderKey); 85 | if (typeof storedObject === 'object' && storedObject && storedObject[siteIdsOrderKey] instanceof Array) { 86 | const storedSitesIdOrder: string[] = storedObject[siteIdsOrderKey]; 87 | const newSites = defaultSiteIdsOrder.filter((s) => !storedSitesIdOrder.includes(s)) 88 | if (newSites.length > 0) { 89 | const newOrder = storedSitesIdOrder.concat(newSites); 90 | await setOrderedSiteIds(newOrder); 91 | return newOrder; 92 | } else { 93 | return storedSitesIdOrder; 94 | } 95 | } else { 96 | await setOrderedSiteIds(defaultSiteIdsOrder); 97 | return defaultSiteIdsOrder; 98 | } 99 | } 100 | export async function setOrderedSiteIds(orderedSiteIds: string[]): Promise { 101 | await storage.set({ 102 | [siteIdsOrderKey]: orderedSiteIds, 103 | }); 104 | } 105 | 106 | export type SiteConfiguration = StoredConfiguration & { 107 | id: string; 108 | defaultConfiguration?: DefaultSiteConfiguration; 109 | } 110 | 111 | export async function getSitesConfiguration(): Promise { 112 | const orderedSiteIds = await getOrderedSiteIds(); 113 | return await Promise.all(orderedSiteIds.map(async (siteId): Promise => 114 | getSiteConfiguration(siteId) 115 | )); 116 | } 117 | 118 | export async function getSiteConfiguration(siteId: string): Promise { 119 | const storedConfig = await getStoredConfig(siteId); 120 | return { 121 | id: siteId, 122 | ...storedConfig, 123 | defaultConfiguration: Sites[siteId], 124 | }; 125 | } 126 | -------------------------------------------------------------------------------- /src/storage/migrations.ts: -------------------------------------------------------------------------------- 1 | import { browser, Runtime } from "webextension-polyfill-ts"; 2 | 3 | export async function idempotentMigrations(_details: Runtime.OnInstalledDetailsType): Promise { 4 | await migrateLocalStorageToSyncStorage(); 5 | } 6 | 7 | async function migrateLocalStorageToSyncStorage() { 8 | if (!browser.storage.sync) return; // a safeguard for browsers that do not support storage.sync 9 | 10 | const localData = await browser.storage.local.get(); 11 | if (localData && Object.keys(localData).length > 0) { 12 | await browser.storage.sync.set(localData); 13 | await browser.storage.local.clear(); 14 | console.log('Data migrated from local storage to sync storage:', JSON.stringify(localData)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "allowJs": true, 15 | }, 16 | "include": ["src/**/*"] 17 | } 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | optimization: { 5 | minimize: false, // ease code review by webextension stores 6 | }, 7 | devtool: false, // related to optimization.minimize=false and https://bugzilla.mozilla.org/show_bug.cgi?id=1437937 8 | entry: { 9 | "injectable-content-script": "./src/injectable-content-script.ts", 10 | "options/main": "./src/options/main.ts", 11 | "popup/main": "./src/popup/main.ts", 12 | "background-listeners-setup": "./src/background-listeners-setup.ts" 13 | }, 14 | output: { 15 | path: path.resolve(__dirname, "addon"), 16 | filename: "[name].js" 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.tsx?$/, 22 | use: 'ts-loader', 23 | exclude: /node_modules/, 24 | }, 25 | { 26 | test: /\.(html|svelte)$/, 27 | use: { 28 | loader: 'svelte-loader', 29 | options: { 30 | preprocess: require('svelte-preprocess')({}), // TypeScript support 31 | }, 32 | }, 33 | exclude: /node_modules/, 34 | }, 35 | ], 36 | }, 37 | resolve: { 38 | extensions: ['.tsx', '.ts', '.js', '.svelte', '.mjs'], 39 | alias: { svelte: path.resolve('node_modules', 'svelte') }, 40 | mainFields: ['svelte', 'browser', 'module', 'main'], 41 | }, 42 | }; 43 | --------------------------------------------------------------------------------