├── screenshots ├── options.png ├── context-menu.jpg └── toolbar-button.jpg ├── icons ├── archive-icon-dark-128.png ├── archive-icon-dark-16.png ├── archive-icon-dark-32.png ├── archive-icon-dark-64.png ├── archive-icon-light-16.png ├── archive-icon-light-32.png ├── archive-icon-light-64.png └── archive-icon-light.128.png ├── popup ├── popup.html └── popup.js ├── README.md ├── options ├── options.html └── options.js ├── manifest.json └── archive.js /screenshots/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/screenshots/options.png -------------------------------------------------------------------------------- /screenshots/context-menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/screenshots/context-menu.jpg -------------------------------------------------------------------------------- /icons/archive-icon-dark-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-dark-128.png -------------------------------------------------------------------------------- /icons/archive-icon-dark-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-dark-16.png -------------------------------------------------------------------------------- /icons/archive-icon-dark-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-dark-32.png -------------------------------------------------------------------------------- /icons/archive-icon-dark-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-dark-64.png -------------------------------------------------------------------------------- /icons/archive-icon-light-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-light-16.png -------------------------------------------------------------------------------- /icons/archive-icon-light-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-light-32.png -------------------------------------------------------------------------------- /icons/archive-icon-light-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-light-64.png -------------------------------------------------------------------------------- /screenshots/toolbar-button.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/screenshots/toolbar-button.jpg -------------------------------------------------------------------------------- /icons/archive-icon-light.128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanmccann/archive-url-firefox-addon/HEAD/icons/archive-icon-light.128.png -------------------------------------------------------------------------------- /popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 |
Archive to Wayback Machine
23 |
24 |
Archive to Ghost Archive
25 |
26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Wayback Machine Firefox Addon 2 | ======== 3 | 4 | This Firefox addon will add a new item to the context menu and the toolbar menu, allowing you to save the current webpage to the [Wayback Machine](http://archive.org/web/). The toolbar button also allows you to save the webpage to [archive.today](archive.today). 5 | 6 | Installation 7 | ======== 8 | 9 | This extension is available for installation via [Mozzila's Add-Ons](https://addons.mozilla.org/en-US/firefox/addon/save-url-to-wayback-machine). 10 | 11 | To install this extension from source, please utilize the following guide - [Temporary Installation in Firefox](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox). -------------------------------------------------------------------------------- /options/options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 | Archivers 12 | 13 | 14 |
15 | 16 | 17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 |

26 | 27 | 28 |
29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Archive URL", 4 | "version": "1.9", 5 | 6 | "author": "Jonathan McCann", 7 | "homepage_url": "https://github.com/jonathanmccann/archive-url-firefox-addon", 8 | 9 | "description": "This Firefox addon will add a new item to the context menu and a button the toolbar menu, allowing you to archive the current webpage to the Wayback Machine, archive.today, and Ghost Archive. When right clicking on a link or an image, you can choose to archive that URL instead of the current URL.", 10 | 11 | "icons": { 12 | "16": "icons/archive-icon-dark-16.png", 13 | "32": "icons/archive-icon-dark-32.png", 14 | "64": "icons/archive-icon-dark-64.png", 15 | "128": "icons/archive-icon-dark-128.png" 16 | }, 17 | 18 | "options_ui": { 19 | "page": "options/options.html" 20 | }, 21 | 22 | "permissions": [ 23 | "contextMenus", 24 | "storage", 25 | "tabs" 26 | ], 27 | 28 | "browser_action": { 29 | "browser_style": false, 30 | "default_icon": { 31 | "16": "icons/archive-icon-dark-16.png", 32 | "32": "icons/archive-icon-dark-32.png" 33 | }, 34 | "default_title": "Archive to Wayback Machine", 35 | "theme_icons": [{ 36 | "light": "icons/archive-icon-light-16.png", 37 | "dark": "icons/archive-icon-dark-16.png", 38 | "size": 16 39 | }, 40 | { 41 | "light": "icons/archive-icon-light-32.png", 42 | "dark": "icons/archive-icon-dark-32.png", 43 | "size": 32 44 | }] 45 | }, 46 | 47 | "background": { 48 | "scripts": ["archive.js"] 49 | } 50 | } -------------------------------------------------------------------------------- /popup/popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("click", function(e) { 2 | if (!e.target.classList.contains("archiver")) { 3 | return; 4 | } 5 | 6 | browser.tabs.query({active: true, currentWindow: true}).then(tabs => { 7 | if (tabs[0]) { 8 | if (e.target.id == "multiple") { 9 | browser.extension.getBackgroundPage().archiveUrlToMultiple(tabs[0].url); 10 | } 11 | else if (e.target.id == "archive") { 12 | browser.extension.getBackgroundPage().archiveUrlArchive(tabs[0].url); 13 | } 14 | else if (e.target.id == "ghostArchive") { 15 | browser.extension.getBackgroundPage().archiveUrlGhostArchive(tabs[0].url); 16 | } 17 | else if (e.target.id == "wayback") { 18 | browser.extension.getBackgroundPage().archiveUrlWayback(tabs[0].url); 19 | } 20 | } 21 | 22 | window.close(); 23 | }); 24 | }); 25 | 26 | function resetArchiveURL() { 27 | browser.storage.local.get("archiveDomain").then(name => { 28 | var archiveDomain = name.archiveDomain; 29 | 30 | if (archiveDomain == undefined) { 31 | archiveDomain = "today"; 32 | } 33 | 34 | document.getElementById("archive").innerHTML = "Archive to archive." + archiveDomain; 35 | }); 36 | 37 | browser.storage.local.get("enabledArchivers", function(name) { 38 | if ((name.enabledArchivers == undefined) || (name.enabledArchivers == "")) { 39 | document.getElementById("wayback").style.display = "block"; 40 | } 41 | else { 42 | var count = 0; 43 | 44 | for (var enabledArchiver of JSON.parse(name.enabledArchivers)) { 45 | document.getElementById(enabledArchiver).style.display = "block"; 46 | 47 | count++; 48 | } 49 | 50 | document.getElementById("multiple").style.display = "block"; 51 | 52 | if (count == 2) { 53 | document.getElementById("multiple").innerHTML = "Archive to both"; 54 | } 55 | else { 56 | document.getElementById("multiple").innerHTML = "Archive to all"; 57 | } 58 | } 59 | }); 60 | } 61 | 62 | document.addEventListener("DOMContentLoaded", resetArchiveURL); -------------------------------------------------------------------------------- /options/options.js: -------------------------------------------------------------------------------- 1 | function saveOptions(e) { 2 | var archiver; 3 | 4 | var archiveDomain = document.options.archiveDomain.value; 5 | 6 | if ((archiveDomain == undefined) || (archiveDomain == "")) { 7 | archiveDomain = "today"; 8 | 9 | document.getElementById("archiveDomain").value = "today"; 10 | } 11 | 12 | document.getElementById("archiveLabel").innerHTML = "archive." + archiveDomain; 13 | 14 | var enabledArchiverCheckboxes = document.querySelectorAll('input[type=checkbox]:checked'); 15 | 16 | var enabledArchivers = []; 17 | 18 | if (enabledArchiverCheckboxes.length == 0) { 19 | enabledArchivers.push("wayback"); 20 | } 21 | else { 22 | for (i = 0; i < enabledArchiverCheckboxes.length; i++) { 23 | enabledArchivers.push(enabledArchiverCheckboxes[i].id) 24 | } 25 | } 26 | 27 | setArchiver(enabledArchivers); 28 | 29 | browser.storage.local.set({ 30 | archiveDomain: archiveDomain, 31 | enabledArchivers: JSON.stringify(enabledArchivers) 32 | }); 33 | 34 | e.preventDefault(); 35 | } 36 | 37 | function setArchiveDomain(archiveDomain) { 38 | if ((archiveDomain == undefined) || (archiveDomain == "")) { 39 | document.getElementById("archiveDomain").value = "today"; 40 | document.getElementById("archiveLabel").innerHTML = "archive.today"; 41 | } 42 | else { 43 | document.getElementById("archiveDomain").value = archiveDomain; 44 | document.getElementById("archiveLabel").innerHTML = "archive." + archiveDomain; 45 | } 46 | } 47 | 48 | function setArchiver(enabledArchivers) { 49 | for (i = 0; i < enabledArchivers.length; i++) { 50 | document.getElementById(enabledArchivers[i]).checked = true; 51 | } 52 | } 53 | 54 | function restoreOptions() { 55 | browser.storage.local.get("archiveDomain", function(archiveDomain) { 56 | setArchiveDomain(archiveDomain.archiveDomain); 57 | }); 58 | 59 | browser.storage.local.get("enabledArchivers", function(enabledArchivers) { 60 | if ((enabledArchivers.enabledArchivers == undefined) || (enabledArchivers.enabledArchivers == "")) { 61 | setArchiver(["wayback"]); 62 | } 63 | else { 64 | setArchiver(JSON.parse(enabledArchivers.enabledArchivers)); 65 | } 66 | }); 67 | } 68 | 69 | document.addEventListener("DOMContentLoaded", restoreOptions); 70 | document.querySelector("form").addEventListener("submit", saveOptions); -------------------------------------------------------------------------------- /archive.js: -------------------------------------------------------------------------------- 1 | var archiveDomain; 2 | var archiver; 3 | 4 | var enabledArchivers = []; 5 | 6 | var archiveCurrentUrlTitleToArchive = "Archive Current URL to archive."; 7 | var archiveCurrentUrlTitleToGhostArchive = "Archive Current URL to Ghost Archive"; 8 | var archiveCurrentUrlTitleToWayback = "Archive Current URL to Wayback Machine"; 9 | var archiveCurrentUrlTitleToMultiple = "Archive Current URL to "; 10 | 11 | var archiveImageUrlTitleToArchive = "Archive Image URL to archive."; 12 | var archiveImageUrlTitleToGhostArchive = "Archive Image URL to Ghost Archive"; 13 | var archiveImageUrlTitleToWayback = "Archive Image URL to Wayback Machine"; 14 | var archiveImageUrlTitleToMultiple = "Archive Image URL to "; 15 | 16 | var archiveLinkUrlTitleToArchive = "Archive Link URL to archive."; 17 | var archiveLinkUrlTitleToGhostArchive = "Archive Link URL to Ghost Archive"; 18 | var archiveLinkUrlTitleToWayback = "Archive Link URL to Wayback Machine"; 19 | var archiveLinkUrlTitleToMultiple = "Archive Link URL to "; 20 | 21 | function saveCurrentUrl() { 22 | browser.tabs.query({active: true, currentWindow: true}, function(tabs) { 23 | if (tabs[0]) { 24 | if (enabledArchivers[0] == "archive") { 25 | archiveUrlArchive(tabs[0].url); 26 | } 27 | else if (enabledArchivers[0] == "ghostArchive") { 28 | archiveUrlGhostArchive(tabs[0].url); 29 | } 30 | else { 31 | archiveUrlWayback(tabs[0].url); 32 | } 33 | } 34 | }); 35 | } 36 | 37 | function archiveUrlArchive(url) { 38 | browser.tabs.create({ 39 | url: "https://archive." + archiveDomain + "/?run=1&url=" + encodeURIComponent(url), 40 | active: false 41 | }) 42 | } 43 | 44 | function archiveUrlGhostArchive(url) { 45 | browser.tabs.create({ 46 | url: "https://ghostarchive.org/save/" + url, 47 | active: false 48 | }) 49 | } 50 | 51 | function archiveUrlWayback(url) { 52 | browser.tabs.create({ 53 | url: "https://web.archive.org/save/" + url, 54 | active: false 55 | }) 56 | } 57 | 58 | function archiveUrlToMultiple(url) { 59 | for (enabledArchiver of enabledArchivers) { 60 | if (enabledArchiver == "archive") { 61 | archiveUrlArchive(url); 62 | } 63 | else if (enabledArchiver == "ghostArchive") { 64 | archiveUrlGhostArchive(url); 65 | } 66 | else if (enabledArchiver == "wayback") { 67 | archiveUrlWayback(url); 68 | } 69 | } 70 | } 71 | 72 | // Check for changes to 'archiver' and update the local value accordingly as well as the toolbar title 73 | function checkForArchiverChanges(changes, area) { 74 | if (area == "local") { 75 | var preferenceChange = false; 76 | 77 | var changedItems = Object.keys(changes); 78 | 79 | for (item of changedItems) { 80 | if (item == "archiveDomain") { 81 | archiveDomain = changes[item].newValue; 82 | 83 | preferenceChange = true; 84 | } 85 | else if (item == "enabledArchivers") { 86 | enabledArchivers = JSON.parse(changes[item].newValue); 87 | 88 | preferenceChange = true; 89 | } 90 | } 91 | 92 | if (preferenceChange) { 93 | updateArchiver(); 94 | } 95 | } 96 | } 97 | 98 | function addArchiveContextMenus() { 99 | browser.contextMenus.create({ 100 | id: "archive-url-archive", 101 | title: archiveCurrentUrlTitleToArchive + archiveDomain, 102 | contexts: ["page"] 103 | }) 104 | 105 | browser.contextMenus.create({ 106 | id: "archive-image-url-archive", 107 | title: archiveImageUrlTitleToArchive + archiveDomain, 108 | contexts: ["image"] 109 | }) 110 | 111 | browser.contextMenus.create({ 112 | id: "archive-link-url-archive", 113 | title: archiveLinkUrlTitleToArchive + archiveDomain, 114 | contexts: ["link"] 115 | }) 116 | } 117 | 118 | function addBothArchiveContextMenus() { 119 | browser.contextMenus.create({ 120 | id: "archive-url-both", 121 | title: archiveCurrentUrlTitleToMultiple + (enabledArchivers.length == 2 ? "both" : "all"), 122 | contexts: ["page"] 123 | }) 124 | 125 | browser.contextMenus.create({ 126 | id: "archive-image-url-both", 127 | title: archiveImageUrlTitleToMultiple + (enabledArchivers.length == 2 ? "both" : "all"), 128 | contexts: ["image"] 129 | }) 130 | 131 | browser.contextMenus.create({ 132 | id: "archive-link-url-both", 133 | title: archiveLinkUrlTitleToMultiple + (enabledArchivers.length == 2 ? "both" : "all"), 134 | contexts: ["link"] 135 | }) 136 | } 137 | 138 | function addGhostArchiveContextMenus() { 139 | browser.contextMenus.create({ 140 | id: "archive-url-ghost-archive", 141 | title: archiveCurrentUrlTitleToGhostArchive, 142 | contexts: ["page"] 143 | }) 144 | 145 | browser.contextMenus.create({ 146 | id: "archive-image-url-ghost-archive", 147 | title: archiveImageUrlTitleToGhostArchive, 148 | contexts: ["image"] 149 | }) 150 | 151 | browser.contextMenus.create({ 152 | id: "archive-link-url-ghost-archive", 153 | title: archiveLinkUrlTitleToGhostArchive, 154 | contexts: ["link"] 155 | }) 156 | } 157 | 158 | function addWaybackContextMenus() { 159 | browser.contextMenus.create({ 160 | id: "archive-url-wayback", 161 | title: archiveCurrentUrlTitleToWayback, 162 | contexts: ["page"] 163 | }) 164 | 165 | browser.contextMenus.create({ 166 | id: "archive-image-url-wayback", 167 | title: archiveImageUrlTitleToWayback, 168 | contexts: ["image"] 169 | }) 170 | 171 | browser.contextMenus.create({ 172 | id: "archive-link-url-wayback", 173 | title: archiveLinkUrlTitleToWayback, 174 | contexts: ["link"] 175 | }) 176 | } 177 | 178 | function updateArchiver() { 179 | browser.contextMenus.removeAll(); 180 | 181 | if (enabledArchivers.length == 1) { 182 | browser.browserAction.setPopup({popup: ""}); 183 | 184 | browser.browserAction.onClicked.addListener(saveCurrentUrl); 185 | 186 | if (enabledArchivers[0] == "archive") { 187 | browser.browserAction.setTitle({title: "Archive to archive." + archiveDomain}); 188 | 189 | addArchiveContextMenus(); 190 | } 191 | else if (enabledArchivers[0] == "ghostArchive") { 192 | browser.browserAction.setTitle({title: "Archive to Ghost Archive"}); 193 | 194 | addGhostArchiveContextMenus(); 195 | } 196 | else { 197 | browser.browserAction.setTitle({title: "Archive to Wayback Machine"}); 198 | 199 | addWaybackContextMenus(); 200 | } 201 | } 202 | else { 203 | browser.browserAction.setPopup({popup: "popup/popup.html"}); 204 | 205 | browser.browserAction.onClicked.removeListener(saveCurrentUrl); 206 | 207 | browser.browserAction.setTitle({title: "Archive current URL"}); 208 | 209 | for (enabledArchiver of enabledArchivers) { 210 | if (enabledArchiver == "archive") { 211 | addArchiveContextMenus(); 212 | } 213 | else if (enabledArchiver == "ghostArchive") { 214 | addGhostArchiveContextMenus(); 215 | } 216 | else if (enabledArchiver == "wayback") { 217 | addWaybackContextMenus(); 218 | } 219 | } 220 | 221 | addBothArchiveContextMenus(); 222 | } 223 | } 224 | 225 | browser.contextMenus.onClicked.addListener(function(info, tab) { 226 | switch (info.menuItemId) { 227 | case "archive-url-archive": 228 | archiveUrlArchive(tab.url); 229 | break; 230 | case "archive-image-url-archive": 231 | archiveUrlArchive(info.srcUrl); 232 | break; 233 | case "archive-link-url-archive": 234 | archiveUrlArchive(info.linkUrl); 235 | break; 236 | case "archive-url-ghost-archive": 237 | archiveUrlGhostArchive(tab.url); 238 | break; 239 | case "archive-image-url-ghost-archive": 240 | archiveUrlGhostArchive(info.srcUrl); 241 | break; 242 | case "archive-link-url-ghost-archive": 243 | archiveUrlGhostArchive(info.linkUrl); 244 | break; 245 | case "archive-url-wayback": 246 | archiveUrlWayback(tab.url); 247 | break; 248 | case "archive-image-url-wayback": 249 | archiveUrlWayback(info.srcUrl); 250 | break; 251 | case "archive-link-url-wayback": 252 | archiveUrlWayback(info.linkUrl); 253 | break; 254 | case "archive-url-both": 255 | archiveUrlToMultiple(tab.url); 256 | break; 257 | case "archive-image-url-both": 258 | archiveUrlToMultiple(info.srcUrl); 259 | break; 260 | case "archive-link-url-both": 261 | archiveUrlToMultiple(info.linkUrl); 262 | break; 263 | } 264 | }); 265 | 266 | // Add listener for changes to local storage to update the archiver 267 | browser.storage.onChanged.addListener(checkForArchiverChanges); 268 | 269 | // Read the preferences from local storage 270 | browser.storage.local.get("archiverDomain", function(name) { 271 | archiveDomain = name.archiveDomain; 272 | 273 | if (archiveDomain == undefined) { 274 | archiveDomain = "today"; 275 | } 276 | 277 | browser.storage.local.get("enabledArchivers", function(name) { 278 | if ((name.enabledArchivers == undefined) || (name.enabledArchivers == "")) { 279 | enabledArchivers.push("wayback"); 280 | } 281 | else { 282 | enabledArchivers = JSON.parse(name.enabledArchivers); 283 | } 284 | 285 | updateArchiver(); 286 | }); 287 | }); --------------------------------------------------------------------------------