├── .gitignore ├── README ├── background.js ├── content.js ├── manifest.json └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.xpi 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Firefox extension that allows to right-click on an element and copy 2 | the page URL with the element's ID, thus retaining an anchor. Useful 3 | when you want to keep a link to specific section of a webpage. 4 | 5 | If you click on an element which doesn't have ID attribute it will still 6 | let you copy an anchored link of the parent section which does have it. 7 | 8 | Works with Firefox 45+. May or may not work with other browsers. 9 | Compiled version can be downloaded from AMO: 10 | https://addons.mozilla.org/en-US/firefox/addon/copy-with-anchor/ 11 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | var anchor_url = ''; 2 | 3 | if (typeof browser === "undefined") { 4 | var browser = chrome; 5 | } 6 | 7 | browser.contextMenus.create({ 8 | "id": "copy-with-ref", 9 | "title": "Copy anchor link", 10 | "visible": false, 11 | "contexts": ["image", "link", "page"], 12 | "onclick": (info, tab) => { 13 | navigator.clipboard.writeText(anchor_url) 14 | } 15 | }); 16 | 17 | browser.runtime.onMessage.addListener(async function (message) { 18 | if (message.cmd == "hide") { 19 | //~ console.log("Hiding menu"); 20 | browser.contextMenus.update("copy-with-ref", {visible: false}); 21 | browser.contextMenus.refresh(); 22 | } 23 | else if (message.cmd == "show") { 24 | anchor_url = message.url; 25 | var fragment = message.anchor.substr(0, 20) + (message.anchor.length > 20 ? '…' : ''); 26 | browser.contextMenus.update("copy-with-ref", { 27 | title: 'Copy anchor link #' + fragment, 28 | visible: true, 29 | }); 30 | browser.contextMenus.refresh(); 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | if (typeof browser === "undefined") { 2 | var browser = chrome; 3 | } 4 | 5 | document.body.addEventListener('mousedown', (event) => { 6 | if (event.button != 2) 7 | return; 8 | browser.runtime.sendMessage({cmd: "hide"}); 9 | }, false); 10 | 11 | function install_right_click_handler(el, anchor) { 12 | el.addEventListener('mousedown', (event) => { 13 | if (event.button != 2) 14 | return; 15 | event.stopImmediatePropagation(); 16 | browser.runtime.sendMessage({ 17 | cmd: "show", 18 | anchor: anchor, 19 | url: window.location.href.split('#')[0] + '#' + anchor, 20 | }); 21 | }, 22 | // Use_capture=false so we process inner-most first 23 | false 24 | ); 25 | } 26 | 27 | // Install contextMenu handler on elements with `id` attribute 28 | for (const el of document.querySelectorAll(` 29 | h1[id], h2[id], h3[id], h4[id], h5[id], h6[id], 30 | p[id], span[id], blockquote[id], a[id], img[id], 31 | ul[id], ol[id], dl[id], li[id], dt[id], 32 | section[id], article[id], aside[id], figure[id], nav[id]`)) { 33 | install_right_click_handler(el, el.id); 34 | } 35 | 36 | // Install contextMenu handler on elements that follow an `` tag 37 | for (const el of document.querySelectorAll('a[name] + *')) { 38 | var a = el.previousSibling; 39 | while (a && !a.name) a = a.previousSibling; // skip empty/text nodes 40 | install_right_click_handler(el, a.name); 41 | } 42 | for (const el of document.querySelectorAll('a[id] + *')) { 43 | var a = el.previousSibling; 44 | while (a && !a.id) a = a.previousSibling; // skip empty/text nodes 45 | install_right_click_handler(el, a.id); 46 | } 47 | 48 | // Install contextMenu handler on elements containing an `` tag 49 | // Install handlers in reverse so the first one actually executes last 50 | for (const el of Array.from(document.querySelectorAll('a[name]'))) { 51 | install_right_click_handler(el.parentNode, el.name); 52 | } 53 | for (const el of Array.from(document.querySelectorAll('a[id]'))) { 54 | install_right_click_handler(el.parentNode, el.id); 55 | } 56 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | 4 | "name": "Copy Current Page URL with Anchor", 5 | "short_name": "Copy Location With Anchor", 6 | "description": "Copy a link to a specific section of a web page by linking to the section's anchor (URL fragment).", 7 | "version": "2.0.1", 8 | 9 | "author": "Alexander Belyaev", 10 | "homepage_url": "https://github.com/certainlyakey/Copy-current-page-URL-with-anchor", 11 | 12 | "background": { 13 | "scripts": ["background.js"] 14 | }, 15 | "content_scripts": [ 16 | { 17 | "matches": [""], 18 | "js": ["content.js"] 19 | } 20 | ], 21 | "permissions": ["contextMenus", "clipboardWrite"] 22 | } 23 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Title With Id Attribute

6 | 7 |

A paragraph with identity.

8 | 9 | 10 |

Title Following A Name

11 | 12 | 13 |

Following its own name, but identity takes priority.

14 | 15 |

Title Containing A Name

16 | 17 |

A paragraph without identity or name.

18 | --------------------------------------------------------------------------------