├── README.md ├── locale ├── de-DE.properties ├── en-US.properties ├── es-ES.properties ├── ro-RO.properties ├── es-ES │ ├── prefwindow.dtd │ ├── ttShortcuts_preferences.dtd │ ├── customXulKeys.dtd │ └── keybinder.properties ├── en-US │ ├── prefwindow.dtd │ ├── ttShortcuts_preferences.dtd │ ├── customXulKeys.dtd │ └── keybinder.properties ├── ro-RO │ ├── prefwindow.dtd │ ├── ttShortcuts_preferences.dtd │ ├── customXulKeys.dtd │ └── keybinder.properties └── de-DE │ ├── prefwindow.dtd │ ├── customXulKeys.dtd │ ├── ttShortcuts_preferences.dtd │ └── keybinder.properties ├── .gitignore ├── data ├── icon18.png ├── icon32.png ├── icon36.png ├── icon64.png ├── blank.html ├── changelog.js ├── prefs.css ├── bug-78414_content.js └── README.md ├── chrome ├── skin │ ├── fresh_snow.png │ ├── fresh_snow_@2X.png │ └── prefs.css └── content │ ├── urlpatterns.xul │ ├── releasenotes.xul │ ├── ttShortcuts_preferences.xul │ └── customXulKeys.xul ├── docs └── main.md ├── chrome.manifest ├── tests └── test-main.js ├── lib ├── prefs │ ├── colors.js │ ├── treeview.js │ ├── prefpane.js │ └── tree.js ├── core │ ├── custom.js │ ├── modifiers.js │ ├── shortcut.js │ ├── overlay.js │ └── key.js ├── util │ ├── serialization.js │ └── windows.js ├── main.js └── patch │ ├── bug-645371.js │ ├── bug-406199.js │ └── bug-78414.js ├── package.json └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | ./data/README.md -------------------------------------------------------------------------------- /locale/de-DE.properties: -------------------------------------------------------------------------------- 1 | ./de-DE/keybinder.properties -------------------------------------------------------------------------------- /locale/en-US.properties: -------------------------------------------------------------------------------- 1 | ./en-US/keybinder.properties -------------------------------------------------------------------------------- /locale/es-ES.properties: -------------------------------------------------------------------------------- 1 | ./es-ES/keybinder.properties -------------------------------------------------------------------------------- /locale/ro-RO.properties: -------------------------------------------------------------------------------- 1 | ./ro-RO/keybinder.properties -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .com.apple.timemachine.supported 3 | -------------------------------------------------------------------------------- /data/icon18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/data/icon18.png -------------------------------------------------------------------------------- /data/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/data/icon32.png -------------------------------------------------------------------------------- /data/icon36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/data/icon36.png -------------------------------------------------------------------------------- /data/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/data/icon64.png -------------------------------------------------------------------------------- /chrome/skin/fresh_snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/chrome/skin/fresh_snow.png -------------------------------------------------------------------------------- /chrome/skin/fresh_snow_@2X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Kamina/keybinder/HEAD/chrome/skin/fresh_snow_@2X.png -------------------------------------------------------------------------------- /data/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Keybinder Release Notes 4 | 5 | 6 | AFDISFEGRE 7 | 8 | -------------------------------------------------------------------------------- /docs/main.md: -------------------------------------------------------------------------------- 1 | The main module is a program that creates a widget. When a user clicks on 2 | the widget, the program loads the mozilla.org website in a new tab. 3 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | content keybinder chrome/content/ contentaccessible=yes 2 | skin keybinder style chrome/skin/ 3 | locale keybinder en-US locale/en-US/ 4 | locale keybinder es-ES locale/es-ES/ 5 | locale keybinder de-DE locale/de-DE/ 6 | locale keybinder ro-RO locale/ro-RO/ -------------------------------------------------------------------------------- /data/changelog.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var changelog_html = self.options.changelog; 6 | this.window.document.body.innerHTML = changelog_html; -------------------------------------------------------------------------------- /locale/es-ES/prefwindow.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /locale/en-US/prefwindow.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /locale/ro-RO/prefwindow.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /locale/de-DE/prefwindow.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /locale/en-US/ttShortcuts_preferences.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /locale/es-ES/ttShortcuts_preferences.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /locale/de-DE/customXulKeys.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /locale/de-DE/ttShortcuts_preferences.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /locale/en-US/customXulKeys.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /locale/ro-RO/ttShortcuts_preferences.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /locale/es-ES/customXulKeys.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /locale/ro-RO/customXulKeys.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/test-main.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const main = require("../lib/main"); 6 | 7 | exports.test_test_run = function(test) { 8 | test.pass("Unit test running!"); 9 | }; 10 | 11 | exports.test_id = function(test) { 12 | test.assert(require("sdk/self").id.length > 0); 13 | }; 14 | 15 | exports.test_url = function(test) { 16 | require("request").Request({ 17 | url: "http://www.mozilla.org/", 18 | onComplete: function(response) { 19 | test.assertEqual(response.statusText, "OK"); 20 | test.done(); 21 | } 22 | }).get(); 23 | test.waitUntilDone(20000); 24 | }; 25 | 26 | exports.test_open_tab = function(test) { 27 | const tabs = require("tabs"); 28 | tabs.open({ 29 | url: "http://www.mozilla.org/", 30 | onReady: function(tab) { 31 | test.assertEqual(tab.url, "http://www.mozilla.org/"); 32 | test.done(); 33 | } 34 | }); 35 | test.waitUntilDone(20000); 36 | }; 37 | -------------------------------------------------------------------------------- /lib/prefs/colors.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | let ColorNames = exports.ColorNames = ["aqua", "aquamarine", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue", "forestgreen", "fuchsia", "gold", "goldenrod", "gray", "green", "greenyellow", "hotpink", "indianred", "indigo", "khaki", "lawngreen", "lightblue", "lightcoral", "lightcyan", "lightgreen", "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightsteelblue", "lime", "limegreen", "magenta", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", "mediumvioletred", "midnightblue", "mistyrose", "navy", "olive", "olivedrab", "orange", "orangered", "orchid", "palegreen", "paleturquoise", "palevioletred", "peru", "pink", "plum", "powderblue", "purple", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen", "sienna", "skyblue", "slateblue", "slategray", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "yellowgreen"]; 6 | 7 | ColorNames.getRandom = function() { 8 | return this[Math.floor(Math.random() * this.length)]; 9 | } -------------------------------------------------------------------------------- /chrome/content/urlpatterns.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /chrome/content/releasenotes.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 51 |
52 |
53 | 54 | 55 | 56 | 57 |
-------------------------------------------------------------------------------- /lib/core/custom.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | const { Windows } = require("../util/windows"); 5 | const { Overlays } = require("../core/overlay"); 6 | 7 | var _ = require("sdk/l10n").get; 8 | const extensionPrefs = require("sdk/simple-prefs"); 9 | 10 | let allCustomKeys = exports.allCustomKeys = (function () { 11 | let cache; 12 | return function () { 13 | if (!cache) { 14 | cache = new Map(); 15 | for (let key of JSON.parse(extensionPrefs.prefs['customXULKeys'])) { 16 | cache.set(key.key, new CustomXUL({key:key.key, label:key.label, command:key.command})) 17 | } 18 | } 19 | return cache; 20 | }; 21 | })(); 22 | 23 | let storeKeys = exports.storeKeys = function() { 24 | let array = []; 25 | for (let [mapKey, mapValue] of allCustomKeys().entries()) { 26 | array.push({"key":mapValue.key, "label":mapValue.label, "command":mapValue.command}); 27 | } 28 | extensionPrefs.prefs['customXULKeys'] = JSON.stringify(array); 29 | } 30 | 31 | let find = exports.find = function (key) { 32 | return allCustomKeys().get(key); 33 | }; 34 | 35 | let filter = exports.filter = function (fun) { 36 | let filtered = new Map(); 37 | for (let key of allCustomKeys().values()) { 38 | if (fun(key)) { 39 | filtered.set(key.id, key); 40 | } 41 | } 42 | return filtered; 43 | } 44 | 45 | let CustomXUL = exports.CustomXUL = function (data) { 46 | this.key = data.key; 47 | this.label = data.label; 48 | this.command = data.command; 49 | } 50 | 51 | CustomXUL.prototype = { 52 | nsID: function () { 53 | return String.concat("Keybinder_", this.key); 54 | }, 55 | 56 | getLabel: function () { 57 | return this.label; 58 | }, 59 | 60 | getKey: function () { 61 | return this.key; 62 | }, 63 | 64 | getCommand: function () { 65 | return this.command; 66 | } 67 | }; -------------------------------------------------------------------------------- /data/prefs.css: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | 7 | prefpane > .content-box, 8 | prefwindow > .paneDeckContainer { 9 | overflow: visible; 10 | } 11 | 12 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(container), 13 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(custom) { 14 | font-weight: 700; 15 | } 16 | 17 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(overridden) { 18 | text-decoration: line-through; 19 | } 20 | 21 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(container) { 22 | padding-left: 4px; 23 | } 24 | 25 | 26 | prefwindow[animated="false"] #ttCustomizableShortcuts_Tree { 27 | -moz-box-flex: 1; 28 | } 29 | 30 | prefwindow[animated="true"] #ttCustomizableShortcuts_Tree { 31 | height: 25em; 32 | } 33 | 34 | hbox { border-style : solid; 35 | border-color: grey; 36 | padding: 1px; 37 | margin: 1px; 38 | } 39 | vbox { border-style : solid; 40 | border-color: black; 41 | padding: 1px; 42 | margin: 1px; 43 | } 44 | separator { 45 | border-style : solid; 46 | border-color: green; 47 | padding: 1px; 48 | margin: 1px; 49 | } 50 | 51 | prefwindow { border-style : solid; 52 | border-color: red; 53 | padding: 1px; 54 | margin: 1px; 55 | } 56 | 57 | spacer { border-style : solid; 58 | border-color: blue; 59 | padding: 1px; 60 | margin: 1px; 61 | } 62 | 63 | #tree_box { border-style : solid; 64 | border-color: purple; 65 | padding: 1px; 66 | margin: 1px; 67 | } 68 | 69 | #button_box { border-style : solid; 70 | border-color: orange; 71 | padding: 1px; 72 | margin: 1px; 73 | } 74 | 75 | #search_box { border-style : solid; 76 | border-color: cyan; 77 | padding: 1px; 78 | margin: 1px; 79 | } 80 | 81 | prefpane { border-style : solid; 82 | border-color: brown; 83 | padding: 1px; 84 | margin: 1px; 85 | -moz-box-flex:1; 86 | } -------------------------------------------------------------------------------- /lib/util/serialization.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | 7 | const keys = require("../core/key"); 8 | const { Shortcut } = require("../core/shortcut"); 9 | const { Modifiers } = require("../core/modifiers"); 10 | 11 | let serialize = exports.serialize = function (data) { 12 | if (!data) { 13 | return; 14 | } 15 | 16 | let Overlay = require("../core/overlay").Overlay; 17 | 18 | if (data instanceof Overlay) { 19 | return { 20 | _type: "overlay", 21 | key: serialize(data.key), 22 | shortcut: serialize(data.shortcut) 23 | }; 24 | } 25 | 26 | if (data instanceof keys.Key) { 27 | return { 28 | _type: "key", 29 | id: data.id 30 | }; 31 | } 32 | 33 | if (data instanceof Shortcut) { 34 | return ((data.disabled && { 35 | _type: "shortcut", 36 | disabled: true 37 | }) || { 38 | _type: "shortcut", 39 | key: data.key, 40 | keycode: data.keycode, 41 | code: data.code, 42 | modifiers: serialize(data.modifiers) 43 | }); 44 | } 45 | if (data instanceof Modifiers) { 46 | return { 47 | _type: "modifiers", 48 | modifiers: data.modifiers 49 | }; 50 | } 51 | 52 | return data; 53 | } 54 | 55 | let unserialize = exports.unserialize = function (data) { 56 | if (!data) return; 57 | 58 | let Overlay = require("../core/overlay").Overlay; 59 | let Overlays = require("../core/overlay").Overlays; 60 | 61 | if ("overlay" == data._type) return new Overlay({ 62 | key: unserialize(data.key), 63 | shortcut: unserialize(data.shortcut) 64 | }, { 65 | dontStore: true 66 | }); 67 | 68 | if ("key" == data._type) { 69 | return keys.find(data.id); 70 | 71 | } 72 | 73 | if ("shortcut" == data._type) { 74 | if (!data.disabled) { 75 | var short_instance = new Shortcut({ 76 | key: data.key, 77 | keycode: data.keycode, 78 | code: data.code, 79 | modifiers: unserialize(data.modifiers) 80 | }); 81 | } else { 82 | var short_instance = new Shortcut({ 83 | disabled: true 84 | }); 85 | } 86 | return short_instance; 87 | } 88 | 89 | if ("modifiers" == data._type) { 90 | return new Modifiers({ 91 | modifiers: data.modifiers 92 | }); 93 | } 94 | 95 | return data; 96 | } 97 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | const { Windows } = require("./util/windows"); 7 | const { windowMediator } = require("./util/functions"); 8 | const system = require("sdk/system"); 9 | const extensionPrefs = require("sdk/simple-prefs"); 10 | const self = require("sdk/self"); 11 | 12 | if (37 < system.version) { 13 | let domainUrlPattern = extensionPrefs.prefs['domainUrlPattern']; 14 | console.log("Firefox 38 or newer detected, checking URL Patterns"); 15 | console.log("URL Patterns: "+domainUrlPattern); 16 | if ("[\\\u0022*\\\u0022]" == domainUrlPattern) { 17 | console.warn("URL Patterns stored in old(cfx) format; correcting."); 18 | let domainUrlPattern = "[\"*\"]" 19 | console.log("URL Patterns (Corrected): "+domainUrlPattern); 20 | extensionPrefs.prefs['domainUrlPattern'] = domainUrlPattern; 21 | } 22 | } 23 | 24 | let { storage } = require("sdk/simple-storage"); 25 | if (storage.overlays) { 26 | if (1 < storage.overlays.length) { 27 | console.log("Detected shortcuts stored in the old format; correcting."); 28 | extensionPrefs.prefs['keybinderOverlays'] = JSON.stringify(storage); 29 | storage.overlays = []; 30 | } 31 | storage = ""; 32 | } 33 | 34 | if ("upgrade" == self.loadReason) { 35 | var md = require('markdown-it/dist/markdown-it.min.js')({ 36 | html: false, 37 | linkify: true, 38 | typographer: true, 39 | xhtmlOut: true 40 | }); 41 | md.use(require("markdown-it-table-of-contents")); 42 | md.use(require("markdown-it-anchor")); 43 | var changelog_html = md.render(self.data.load("README.md")); 44 | var releaseNotes = Windows.getMostRecentWindow().openDialog("chrome://keybinder/content/releasenotes.xul","Changelog","centerscreen,titlebar=yes,dialog=yes,scrollbars,modal=no,chrome=yes",changelog_html); 45 | Windows.getMostRecentWindow().setTimeout(function() {windowMediator.getMostRecentWindow("Keybinder:ReleaseNotes").focus()},0); 46 | } 47 | 48 | require("./patch/bug-78414"); 49 | 50 | exports.main = function(options, callbacks) { 51 | Windows.addEventListener("keydown", Windows.handleKeyPress); 52 | 53 | }; 54 | 55 | exports.onUnload = function(reason) { 56 | Windows.enableKeys(); 57 | if (windowMediator.getMostRecentWindow("Keybinder:URLPatterns")) { windowMediator.getMostRecentWindow("Keybinder:URLPatterns").window.close(); } 58 | if (windowMediator.getMostRecentWindow("Keybinder:config")) { windowMediator.getMostRecentWindow("Keybinder:config").window.close(); } 59 | if (windowMediator.getMostRecentWindow("Keybinder:CustomXUL")) { windowMediator.getMostRecentWindow("Keybinder:CustomXUL").window.close(); } 60 | Windows.removeEventListener("keydown", Windows.handleKeyPress); 61 | }; 62 | -------------------------------------------------------------------------------- /chrome/content/ttShortcuts_preferences.xul: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /chrome/content/customXulKeys.xul: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /lib/patch/bug-645371.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 7 | const { Windows } = require("../util/windows"); 8 | 9 | exports.applyPatch = function (window) { 10 | let { gBrowser, TabView, document } = window; 11 | 12 | if (document.getElementById("key_nextTabGroup")) { 13 | return; 14 | } 15 | 16 | let mainCommandSet = document.getElementById("mainCommandSet"); 17 | let mainKeyset = document.getElementById("mainKeyset"); 18 | 19 | // 20 | let cmd1 = createXulElement("command", { 21 | "id": "Browser:NextTabGroup", 22 | "oncommand": "TabView.switchToNextGroup();" 23 | }, mainCommandSet); 24 | 25 | // 26 | let cmd2 = createXulElement("command", { 27 | "id": "Browser:PreviousTabGroup", 28 | "oncommand": "TabView.switchToPreviousGroup();" 29 | }, mainCommandSet); 30 | 31 | // 32 | let key1 = createXulElement("key", { 33 | "id": "key_nextTabGroup", 34 | "key": "`", 35 | "command": "Browser:NextTabGroup", 36 | "modifiers": "accel" 37 | }, mainKeyset); 38 | 39 | // 40 | let key2 = createXulElement("key", { 41 | "id": "key_previousTabGroup", 42 | "key": "`", 43 | "command": "Browser:PreviousTabGroup", 44 | "modifiers": "accel,shift" 45 | }, mainKeyset); 46 | 47 | TabView.switchToNextGroup = function() { 48 | switchGroupItem(false); 49 | }; 50 | 51 | TabView.switchToPreviousGroup = function() { 52 | switchGroupItem(true); 53 | }; 54 | 55 | function createXulElement(tagName, attrs, parent) { 56 | let element = document.createElementNS(XUL_NS, tagName); 57 | 58 | if (attrs) { 59 | for (let name of Object.keys(attrs)) { 60 | element.setAttribute(name, attrs[name]); 61 | } 62 | } 63 | 64 | if (parent) { 65 | parent.appendChild(element); 66 | } 67 | 68 | return element; 69 | } 70 | 71 | function switchGroupItem(reverse) { 72 | let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length; 73 | if (TabView.isVisible() || !numHiddenTabs) 74 | return; 75 | 76 | TabView._initFrame(function () { 77 | let groupItems = TabView._window.GroupItems; 78 | let tabItem = groupItems.getNextGroupItemTab(reverse); 79 | if (!tabItem) 80 | return; 81 | 82 | // Switch to the new tab, and close the old group if it's now empty. 83 | let oldGroupItem = groupItems.getActiveGroupItem(); 84 | gBrowser.selectedTab = tabItem.tab; 85 | oldGroupItem.closeIfEmpty(); 86 | }); 87 | } 88 | }; -------------------------------------------------------------------------------- /lib/core/modifiers.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | const prefs = require("sdk/preferences/service"); 7 | const system = require("sdk/system"); 8 | var _ = require("sdk/l10n").get; 9 | 10 | const modifierKeys = { 11 | 16: "shift", 12 | 17: "control", 13 | 18: "alt", 14 | 91: "meta", 15 | 92: "meta", 16 | 93: "meta", 17 | 224: "meta", 18 | 225: "altgr" 19 | }; 20 | 21 | const modifierNames = { 22 | control: _("ctrlKey"), 23 | meta: function() { 24 | let tmpString = ""; 25 | switch (system.platform) { 26 | case "darwin": 27 | tmpString = _("cmdKey"); 28 | break; 29 | case "winnt": 30 | tmpString = _("winKey"); 31 | break; 32 | case "linux": 33 | tmpString = _("superKey"); 34 | break; 35 | } 36 | return tmpString; 37 | }, 38 | shift: _("shiftKey"), 39 | alt: _("altKey"), 40 | altgr: _("altgrKey") 41 | }; 42 | 43 | function getModifierState(event, modifier) { 44 | // Pressing Alt/Option on OS X shouldn't yield true for AltGraph. 45 | if ("darwin" == system.platform && modifier == "AltGraph") { 46 | return false; 47 | } 48 | if (event.getModifierState(modifier)) { 49 | return true; 50 | } 51 | 52 | // getModifierState() seems to always return false on Linux when only a single modifier key is pressed. Work around by checking the keyCode. 53 | if ("linux" == system.platform) { 54 | return (modifierKeys[event.keyCode] || "") == modifier.toLowerCase(); 55 | } 56 | 57 | return false; 58 | } 59 | 60 | function getAccelKeyName() { 61 | return modifierKeys[prefs.get("ui.key.accelKey")] || "control"; 62 | } 63 | 64 | function isAccelKeyPressed(event) { 65 | let accelKeyName = getAccelKeyName().replace("control", "ctrl"); 66 | return event[accelKeyName + "Key"]; 67 | } 68 | 69 | let Modifiers = exports.Modifiers = function (data) { 70 | this.modifiers = data.modifiers; 71 | } 72 | 73 | Modifiers.prototype.toString = function() { 74 | let keys = {}; 75 | this.modifiers.forEach(function(modifier) { 76 | keys[modifier.toLowerCase()] = 1; 77 | }); 78 | 79 | if (keys.accel) { keys[getAccelKeyName()] = 1; } 80 | 81 | let names = []; 82 | for (let name in modifierNames) { 83 | if (keys[name]) { 84 | let tmpName = modifierNames[name]; 85 | if ("meta" == name) { tmpName = modifierNames.meta(); } 86 | names.push(tmpName); 87 | } 88 | } 89 | 90 | return names.join(" + "); 91 | }; 92 | 93 | Modifiers.fromEvent = function(event) { 94 | let modifiers = []; 95 | if (getModifierState(event, "Shift")) { modifiers.push("shift"); } 96 | if ("winnt" == system.platform) { 97 | if ((getModifierState(event, "Control") && getModifierState(event,"Alt")) || (getModifierState(event,"AltGraph"))) { modifiers.push("altgr"); } 98 | } 99 | else if ("linux" == system.platform) { 100 | if (getModifierState(event, "AltGraph")) { modifiers.push("altgr"); } 101 | } 102 | if (-1 == modifiers.indexOf("altgr")) { 103 | if (getModifierState(event,"Control")) { modifiers.push("control"); } 104 | if (getModifierState(event, "Alt")) { modifiers.push("alt"); } 105 | } 106 | if (getModifierState(event, "Meta") || getModifierState(event, "OS")) { 107 | modifiers.push("meta"); 108 | } 109 | 110 | if (modifiers.length) { 111 | return new Modifiers({modifiers: modifiers}); 112 | } 113 | } 114 | 115 | Modifiers.isModifier = function(key) { 116 | return key in modifierKeys; 117 | } 118 | -------------------------------------------------------------------------------- /data/bug-78414_content.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | var clickListeners = {}; 5 | var timesCrawled = 0; 6 | var pluginCrawlLimit = self.options.pluginCrawlLimit; 7 | 8 | // var observerTest = new MutationObserver( 9 | // 10 | // function (changes) { 11 | // changes.forEach(function(event) { 12 | // console.warn("We've got a mutation, type: "+JSON.stringify(event.type,null,2)); 13 | // console.warn("We've got a mutation, target: "+JSON.stringify(event.target,null,2)); 14 | // console.warn("We've got a mutation, addedNodes: "+JSON.stringify(event.addedNodes,null,2)); 15 | // if ("childList" == event.type || "subtree" == event.type) { 16 | // 17 | // for (let node of event.addedNodes.values()) { 18 | // 19 | // console.warn("Debugging a node... id: "+node.id+", name: "+node.nodeName+", parent: "+node.parentNode.id); 20 | // 21 | // }; 22 | // 23 | // 24 | // 25 | // } 26 | // }); 27 | // } 28 | // ); 29 | 30 | var observerConfig = { subtree: true }; 31 | 32 | var checkAgain = function () { 33 | if (timesCrawled <= pluginCrawlLimit) { 34 | timesCrawled++; 35 | self.port.emit("noEmbeds", { "ID": workerID, "checkAgain": true } ); // Fail me ONE more time... 36 | } 37 | else { 38 | self.port.emit("noEmbeds", { "ID": workerID, "checkAgain": false } ); 39 | } 40 | } 41 | 42 | function checkPlugins (workerID) { 43 | var cssSelectors = self.options.cssSelectors; 44 | var stealFocusDelay = self.options.stealFocusDelay; 45 | var shiftAllowFocus = self.options.shiftAllowFocus; 46 | var embeds = (document.querySelectorAll(cssSelectors) || []); 47 | 48 | for (let i = 0; i < (embeds.length || 0); i++) { 49 | 50 | var listener = function(evt) { 51 | if ((true != shiftAllowFocus || true != evt.shiftKey) && (1 == evt.which)) { 52 | window.setTimeout(function() { 53 | // window.alert('THE EVENT IS WORKING'); 54 | evt.target.blur() 55 | }, stealFocusDelay) 56 | } 57 | } // Capture only left-clicks, and check whether we want to ignore shift+click. 58 | if (!embeds[i].hasAttribute('KeybinderListenerAdded')) { 59 | embeds[i].addEventListener('click', listener); 60 | embeds[i].setAttribute('KeybinderListenerAdded',true); 61 | } 62 | else { console.log(embeds[i].getAttribute("id")+" already has a listener, skipping!"); } 63 | clickListeners[embeds[i].id] = listener; // Store a reference to the new listener for eventual removal. 64 | } 65 | if (embeds.length < 1) { 66 | let body = document.getElementsByTagName("BODY")[0]; 67 | // let html = document.getElementsByTagName("HTML")[0]; 68 | // observerTest.observe(html,observerConfig); 69 | if (!(body.hasAttribute("KeybinderListenerAdded"))) { 70 | body.addEventListener("DOMNodeInserted", checkAgain); 71 | body.setAttribute("KeybinderListenerAdded",true); 72 | } 73 | } 74 | } 75 | 76 | self.port.on("findPlugins", function findPlugins(workerInfo) {// Look for objects to modify. 77 | workerID = workerInfo["ID"].toString(); 78 | checkPlugins(workerID); 79 | }); 80 | 81 | self.port.on("removeDOMListener", function removeDOMListener() { 82 | let body = document.getElementsByTagName("BODY")[0]; 83 | body.removeEventListener("DOMNodeInserted", checkAgain); 84 | body.removeAttribute("KeybinderListenerAdded"); 85 | self.port.emit("disposeOfMinion", { "workerID": workerID }); // Useless worker will need to be... "handled." 86 | }); 87 | 88 | self.port.on("removeMods", function removeMods(workerID) { 89 | for (let i of Object.keys(clickListeners)) { 90 | document.getElementById(i).removeEventListener('click', clickListeners[i]); 91 | document.getElementById(i).removeAttribute('KeybinderListenerAdded'); 92 | } 93 | self.port.emit("noEmbeds", { "ID": workerID, "checkAgain": false } ); 94 | }); 95 | -------------------------------------------------------------------------------- /lib/core/shortcut.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | 7 | const { keys, domKeys } = require("../util/functions"); 8 | const {Modifiers} = require("../core/modifiers"); 9 | var _ = require("sdk/l10n").get; 10 | 11 | let Shortcut = exports.Shortcut = function (data) { 12 | this.key = data.key; 13 | this.keycode = data.keycode; 14 | this.code = data.code; 15 | this.modifiers = data.modifiers; 16 | this.disabled = data.disabled; 17 | } 18 | 19 | Shortcut.prototype.equals = function(obj) { 20 | if (obj instanceof Shortcut) { 21 | return obj.toString() == this.toString(); 22 | } 23 | 24 | return false; 25 | } 26 | 27 | Shortcut.prototype.toString = function() { 28 | if (this._toStringCache) { return this._toStringCache; } 29 | // console.warn("Shortcut.toString()... this.code is: "+this.code+", this.keycode: "+this.keycode); 30 | let parts = []; 31 | 32 | if (this.modifiers) { parts.push(this.modifiers.toString() + " + "); } 33 | 34 | if (this.keycode) { 35 | if (!domKeys.virtual_keys[this.keycode].key) { 36 | let keyName = domKeys.keycodes[domKeys.virtual_keys[this.keycode].keyCode].replace(/^VK_/, ""); 37 | keyName = keyName[0] + keyName.substr(1).toLowerCase(); 38 | keyName = keyName.replace(/_[a-z]/i, str => str[1].toUpperCase()); 39 | var l10n_keyName = _(keyName + "_Key"); 40 | l10n_keyName = l10n_keyName.replace(/(^F\d{1,3})_Key/, "$1"); 41 | } 42 | parts.push(domKeys.virtual_keys[this.keycode].key ? domKeys.virtual_keys[this.keycode].key : l10n_keyName); 43 | } 44 | else { 45 | 46 | let l10n_keyName = _(this.code + "_Key"); 47 | parts.push(l10n_keyName) 48 | 49 | } 50 | // if (1 < this.keycode.length) { 51 | // let keyName = this.keycode.replace(/^VK_/, ""); 52 | // keyName = keyName[0] + keyName.substr(1).toLowerCase(); 53 | // keyName = keyName.replace(/_[a-z]/i, str => str[1].toUpperCase()); 54 | // if (1 == keyName.length) var l10n_keyName = keyName; 55 | // else var l10n_keyName = _(keyName + "_Key"); 56 | // l10n_keyName = l10n_keyName.replace(/(^F\d{1,3})_Key/, "$1"); 57 | // parts.push(l10n_keyName); 58 | // } 59 | 60 | return this._toStringCache = parts.join(""); 61 | } 62 | 63 | Shortcut.prototype.isComplete = function() { 64 | if (this.keycode) { 65 | let tempKeyCode = domKeys.virtual_keys[this.keycode].keyCode; 66 | if (48 <= tempKeyCode && tempKeyCode <= 90) { 67 | // this.modifiers && console.warn("Modifiers? "+JSON.stringify(this.modifiers.modifiers,null,2)); 68 | if (this.modifiers) { 69 | 70 | if (1 == this.modifiers.modifiers.length && this.modifiers.modifiers[0] == "shift") { 71 | // console.warn("Sorry. Just shift is not enough when typing a letter."); 72 | return false; 73 | } 74 | return !!(this.modifiers); 75 | } 76 | 77 | } 78 | else { 79 | return true; 80 | } 81 | } 82 | else { return !!(this.code); } 83 | } 84 | 85 | Shortcut.fromEvent = function(event) { 86 | let data = { 87 | modifiers: Modifiers.fromEvent(event) 88 | }; 89 | 90 | // console.warn("Shortcut.fromEvent()... keyCode: "+event.keyCode+", key: "+event.key+", code: "+event.code+", which: "+event.which); 91 | 92 | if (event.keyCode == 0) { tmpKey = event.code; } 93 | else { tmpKey = event.keyCode } 94 | 95 | if (/^[0-9]+$/.test(tmpKey)) { 96 | // console.warn("Shortcut.fromEvent()... tmpKey's value is: "+tmpKey+", and I think that's a number..."); 97 | if (!Modifiers.isModifier(tmpKey)) { data.keycode = domKeys.keycodes[tmpKey]; } 98 | } 99 | else { 100 | if (!Modifiers.isModifier(tmpKey)) { 101 | data.code = tmpKey; 102 | } 103 | } 104 | var shortcut = new Shortcut(data); 105 | // console.warn("Is this a complete Shortcut?: "+shortcut.isComplete()); 106 | // console.warn("Shortcut.fromEvent()... let's dump our shortcut: "+JSON.stringify(shortcut,null,2)); 107 | // console.warn("Shortcut.fromEvent().toString(): "+shortcut.toString()); 108 | return shortcut; 109 | } 110 | -------------------------------------------------------------------------------- /lib/patch/bug-406199.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 7 | 8 | exports.applyPatch = function (window, ctrlTabPreviewsToggled = false) { 9 | let { gBrowser, TabView, document } = window; 10 | 11 | var _ = require("sdk/l10n").get; 12 | var mainCommandSet = document.getElementById("mainCommandSet"); 13 | var mainKeyset = document.getElementById("mainKeyset"); 14 | const extensionPrefs = require("sdk/simple-prefs"); 15 | 16 | if (!document.getElementById("Browser:NextTab")) { var cmd1 = createXulElement("command", { // 17 | "id": "Browser:NextTab", 18 | "oncommand": "gBrowser.tabContainer.advanceSelectedTab(1, true);" 19 | }, mainCommandSet); } 20 | 21 | if (!document.getElementById("Browser:PrevTab")) { var cmd2 = createXulElement("command", { // 22 | "id": "Browser:PrevTab", 23 | "oncommand": "gBrowser.tabContainer.advanceSelectedTab(-1, true);" 24 | }, mainCommandSet); } 25 | 26 | if (!document.getElementById("Browser:CloseOtherTabs")) { var cmd3 = createXulElement("command", { // 27 | "id": "Browser:CloseOtherTabs", 28 | "oncommand": "gBrowser.removeAllTabsBut(gBrowser.mCurrentTab);" 29 | }, mainCommandSet); } 30 | 31 | if (ctrlTabPreviewsToggled != true) { 32 | 33 | if (!document.getElementById("key_nextTab")) { 34 | var key1 = createXulElement("key", { // 35 | "id": "key_nextTab", 36 | "keycode": "VK_TAB", 37 | "command": "Browser:NextTab", 38 | "modifiers": "control" 39 | }, mainKeyset); 40 | extensionPrefs.prefs["keysMapDirty"] = true; 41 | } 42 | 43 | if (!document.getElementById("key_prevTab")) { 44 | var key2 = createXulElement("key", { // 45 | "id": "key_prevTab", 46 | "keycode": "VK_TAB", 47 | "command": "Browser:PrevTab", 48 | "modifiers": "control,shift" 49 | }, mainKeyset); 50 | extensionPrefs.prefs["keysMapDirty"] = true; 51 | } 52 | 53 | } 54 | else { 55 | 56 | if (document.getElementById("key_nextTab")) { 57 | document.getElementById("key_nextTab").parentNode.removeChild(document.getElementById("key_nextTab")); 58 | extensionPrefs.prefs["keysMapDirty"] = true; 59 | } 60 | 61 | if (document.getElementById("key_prevTab")) { 62 | document.getElementById("key_prevTab").parentNode.removeChild(document.getElementById("key_prevTab")); 63 | extensionPrefs.prefs["keysMapDirty"] = true; 64 | } 65 | } 66 | 67 | 68 | 69 | if (!document.getElementById("key_closeOther")) { var key3 = createXulElement("key", { // 70 | "id": "key_closeOther", 71 | "key": _("closeCmd_key"), 72 | "command": "Browser:CloseOtherTabs", 73 | "modifiers": "control,alt" 74 | }, mainKeyset); } 75 | 76 | if (!document.getElementById("key_quitApplication") && !document.getElementById("key_quitApplicationCmd")) { var key4 = createXulElement("key", { // 77 | "id": "key_quitApplication", 78 | "key": _("quitCmd_key"), 79 | "command": "cmd_quitApplication", 80 | "modifiers": "accel" 81 | }, mainKeyset); } 82 | 83 | if (!document.getElementById("key_preferencesCmd") && !document.getElementById("key_preferencesCmdMac")) { var key5 = createXulElement("key", { // 84 | "id": "key_preferencesCmd", 85 | "key": ",", 86 | "oncommand": "openPreferences();", 87 | "modifiers": "accel" 88 | }, mainKeyset); } 89 | 90 | function createXulElement(tagName, attrs, parent) { 91 | let element = document.createElementNS(XUL_NS, tagName); 92 | 93 | if (attrs) { 94 | for (let name of Object.keys(attrs)) { 95 | element.setAttribute(name, attrs[name]); 96 | } 97 | } 98 | 99 | if (parent) { 100 | parent.appendChild(element); 101 | } 102 | 103 | return element; 104 | } 105 | require("../prefs/prefpane").createNewMenuItem(window, "tabSwitching"); // Add "Next/Previous Tab" items to the "Window" Menu. 106 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "keybinder", 3 | "license": "MPL 2.0", 4 | "author": "Gregorio Litenstein ", 5 | "homepage": "https://github.com/Lord-Kamina/keybinder", 6 | "version": "2.0.5", 7 | "title": "Keybinder", 8 | "id": "keybinder@fail.cl", 9 | "icon": { 10 | "18": "resource://keybinder-at-fail-dot-cl/data/icon18.png", 11 | "32": "resource://keybinder-at-fail-dot-cl/data/icon32.png", 12 | "36": "resource://keybinder-at-fail-dot-cl/data/icon36.png", 13 | "64": "resource://keybinder-at-fail-dot-cl/data/icon64.png" 14 | }, 15 | "main": "lib/main", 16 | "description": "Enables customization and overriding of several default shortcuts.", 17 | "contributors": [ 18 | "Gregorio Litenstein ", 19 | "Tim Taubert ", 20 | "Natz (http://cheetahamsloth.deviantart.com)" 21 | ], 22 | "engines": { 23 | "firefox": ">=38.0a1" 24 | }, 25 | "permissions": { 26 | "private-browsing": true, 27 | "multiprocess": true 28 | }, 29 | "preferences": [ 30 | { 31 | "name": "overridePluginFocus", 32 | "type": "bool", 33 | "title": "Steal focus from plug-ins?", 34 | "description": "When this is on, plugins such as Flash will be told to release mouse focus a certain amount of time after they have been clicked on.", 35 | "value": true 36 | }, 37 | { 38 | "name": "shiftAllowFocus", 39 | "type": "bool", 40 | "title": "Allow plug-ins to keep focus on Shift+Click?", 41 | "description": "When this is on, plugins such as Flash will be allows to retain mouse focus if Shift was pressed during the click.", 42 | "value": true 43 | }, 44 | { 45 | "name": "UrlPatternsDialog", 46 | "type": "control", 47 | "label": "Edit", 48 | "title": "Address Patterns", 49 | "description": "Edit address patterns used to define the websites on which we'll force plug-ins to release focus. Valid patterns are full URLs and domain names with or without '*' as a wildcard. Only one * allowed per URL, the add-on will automatically remove invalid entries from the list." 50 | }, 51 | { 52 | "name": "ShortcutsDialog", 53 | "type": "control", 54 | "label": "Change", 55 | "title": "Shortcut Mappings", 56 | "description": "Open the main extension dialog, which will allow you to edit or disable most application shortcuts." 57 | }, 58 | { 59 | "name": "pluginCssSelectors", 60 | "type": "string", 61 | "title": "List of CSS Selectors by which to find plugins", 62 | "description": "This list will define which plugins the extension will try to override as described above.", 63 | "value": "embed,object" 64 | }, 65 | { 66 | "name": "domainUrlPattern", 67 | "type": "string", 68 | "title": "Address Pattern", 69 | "value": "[\"*\"]", 70 | "hidden": true 71 | }, 72 | { 73 | "name": "pluginCrawlLimit", 74 | "type": "menulist", 75 | "title": "Retry Limit", 76 | "description": "Some sites use javascript to add flash after everything is loaded, in these cases we need to rescan the page when a change in the structure is detected. This determines the maximum number of items this should be allowed per page. Default is 5 times.", 77 | "value": 5, 78 | "options": [ 79 | { 80 | "value": 1, 81 | "label": 1 82 | }, 83 | { 84 | "value": 2, 85 | "label": 2 86 | }, 87 | { 88 | "value": 3, 89 | "label": 3 90 | }, 91 | { 92 | "value": 4, 93 | "label": 4 94 | }, 95 | { 96 | "value": 5, 97 | "label": 5 98 | }, 99 | { 100 | "value": 6, 101 | "label": 6 102 | }, 103 | { 104 | "value": 7, 105 | "label": 7 106 | }, 107 | { 108 | "value": 8, 109 | "label": 8 110 | }, 111 | { 112 | "value": 9, 113 | "label": 9 114 | }, 115 | { 116 | "value": 10, 117 | "label": 10 118 | } 119 | ] 120 | }, 121 | { 122 | "name": "stealFocusDelay", 123 | "type": "integer", 124 | "title": "Focus Blur Delay", 125 | "description": "This value corresponds to the amount of time (in milliseconds) before plugins are told to release mouse focus. Default is 5 seconds.", 126 | "value": 5000 127 | }, 128 | { 129 | "name": "allowCustomXULKeys", 130 | "type": "bool", 131 | "title": "Allow Custom XUL keys", 132 | "description": "Turn this on to be able to create entirely new XUL keys, which can later be used to create entirely new custom mappings. WARNING: Using this feature requires some knowledge of the browser internals; don't use it unless you know what you are doing or you risk breaking something inside Firefox.", 133 | "value": false 134 | }, 135 | { 136 | "name": "keysMapDirty", 137 | "type": "bool", 138 | "title": "Dirty Keys Map", 139 | "description": "If this value is true, we should ignore the cache and rebuild the list of Keys from scratch. (Used when enabling/disabling the Custom XUL keys functionality)", 140 | "value": false, 141 | "hidden": true 142 | }, 143 | { 144 | "name": "customXULDialog", 145 | "type": "control", 146 | "label": "Change", 147 | "title": "Custom XUL Keys", 148 | "description": "Open the main extension dialog, which will allow you to edit or disable most application shortcuts." 149 | }, 150 | { 151 | "name": "customXULKeys", 152 | "type": "string", 153 | "title": "Custom XUL Keys", 154 | "value": "[]", 155 | "hidden": true 156 | }, 157 | { 158 | "name": "keybinderOverlays", 159 | "type": "string", 160 | "title": "Shortcut Definitions", 161 | "value": "{}", 162 | "hidden": true 163 | } 164 | ], 165 | "dependencies": { 166 | "markdown-it": "^8.0.1", 167 | "markdown-it-anchor": "^2.5.0", 168 | "markdown-it-table-of-contents": "^0.2.3", 169 | "punycode": "^1.4.1", 170 | "uc.micro": "^1.0.3" 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /locale/de-DE/keybinder.properties: -------------------------------------------------------------------------------- 1 | Application_group= Applikation 2 | Navigation_group= Navigation 3 | CurrentPage_group= Aktuelle Seite 4 | Editing_group= Bearbeiten 5 | Search_group= Suchen 6 | WindowsTabs_group= Fenster und Tabs 7 | BookmarksHistory_group= Lesezeichen und Chronik 8 | Tools_group= Werkzeuge 9 | Other_group= Verschiedenes 10 | DeveloperTools_group= Programmierer-Werkzeuge 11 | MenuItem_label= Keybinder 12 | conflictWarning.title= Achtung 13 | conflictWarning.text= Bitte lösen Sie die Zuweisungskonflikte, bevor Sie diesen Dialog zu schließen. 14 | previousTab_label= Zeige vorherigen Tab 15 | nextTab_label= Zeige nächsten Tab 16 | shiftKey= Umschalt 17 | ctrlKey= Strg 18 | altKey= Alt 19 | metaKey= Meta 20 | winKey= Win 21 | cmdKey= Befehl 22 | superKey= Super 23 | osKey= OS 24 | altgrKey= AltGr 25 | Left_Key= Pfeiltaste links 26 | Right_Key= Pfeiltaste rechts 27 | Down_Key= Pfeiltaste abwärts 28 | Up_Key= Pfeiltaste aufwärts 29 | Home_Key= Position1 30 | End_Key= Ende 31 | Delete_Key= Entf 32 | Insert_Key= Einfg 33 | PageUp_Key= Bild auf 34 | PageDown_Key= Bild ab 35 | Tab_Key= Tabulator 36 | Back_Key= Rückschritttaste 37 | BackSpace_Key= Rückschritttaste 38 | Backslash_Key= \\ 39 | IntlBackslash_Key= \\ 40 | Quote_Key= ' 41 | MetaMask_Key= Rückschritttaste 42 | Return_Key= Eingabe 43 | Pause_Key= Unterbrechen (Pause) 44 | Capital_Key= Feststelltaste 45 | Escape_Key= Escape 46 | Space_Key= Freizeichen 47 | Clear_Key= Auswahl löschen 48 | Print_Key= Drucken 49 | Add_Key= + (Plus) 50 | Cancel_Key= Cancel 51 | Substract_Key= - (Minus) 52 | HyphenMinus_Key= - (Hyphen) 53 | Multiply_Key= * (Sternchen) 54 | Divide_Key= / (Schrägstrich) 55 | Numlock_Key= Num-Lock-Taste 56 | Numpad0_Key= Ziffernblock 0 57 | Numpad1_Key= Ziffernblock 1 58 | Numpad2_Key= Ziffernblock 2 59 | Numpad3_Key= Ziffernblock 3 60 | Numpad4_Key= Ziffernblock 4 61 | Numpad5_Key= Ziffernblock 5 62 | Numpad6_Key= Ziffernblock 6 63 | Numpad7_Key= Ziffernblock 7 64 | Numpad8_Key= Ziffernblock 8 65 | Numpad9_Key= Ziffernblock 9 66 | Scroll_Key= Rollen (Scroll-Lock) 67 | ScrollLock_Key= Rollen (Scroll-Lock) 68 | Convert_Key= Umwandlung 69 | Nonconvert_Key= Keine-Umwandlung 70 | Execute_Key= Eingabe 71 | Snapshot_Key= Drucken 72 | Help_Key= Hilfe 73 | Apps_Key= Kontextmenü 74 | Sleep_Key= Standby 75 | Separator_Key= Senkrechter Strich 76 | Function_Key= Fn 77 | Decimal_Key= Dezimal 78 | Comma_Key= , 79 | Period_Key= . 80 | Colon_Key= : 81 | Semicolon_Key= ; 82 | BackQuote_Key= ` 83 | Plus_Key= + (Plus) 84 | None_Key= Kein 85 | closeCmd_key= W 86 | quitCmd_key= Q 87 | disableButton_true= Aktivieren 88 | disableButton_true_key= N 89 | disableButton_false= Deaktivieren 90 | disableButton_false_key= D 91 | focusURLBar_label= Aktivieren der Adresszeile 92 | focusURLBar2_label= Aktivieren der Adresszeile 2 93 | key_search_label= Web-Suche 94 | key_search2_label= Web-Suche 2 95 | key_stop_label= Stopp 96 | key_stop_mac_label= Stopp 2 97 | key_reload_label= Neuladen der aktuellen Seite 98 | key_reload2_label= Neuladen der aktuellen Seite 2 99 | key_forceReload_label= Erzwinge Neuladen der aktuellen Seite 100 | key_forceReload2_label= Erzwinge Neuladen der aktuellen Seite 2 101 | key_toggleMute_label= Stummschaltung umschalten 102 | goHome_label= Startseite 103 | goBackKb_label= Zurück 104 | goBackKb2_label= Zurück 2 105 | goForwardKb_label= Vorwärts 106 | goForwardKb2_label= Vorwärts 2 107 | key_close_label= Schließe Tab 108 | key_undoCloseTab_label= Geschlossenen Tab wiederherstellen 109 | key_undoCloseWindow_label= Geschlossenes Fenster wiederherstellen 110 | key_toggleAddonBar_label= Add-On Leiste ein-/ausschalten 111 | key_findPrevious_label= Vorherige Fundstelle 112 | key_selectLastTab_label= Letzten Tab auswählen 113 | key_closeOther_label= Alle anderen Tabs schließen 114 | key_prevTab_label= Zeige vorherigen Tab 115 | key_nextTab_label= Zeige nächsten Tab 116 | key_selectTab1_label= Wähle Tab 1 117 | key_selectTab2_label= Wähle Tab 2 118 | key_selectTab3_label= Wähle Tab 3 119 | key_selectTab4_label= Wähle Tab 4 120 | key_selectTab5_label= Wähle Tab 5 121 | key_selectTab6_label= Wähle Tab 6 122 | key_selectTab7_label= Wähle Tab 7 123 | key_selectTab8_label= Wähle Tab 8 124 | key_webide_label= Öffne WebIDE 125 | key_switchTextDirection_label= Textrichtung umschalten 126 | key_findSelection_label= Finde Auswahl 127 | key_tabview_label= Tab Gruppen 128 | key_nextTabGroup_label= Zur nächsten Tab-Gruppe wechseln 129 | key_previousTabGroup_label= Zur vorherigen Tab-Gruppe wechseln 130 | key_sanitize_mac_label= Neueste Chronik löschen... 131 | key_devToolboxMenuItemF12_label= Programmierer-Werkzeuge 132 | key_devToolboxMenuItem_label= Programmierer-Werkzeuge 2 133 | key_fullScreen_old_label= Vollbild 134 | key_minimizeWindow_label= Fenster minimieren 135 | key_firebug_toggleBreakOn_label= Firebug: Skriptausführung anhalten/ausführen 136 | key_firebug_detachFirebug_label= Firebug: Im neuem Fenster öffnen 137 | key_firebug_closeFirebug_label= Firebug: Firebug deaktivieren 138 | key_firebug_focusCommandLine_label= Firebug: Kommandozeile fokussieren 139 | key_firebug_toggleInspecting_label= Firebug: Untersuchung an/aus 140 | key_foxyproxyquickadd_label= Foxyproxy: Schnellhinzufügen 141 | key_foxyproxychangeproxy_label= Foxyproxy: Proxy bearbeiten 142 | downloadbar-tgglky_label= Download Status Bar: Toggle Downloads Bar 143 | focusChatBar_label= Chatleiste fokussieren 144 | secureLoginShortcut_label= Secure Login Addon 145 | overridePluginFocus_title= Fokus von Plug-ins entwenden? 146 | overridePluginFocus_description= Wenn aktiviert, dann wird der Fokus von Plug-ins (z.B. Flash) nach einer gewissen Zeit entwendet. 147 | pluginCssSelectors_title= Liste vof CSS-Selektoren, um Plug-ins zu finden 148 | pluginCssSelectors_description= Diese Liste wird versuch zu überschreiben, mit den angegebenen Werten. 149 | UrlPatternsDialog_title= Adressmuster 150 | UrlPatternsDialog_description= Seiten auf denen der Fokus von Plug-ins immer entwendet werden soll. Der Standardwert ist '*'. 151 | UrlPatternsDialog_label= Anpassen 152 | ShortcutsDialog_title= Tastaturkürzel 153 | ShortcutsDialog_description= Öffnet den Tastaturkürzel-Dialog, in der viele Tastaturkürzel verändert werden können. 154 | ShortcutsDialog_label= Anpassen 155 | stealFocusDelay_title= Wartezeit um den Fokus von Plug-ins zu entwenden 156 | stealFocusDelay_description= Dieser Wert (in Millisekunden) wird gewartet, bis der Fokus von Plug-ins entzogen wird. Der Standardwert ist '5' Sekunden. 157 | shiftAllowFocus_title= Erlaube Plug-ins den Fokus zu behalten bei gedrückter Umschalttaste+Klick? 158 | shiftAllowFocus_description= Mit dieser Option kann Plug-ins erlaubt werden den Fokus zu behalten, bei gedrückter Umschalttaste+Klick. -------------------------------------------------------------------------------- /lib/core/overlay.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var storage = {}; 6 | const extensionPrefs = require("sdk/simple-prefs"); 7 | const { Shortcut } = require("../core/shortcut"); 8 | const { ColorNames } = require("../prefs/colors"); 9 | const { serialize, unserialize } = require("../util/serialization"); 10 | 11 | let Overlay = exports.Overlay = function (data, options) { 12 | this.key = data.key; 13 | this.shortcut = data.shortcut; 14 | 15 | Overlays.removeByKey(this.key); 16 | 17 | if (!this.key.shortcut.equals(this.shortcut) && !(this.shortcut.disabled)) { 18 | let overlays = Overlays.overlays; 19 | overlays.keys[this.key.toString()] = this; 20 | overlays.custom[this.shortcut.toString()] = this; 21 | overlays.overridden[this.key.shortcut.toString()] = this; 22 | } 23 | 24 | if (this.shortcut.disabled) { 25 | Overlays.overlays.disabled[this.key.toString()] = this; 26 | } 27 | 28 | if (!options || !options.dontStore) { 29 | Overlays.store(); 30 | } 31 | }; 32 | 33 | Overlay.prototype.remove = function() { 34 | let overlays = Overlays.overlays; 35 | delete overlays.keys[this.key.toString()]; 36 | delete overlays.disabled[this.key.toString()]; 37 | delete overlays.custom[this.shortcut.toString()]; 38 | delete overlays.overridden[this.key.shortcut.toString()]; 39 | Overlays.store(); 40 | }; 41 | 42 | let Overlays = exports.Overlays = { 43 | _overlays: null, 44 | 45 | _load: function() { 46 | this._overlays = { 47 | keys: {}, 48 | custom: {}, 49 | overridden: {}, 50 | conflicting: {}, 51 | disabled: {} 52 | }; 53 | storage = JSON.parse(extensionPrefs.prefs['keybinderOverlays']); 54 | (storage.overlays || []).forEach(unserialize); 55 | Overlays.getDuplicates(); 56 | require("../util/windows").Windows.disableKeys(); 57 | return this._overlays; 58 | }, 59 | 60 | store: function() { 61 | storage.overlays = []; 62 | for (let key in this.overlays.keys) { 63 | storage.overlays.push(serialize(this.overlays.keys[key])); 64 | } 65 | for (let key in this.overlays.disabled) { 66 | storage.overlays.push(serialize(this.overlays.disabled[key])); 67 | } 68 | extensionPrefs.prefs['keybinderOverlays'] = JSON.stringify(storage); 69 | }, 70 | 71 | get overlays() { 72 | return this._overlays || this._load(); 73 | }, 74 | 75 | findByKey: function(key) { 76 | let idx = key.toString(); 77 | if (idx in this.overlays.keys) { 78 | return this.overlays.keys[idx]; 79 | } 80 | return null; 81 | }, 82 | 83 | findByDisabledKey: function(key) { 84 | let idx = key.toString(); 85 | if (idx in this.overlays.disabled) { 86 | return this.overlays.disabled[idx]; 87 | } 88 | return null; 89 | }, 90 | 91 | findByDisabledShortcut: function(shortcut) { 92 | const keys = require("../core/key"); 93 | let idx = shortcut.toString(); 94 | function filter(key) { 95 | return key.shortcut.toString().toLowerCase() == idx.toLowerCase(); 96 | } 97 | let cache = keys.filter(filter); 98 | let tempKey = cache.keys().next().value; 99 | if (tempKey) { return (this.findByDisabledKey(tempKey) || null); } 100 | return null; 101 | }, 102 | 103 | findByCustomShortcut: function(shortcut) { 104 | let idx = shortcut.toString(); 105 | if (idx in this.overlays.custom) { 106 | return this.overlays.custom[idx]; 107 | } 108 | return null; 109 | }, 110 | 111 | findByOverriddenShortcut: function(shortcut) { 112 | let idx = shortcut.toString(); 113 | if (idx in this.overlays.overridden) { 114 | return this.overlays.overridden[idx]; 115 | } 116 | return null; 117 | }, 118 | 119 | findByConflictingShortcut: function(shortcut) { 120 | let idx = shortcut.toString(); 121 | if (idx in this.overlays.conflicting) { 122 | return this.overlays.conflicting[idx]; 123 | } 124 | return null; 125 | }, 126 | 127 | getConflictingShortcutColor: function(shortcut) { 128 | let idx = shortcut.toString(); 129 | let groupColor = this.overlays.conflicting[idx][this.overlays.conflicting[idx].length - 1].groupColor; 130 | if (typeof(groupColor) != "undefined") { return groupColor; } 131 | }, 132 | 133 | removeByKey: function(key) { 134 | let overlay = (this.findByKey(key) || this.findByDisabledKey(key)); 135 | if (overlay) { overlay.remove(); } 136 | this.getDuplicates(); 137 | }, 138 | getDuplicates: function() { 139 | if (!shortcutKeys) { var shortcutKeys = {}; } 140 | let overlays = Overlays.overlays; 141 | 142 | for (let iterate in overlays.keys) { 143 | let currKey = overlays.keys[iterate]; 144 | 145 | if (currKey.hasOwnProperty("shortcut") && !(currKey.key.disable)) { 146 | let currShort = currKey.shortcut.toString(); 147 | if (!shortcutKeys[currShort]) { shortcutKeys[currShort] = []; } 148 | 149 | shortcutKeys[currShort].push(iterate); 150 | var filteredShortcutKeys = {}; 151 | 152 | for (let i in shortcutKeys) { 153 | if (shortcutKeys[i].length > 1) { 154 | filteredShortcutKeys[i] = shortcutKeys[i]; // Add Shortcut to final array only if multiple mappings found. 155 | } 156 | } 157 | 158 | } 159 | 160 | } 161 | for (let i in filteredShortcutKeys) { 162 | if (!usedColors) { var usedColors = []; } 163 | if (typeof(overlays.conflicting[i]) != "undefined") { 164 | let prevColor = overlays.conflicting[i][overlays.conflicting[i].length - 1].groupColor; 165 | if (typeof(prevColor) != "undefined") { // If already assigned, use previous color to avoid confusion. 166 | groupColor = overlays.conflicting[i][overlays.conflicting[i].length - 1].groupColor; 167 | usedColors.push(groupColor); 168 | } 169 | } else { 170 | do { 171 | var groupColor = ColorNames.getRandom(); // Get random color to identify conflicting shortcuts. 172 | usedColors.push(groupColor); 173 | } 174 | while (groupColor in usedColors); // Make sure colors do not repeat between different groups of key mappings. 175 | } 176 | filteredShortcutKeys[i].push({ 177 | "groupColor": groupColor 178 | }); 179 | } 180 | 181 | overlays.conflicting = filteredShortcutKeys; 182 | } 183 | }; 184 | -------------------------------------------------------------------------------- /locale/en-US/keybinder.properties: -------------------------------------------------------------------------------- 1 | Application_group= Application 2 | Navigation_group= Navigation 3 | CurrentPage_group= Current Page 4 | Editing_group= Editing 5 | Search_group= Search 6 | WindowsTabs_group= Windows & Tabs 7 | BookmarksHistory_group= Bookmarks & History 8 | Tools_group= Tools 9 | Other_group= Other 10 | CustomXUL_group= Custom Keys 11 | DeveloperTools_group= Developer Tools 12 | MenuItem_label= Keybinder 13 | conflictWarning.title= Warning 14 | conflictWarning.text= Please solve shortcut conflicts before closing the dialog. 15 | customXulConflict.text= There is already a key with that id. 16 | customXulNewKey.title= Enter a valid id for the new key. 17 | customXulNewKey.text= Use only letters, numbers, score and underscore. 18 | customXulBadCommand.text= Invalid command; remember, ids are case-sensitive. 19 | NewCustomXulInvalid.text= Use only letters, numbers, score and underscore. 20 | NewCustomXul.label= Label for your new key. 21 | NewCustomXul.command= Browser:sampleCommand 22 | customXulConfirmDelete.title= Confirm Deletion 23 | customXulConfirmDelete.text= Are you sure you wish to delete the selected key? 24 | previousTab_label= Show Previous Tab 25 | nextTab_label= Show Next Tab 26 | shiftKey= Shift 27 | ctrlKey= Control 28 | altKey= Alt 29 | metaKey= Meta 30 | winKey= Win 31 | cmdKey= Command 32 | superKey= Super 33 | osKey= OS 34 | altgrKey= AltGraph 35 | Left_Key= ArrowLeft 36 | Right_Key= ArrowRight 37 | Down_Key= ArrowDown 38 | Up_Key= ArrowUp 39 | Home_Key= Home 40 | End_Key= End 41 | Delete_Key= Delete 42 | Insert_Key= Insert 43 | PageUp_Key= Page Up 44 | PageDown_Key= Page Down 45 | Tab_Key= Tab 46 | Back_Key= Backspace 47 | BackSpace_Key= Backspace 48 | Backslash_Key= \\ 49 | IntlBackslash_Key= \\ 50 | Quote_Key= ' 51 | MetaMask_Key= Backspace 52 | Return_Key= Enter 53 | Pause_Key= Pause 54 | Capital_Key= CapsLock 55 | Escape_Key= Escape 56 | Space_Key= Space 57 | Clear_Key= Clear 58 | Print_Key= Print 59 | Add_Key= + (Sumar) 60 | Cancel_Key= Cancel 61 | Substract_Key= - (Restar) 62 | HyphenMinus_Key= - (Guión) 63 | Multiply_Key= * (Multiplicar) 64 | Divide_Key= / (Dividir) 65 | Numlock_Key= NumLock 66 | Numpad0_Key= Numpad 0 67 | Numpad1_Key= Numpad 1 68 | Numpad2_Key= Numpad 2 69 | Numpad3_Key= Numpad 3 70 | Numpad4_Key= Numpad 4 71 | Numpad5_Key= Numpad 5 72 | Numpad6_Key= Numpad 6 73 | Numpad7_Key= Numpad 7 74 | Numpad8_Key= Numpad 8 75 | Numpad9_Key= Numpad 9 76 | Scroll_Key= ScrollLock 77 | ScrollLock_Key= ScrollLock 78 | Convert_Key= Convert 79 | Nonconvert_Key= NonConvert 80 | Execute_Key= Execute 81 | Snapshot_Key= PrintScreen 82 | Help_Key= Help 83 | Apps_Key= ContextMenu 84 | Sleep_Key= Standby 85 | Separator_Key= Separator 86 | Function_Key= Fn 87 | Decimal_Key= Decimal 88 | Comma_Key= , 89 | Period_Key= . 90 | Colon_Key= : 91 | Semicolon_Key= ; 92 | BackQuote_Key= ` 93 | Plus_Key= + (Sumar) 94 | None_Key= None 95 | closeCmd_key= W 96 | quitCmd_key= Q 97 | disableButton_true= Enable 98 | disableButton_true_key= N 99 | disableButton_false= Disable 100 | disableButton_false_key= D 101 | focusURLBar_label= Focus URL Bar 102 | focusURLBar2_label= Focus URL Bar 2 103 | key_search_label= Web Search 104 | key_search2_label= Web Search 2 105 | key_stop_label= Stop 106 | key_stop_mac_label= Stop 2 107 | key_reload_label= Reload current page 108 | key_reload2_label= Reload current page 2 109 | key_forceReload_label= Force Reload current page 110 | key_forceReload2_label= Force Reload current page 2 111 | key_toggleMute_label= Toggle Mute 112 | goHome_label= Home Page 113 | goBackKb_label= Back 114 | goBackKb2_label= Back 2 115 | goForwardKb_label= Forward 116 | goForwardKb2_label= Forward 2 117 | key_close_label= Close Tab 118 | key_undoCloseTab_label= Undo Close Tab 119 | key_undoCloseWindow_label= Undo Close Window 120 | key_toggleAddonBar_label= Toggle Add-on Bar 121 | key_findPrevious_label= Find Previous 122 | key_selectLastTab_label= Select Last Tab 123 | key_closeOther_label= Close Other Tabs 124 | key_prevTab_label= Show Previous Tab 125 | key_nextTab_label= Show Next Tab 126 | key_selectTab1_label= Select Tab 1 127 | key_selectTab2_label= Select Tab 2 128 | key_selectTab3_label= Select Tab 3 129 | key_selectTab4_label= Select Tab 4 130 | key_selectTab5_label= Select Tab 5 131 | key_selectTab6_label= Select Tab 6 132 | key_selectTab7_label= Select Tab 7 133 | key_selectTab8_label= Select Tab 8 134 | key_webide_label= Show Web IDE 135 | key_switchTextDirection_label= Switch Text Direction 136 | key_findSelection_label= Find Selection 137 | key_tabview_label= Tab Groups 138 | key_nextTabGroup_label= Switch To Next Tab Group 139 | key_previousTabGroup_label= Switch To Previous Tab Group 140 | key_sanitize_mac_label= Clear Recent History 141 | key_devToolboxMenuItemF12_label= Developer Tools 142 | key_devToolboxMenuItem_label= Developer Tools 2 143 | key_fullScreen_old_label= Enter Full Screen 2 144 | key_minimizeWindow_label= Minimize Window 145 | key_firebug_toggleBreakOn_label= Firebug: Toggle Break On ... 146 | key_firebug_detachFirebug_label= Firebug: Open in New Window 147 | key_firebug_closeFirebug_label= Firebug: Deactivate 148 | key_firebug_focusCommandLine_label= Firebug: Focus Command Line 149 | key_firebug_toggleInspecting_label= Firebug: Toggle Inspecting 150 | key_foxyproxyquickadd_label= Foxyproxy: Quick Add 151 | key_foxyproxychangeproxy_label= Foxyproxy: Change Proxy 152 | downloadbar-tgglky_label= Download Status Bar: Toggle Downloads Bar 153 | focusChatBar_label= Focus Chat Bar 154 | secureLoginShortcut_label= Secure Login Addon 155 | homepage_title= Homepage 156 | overridePluginFocus_title= Steal focus from plug-ins? 157 | overridePluginFocus_description= When this is on, plugins such as Flash will be told to release mouse/keyboard focus a certain amount of time after they have been clicked on. 158 | pluginCssSelectors_title= List of CSS Selectors by which to find plugins 159 | pluginCssSelectors_description= This list will define which plugins the extension will try to override as described above. 160 | UrlPatternsDialog_title= Address Patterns 161 | UrlPatternsDialog_description= Sites on which we won't allow plug-ins to keep mouse/keyboard focus. The default value is '*'. 162 | UrlPatternsDialog_label= Edit 163 | ShortcutsDialog_title= Shortcut Mappings 164 | ShortcutsDialog_description= Open the main extension dialog, which will allow you to edit or disable most application shortcuts. 165 | ShortcutsDialog_label= Change 166 | stealFocusDelay_title= Focus Blur Delay 167 | stealFocusDelay_description= This value corresponds to the amount of time (in milliseconds) before plugins are told to release mouse/keyboard focus. The default value is 5 seconds. 168 | shiftAllowFocus_title= Allow plug-ins to keep focus on Shift+Click? 169 | shiftAllowFocus_description= When this is on, plugins such as Flash will be allowed to retain mouse/keyboard focus if Shift was pressed during the click. 170 | pluginCrawlLimit_title= Retry Limit 171 | pluginCrawlLimit_description= Some sites use javascript to add flash after everything is loaded, in these cases we need to rescan the page when a change in the structure is detected. This determines the maximum number of items this should be allowed per page. Default is 5 times. -------------------------------------------------------------------------------- /lib/patch/bug-78414.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const extensionPrefs = require("sdk/simple-prefs"); 6 | const self = require("sdk/self"); 7 | const pageMod = require("sdk/page-mod"); 8 | var pageModObject = {}; 9 | var placeholder = "*.this-is-a-ridiculous-string-shouldnevermatchanything.com"; 10 | const uuid = require("sdk/util/uuid"); 11 | const { Windows } = require("../util/windows"); 12 | 13 | pageWorkers = {}; 14 | if ('undefined' == typeof(changedFocusDelay)) { 15 | var changedFocusDelay = false; 16 | } 17 | if ('undefined' == typeof(changedUrlPattern)) { 18 | var changedUrlPattern = false; 19 | } 20 | 21 | function checkUrlPattern() { 22 | return JSON.parse(extensionPrefs.prefs['domainUrlPattern']); 23 | } 24 | 25 | function togglePageMod() { 26 | if ('undefined' == typeof(pageModCalledOnce)) pageModCalledOnce = true; 27 | 28 | if (true == extensionPrefs.prefs['overridePluginFocus']) { 29 | pageModObject = pageMod.PageMod({ 30 | include: checkUrlPattern(), 31 | attachTo: ['top', 'frame', 'existing'], 32 | contentScriptFile: self.data.url("bug-78414_content.js"), 33 | contentScriptWhen: "end", 34 | 35 | onAttach: function(worker) { 36 | let workerID = uuid.uuid(); 37 | pageWorkers[workerID] = worker; 38 | // console.warn("[ADDON SCRIPT] We Just generated a workerID UUID: "+workerID); 39 | worker.port.emit("findPlugins", { "ID" : workerID.toString() }); 40 | 41 | worker.port.on("noEmbeds", function(payload) { 42 | if ( !! payload['number']) { 43 | // console.warn("[ADDON SCRIPT] payload number exists?"); 44 | let workerID = payload.number; 45 | } 46 | else { 47 | // console.warn("[ADDON SCRIPT] Payload is: "+JSON.stringify(payload,null,2)); 48 | let workerID = payload['ID']; 49 | // console.warn("[ADDON SCRIPT] payload's workerID IS: "+workerID); 50 | } 51 | if (payload['checkAgain'] == true) { 52 | // console.warn("[ADDON SCRIPT] Haven't yet reached the max number of tries, go again!"); 53 | worker.port.emit("findPlugins", { "ID" : workerID.toString() }); 54 | } 55 | else { 56 | // console.warn("[ADDON SCRIPT] And this time, we shouldn't keep looking..."); 57 | worker.port.emit("removeDOMListener", { "ID" : workerID.toString() }); 58 | } 59 | }); 60 | 61 | worker.port.on("disposeOfMinion", function(payload) { 62 | let workerID = payload['ID']; 63 | // console.warn("Debugging a worker... "+JSON.stringify(worker,null,2)); 64 | // console.warn("Debugging a payload... "+JSON.stringify(payload,null,2)); 65 | worker.destroy(workerID); 66 | }); 67 | 68 | worker.on('detach', function(target = workerID) { 69 | // console.warn("Do we have a worker ID?: "+target); 70 | delete pageWorkers[target]; // Kill useless workers. 71 | // that sounded unnecessarily evil, didn't it? 72 | if (0 == Object.keys(pageWorkers).length) { 73 | if (false == extensionPrefs.prefs['overridePluginFocus']) { 74 | Windows.getMostRecentWindow().setTimeout(destroyPageMod, 0); 75 | } // If turned off, and all workers already gone, destroy. 76 | } 77 | }); 78 | }, 79 | contentScriptOptions: { 80 | "cssSelectors": extensionPrefs.prefs['pluginCssSelectors'], 81 | "shiftAllowFocus": extensionPrefs.prefs['shiftAllowFocus'], 82 | "stealFocusDelay": extensionPrefs.prefs['stealFocusDelay'], 83 | "pluginCrawlLimit": extensionPrefs.prefs['pluginCrawlLimit'] 84 | } 85 | }); 86 | 87 | extensionPrefs.on('domainUrlPattern', function() { 88 | changedUrlPattern = true; 89 | extensionPrefs.prefs['overridePluginFocus'] = false 90 | }); 91 | extensionPrefs.on('pluginCrawlLimit', function() { 92 | pageModObject.contentScriptOptions['pluginCrawlLimit'] = extensionPrefs.prefs['pluginCrawlLimit'] 93 | }); 94 | extensionPrefs.on('pluginCssSelectors', function() { 95 | pageModObject.contentScriptOptions['cssSelectors'] = extensionPrefs.prefs['pluginCssSelectors'] 96 | }); 97 | extensionPrefs.on("stealFocusDelay", function() { 98 | changedFocusDelay = true; 99 | extensionPrefs.prefs['overridePluginFocus'] = false 100 | }); 101 | } 102 | else if (false == extensionPrefs.prefs['overridePluginFocus']) { 103 | extensionPrefs.removeListener('domainUrlPattern', function() { 104 | changedUrlPattern = true; 105 | extensionPrefs.prefs['overridePluginFocus'] = false 106 | }); 107 | extensionPrefs.removeListener('pluginCrawlLimit', function() { 108 | pageModObject.contentScriptOptions['pluginCrawlLimit'] = extensionPrefs.prefs['pluginCrawlLimit'] 109 | }); 110 | extensionPrefs.removeListener('pluginCssSelectors', function() { 111 | pageModObject.contentScriptOptions['cssSelectors'] = extensionPrefs.prefs['pluginCssSelectors'] 112 | }); 113 | extensionPrefs.removeListener("stealFocusDelay", function() { 114 | changedFocusDelay = true; 115 | extensionPrefs.prefs['overridePluginFocus'] = false 116 | }); 117 | 118 | for (let worker of Object.keys(pageWorkers)) { 119 | pageWorkers[worker].port.emit("removeMods", pageWorkers[worker]); 120 | } 121 | if (0 == Object.keys(pageWorkers).length) { 122 | if (false == extensionPrefs.prefs['overridePluginFocus']) { 123 | Windows.getMostRecentWindow().setTimeout(destroyPageMod, 0); 124 | } // If turned off, and all workers already gone, destroy. 125 | } 126 | } 127 | } 128 | 129 | function destroyPageMod() { 130 | if ('undefined' != typeof(pageModObject)) { 131 | if ('undefined' != typeof(pageModObject.destroy)) { pageModObject.destroy() } 132 | } 133 | if (true == changedFocusDelay) { 134 | Windows.getMostRecentWindow().setTimeout(function() { 135 | changedFocusDelay = false; 136 | extensionPrefs.prefs['overridePluginFocus'] = true 137 | }, 0) 138 | } 139 | if (true == changedUrlPattern) { 140 | Windows.getMostRecentWindow().setTimeout(function() { 141 | changedUrlPattern = false; 142 | extensionPrefs.prefs['overridePluginFocus'] = true 143 | }, 0) 144 | } 145 | } 146 | 147 | extensionPrefs.on("overridePluginFocus", function() { 148 | togglePageMod() 149 | }); 150 | 151 | if (("undefined" == typeof(pageModCalledOnce)) || (!pageModCalledOnce)) { 152 | togglePageMod(); 153 | } 154 | -------------------------------------------------------------------------------- /locale/es-ES/keybinder.properties: -------------------------------------------------------------------------------- 1 | Application_group= Aplicación 2 | Navigation_group= Navegar 3 | CurrentPage_group= Página Actual 4 | Editing_group= Edición 5 | Search_group= Búsqueda 6 | WindowsTabs_group= Ventanas y Páginas 7 | BookmarksHistory_group= Historial & Marcadores 8 | Tools_group= Herramientas 9 | Other_group= Otros 10 | DeveloperTools_group= Herramientas de Desarrollador 11 | CustomXUL_group= Personalizados 12 | MenuItem_label= Keybinder 13 | conflictWarning.title= Advertencia 14 | conflictWarning.text= Debes resolver los conflictos de teclas antes de cerrar este panel. 15 | customXulConflict.text= Ya existe un elemento con este identificador. 16 | customXulNewKey.title= Ingresa un identificador válido para el nuevo elemento. 17 | customXulNewKey.text= Usa solamente letras, números, guión y guión bajo. 18 | customXulBadCommand.text= Comando inválido; recuerda que los identificadores distinguen mayúsculas de minúsculas. 19 | NewCustomXulInvalid.text= Usa solamente letras, números, guión y guión bajo. 20 | NewCustomXul.label= Etiqueta descriptiva para tu nuevo elemento. 21 | NewCustomXul.command= Browser:comandoDeMuestra 22 | customXulConfirmDelete.title= Confirmar Borrado 23 | customXulConfirmDelete.text= Estás seguro que quieres borrar el elemento seleccionado? 24 | previousTab_label= Mostrar Pestaña Anterior 25 | nextTab_label= Mostrar Pestaña Siguiente 26 | shiftKey= Mayus 27 | ctrlKey= Control 28 | altKey= Alt 29 | metaKey= Meta 30 | winKey= Win 31 | cmdKey= Command 32 | superKey= Super 33 | osKey= OS 34 | altgrKey= AltGraph 35 | Left_Key= FlechaIzquierda 36 | Right_Key= FlechaDerecha 37 | Down_Key= FlechaAbajo 38 | Up_Key= FlechaArriba 39 | Home_Key= Inicio 40 | End_Key= Fin 41 | Delete_Key= Supr 42 | Insert_Key= Insert 43 | PageUp_Key= RePág 44 | PageDown_Key= AvPág 45 | Tab_Key= Tab 46 | Back_Key= Borrar 47 | BackSpace_Key= Borrar 48 | Backslash_Key= \\ 49 | IntlBackslash_Key= \\ 50 | Quote_Key= ' 51 | MetaMask_Key= Borrar 52 | Return_Key= Intro 53 | Pause_Key= Pausa 54 | Capital_Key= BloqMayús 55 | Escape_Key= Escape 56 | Space_Key= Espacio 57 | Clear_Key= Borrar 58 | Print_Key= Impr 59 | Add_Key= + 60 | Cancel_Key= Cancelar 61 | Substract_Key= - (Restar) 62 | HyphenMinus_Key= - (Guión) 63 | Multiply_Key= * (Multiply) 64 | Divide_Key= / (Divide) 65 | Numlock_Key= NumLock 66 | Numpad0_Key= Numpad 0 67 | Numpad1_Key= Numpad 1 68 | Numpad2_Key= Numpad 2 69 | Numpad3_Key= Numpad 3 70 | Numpad4_Key= Numpad 4 71 | Numpad5_Key= Numpad 5 72 | Numpad6_Key= Numpad 6 73 | Numpad7_Key= Numpad 7 74 | Numpad8_Key= Numpad 8 75 | Numpad9_Key= Numpad 9 76 | Scroll_Key= BloqDespl 77 | ScrollLock_Key= BloqDespl 78 | Convert_Key= Convertir 79 | Nonconvert_Key= NoConvertir 80 | Execute_Key= Ejecutar 81 | Snapshot_Key= Impr Pant 82 | Help_Key= Ayuda 83 | Apps_Key= Menú 84 | Sleep_Key= Suspender 85 | Separator_Key= Separador 86 | Decimal_Key= Decimal 87 | Comma_Key= , 88 | Period_Key= . 89 | Colon_Key= : 90 | Semicolon_Key= ; 91 | BackQuote_Key= ` 92 | Plus_Key= + 93 | None_Key= Ninguna 94 | closeCmd_key= W 95 | quitCmd_key= Q 96 | disableButton_true= Habilitar 97 | disableButton_true_key= H 98 | disableButton_false= Deshabilitar 99 | disableButton_false_key= D 100 | focusURLBar_label= Enfocar Barra de Direcciones 101 | focusURLBar2_label= Enfocar Barra de Direcciones 2 102 | key_search_label= Búsqueda Web 103 | key_search2_label= Búsqueda Web 2 104 | key_stop_label= Detener 105 | key_stop_mac_label= Detener 2 106 | key_reload_label= Actualizar Página Actual 107 | key_reload2_label= Actualizar Página Actual 2 108 | key_forceReload_label= Actualizar Página Actual (Ignorar Caché) 109 | key_forceReload2_label= Actualizar Página Actual (Ignorar Caché) 2 110 | key_toggleMute_label= Prender/Apagar Sonido 111 | goHome_label= Página de Inicio 112 | goBackKb_label= Atrás 113 | goBackKb2_label= Atrás 2 114 | goForwardKb_label= Siguiente 115 | goForwardKb2_label= Siguiente 2 116 | key_close_label= Cerrar Página 117 | key_undoCloseTab_label= Deshacer Cerrar Página 118 | key_undoCloseWindow_label= Deshacer Cerrar Ventana 119 | key_toggleAddonBar_label= Barra de Extensiones 120 | key_findPrevious_label= Buscar Previo 121 | key_selectLastTab_label= Seleccionar Última Pestaña 122 | key_closeOther_label= Cerrar Demás Pestañas 123 | key_prevTab_label= Mostrar Pestaña Anterior 124 | key_nextTab_label= Mostrar Pestaña Siguiente 125 | key_selectTab1_label= Seleccionar Pestaña 1 126 | key_selectTab2_label= Seleccionar Pestaña 2 127 | key_selectTab3_label= Seleccionar Pestaña 3 128 | key_selectTab4_label= Seleccionar Pestaña 4 129 | key_selectTab5_label= Seleccionar Pestaña 5 130 | key_selectTab6_label= Seleccionar Pestaña 6 131 | key_selectTab7_label= Seleccionar Pestaña 7 132 | key_selectTab8_label= Seleccionar Pestaña 8 133 | key_webide_label= Mostrar IDE Web 134 | key_switchTextDirection_label= Cambiar Dirección del Texto 135 | key_findSelection_label= Buscar selección 136 | key_tabview_label= Grupos de Pestañas 137 | key_nextTabGroup_label= Cambiar a Grupo Siguiente de Pestañas 138 | key_previousTabGroup_label= Cambiar a Grupo Anterior de Pestañas 139 | key_sanitize_mac_label= Borrar Historial 140 | key_devToolboxMenuItemF12_label= Herramientas de Desarrollador 141 | key_devToolboxMenuItem_label= Herramientas de Desarrollador 2 142 | key_fullScreen_old_label= Pantalla Completa 2 143 | key_minimizeWindow_label= Minimizar Ventana 144 | key_firebug_toggleBreakOn_label= Firebug: Conmutar activación de parada ... 145 | key_firebug_detachFirebug_label= Firebug: Abrir Firebug en una ventana nueva 146 | key_firebug_closeFirebug_label= Firebug: Desactivar Firebug 147 | key_firebug_focusCommandLine_label= Firebug: Enfocar línea de comandos 148 | key_firebug_toggleInspecting_label= Firebug: Conmutar inspección 149 | key_foxyproxyquickadd_label= Foxyproxy: QuickAdd 150 | key_foxyproxychangeproxy_label= Foxyproxy: Cambiar Proxy 151 | downloadbar-tgglky_label= Download Status Bar: Mostrar/Ocultar Barra de Descargas 152 | focusChatBar_label= Enfocar Chat 153 | secureLoginShortcut_label= Secure Login Addon 154 | homepage_title= Sitio Web 155 | overridePluginFocus_title= Quitar foco a los plug-ins? 156 | overridePluginFocus_description= Cuando esta opción está activada, la extensión fuerza a los plug-ins como Flash a perder el foco del mouse/teclado. 157 | pluginCssSelectors_title= Lista de Selectores CSS que se asumirán como plug-ins. 158 | pluginCssSelectors_description= Esta lista define los elementos que se considerarán un plug-in y a los cuales intentaremos quitar el foco. 159 | UrlPatternsDialog_title= Patrones de URL 160 | UrlPatternsDialog_description= Sitios en que no permitiremos a los plug-in retener foco del mouse/teclado, el valor por defecto es '*'. 161 | UrlPatternsDialog_label= Editar 162 | ShortcutsDialog_title= Asignación de Teclas 163 | ShortcutsDialog_description= Abre la ventana principal de la extensión, mediante la cual podrás cambiar o deshabilitar la mayor parte de los atajos de teclado de la aplicación. 164 | ShortcutsDialog_label= Cambiar 165 | stealFocusDelay_title= Latencia para recuperar el foco 166 | stealFocusDelay_description= Este valor corresponde a la cantidad de tiempo (en milisegundos) antes de hacer que los plug-in suelten el foco del mouse/teclado. El valor por defecto es de 5 segundos. 167 | shiftAllowFocus_title= Permitir que los plug-ins conserven el foco al hacer Click con Shift presionado? 168 | shiftAllowFocus_description= Cuando esta opción está activada, la extensión permite que los plug-in retengan el foco del mouse/teclado si al hacer Click se presiona también Shift. 169 | pluginCrawlLimit_title= Límite de intentos 170 | pluginCrawlLimit_description= Algunos sitios usan javascript para agregar Flash una vez que todo lo demás ya ha sido cargado; en estos casos, debemos volver a revisar la página para poder encontrar los plug-in. Este es el máximo número de veces que revisaremos una página en caso de no encontrar nada. Por defecto son 5. -------------------------------------------------------------------------------- /locale/ro-RO/keybinder.properties: -------------------------------------------------------------------------------- 1 | Application_group= Aplicatie 2 | Navigation_group= Navigare 3 | CurrentPage_group= Pagina curenta 4 | Editing_group= Se modifica 5 | Search_group= Cauta 6 | WindowsTabs_group= Ferestre si tab-uri 7 | BookmarksHistory_group= Preferate si Istoric 8 | Tools_group= Unelte 9 | Other_group= Altele 10 | CustomXUL_group= Parametri personalizati 11 | DeveloperTools_group= Unelte pentru programatori 12 | MenuItem_label= Scurtaturi tastatura 13 | conflictWarning.title= Avertisment 14 | conflictWarning.text= Corecteaza coliziunea intre scurtaturi inainte de a inchide fereastra curenta. 15 | customXulConflict.text= Exista deja un parametru cu identificatorul specificat. 16 | customXulNewKey.title= Specifica un identificator valid pentru noul parametru 17 | customXulNewKey.text= Sunt acceptate doar caractere alfanumerice, "score" si "underscore" 18 | customXulBadCommand.text= Comanda invalida; atentie, identificatorii tin cont de diferentele intre litere mici si majuscule. 19 | NewCustomXulInvalid.text= Foloseste doar caractere alfanumerice, "score" si "underscore" 20 | NewCustomXul.label= Eticheta pentru noul parametru 21 | NewCustomXul.command= Browser:sampleCommand 22 | customXulConfirmDelete.title= Confirma stergerea 23 | customXulConfirmDelete.text= Esti sigur ca doresti sa stergi parametrul selectat? 24 | previousTab_label= Arata tab-ul anterior 25 | nextTab_label= Arata tab-ul urmator 26 | shiftKey= Shift 27 | ctrlKey= Control 28 | altKey= Alt 29 | metaKey= Meta 30 | winKey= Win 31 | cmdKey= Command 32 | superKey= Super 33 | osKey= OS 34 | altgrKey= AltGraph 35 | Left_Key= Tasta directionala (sageata) stanga 36 | Right_Key= Tasta directionala (sageata) dreapta 37 | Down_Key= Tasta directionala (sageata) jos 38 | Up_Key= Tasta directionala (sageata) sus 39 | Home_Key= Home 40 | End_Key= End 41 | Delete_Key= Delete 42 | Insert_Key= Insert 43 | PageUp_Key= Page Up 44 | PageDown_Key= Page Down 45 | Tab_Key= Tab 46 | Back_Key= Backspace 47 | BackSpace_Key= Backspace 48 | Backslash_Key= \\ 49 | IntlBackslash_Key= \\ 50 | Quote_Key= ' 51 | MetaMask_Key= Backspace 52 | Return_Key= Enter 53 | Pause_Key= Pause 54 | Capital_Key= CapsLock 55 | Escape_Key= Escape 56 | Space_Key= Space 57 | Clear_Key= Clear 58 | Print_Key= Print 59 | Add_Key= Add 60 | Cancel_Key= Cancel 61 | Substract_Key= - (Substract) 62 | HyphenMinus_Key= - (Hyphen) 63 | Multiply_Key= * (Multiply) 64 | Divide_Key= / (Divide) 65 | Numlock_Key= NumLock 66 | Numpad0_Key= Numpad 0 67 | Numpad1_Key= Numpad 1 68 | Numpad2_Key= Numpad 2 69 | Numpad3_Key= Numpad 3 70 | Numpad4_Key= Numpad 4 71 | Numpad5_Key= Numpad 5 72 | Numpad6_Key= Numpad 6 73 | Numpad7_Key= Numpad 7 74 | Numpad8_Key= Numpad 8 75 | Numpad9_Key= Numpad 9 76 | Scroll_Key= ScrollLock 77 | ScrollLock_Key= ScrollLock 78 | Convert_Key= Convert 79 | Nonconvert_Key= NonConvert 80 | Execute_Key= Execute 81 | Snapshot_Key= PrintScreen 82 | Help_Key= Help 83 | Apps_Key= ContextMenu 84 | Sleep_Key= Standby 85 | Separator_Key= Separator 86 | Function_Key= Fn 87 | Decimal_Key= Decimal 88 | Comma_Key= , 89 | Period_Key= . 90 | Colon_Key= : 91 | Semicolon_Key= ; 92 | BackQuote_Key= ` 93 | Plus_Key= + (Plus) 94 | None_Key= None 95 | closeCmd_key= W 96 | quitCmd_key= Q 97 | disableButton_true= Deblocheaza 98 | disableButton_true_key= N 99 | disableButton_false= Blocheaza 100 | disableButton_false_key= D 101 | focusURLBar_label= Selecteaza bara de adrese 102 | focusURLBar2_label= Selecteaza bara de adrese #2 103 | key_search_label= Cautare web 104 | key_search2_label= Cautare web #2 105 | key_stop_label= Stop 106 | key_stop_mac_label= Stop 2 107 | key_reload_label= Reincarca pagina curenta 108 | key_reload2_label= Reincarca pagina curenta 2 109 | key_forceReload_label= Reincarca fortat pagina curenta 110 | key_forceReload2_label= Reincarca pagina curenta 2 111 | key_toggleMute_label= Comuta mod silentios 112 | goHome_label= Prima pagina 113 | goBackKb_label= Inapoi 114 | goBackKb2_label= Inapoi 2 115 | goForwardKb_label= Inainte 116 | goForwardKb2_label= Inainte 2 117 | key_close_label= Inchide tab-ul 118 | key_undoCloseTab_label= Redeschide tabul inchis anterior 119 | key_undoCloseWindow_label= Redeschide fereastra inchisa anterior 120 | key_toggleAddonBar_label= Comuta bara de extensii 121 | key_findPrevious_label= Rezultatul anterior 122 | key_selectLastTab_label= Selecteaza ultimul tab 123 | key_closeOther_label= Inchide alte tab-uri 124 | key_prevTab_label= Arata tab-ul anterior 125 | key_nextTab_label= Arata tab-ul urmator 126 | key_selectTab1_label= Selecteaza tab-ul 1 127 | key_selectTab2_label= Selecteaza tab-ul 2 128 | key_selectTab3_label= Selecteaza tab-ul 3 129 | key_selectTab4_label= Selecteaza tab-ul 4 130 | key_selectTab5_label= Selecteaza tab-ul 5 131 | key_selectTab6_label= Selecteaza tab-ul 6 132 | key_selectTab7_label= Selecteaza tab-ul 7 133 | key_selectTab8_label= Selecteaza tab-ul 8 134 | key_webide_label= Deschide editorul web 135 | key_switchTextDirection_label= Comuta directia textului 136 | key_findSelection_label= Cauta selectia 137 | key_tabview_label= Grupuri de tab-uri 138 | key_nextTabGroup_label= Salt la grupul urmator de tab-uri 139 | key_previousTabGroup_label= Salt la grupul anterior de tab-uri 140 | key_sanitize_mac_label= Goleste istoricul 141 | key_devToolboxMenuItemF12_label= Unelte pentru programatori 142 | key_devToolboxMenuItem_label= Unelte pentru programatori 2 143 | key_fullScreen_old_label= Afiseaza pe tot ecranul 2 144 | key_minimizeWindow_label= Minimizeaza fereastra 145 | key_firebug_toggleBreakOn_label= Firebug: Comuta intrerupere la... 146 | key_firebug_detachFirebug_label= Firebug: Deschide in fereastra noua 147 | key_firebug_closeFirebug_label= Firebug: Dezactiveaza 148 | key_firebug_focusCommandLine_label= Firebug: Selecteaza linia de comanda 149 | key_firebug_toggleInspecting_label= Firebug: Comuta inspectorul 150 | key_foxyproxyquickadd_label= Foxyproxy: Adaugare rapida 151 | key_foxyproxychangeproxy_label= Foxyproxy: Schimba Proxy 152 | downloadbar-tgglky_label= Bara de stare pentru descarcari: Comuta 153 | focusChatBar_label= Selecteaza bara de mesaje (chat) 154 | secureLoginShortcut_label= Extensie pentru autentificare securizata 155 | homepage_title= Prima pagina 156 | overridePluginFocus_title= Evidentiaza in detrimentul extensiilor? 157 | overridePluginFocus_description= Cand optiunea este activa, extensiile precum Flash vor fi instruite sa elibereze controlul mouse-ului/tastaturii pentru o perioada prestabilita de timp dupa ce au primit click-uri. 158 | pluginCssSelectors_title= Lista de selectori CSS folosita la cautarea de extensii 159 | pluginCssSelectors_description= Aceasta lista va determina pentru care extensii vor fi inlocuite scurtaturile, asa cum s-a descris mai sus. 160 | UrlPatternsDialog_title= Sabloane de adrese 161 | UrlPatternsDialog_description= URL-uri pentru care nu se va permite extensiilor sa fie preselectate. Valoarea implicita este '*'. 162 | UrlPatternsDialog_label= Modifica 163 | ShortcutsDialog_title= Lista scurtaturi 164 | ShortcutsDialog_description= Deschide fereastra principala a extensiei, care iti va permite modificarea sau blocarea majoritatii scurtaturilor. 165 | ShortcutsDialog_label= Actualizeaza 166 | stealFocusDelay_title= Intarzierea deselectarii 167 | stealFocusDelay_description= Aceasta valoarea corespunde duratei (in milisecunde) pana ce extensiile vor fi instruite sa elibereze controlul mouse-ului/tastaturii. Valoarea implicita este 5 secunde. 168 | shiftAllowFocus_title= Permite extensiilor sa pastreze selectia la combinatia Shift+Click? 169 | shiftAllowFocus_description= Cand optiunea este activa, extensii precum Flash vor avea permisiunea de a prelua controlul mouse-ului/tastaturii daca tasta Shift este apasata in timpul click-ului. 170 | pluginCrawlLimit_title= Limita reincercarilor 171 | pluginCrawlLimit_description= Unele site-uri folosesc javascript pentru a incarca extensia Flash dupa ce alte componente sunt incarcate, caz in care este nevoie sa se scaneze din nou pagina cand se determina o schimbare in structura sa. Aceasta optiune denota numarul maxim de componente permise in pagina. Valoarea implicita este 5. 172 | -------------------------------------------------------------------------------- /lib/util/windows.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 6 | const sdkWindows = require("sdk/windows").browserWindows; 7 | const { viewFor } = require("sdk/view/core"); 8 | const prefpane = require("../prefs/prefpane"); 9 | const utils = require("sdk/window/utils"); 10 | const keys = require("../core/key"); 11 | const system = require("sdk/system"); 12 | const { Overlays } = require("../core/overlay"); 13 | const { Shortcut } = require("../core/shortcut"); 14 | const CustomXUL = require("../core/custom"); 15 | if (45 > system.version) { var bug645371 = require("../patch/bug-645371"); } 16 | else console.log("Firefox 45.0 or newer detected, excluding patch for bug-645371.") 17 | var bug406199 = require("../patch/bug-406199"); 18 | const extensionPrefs = require("sdk/simple-prefs"); 19 | 20 | var { PrefsTarget } = require("sdk/preferences/event-target"); 21 | var target = PrefsTarget({ branchName: "browser.ctrlTab" }); 22 | 23 | target.on(".previews", function() { 24 | 25 | console.log("browser.ctrlTab.previews has been turned "+ (!!target.prefs[".previews"] ? "on, we'll turn off our tab-switching feature in response.":"off, we'll turn on our tab-switching feature in response.")); 26 | 27 | Windows.addEventListener(null,null,true); 28 | 29 | 30 | }); 31 | 32 | let Windows = exports.Windows = { 33 | 34 | implementCustomXUL: function () { 35 | for (let window of utils.windows(null, {includePrivate:true})) { 36 | window = viewFor(window); 37 | if (utils.isBrowser(window)) { 38 | let customXulKeyset = window.document.querySelector("keyset[id='KeybinderCustomXUL']"); 39 | while ((customXulKeyset || {}).lastChild) { 40 | customXulKeyset.removeChild(customXulKeyset.lastChild); 41 | } 42 | } 43 | } 44 | 45 | if (true == extensionPrefs.prefs["allowCustomXULKeys"]) { 46 | for (let window of utils.windows(null, {includePrivate:true})) { 47 | window = viewFor(window); 48 | if (utils.isBrowser(window)) { 49 | 50 | if ("undefined" != typeof(customXulKeyset)) { let newKeyset = customXulKeyset } 51 | else { 52 | let newKeyset = Windows.createXulElement("keyset", 53 | {"id":"KeybinderCustomXUL"}, 54 | window.document.getElementsByTagName("window")[0], 55 | window 56 | ); 57 | } 58 | for (let [mapKey, mapValue] of CustomXUL.allCustomKeys().entries()) { 59 | let newKeyset = window.document.querySelector("keyset[id='KeybinderCustomXUL']"); 60 | let currentKey = Windows.createXulElement("key", 61 | { 62 | "id": mapValue.nsID(), 63 | "label":mapValue.getLabel(), 64 | "command":mapValue.getCommand(), 65 | "key":"", 66 | "modifiers":"" 67 | }, 68 | newKeyset, 69 | window); 70 | } 71 | 72 | let newKeyset = window.document.querySelector("keyset[id='KeybinderCustomXUL']"); 73 | 74 | } 75 | } 76 | } 77 | extensionPrefs.prefs["keysMapDirty"] = true; 78 | }, 79 | 80 | handleKeyPress: function (event) { 81 | 82 | let shortcut = Shortcut.fromEvent(event); 83 | if (shortcut.isComplete()) { // check if this is a custom shortcut 84 | let overlay = Overlays.findByCustomShortcut(shortcut); 85 | if (overlay) { 86 | overlay.key.executeCommand(); 87 | event.preventDefault(); 88 | event.stopPropagation(); 89 | } 90 | else { // check if this is either an overridden or disabled shortcut. 91 | // console.warn("Let's test for an overridden shortcut?") 92 | overlay = Overlays.findByOverriddenShortcut(shortcut) || Overlays.findByDisabledShortcut(shortcut); 93 | if (overlay) { // This shortcut has been disabled, so let's intercept it. 94 | event.preventDefault(); 95 | event.stopPropagation(); 96 | } 97 | } 98 | } 99 | }, 100 | addEventListener: function(type, callback, justRefreshTabSwitching = false) { 101 | for (let window of utils.windows(null, {includePrivate:true})) { 102 | if ("undefined" != typeof(bug645371)) { bug645371.applyPatch(utils.getToplevelWindow(window)); } 103 | if ("undefined" != typeof(bug406199)) { bug406199.applyPatch(utils.getToplevelWindow(window),!!(require('sdk/preferences/service').get("browser.ctrlTab.previews"))); } 104 | if ("main-window" == window.document.documentElement.getAttribute("id")) { 105 | prefpane.createNewMenuItem(window); 106 | } 107 | if (false == justRefreshTabSwitching) { window.addEventListener(type, callback, false); } // Don't add listeners if we're just toggling tab-switching. 108 | } 109 | }, 110 | 111 | removeEventListener: function(type, callback) { 112 | for (let window of utils.windows(null, {includePrivate:true})) { 113 | window.removeEventListener(type, callback, false); 114 | 115 | for (let i of ["sep-switch-tabs","ttshortcutsMenuItem","prevTab-command","nextTab-command"]) { 116 | let node = window.document.getElementById(i); 117 | if (node) { 118 | node.parentNode.removeChild(node); 119 | } 120 | } 121 | } 122 | }, 123 | 124 | toggleKey: function(key, state) { 125 | let id = "#" + key; 126 | for (let window of utils.windows(null, {includePrivate:true})) { 127 | if (utils.isBrowser(window)) { 128 | let keyToToggle = window.document.documentElement.querySelector(id); 129 | keyToToggle.setAttribute("disabled", state); 130 | } 131 | } 132 | }, 133 | 134 | disableKeys: function() { 135 | let disabledOverlay = Object.keys(Overlays.overlays.disabled); 136 | if (disabledOverlay.length > 0) { 137 | let disabledSelector = "#" + disabledOverlay.join(", #"); 138 | 139 | for (let window of utils.windows(null, {includePrivate:true})) { 140 | if (utils.isBrowser(window)) { 141 | let disabledKeys = window.document.documentElement.querySelectorAll(disabledSelector); 142 | 143 | for (let dkey = 0; dkey < disabledKeys.length; dkey++) { 144 | disabledKeys[dkey].setAttribute("disabled", "true"); 145 | } 146 | } 147 | } 148 | } 149 | }, 150 | 151 | enableKeys: function() { 152 | let disabledOverlay = Object.keys(Overlays.overlays.disabled); 153 | if (disabledOverlay.length > 0) { 154 | let disabledSelector = "#" + disabledOverlay.join(", #"); 155 | 156 | for (let window of utils.windows(null, {includePrivate:true})) { 157 | if (utils.isBrowser(window)) { 158 | let disabledKeys = window.document.documentElement.querySelectorAll(disabledSelector); 159 | 160 | for (let dkey = 0; dkey < disabledKeys.length; dkey++) { 161 | disabledKeys[dkey].setAttribute("disabled", "false"); 162 | } 163 | } 164 | } 165 | } 166 | }, 167 | 168 | getMostRecentWindow: function() { 169 | return utils.getMostRecentBrowserWindow(); 170 | }, 171 | getElementById: function(id) { 172 | return Windows.getMostRecentWindow().document.getElementById(id); 173 | }, 174 | 175 | querySelector: function(sel) { 176 | return Windows.getMostRecentWindow().document.querySelector(sel); 177 | }, 178 | 179 | querySelectorAll: function(sel) { 180 | return Windows.getMostRecentWindow().document.querySelectorAll(sel); 181 | }, 182 | 183 | createEvent: function(type) { 184 | return Windows.getMostRecentWindow().document.createEvent(type); 185 | }, 186 | 187 | createXulElement: function(tagName, attrs, parent, window = null) { 188 | let element = (window || Windows.getMostRecentWindow()).document.createElementNS(XUL_NS, tagName); 189 | 190 | if (attrs) { 191 | for (let name in attrs) 192 | element.setAttribute(name, attrs[name]); 193 | } 194 | 195 | if (parent) { parent.appendChild(element); } 196 | 197 | return element; 198 | } 199 | }; 200 | 201 | sdkWindows.on("open",function (window) { 202 | window = viewFor(window); 203 | if (utils.isBrowser(window)) { 204 | Windows.implementCustomXUL(); 205 | window.setTimeout(function () { 206 | Windows.addEventListener("keydown", Windows.handleKeyPress, false) 207 | Overlays._overlays && Windows.disableKeys(); 208 | } 209 | ,8); 210 | } 211 | }); 212 | 213 | sdkWindows.on("close",function (window) { 214 | window = viewFor(window); 215 | if (utils.isBrowser(window)) { 216 | window.removeEventListener("keydown", Windows.handleKeyPress, false); 217 | } 218 | }); -------------------------------------------------------------------------------- /lib/core/key.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | 7 | const { Windows } = require("../util/windows"); 8 | 9 | const { Shortcut } = require("../core/shortcut"); 10 | const { Modifiers } = require("../core/modifiers"); 11 | 12 | let Key = exports.Key = function (data) { 13 | let { domKeys } = require("../util/functions"); 14 | const { Windows } = require("../util/windows"); 15 | let element = Windows.getElementById(data.id); 16 | this.id = data.id; 17 | this.element = element; 18 | // this.key = element.getAttribute("key").toUpperCase().codePointAt(0); 19 | 20 | if (element.hasAttribute("keycode")) { 21 | this.keycode = element.getAttribute("keycode"); 22 | } 23 | else if (element.hasAttribute("key")) { 24 | this.keycode = domKeys.keys[element.getAttribute("key").toUpperCase()]; 25 | } 26 | 27 | // console.warn("Analyzing key id: "+element.getAttribute("id")+": element.key(raw): ("+element.getAttribute("key")+"), element.keycode: "+element.getAttribute("keycode")); 28 | 29 | if (element.hasAttribute("modifiers")) { 30 | let modifiers = element.getAttribute("modifiers").split(/[,\s]/); 31 | this.modifiers = new Modifiers({ 32 | modifiers: modifiers 33 | }); 34 | } 35 | 36 | this.shortcut = new Shortcut({ 37 | // key: this.key, 38 | keycode: this.keycode, 39 | modifiers: this.modifiers 40 | }); 41 | } 42 | 43 | Key.prototype = { 44 | executeCommand: function () { 45 | const { Windows } = require("../util/windows"); 46 | if (this.element.hasAttribute("command")) { 47 | let command = this.element.getAttribute("command"); 48 | command = Windows.getElementById(command); 49 | command && command.doCommand(); 50 | return; 51 | } 52 | 53 | if (this.element.hasAttribute("oncommand")) { 54 | let sourceEvent = Windows.createEvent("Events"); 55 | sourceEvent.initEvent("command", false, false); 56 | let event = Windows.createEvent("XULCommandEvents"); 57 | event.initCommandEvent("command", true, false, null, null, false, false, false, false, sourceEvent); 58 | this.element.dispatchEvent(event); 59 | return; 60 | } 61 | let id = this.id; 62 | let menuitem = (Windows.querySelector(`menuitem[key="${id}"][command]`) || Windows.querySelector(`menuitem[key="${id}"][oncommand]`)); 63 | 64 | if (menuitem) { 65 | if (menuitem.hasAttribute("command")) { 66 | let command = menuitem.getAttribute("command"); 67 | command = Windows.getElementById(command); 68 | command && command.doCommand(); 69 | } 70 | else if (menuitem.hasAttribute("oncommand")) { 71 | let sourceEvent = Windows.createEvent("Events"); 72 | sourceEvent.initEvent("command", false, false); 73 | let event = Windows.createEvent("XULCommandEvents"); 74 | event.initCommandEvent("command", true, false, null, null, false, false, false, false, sourceEvent); 75 | menuitem.dispatchEvent(event); 76 | return; 77 | } 78 | } 79 | }, 80 | 81 | getLabel: function () { 82 | return this.element.getAttribute("label"); 83 | }, 84 | 85 | toString: function () { 86 | return this.id; 87 | } 88 | }; 89 | 90 | let all = exports.all = (function () { 91 | let cache; 92 | return function () { 93 | const extensionPrefs = require("sdk/simple-prefs"); 94 | if (!cache || (true == extensionPrefs.prefs["keysMapDirty"])) { 95 | cache = new Map(); 96 | for (let child of require("../util/windows").Windows.querySelectorAll("key")) { 97 | if (child.getAttribute("command") == "Browser:Reload" && !child.hasAttribute("id")) { child.setAttribute("id", "key_reload2") } // Fix Reload Key without an ID. 98 | if (child.getAttribute("command") == "Browser:ReloadSkipCache" && !child.hasAttribute("id")) { child.setAttribute("id", "key_forceReload") } // Fix ReloadSkipCache Key without an ID. 99 | 100 | if (child.hasAttribute("oncommand")) { 101 | let id = child.getAttribute("id"); 102 | // console.warn("Current key, with id: "+id+", has an onCommand with value: "+child.getAttribute("oncommand")+", do we also have a command? "+child.getAttribute("command")); 103 | let menuitem = (require("../util/windows").Windows.querySelector(`menuitem[key="${id}"][command]`) || require("../util/windows").Windows.querySelector(`menuitem[key="${id}"][oncommand]`)); 104 | 105 | if (menuitem) { 106 | // console.warn("A menuitem was found!, let's see: "+XMLSerializer.serializeToString(menuitem)); 107 | let command = (menuitem.getAttribute("command") || menuitem.getAttribute("oncommand")); 108 | } 109 | 110 | 111 | } 112 | 113 | if (";" == child.getAttribute("oncommand")) { 114 | let id = child.getAttribute("id"); 115 | let menuitem = (require("../util/windows").Windows.querySelector(`menuitem[key="${id}"][command]`) || require("../util/windows").Windows.querySelector(`menuitem[key="${id}"][oncommand]`)); 116 | // console.warn("oncommand's value was (;), so we'll look for a menuitem... Was it found? "+(menuitem ? XMLSerializer.serializeToString(menuitem) : "null")); 117 | // console.warn("The full value of the key is: "+XMLSerializer.serializeToString(child)); 118 | if (menuitem) { 119 | let command = (menuitem.getAttribute("command") || menuitem.getAttribute("oncommand")); 120 | child.setAttribute("command",command); 121 | } 122 | } 123 | 124 | if (child.hasAttribute("id")) { 125 | let { unusableKeys } = require("../util/functions"); 126 | let id = child.getAttribute("id"); 127 | if ("tabGroups-key-tabView" != id && "tabGroups-key-nextGroup" != id && "tabGroups-key-previousGroup" != id) { 128 | cache.set(id, new Key({id: id})); 129 | } 130 | } 131 | } 132 | extensionPrefs.prefs["keysMapDirty"] = false; 133 | } 134 | return cache; 135 | }; 136 | })(); 137 | 138 | let find = exports.find = function (id) { 139 | return all().get(id); 140 | }; 141 | 142 | let filter = exports.filter = function (fun) { 143 | let filtered = new Map(); 144 | for (let key of all().values()) { 145 | if (fun(key)) { 146 | filtered.set(key.id, key); 147 | } 148 | } 149 | return filtered; 150 | } 151 | 152 | let filterByShortcut = exports.filterByShortcut = function (shortcut) { 153 | function filter(shortcut) { 154 | return key.shortcut.toString().toLowerCase() == (term.toLowerCase()); 155 | } 156 | 157 | let filtered = new Map(); 158 | for (let key of all().values()) { 159 | 160 | if (fun(key)) { 161 | filtered.set(key.id, key); 162 | } 163 | } 164 | return filtered; 165 | } 166 | 167 | let group = exports.group = function (keys, groups, defaultGroup) { 168 | let remaining = new Map(keys); 169 | let retval = new Map(); 170 | 171 | for (let name in groups) { 172 | let group = []; 173 | if ("CustomXUL" != name) { 174 | for (let id of groups[name]) { 175 | if (remaining.has(id)) { 176 | group.push(keys.get(id)); 177 | remaining.delete(id); 178 | } 179 | } 180 | } 181 | else { 182 | 183 | for (let [id, key] of remaining.entries()) { 184 | 185 | if (/^Keybinder_/.test(id)) { group.push(keys.get(id)); remaining.delete(id) } 186 | 187 | } 188 | 189 | } 190 | if (group.length) { 191 | retval.set(name, group); 192 | } 193 | } 194 | 195 | if (remaining.size) { 196 | retval.set(defaultGroup, [...remaining.values()]); 197 | } 198 | 199 | return retval; 200 | } 201 | 202 | // let children = Windows.querySelectorAll("key"); 203 | 204 | // for (let c = 0; c < children.length; c++) { 205 | // let key = children[c]; 206 | // var element = key; 207 | // if (key.getAttribute("command") == "Browser:Reload" && !key.getAttribute("id")) key.setAttribute("id", "key_reload2"); // Fix Reload Key without an ID. 208 | // if (key.getAttribute("command") == "Browser:ReloadSkipCache" && !key.getAttribute("id")) key.setAttribute("id", "key_forceReload"); // Fix ReloadSkipCache Key without an ID. 209 | // if (key.hasAttribute("id")) { 210 | // let id = key.getAttribute("id"); 211 | // this._keys[id] = new Key({ 212 | // id: id 213 | // }); 214 | // 215 | // } 216 | // else { // The other ID-less keys are basically just redundant. Leaving this debug bit in case somebody wants to use them, though. 217 | // const {Cc,Ci} = require("chrome"); 218 | // var XMLSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]. 219 | // createInstance(Ci.nsIDOMSerializer); 220 | // let noidElement = key; 221 | // console.info("DEBUG: Found key without ID: "+XMLSerializer.serializeToString(noidElement)); 222 | // 223 | // 224 | // } 225 | // } 226 | // return this._keys; 227 | // }, 228 | // 229 | // get keys() { 230 | // return this._keys || this._loadKeys(); 231 | // } 232 | // 233 | // }; -------------------------------------------------------------------------------- /lib/prefs/treeview.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | const keys = require("../core/key"); 7 | const CustomXUL = require("../core/custom"); 8 | 9 | const { Windows } = require("../util/windows"); 10 | const { Overlays } = require("../core/overlay"); 11 | 12 | var _ = require("sdk/l10n").get; 13 | const extensionPrefs = require("sdk/simple-prefs"); 14 | 15 | let { GROUPS, LABELS } = require("../util/functions"); 16 | 17 | function findKeyLabel(key) { 18 | const { Windows } = require("../util/windows"); 19 | let label = key.getLabel(); 20 | 21 | if (label) { 22 | return label; 23 | } 24 | 25 | let id = key.id; 26 | if (id in LABELS) { 27 | return LABELS[id]; 28 | } 29 | 30 | // try to find a menuitem 31 | let menuitem = Windows.querySelector(`menuitem[key="${id}"][label]`); 32 | return menuitem ? menuitem.getAttribute("label") : id; 33 | } 34 | 35 | let TreeView = exports.TreeView = function (term) { 36 | function filter(key) { 37 | return findKeyLabel(key).toLowerCase().includes(term.toLowerCase()) || 38 | key.shortcut.toString().toLowerCase().includes(term.toLowerCase()); 39 | } 40 | this._buildGroups(term ? keys.filter(filter) : keys.all()); 41 | this._buildRows(); 42 | }; 43 | 44 | TreeView.prototype = { 45 | _buildGroups: function(map) { 46 | this.groups = []; 47 | 48 | for (let [gname, gkeys] of keys.group(map, GROUPS, "Other")) { 49 | let lname = String.concat(gname,"_group"); 50 | let group = {type: "group", name: _(lname), parentIdx: -1, open: true, keys: gkeys}; 51 | this.groups.push(group); 52 | } 53 | }, 54 | 55 | _buildRows: function() { 56 | this.rows = []; 57 | 58 | for (let group of this.groups) { 59 | let parentIdx = this.rows.push(group) - 1; 60 | 61 | if (group.open) { 62 | group.keys.sort((a, b) => findKeyLabel(a) > findKeyLabel(b) ? 1 : findKeyLabel(a) < findKeyLabel(b) ? -1 : 0); 63 | for (let key of group.keys) { 64 | this.rows.push({type: "key", key: key, parentIdx: parentIdx}); 65 | } 66 | } 67 | } 68 | }, 69 | 70 | get rowCount() { return this.rows.length }, 71 | 72 | isContainer: function(idx) { 73 | return ("group" == this.rows[idx].type); 74 | }, 75 | 76 | isEditable: function(idx, column) { 77 | return column.index && !this.isContainer(idx); 78 | }, 79 | 80 | isContainerOpen: function(idx) { 81 | return this.rows[idx].open; 82 | }, 83 | 84 | getLevel: function(idx) { 85 | return +!this.isContainer(idx); 86 | }, 87 | 88 | getParentIndex: function(idx) { 89 | return this.rows[idx].parentIdx; 90 | }, 91 | 92 | toggleOpenState: function(idx) { 93 | let row = this.rows[idx]; 94 | 95 | let numRows = -row.keys.length; 96 | if (row.open = !row.open) { numRows *= -1; } 97 | 98 | this._buildRows(); 99 | this.treebox.rowCountChanged(idx + 1, numRows); 100 | this.treebox.invalidateRow(idx); 101 | }, 102 | 103 | hasNextSibling: function(idx, after) { 104 | let level = this.getLevel(idx); 105 | for (let t = after + 1; t < this.rowCount; t++) { 106 | let nextLevel = this.getLevel(t); 107 | if (nextLevel == level) { return true; } 108 | if (nextLevel < level) { return false; } 109 | } 110 | }, 111 | 112 | getCellText: function(idx, column) { 113 | let row = this.rows[idx]; 114 | if (this.isContainer(idx)) { return (column.index ? "" : row.name); } 115 | 116 | let key = row.key; 117 | if (!column.index) { return findKeyLabel(key); } 118 | 119 | let overlay = Overlays.findByKey(key); 120 | return (overlay ? overlay.shortcut : key.shortcut).toString(); 121 | }, 122 | 123 | getCellValue: function(idx, column) { 124 | if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); } 125 | }, 126 | 127 | getCellProperties: function(idx, column) { 128 | if (this.isContainer(idx)) { return; } 129 | 130 | if (!column.index) { return; } 131 | 132 | let props = []; 133 | let key = this.rows[idx].key; 134 | if (Overlays.findByKey(key)) { 135 | props.push("custom"); 136 | let toStringCache = Overlays.findByKey(key).shortcut.toString(); 137 | 138 | if (Overlays.findByConflictingShortcut(toStringCache)) { 139 | let groupColor = Overlays.getConflictingShortcutColor(toStringCache); 140 | props.push("maketext" + groupColor); 141 | } 142 | } 143 | if ((Overlays.findByCustomShortcut(key.shortcut)) && (!Overlays.findByOverriddenShortcut(key.shortcut)) && (!Overlays.findByDisabledKey(key))) // Display as overridden only if using default shortcut and not disabled. 144 | props.push("overridden"); 145 | 146 | if (Overlays.findByDisabledKey(key)) { props.push("disabled"); } 147 | 148 | return props.join(" "); 149 | }, 150 | 151 | setTree: function(treebox) { 152 | this.treebox = treebox; 153 | }, 154 | 155 | isContainerEmpty: function() { return false }, 156 | setCellText: function() { }, 157 | isSeparator: function() { return false }, 158 | isSorted: function() { return false }, 159 | getImageSrc: function() {}, 160 | getRowProperties: function() {}, 161 | getColumnProperties: function() {} 162 | }; 163 | 164 | let customXULTreeView = exports.customXULTreeView = function (term, find = false, tree = null, filtered = false, newXul = false) { 165 | 166 | function findNewRow(row) { 167 | return row.key == this; 168 | } 169 | 170 | function filter(key) { 171 | return key.getKey().toLowerCase().includes(term.toLowerCase()) || key.getLabel().toLowerCase().includes(term.toLowerCase()) || 172 | key.getCommand().toLowerCase().includes(term.toLowerCase()) 173 | } 174 | 175 | if (!find) { this._buildRows(term ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys()); } 176 | 177 | else { 178 | this._buildRows(!!filtered ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys()); 179 | let toSelect = this.rows.find(findNewRow,term); 180 | toSelect = this.rows.indexOf(toSelect); 181 | const { Windows } = require("../util/windows"); 182 | Windows.getMostRecentWindow().setTimeout(function () { 183 | tree.view.selection.adjustSelection((toSelect -1),1); 184 | tree.view.selection.select(toSelect); 185 | if (!!newXul) { tree.startEditing(tree.view.selection.currentIndex,tree.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); } 186 | },0); 187 | } 188 | }; 189 | 190 | customXULTreeView.prototype = { 191 | _buildRows: function(map) { 192 | this.rows = []; 193 | let sortedKeys = new Map([...map.entries()].sort((a,b) => a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0)); 194 | for (let [mapKey, mapValue] of sortedKeys.entries()) { 195 | this.rows.push({type:"customXULKey", key:mapValue.key, label:mapValue.label, command:mapValue.command}); 196 | } 197 | }, 198 | 199 | get rowCount() { return this.rows.length }, 200 | 201 | isContainer: function(idx) { 202 | return (this.rows.length > 1 ? ("group" == this.rows[idx].type) : null); 203 | }, 204 | 205 | performActionOnRow: function(action, idx) { 206 | if ("select" == action) { this.treebox.view.selection.select(idx) } 207 | }, 208 | 209 | 210 | isEditable: function(idx, column) { 211 | return column.index && !this.isContainer(idx); 212 | }, 213 | 214 | isSelectable: function(idx, column) { 215 | return column.index && !this.isContainer(idx); 216 | }, 217 | 218 | isContainerOpen: function(idx) { 219 | return this.rows[idx].open; 220 | }, 221 | 222 | getLevel: function(idx) { 223 | return +!this.isContainer(idx); 224 | }, 225 | 226 | getParentIndex: function(idx) { 227 | return this.rows[idx].parentIdx; 228 | }, 229 | 230 | toggleOpenState: function(idx) { 231 | let row = this.rows[idx]; 232 | 233 | let numRows = -row.keys.length; 234 | if (row.open = !row.open) { numRows *= -1; } 235 | 236 | this._buildRows(); 237 | this.treebox.rowCountChanged(idx + 1, numRows); 238 | this.treebox.invalidateRow(idx); 239 | }, 240 | 241 | hasNextSibling: function(idx, after) {}, 242 | 243 | getCellText: function(idx, column) { 244 | let row = this.rows[idx]; 245 | if (!row.key || !CustomXUL.find(row.key)) { return } 246 | if ("KeybinderCustomXUL_NameCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).key } 247 | if ("KeybinderCustomXUL_LabelCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).label } 248 | if ("KeybinderCustomXUL_ActionCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).command } 249 | }, 250 | 251 | getCellValue: function(idx, column) { 252 | if (this.rows.length < 1) { return; } 253 | if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); } 254 | }, 255 | 256 | getCellProperties: function(idx, column) {}, 257 | 258 | setTree: function(treebox) { 259 | this.treebox = treebox; 260 | }, 261 | 262 | isContainerEmpty: function() { return false }, 263 | setCellText: function() { }, 264 | isSeparator: function() { return false }, 265 | isSorted: function() { return false }, 266 | getImageSrc: function() {}, 267 | getRowProperties: function() {}, 268 | getColumnProperties: function() {} 269 | }; 270 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # Keybinder 2 | 3 | *Keybinder has been updated!* 4 | 5 | This Firefox add-on allows the user to customize various shortcuts, as well as completely disable the ones he doesn't like. 6 | It is based (and a large part of the code) was originally written by Tim Taubert for the "Customizable Shortcuts" add-on that can be found here: https://addons.mozilla.org/en-US/firefox/addon/customizable-shortcuts/ 7 | After much work and, at one point trying to contribute to the original project but being unable to reconcile all the changes I made with the original author, I decided to release my own branch of the extension. 8 | 9 | This extension supports localization, if you are fluent in a language other than English or Spanish, you can help at , 10 | No coding knowledge required. 11 | 12 | ## Current Version 13 | - Version: 2.0.5 14 | - Date: 2017-04-13 15 | - Official Releases @ AMO: 16 | - Support site: 17 | 18 | ## Known Issues 19 | + Certain commands can only be disabled, not remapped (So far: "Hide Firefox" and "Hide Others" in macOS) 20 | 21 | ## TO DO 22 | + Port the bug-78414 workaround to MutationObserver. 23 | + Various Localizations 24 | 25 | # Release Notes 26 | 27 | # SORRY GUYS, BUT I'M AFRAID THIS IS THE END OF THE LINE. 28 | I wanted to release a last update to address a couple bugs, and mkz's locale but I'm afraid this will be the last update to Keybinder. Mozilla's self-destruct course wth Firefox (coupled with not even having adopted all WebExtensions APIs yet) makes this addon impossible to maintain (and sadly, soon to cease working at all). Already many commands will not work (especially new ones) as XUL is being deprecated, and XUL keysets was the foundation this add-on relied on. I'm probably going to move to Vivaldi or something, I guess. Way to go, Mozilla. 29 | 30 | [[toc]] 31 | 32 | ## [2.0.5] - 2017-04-13 33 | + ### Added 34 | + ro-RO Localization, by mkz over at Babelzilla 35 | + ### Changed 36 | + The Extension now checks whether tab previews are enabled before adding the next-tab and prev-tab commands. 37 | + ### Fixed 38 | + Made sure content workers were properly removed. 39 | 40 | ## [2.0.4] - 2016-12-21 41 | + ### Changed 42 | + Shortcuts and Custom XUL windows can now be resized. 43 | + Release notes window is now given focus when the extension is updated. 44 | + ### Fixed 45 | + Removed that annoying "THE EVENT IS WORKING" alert. 46 | + Fixed shortcuts relying on the KeyboardEvent.code fallback not actually storing that code, this should fix most issues with shortcuts not persisting across restarts. 47 | + Minor changes to locale, and escaping of Backslashes to prevent some labels breaking. 48 | + Fixed a typo in the toolbar button. 49 | 50 | ## [2.0.3] - 2016-11-29 51 | + ### Fixed 52 | + Use minified libraries where possible, deleted several files with unused code from library folders, in order to accomodate AMO review process. 53 | 54 | ## [2.0.1] - 2016-11-24 55 | + ### Changed 56 | + Replace various locale .properties files with symlinks to eliminate potential confusions when translating. 57 | + ### Fixed 58 | + Prevent possible error on start-up, related to updating of extension settings. 59 | 60 | ## [2.0.0] - 2016-11-23 61 | 62 | + ### Added 63 | + At the request of many, added an optional (and experimental) feature to create custom XUL keys, which can then be assigned to shortcuts in the usual way. As of now, I have limited these custom keys to commands already defined in the Firefox commandset (i.e., no custom javascript). 64 | + Implemented a multi-pass mode that makes the "Steal focus" feature more robust, specifically, it can now be configured to scan a site more than once, allowing it to deal with sites that use scripting to create embeds after page-loading has completed (Reported by Randy\_Sinn on http://www.pooq.co.kr) 65 | + Initial German Localization (by undef-labs, needs some updating). 66 | 67 | + ### Changed 68 | + Besides the Tools menu and the extensions preferences, the mappings dialog can now also be accessed via a toolbar button. 69 | + Add "Quit" and "Preferences" shortcuts when not defined normally. 70 | + Moved several previously uncategorized shortcuts into their proper groups. Removed duplicate shortcuts in more than one group. 71 | + Completely re-factored the way keypresses are parsed, in order to make handling more robust. Now using KeyboardEvent.keyCode and KeyboardEvent.code when the former is unavailable, thus bringing back support for dead keys, among other things. 72 | + Shortcut mappings will now be stored alongside the rest of preferences. The extension will detect previously added shortcuts and convert them automatically when updating. 73 | 74 | + ### Fixed 75 | + Implemented proper sizes for all icons. 76 | + Fixed bug caused by obsolete keycode VK\_ENTER. 77 | + Made the release notes into a more workable format. 78 | + Windows.getMostRecentWindow() sometimes returned an incorrect window, which caused odd-behavior in some commands like selecting a specific tab; to fix this we now use the low-level API. (Original bug reported by Rami El Khatib) 79 | + Fixed sorting in the main shortcut dialog. 80 | + Fixed disabling and remapping of shortcuts defined only by menu items. 81 | + Fixed bug with disabling keys: All keys should now be re-enabled when the extension unloads. 82 | + Loads of smaller bug-fixes. 83 | 84 | ## [1.3.0] - 2016-04-11 85 | + ### Changed 86 | + Changed the way AltGraph is handled: In Linux, it's a distinct modifier. In Windows it's Control+Alt. 87 | + Finally removed dependence on the deprecated window-utils API. 88 | 89 | + ### Fixed 90 | + Fixed a bug caused by modifying and using shortcuts defined only by a menuitem (Copy, Cut, Paste, etc.), these now work correctly. 91 | 92 | ## [1.2.60] - 2016-03-28 93 | + ### Added 94 | + URL Patterns dialog now closable (without saving) by pushing Escape or Ctrl(Cmd on Mac)+W 95 | + URL Pattern dialog will now close and save changes by pressing Ctrl(Cmd on Mac)+Enter. 96 | + e10s support. The extension now officially supports multi-process firefox. 97 | 98 | + ### Changed 99 | + Textbox on URL Patterns dialog now selected by default. 100 | 101 | + ### Fixed 102 | + Changed problematic "exports" syntax. 103 | + Small cleanups and removed some deprecated function calls. 104 | + Fixed Application Icon. 105 | + Fixed regression that caused an error on Windows. 106 | 107 | ## [1.2.41] - 2016-03-24 108 | + ### Fixed 109 | + Get rid of deprecated expression closures. 110 | + Some refactoring and simplification of the code that manages listeners and opening new windows to fix the script becoming stumped if Firefox was left open but without any windows. 111 | 112 | ## [1.2.3] - 2016-03-23 113 | + ### Fixed 114 | + Squash a bug that would cause the extension to throw if a new window was opened while the shortcuts configuration window was also open. 115 | 116 | ## [1.2.2] - 2016-03-22 117 | 118 | + ### Changed 119 | + Consolidated locales in a single folder for compatibility with BabelZilla. 120 | 121 | ## [1.2.1] - 2016-03-21 122 | 123 | *This version implements several bug-fixes as well as some changes and additions and several of the updates Tim Taubert had made to his add-on between the time I branched my fork and when he decided to discontinue it.* 124 | 125 | + ### Added 126 | + Added some new key labels and modified groupings somewhat. 127 | + Added accesskeys to the buttons in the main configure dialog, to improve user experience and keyboard-friendliness. 128 | + The dialog now supports several keyboard shortcuts for various tasks: 129 | + Activate the filter by pressing Ctrl(Cmd on Mac)+F 130 | + Clear the filter by pressing Escape 131 | + Edit a shortcut by selecting it with the mouse or arrow keys and pressing Enter. 132 | + Cancel editing a shortcut by pressing Escape 133 | + Close the dialog by pressing Escape or Ctrl(Cmd on Mac)+W 134 | 135 | + ### Changed 136 | + The main dialog is now accessible (in addition to the usual place) from the extension preferences interface. 137 | + Added some new key labels and modified groupings somewhat. 138 | + More robust handling of modifiers, support for OS and AltGr modifiers. 139 | + Give platform-appropriate name to the "Meta" key. 140 | + When editing a shortcut, Keybinder will now automatically update it once it detects it's a valid shortcut. 141 | + Due to incompatibility in handling and storing shortcut definitions with Quicksaver's extension, Keybinder will now completely ignore Tab Groups on Firefox 45+. 142 | 143 | + ### Fixed 144 | + Fixed incorrect JSON parsing of URL Patterns preference that was causing Keybinder to throw. 145 | + Fixed a check in the pageMod code to prevent an occasional error. 146 | + Several issues with incorrect modifiers being read on some platforms. 147 | 148 | ## [1.1.2] - 2016-03-09 149 | + ### Fixed 150 | + Fixed an incorrect path in overlay.js that was causing an error. 151 | + Fixed compatibility with Firefox 45: If version is 45 or higher, the extension excludes the code targeting Tab Groups, UNLESS the TabGroups extension is also installed. 152 | 153 | ## [1.1.1] - 2014-09-16 154 | + ### Changed 155 | + Due to the way onKeyDown works, the extension will ignore dead keys and some international characters that ended up being represented as "null" and preventing the extension from working properly. 156 | 157 | + ### Fixed 158 | + Fixed bug when opening the shortcut dialog when already open; now, it focuses the window instead of trying to recreate it. 159 | + Same thing for URL patterns in the extension manager. 160 | + Clean-up script wasn't getting rid of menu items. 161 | + Prevent the extension from adding redundant menu items. 162 | 163 | ## [1.1] - 2014-08-19 164 | + ### Changed 165 | + Renamed Extension and changed icon. 166 | + As per the original add-on, changed to MPL 2.0. 167 | + Added sanity checks and a more robust interface for adding URLS to be scanned for plugins. 168 | + All changes to settings should now be applied immediately to open pages, as soon as they are made. 169 | 170 | + ### Fixed 171 | + Improved the flexibility and robustness of the content-scripts 172 | + Improved the code and handling of workers. 173 | + Make sure they actually get removed once they're not being used anymore. 174 | 175 | ## [1.0] - 2014-01-20 176 | + ### Added 177 | + Detects, color codes and warns conflicting shortcuts. 178 | + Added supports for a few of the shortcuts with no ID. 179 | + Added Private-Browsing support (although, to make sure we don't write to disk during Private Browsing, the Tools menu will only display the addon on non-Private windows) 180 | + Added work-around for bug 78414. 181 | + Added work-around for bug 406199. 182 | 183 | + ### Changed 184 | + Moved Key Mappings to its own dialog, under Tools Menus. 185 | + The extension should now be localizable. 186 | 187 | + ### Fixed 188 | + Fixed the configuration dialog search. 189 | + Prevent the extension from mapping unknown non-US keys to shortcuts (tildes, accents, etc.) 190 | + Several other minor changes and fixed from the original extension. 191 | -------------------------------------------------------------------------------- /chrome/skin/prefs.css: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #ttShortcutsPrefsDialog { 6 | width: 550px; 7 | height: auto; 8 | overflow: visible; 9 | } 10 | 11 | #KeybinderCustomXULDialog { 12 | width: 550px; 13 | height: auto; 14 | overflow: visible; 15 | } 16 | 17 | #KeybinderCustomXUL_NameCol { 18 | width: 100px; 19 | max-width: 100px; 20 | } 21 | 22 | #KeybinderCustomXUL_ActionCol { 23 | width: 150px; 24 | max-width: 150px; 25 | } 26 | 27 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(container), 28 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(custom) { 29 | font-weight: 700; 30 | } 31 | 32 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(overridden) { 33 | text-decoration: line-through; 34 | } 35 | 36 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(disabled) { 37 | text-decoration: line-through; 38 | color:red; 39 | } 40 | 41 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(container) { 42 | padding-left: 4px; 43 | } 44 | 45 | 46 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextaqua) { 47 | font-weight: 700; 48 | color:aqua; 49 | } 50 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextaquamarine) { 51 | font-weight: 700; 52 | color:aquamarine; 53 | } 54 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextblue) { 55 | font-weight: 700; 56 | color:blue; 57 | } 58 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextblueviolet) { 59 | font-weight: 700; 60 | color:blueviolet; 61 | } 62 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextbrown) { 63 | font-weight: 700; 64 | color:brown; 65 | } 66 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextburlywood) { 67 | font-weight: 700; 68 | color:burlywood; 69 | } 70 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextcadetblue) { 71 | font-weight: 700; 72 | color:cadetblue; 73 | } 74 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextchartreuse) { 75 | font-weight: 700; 76 | color:chartreuse; 77 | } 78 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextchocolate) { 79 | font-weight: 700; 80 | color:chocolate; 81 | } 82 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextcoral) { 83 | font-weight: 700; 84 | color:coral; 85 | } 86 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextcornflowerblue) { 87 | font-weight: 700; 88 | color:cornflowerblue; 89 | } 90 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextcrimson) { 91 | font-weight: 700; 92 | color:crimson; 93 | } 94 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextcyan) { 95 | font-weight: 700; 96 | color:cyan; 97 | } 98 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkblue) { 99 | font-weight: 700; 100 | color:darkblue; 101 | } 102 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkcyan) { 103 | font-weight: 700; 104 | color:darkcyan; 105 | } 106 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkgoldenrod) { 107 | font-weight: 700; 108 | color:darkgoldenrod; 109 | } 110 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkgray) { 111 | font-weight: 700; 112 | color:darkgray; 113 | } 114 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkgreen) { 115 | font-weight: 700; 116 | color:darkgreen; 117 | } 118 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkgrey) { 119 | font-weight: 700; 120 | color:darkgrey; 121 | } 122 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkkhaki) { 123 | font-weight: 700; 124 | color:darkkhaki; 125 | } 126 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkmagenta) { 127 | font-weight: 700; 128 | color:darkmagenta; 129 | } 130 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkolivegreen) { 131 | font-weight: 700; 132 | color:darkolivegreen; 133 | } 134 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkorange) { 135 | font-weight: 700; 136 | color:darkorange; 137 | } 138 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkorchid) { 139 | font-weight: 700; 140 | color:darkorchid; 141 | } 142 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkred) { 143 | font-weight: 700; 144 | color:darkred; 145 | } 146 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarksalmon) { 147 | font-weight: 700; 148 | color:darksalmon; 149 | } 150 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkseagreen) { 151 | font-weight: 700; 152 | color:darkseagreen; 153 | } 154 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkslateblue) { 155 | font-weight: 700; 156 | color:darkslateblue; 157 | } 158 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkslategray) { 159 | font-weight: 700; 160 | color:darkslategray; 161 | } 162 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkturquoise) { 163 | font-weight: 700; 164 | color:darkturquoise; 165 | } 166 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdarkviolet) { 167 | font-weight: 700; 168 | color:darkviolet; 169 | } 170 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdeeppink) { 171 | font-weight: 700; 172 | color:deeppink; 173 | } 174 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdeepskyblue) { 175 | font-weight: 700; 176 | color:deepskyblue; 177 | } 178 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdimgray) { 179 | font-weight: 700; 180 | color:dimgray; 181 | } 182 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextdodgerblue) { 183 | font-weight: 700; 184 | color:dodgerblue; 185 | } 186 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextforestgreen) { 187 | font-weight: 700; 188 | color:forestgreen; 189 | } 190 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextfuchsia) { 191 | font-weight: 700; 192 | color:fuchsia; 193 | } 194 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextgold) { 195 | font-weight: 700; 196 | color:gold; 197 | } 198 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextgoldenrod) { 199 | font-weight: 700; 200 | color:goldenrod; 201 | } 202 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextgray) { 203 | font-weight: 700; 204 | color:gray; 205 | } 206 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextgreen) { 207 | font-weight: 700; 208 | color:green; 209 | } 210 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextgreenyellow) { 211 | font-weight: 700; 212 | color:greenyellow; 213 | } 214 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketexthotpink) { 215 | font-weight: 700; 216 | color:hotpink; 217 | } 218 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextindianred) { 219 | font-weight: 700; 220 | color:indianred; 221 | } 222 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextindigo) { 223 | font-weight: 700; 224 | color:indigo; 225 | } 226 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextkhaki) { 227 | font-weight: 700; 228 | color:khaki; 229 | } 230 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlawngreen) { 231 | font-weight: 700; 232 | color:lawngreen; 233 | } 234 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightblue) { 235 | font-weight: 700; 236 | color:lightblue; 237 | } 238 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightcoral) { 239 | font-weight: 700; 240 | color:lightcoral; 241 | } 242 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightcyan) { 243 | font-weight: 700; 244 | color:lightcyan; 245 | } 246 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightgreen) { 247 | font-weight: 700; 248 | color:lightgreen; 249 | } 250 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightpink) { 251 | font-weight: 700; 252 | color:lightpink; 253 | } 254 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightsalmon) { 255 | font-weight: 700; 256 | color:lightsalmon; 257 | } 258 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightseagreen) { 259 | font-weight: 700; 260 | color:lightseagreen; 261 | } 262 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightskyblue) { 263 | font-weight: 700; 264 | color:lightskyblue; 265 | } 266 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightslategray) { 267 | font-weight: 700; 268 | color:lightslategray; 269 | } 270 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlightsteelblue) { 271 | font-weight: 700; 272 | color:lightsteelblue; 273 | } 274 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlime) { 275 | font-weight: 700; 276 | color:lime; 277 | } 278 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextlimegreen) { 279 | font-weight: 700; 280 | color:limegreen; 281 | } 282 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmagenta) { 283 | font-weight: 700; 284 | color:magenta; 285 | } 286 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumaquamarine) { 287 | font-weight: 700; 288 | color:mediumaquamarine; 289 | } 290 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumblue) { 291 | font-weight: 700; 292 | color:mediumblue; 293 | } 294 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumorchid) { 295 | font-weight: 700; 296 | color:mediumorchid; 297 | } 298 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumpurple) { 299 | font-weight: 700; 300 | color:mediumpurple; 301 | } 302 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumseagreen) { 303 | font-weight: 700; 304 | color:mediumseagreen; 305 | } 306 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumslateblue) { 307 | font-weight: 700; 308 | color:mediumslateblue; 309 | } 310 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumspringgreen) { 311 | font-weight: 700; 312 | color:mediumspringgreen; 313 | } 314 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumturquoise) { 315 | font-weight: 700; 316 | color:mediumturquoise; 317 | } 318 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmediumvioletred) { 319 | font-weight: 700; 320 | color:mediumvioletred; 321 | } 322 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmidnightblue) { 323 | font-weight: 700; 324 | color:midnightblue; 325 | } 326 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextmistyrose) { 327 | font-weight: 700; 328 | color:mistyrose; 329 | } 330 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextnavy) { 331 | font-weight: 700; 332 | color:navy; 333 | } 334 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextolive) { 335 | font-weight: 700; 336 | color:olive; 337 | } 338 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextolivedrab) { 339 | font-weight: 700; 340 | color:olivedrab; 341 | } 342 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextorange) { 343 | font-weight: 700; 344 | color:orange; 345 | } 346 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextorangered) { 347 | font-weight: 700; 348 | color:orangered; 349 | } 350 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextorchid) { 351 | font-weight: 700; 352 | color:orchid; 353 | } 354 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpalegreen) { 355 | font-weight: 700; 356 | color:palegreen; 357 | } 358 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpaleturquoise) { 359 | font-weight: 700; 360 | color:paleturquoise; 361 | } 362 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpalevioletred) { 363 | font-weight: 700; 364 | color:palevioletred; 365 | } 366 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextperu) { 367 | font-weight: 700; 368 | color:peru; 369 | } 370 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpink) { 371 | font-weight: 700; 372 | color:pink; 373 | } 374 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextplum) { 375 | font-weight: 700; 376 | color:plum; 377 | } 378 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpowderblue) { 379 | font-weight: 700; 380 | color:powderblue; 381 | } 382 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextpurple) { 383 | font-weight: 700; 384 | color:purple; 385 | } 386 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextrosybrown) { 387 | font-weight: 700; 388 | color:rosybrown; 389 | } 390 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextroyalblue) { 391 | font-weight: 700; 392 | color:royalblue; 393 | } 394 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextsaddlebrown) { 395 | font-weight: 700; 396 | color:saddlebrown; 397 | } 398 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextsalmon) { 399 | font-weight: 700; 400 | color:salmon; 401 | } 402 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextsandybrown) { 403 | font-weight: 700; 404 | color:sandybrown; 405 | } 406 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextseagreen) { 407 | font-weight: 700; 408 | color:seagreen; 409 | } 410 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextsienna) { 411 | font-weight: 700; 412 | color:sienna; 413 | } 414 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextskyblue) { 415 | font-weight: 700; 416 | color:skyblue; 417 | } 418 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextslateblue) { 419 | font-weight: 700; 420 | color:slateblue; 421 | } 422 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextslategray) { 423 | font-weight: 700; 424 | color:slategray; 425 | } 426 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextspringgreen) { 427 | font-weight: 700; 428 | color:springgreen; 429 | } 430 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextsteelblue) { 431 | font-weight: 700; 432 | color:steelblue; 433 | } 434 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketexttan) { 435 | font-weight: 700; 436 | color:tan; 437 | } 438 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextteal) { 439 | font-weight: 700; 440 | color:teal; 441 | } 442 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextthistle) { 443 | font-weight: 700; 444 | color:thistle; 445 | } 446 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketexttomato) { 447 | font-weight: 700; 448 | color:tomato; 449 | } 450 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextturquoise) { 451 | font-weight: 700; 452 | color:turquoise; 453 | } 454 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextviolet) { 455 | font-weight: 700; 456 | color:violet; 457 | } 458 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextwheat) { 459 | font-weight: 700; 460 | color:wheat; 461 | } 462 | #ttCustomizableShortcuts_Tree > treechildren::-moz-tree-cell-text(maketextyellowgreen) { 463 | font-weight: 700; 464 | color:yellowgreen; 465 | } 466 | 467 | hbox, 468 | vbox, 469 | separator, 470 | spacer, 471 | #ttCustomizableShortcuts_TreeBox, 472 | #ttCustomizableShortcuts_ButtonBox, 473 | #ttCustomizableShortcuts_SearchBox 474 | { 475 | padding: 1px; 476 | margin: 1px; 477 | } 478 | 479 | #ttCustomizableShortcuts_Reset, 480 | #ttCustomizableShortcuts_Edit, 481 | #ttCustomizableShortcuts_Disable { 482 | padding-left:1px; 483 | padding-right:1px; 484 | width:auto; 485 | } -------------------------------------------------------------------------------- /lib/prefs/prefpane.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 6 | 7 | const { Windows } = require("../util/windows"); 8 | 9 | const keys = require("../core/key"); 10 | const { Overlay, Overlays } = require("../core/overlay"); 11 | const { PreferenceTree, customXULTree } = require("../prefs/tree"); 12 | const utils = require("sdk/window/utils"); 13 | const { TreeView, customXULTreeView } = require("../prefs/treeview"); 14 | const CustomXUL = require("../core/custom"); 15 | const events = require("sdk/system/events"); 16 | const { ActionButton } = require("sdk/ui/button/action"); 17 | const extensionPrefs = require("sdk/simple-prefs"); 18 | const { windowMediator, prompterService } = require("../util/functions"); 19 | var _ = require("sdk/l10n").get; 20 | 21 | 22 | function occurrences(string, subString, allowOverlapping) { 23 | /** Function count the occurrences of substring in a string; 24 | * @param {String} string Required. The string; 25 | * @param {String} subString Required. The string to search for; 26 | * @param {Boolean} allowOverlapping Optional. Default: false; 27 | */ 28 | string += ""; 29 | subString += ""; 30 | if (subString.length <= 0) return string.length + 1; 31 | 32 | var n = 0, 33 | pos = 0; 34 | var step = (allowOverlapping) ? (1) : (subString.length); 35 | 36 | while (true) { 37 | pos = string.indexOf(subString, pos); 38 | if (pos >= 0) { 39 | n++; 40 | pos += step; 41 | } else break; 42 | } 43 | return (n); 44 | } 45 | 46 | function ValidURL(str) { 47 | var strictPattern = new RegExp(/^((https?:\/\/(?!\*))|(\*\.))((([a-z\d]([a-z\d-]*[a-z\d])*|(\*))\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(\:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(\#[-a-z\d_]*)?$/i); // Strict URL matching pattern. 48 | var commonError_1 = new RegExp(/(^https?:\/\/){0}(\d{1,3}\.){3}\d(\:\d{2,5})?/i); // Catch IP without protocol, will try prepending 'http://' before failing. 49 | var commonError_2 = new RegExp(/^(https?:\/\/){0}([-a-z\d%_~+]{2,})\.([a-z-_]){2,}([0-9])*(\/|\:)?/i) // Catch domain.tld without protocol or wildcard; // will try prepending '*.' before failing. 50 | if ("" == str) { 51 | return false; 52 | } 53 | if (occurrences(str, '*') > 1) { 54 | return false; 55 | } 56 | if ('*' == str) { 57 | return str; 58 | } else if (!strictPattern.test(str)) { 59 | if (true == commonError_1.test(str)) { 60 | return String.concat("http://", str); 61 | } else if (true == commonError_2.test(str)) { 62 | return String.concat("*.", str); 63 | } else { 64 | return false; 65 | } 66 | } else { 67 | return str; 68 | } 69 | } 70 | 71 | function MenuItem(window, kind) { 72 | if (!utils.isBrowser(window)) { return } // Catch non-browser windows. 73 | this.window = window; 74 | if ("tabSwitching" == kind) { 75 | if (this.window.document.getElementById("prevTab-command")) { return; } // Prevent creating duplicate menu items. 76 | let parentmenu = this.window.document.getElementById("windowPopup"); 77 | if (!parentmenu) { return; } // Only add the menu items if the Windows menu exists. 78 | let windowsep = this.window.document.getElementById("sep-window-list"); 79 | this.separator = this._createElement("menuseparator"); 80 | parentmenu.insertBefore(this.separator, windowsep); 81 | this.separator.setAttribute("id", "sep-switch-tabs"); 82 | 83 | this.prevtab = this._createElement("menuitem"); 84 | parentmenu.insertBefore(this.prevtab, this.separator.nextSibling); 85 | this.prevtab.setAttribute("id", "prevTab-command"); 86 | this.prevtab.setAttribute("command", "Browser:PrevTab"); 87 | this.prevtab.setAttribute("label", _("previousTab_label")); 88 | 89 | this.nexttab = this._createElement("menuitem"); 90 | parentmenu.insertBefore(this.nexttab, this.prevtab.nextSibling); 91 | this.nexttab.setAttribute("id", "nextTab-command"); 92 | this.nexttab.setAttribute("command", "Browser:NextTab"); 93 | this.nexttab.setAttribute("label", _("nextTab_label")); 94 | } else { 95 | let self = this; 96 | if (this.window.document.getElementById("ttshortcutsMenuItem")) { return; } // Prevent creating duplicate menu items. 97 | this.element = this._createElement("menuitem"); 98 | this.element.setAttribute("id", "ttshortcutsMenuItem"); 99 | this.element.setAttribute("class", "menu-iconic"); 100 | this.element.setAttribute("image", require("sdk/self").data.url("icon18.png")); 101 | this.element.setAttribute("label", _("MenuItem_label")); 102 | this.element.setAttribute("insertBefore", "sanitizeSeparator"); 103 | this.element.setAttribute("oncommand", "if (!Cc[\"@mozilla.org/appshell/window-mediator;1\"] .getService(Ci.nsIWindowMediator).getMostRecentWindow('Keybinder:config')) { window.openDialog('chrome://keybinder/content/ttShortcuts_preferences.xul','Keybinder','chrome,titlebar=yes,close=no,toolbar=no,centerscreen,dialog=yes,resizable=yes') } else { Cc[\"@mozilla.org/appshell/window-mediator;1\"] .getService(Ci.nsIWindowMediator).getMostRecentWindow('Keybinder:config').focus() }"); 104 | let parentmenu = this.window.document.getElementById("menu_ToolsPopup"); 105 | parentmenu.appendChild(this.element); 106 | } 107 | } 108 | 109 | MenuItem.prototype = { 110 | _built: false, 111 | 112 | _createStylesheet: function(href) { 113 | let doc = this.window.document; 114 | let style = doc.createProcessingInstruction("xml-stylesheet", 'href="' + href + '"'); 115 | doc.insertBefore(style, doc.querySelector("window")); 116 | }, 117 | 118 | _createElement: function(tag, parent) { 119 | let element = this.window.document.createElementNS(XUL_NS, tag); 120 | parent && parent.appendChild(element); 121 | return element; 122 | }, 123 | 124 | _build: function() { 125 | return; 126 | }, 127 | }; 128 | 129 | function warnforConflicting() { 130 | prompterService.alert(window, _("conflictWarning.title"), _("conflictWarning.text")); 131 | } 132 | 133 | let createNewMenuItem = exports.createNewMenuItem = function (window, kind) { 134 | new MenuItem (window, kind); 135 | } 136 | 137 | events.on("toplevel-window-ready",function(event) { 138 | event.subject.setTimeout(function() { 139 | let id = event.subject.window.document.documentElement.getAttribute("id"); 140 | switch (id) { 141 | case "ttShortcutsPrefsDialog": 142 | preferenceDialogInit(event.subject.window); 143 | break; 144 | case "urlPatternsDialog": 145 | let window = event.subject.window; 146 | window.validateUrls = function() { 147 | let tempArray = (window.document.getElementById("textbox").value).split('\n'); 148 | let arraySize = tempArray.length; 149 | let i = 0; 150 | for (let l of tempArray) { 151 | if (!ValidURL(l)) { 152 | tempArray[i] = ""; 153 | } else { 154 | tempArray[i] = ValidURL(l); 155 | } 156 | i++; 157 | } 158 | tempArray = tempArray.filter(function(i) { 159 | return i != "" 160 | }); 161 | extensionPrefs.prefs['domainUrlPattern'] = JSON.stringify(tempArray); 162 | } 163 | break; 164 | case "KeybinderCustomXULDialog": 165 | customXULDialogInit(event.subject.window); 166 | break; 167 | } 168 | },7); 169 | }, true); 170 | 171 | function keybinderDialog (id, uri, name, options, extras = null) { 172 | const { Windows } = require("../util/windows"); 173 | if (!windowMediator.getMostRecentWindow(id)) { 174 | Windows.getMostRecentWindow().openDialog(uri, name, options, extras); 175 | } 176 | else { 177 | windowMediator.getMostRecentWindow(id).focus(); 178 | } 179 | } 180 | 181 | extensionPrefs.on("UrlPatternsDialog", function() { 182 | keybinderDialog('Keybinder:URLPatterns', 'chrome://keybinder/content/urlpatterns.xul', 'UrlPatterns', 'chrome,titlebar=yes,close=no,toolbar=no,centerscreen,dialog=yes', (JSON.parse(extensionPrefs.prefs['domainUrlPattern'])).join("\n")); 183 | }); 184 | 185 | 186 | extensionPrefs.on("allowCustomXULKeys", function() { 187 | const { Windows } = require("../util/windows"); 188 | Windows.implementCustomXUL(); 189 | }); 190 | 191 | extensionPrefs.on("ShortcutsDialog", function() { 192 | keybinderDialog('Keybinder:config', 'chrome://keybinder/content/ttShortcuts_preferences.xul', 'Keybinder', 'chrome,titlebar=yes,close=no,toolbar=no,centerscreen,dialog=yes,resizable=yes'); 193 | }); 194 | 195 | extensionPrefs.on("customXULDialog", function() { 196 | keybinderDialog('Keybinder:CustomXUL', 'chrome://keybinder/content/customXulKeys.xul', 'CustomXUL', 'chrome,titlebar=yes,close=no,toolbar=no,centerscreen,dialog=yes,resizable=yes'); 197 | }); 198 | 199 | var keybinderButton = ActionButton({ 200 | id: "keybinderToolbarButton", 201 | label: "Keybinder", 202 | icon: { 203 | "18":"resource://keybinder-at-fail-dot-cl/data/icon18.png", 204 | "32":"resource://keybinder-at-fail-dot-cl/data/icon32.png", 205 | "36":"resource://keybinder-at-fail-dot-cl/data/icon36.png", 206 | "64":"resource://keybinder-at-fail-dot-cl/data/icon64.png" 207 | }, 208 | onClick: handleKeybinderClick 209 | }); 210 | 211 | function handleKeybinderClick () { 212 | keybinderDialog('Keybinder:config', 'chrome://keybinder/content/ttShortcuts_preferences.xul', 'Keybinder', 'chrome,titlebar=yes,close=no,toolbar=no,centerscreen,dialog=yes,resizable=yes'); 213 | } 214 | 215 | function preferenceDialogInit (window) { 216 | let self = this; 217 | this.window = window; 218 | this.filter = this.window.document.getElementById("ttCustomizableShortcuts_Filter"); 219 | this.resetButton = this.window.document.getElementById("ttCustomizableShortcuts_Reset"); 220 | this.editButton = this.window.document.getElementById("ttCustomizableShortcuts_Edit"); 221 | this.disableButton = this.window.document.getElementById("ttCustomizableShortcuts_Disable"); 222 | this.acceptButton = this.window.document.getElementById("ttCustomizableShortcuts_Accept"); 223 | 224 | let tree = new PreferenceTree(this.window); 225 | this.treeElement = tree.toElement(); 226 | this.window.setTimeout(function() { self.treeElement.view = new TreeView() }, 0); 227 | 228 | this.acceptButton.addEventListener("command", function() { 229 | if ("undefined" != typeof(Overlays.overlays.conflicting)) { 230 | let checkForConflicting = Object.keys(Overlays.overlays.conflicting).length; 231 | if (checkForConflicting >= 1) { warnforConflicting(); } 232 | else { windowMediator.getMostRecentWindow("Keybinder:config").window.close(); } 233 | } 234 | else { windowMediator.getMostRecentWindow("Keybinder:config").window.close(); } 235 | }, false); 236 | 237 | this.resetButton.addEventListener("command", function() { 238 | let row = self.treeElement.currentIndex; 239 | let column = self.treeElement.columns.getLastColumn(); 240 | let id = self.treeElement.view.getCellValue(row, column); 241 | let key = keys.find(id); 242 | Overlays.removeByKey(key); 243 | self.resetButton.setAttribute("disabled", true); 244 | self.treeElement.treeBoxObject.invalidateRow(row); 245 | }, false); 246 | 247 | this.editButton.addEventListener("command", function() { 248 | let row = self.treeElement.currentIndex; 249 | let column = self.treeElement.columns.getLastColumn(); 250 | self.treeElement.startEditing(row, column); 251 | }, false); 252 | 253 | this.disableButton.addEventListener("command", function() { 254 | const { Windows } = require("../util/windows"); 255 | let row = self.treeElement.currentIndex; 256 | let column = self.treeElement.columns.getLastColumn(); 257 | let id = self.treeElement.view.getCellValue(row, column); 258 | let key = keys.find(id); 259 | if (!Overlays.findByDisabledKey(key)) { 260 | new Overlay({ 261 | key: key, 262 | shortcut: { 263 | disabled: true 264 | } 265 | }); 266 | self.disableButton.label = _("disableButton_true"); 267 | self.disableButton.acesskey = _("disableButton_true_key"); 268 | self.treeElement.treeBoxObject.invalidateRow(row); 269 | } else { 270 | Overlays.removeByKey(key); 271 | self.disableButton.label = _("disableButton_false"); 272 | self.disableButton.acesskey = _("disableButton_false_key"); 273 | self.treeElement.treeBoxObject.invalidateRow(row); 274 | } 275 | 276 | let isDisabled = ( !! Overlays.findByDisabledKey(key)).toString(); 277 | Windows.toggleKey(key, isDisabled); 278 | }, false); 279 | 280 | this.filter.addEventListener("command", function() { 281 | self.treeElement.view = new TreeView(self.filter.value); 282 | }, false); 283 | } 284 | 285 | function customXULDialogInit (window) { 286 | let self = this; 287 | this.window = window; 288 | this.filter = this.window.document.getElementById("KeybinderCustomXUL_Filter"); 289 | this.addButton = this.window.document.getElementById("KeybinderCustomXUL_Add"); 290 | this.editButton = this.window.document.getElementById("KeybinderCustomXUL_Edit"); 291 | this.deleteButton = this.window.document.getElementById("KeybinderCustomXUL_Delete"); 292 | this.acceptButton = this.window.document.getElementById("KeybinderCustomXUL_Accept"); 293 | let customXULKeys = extensionPrefs.prefs['customXULKeys']; 294 | let conflictingKeys = []; 295 | 296 | let tree = new customXULTree(this.window); 297 | this.treeElement = tree.toElement(); 298 | this.window.setTimeout(function() { self.treeElement.view = new customXULTreeView() }, 0); 299 | 300 | this.addButton.addEventListener("command", function() { 301 | let input = {}; 302 | let newKey = prompterService.prompt(null, _("customXulNewKey.title"), _("customXulNewKey.text"), input, null, {}) 303 | if (!!newKey) { 304 | let window = windowMediator.getMostRecentWindow("Keybinder:CustomXUL"); 305 | let tree = window.document.getElementById("KeybinderCustomXUL_Tree"); 306 | if (!(/^[a-zA-Z0-9-_]+$/).test(input.value)) { prompterService.alert(window, _("conflictWarning.title"), _("NewCustomXulInvalid.text")); return } // Catch invalid keys 307 | if (CustomXUL.find(input.value)) { prompterService.alert(window, _("conflictWarning.title"), _("customXulConflict.text")); return } // Catch duplicates. 308 | CustomXUL.allCustomKeys().set(input.value, new CustomXUL.CustomXUL({key:input.value, label:_("NewCustomXul.label"), command:_("NewCustomXul.command")})); 309 | let filter = window.document.getElementById("KeybinderCustomXUL_Filter"); 310 | if (!!filter.value) { 311 | filter.value = input.value; 312 | window.setTimeout(function() { 313 | tree.view = new customXULTreeView(input.value,true,tree,true,true); // Let's make sure to avoid possible errors if rows were filtered. 314 | },0,tree); 315 | } 316 | else { 317 | window.setTimeout(function() { 318 | tree.view = new customXULTreeView(input.value,true,tree,false,true); 319 | },0,tree); 320 | } 321 | } 322 | else { return } 323 | }, false); 324 | 325 | this.acceptButton.addEventListener("command", function() { 326 | const { Windows } = require("../util/windows"); 327 | Windows.implementCustomXUL(); 328 | windowMediator.getMostRecentWindow("Keybinder:CustomXUL").window.close(); 329 | }, false); 330 | 331 | this.editButton.addEventListener("command", function() { 332 | let row = self.treeElement.currentIndex; 333 | let column = self.treeElement.columns.getNamedColumn("KeybinderCustomXUL_NameCol"); 334 | self.treeElement.startEditing(row, column); 335 | }, false); 336 | 337 | this.deleteButton.addEventListener("command", function() { 338 | let row = self.treeElement.view.selection.currentIndex; 339 | let column = self.treeElement.columns.getNamedColumn("KeybinderCustomXUL_NameCol"); 340 | let id = self.treeElement.view.getCellValue(row, column); 341 | if (CustomXUL.find(id)) { 342 | let window = windowMediator.getMostRecentWindow("Keybinder:CustomXUL"); 343 | let tree = window.document.getElementById("KeybinderCustomXUL_Tree"); 344 | if (!prompterService.confirm(null,_("customXulConfirmDelete.title"),_("customXulConfirmDelete.text"))) { return } 345 | CustomXUL.allCustomKeys().delete(id); 346 | window.setTimeout(function() { tree.view = new customXULTreeView(); tree.view.selection.select(0); }, 0); 347 | } 348 | }, false); 349 | 350 | this.filter.addEventListener("command", function() { 351 | self.treeElement.view = new customXULTreeView(self.filter.value); 352 | }, false); 353 | } 354 | 355 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /lib/prefs/tree.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | 6 | 7 | const keys = require("../core/key"); 8 | const CustomXUL = require("../core/custom"); 9 | const { customXULTreeView } = require("../prefs/treeview"); 10 | const { Overlay, Overlays } = require("../core/overlay"); 11 | const { Shortcut } = require("../core/shortcut"); 12 | var _ = require("sdk/l10n").get; 13 | const { prompterService, windowMediator } = require("../util/functions"); 14 | 15 | const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 16 | 17 | let PreferenceTree = exports.PreferenceTree = function (window) { 18 | let self = this; 19 | 20 | this.window = window; 21 | // this.window.addEventListener("keydown", function(e) { self._onKeyDown(e) }, true); 22 | // this.window.addEventListener("keypress", function(event) { self._onKeyPress(event) }, true); 23 | this.element = this.window.document.getElementById("ttCustomizableShortcuts_Tree"); 24 | 25 | this.element.addEventListener("keydown", function(event) { self._onKeyPress(event) }, true); 26 | 27 | this.element.addEventListener("select", function(e) { self._onSelect(e) }, false); 28 | this.window.setTimeout(function () { 29 | this.window.document.getElementById("ttCustomizableShortcuts_Tree").focus(); 30 | this.window.document.getElementById("ttCustomizableShortcuts_Tree").view.selection.select(1); 31 | },1); 32 | } 33 | 34 | PreferenceTree.prototype = { 35 | _createElement: function(tag, parent) { 36 | let element = this.window.document.createElementNS(XUL_NS, tag); 37 | parent && parent.appendChild(element); 38 | return element; 39 | }, 40 | 41 | _onSelect: function(event) { 42 | let row = this.element.currentIndex; 43 | let column = this.element.columns.getLastColumn(); 44 | let id = this.element.view.getCellValue(row, column); 45 | if (this.element.view.isContainer(row) == true) { // Catch Error Produced by selecting a row with no key. 46 | return; 47 | } 48 | let key = keys.find(id); 49 | this.window.document.getElementById("ttCustomizableShortcuts_Reset").setAttribute("disabled", !(Overlays.findByKey(key) || Overlays.findByDisabledKey(key))); 50 | this.window.document.getElementById("ttCustomizableShortcuts_Edit").setAttribute("disabled", false); 51 | this.window.document.getElementById("ttCustomizableShortcuts_Disable").setAttribute("disabled", false); 52 | let isDisabled = !! Overlays.findByDisabledKey(key); 53 | let buttonLabel = "disableButton_" + isDisabled; 54 | this.window.document.getElementById("ttCustomizableShortcuts_Disable").setAttribute("label", _(buttonLabel)); 55 | }, 56 | _onKeyPress: function(event) { 57 | if (this.element.editingColumn) { 58 | 59 | event.preventDefault(); 60 | event.stopPropagation(); 61 | 62 | let shortcut = Shortcut.fromEvent(event); 63 | shortcut.isComplete() && (this.element.inputField.value = shortcut.toString()); 64 | 65 | if (("VK_ESCAPE" == shortcut.keycode) && ("Esc" == shortcut.toString())) { 66 | event.preventDefault(); 67 | event.stopPropagation(); 68 | this.element.stopEditing(true); 69 | this.element.focus(); 70 | this._onSelect(event); 71 | return 72 | } 73 | if ("None" == shortcut.toString() || "+None" == shortcut.toString().substr(-5)) { // Prevent dysfunctional shortcuts with unrecognized and/or dead keys. 74 | event.preventDefault(); 75 | event.stopPropagation(); 76 | this.element.stopEditing(true); 77 | this.element.focus(); 78 | this._onSelect(event); 79 | let row = this.element.currentIndex; 80 | let column = this.element.columns.getLastColumn(); 81 | this.element.startEditing(row, column); 82 | return; 83 | } 84 | 85 | const { editingRow, editingColumn } = this.element; 86 | let id = this.element.view.getCellValue(editingRow, editingColumn); 87 | 88 | if (shortcut.isComplete()) { 89 | new Overlay({ 90 | key: keys.find(id), 91 | shortcut: shortcut 92 | }); 93 | this.element.stopEditing(true); 94 | this.element.focus(); //Restore focus so we can navigate the list with the keyboard arrows. 95 | this._onSelect(event); 96 | Overlays.getDuplicates(); 97 | this.window.document.getElementById("ttCustomizableShortcuts_Reset").setAttribute("disabled", false); 98 | } 99 | } 100 | else { 101 | if (("Enter" == event.key) || ("VK_ENTER" == event.keycode) || ("VK_RETURN" == event.keycode)) { 102 | event.preventDefault(); 103 | event.stopPropagation(); 104 | this.window.document.getElementById("ttCustomizableShortcuts_Edit").click(); 105 | } 106 | } 107 | }, 108 | 109 | toElement: function() { 110 | return this.element; 111 | } 112 | }; 113 | 114 | let customXULTree = exports.customXULTree = function (window) { 115 | let self = this; 116 | this.window = window; 117 | this.element = this.window.document.getElementById("KeybinderCustomXUL_Tree"); 118 | this.element.addEventListener("keypress", function(event) { self._onKeyPress(event) }, true); 119 | this.element.inputField.addEventListener("keydown", function(event) { self._onKeyDown(event, this.editingColumn) }, true); 120 | this.element.inputField.addEventListener("paste", function(event) { self._onPaste(event) }, true); 121 | this.element.addEventListener("change", function(event, target) { self._onChange(event, this.editingColumn) }, true); 122 | this.element.addEventListener("select", function(event) { self._onSelect(event) }, false); 123 | this.window.setTimeout(function () { 124 | this.window.document.getElementById("KeybinderCustomXUL_Tree").view.selection.select(0) 125 | this.window.document.getElementById("KeybinderCustomXUL_Tree").focus(); 126 | this.window.document.getElementById("KeybinderCustomXUL_Add").setAttribute("disabled", false); 127 | },1); 128 | } 129 | 130 | customXULTree.prototype = { 131 | _createElement: function(tag, parent) { 132 | let element = this.window.document.createElementNS(XUL_NS, tag); 133 | parent && parent.appendChild(element); 134 | return element; 135 | }, 136 | 137 | _onSelect: function(event) { 138 | if (this.element.currentIndex < 0) { this.element.view.selection.select(0) } 139 | let row = this.element.view.selection.currentIndex; 140 | let key = this.element.view.getCellValue(row, this.element.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); 141 | if ((this.element.view.isContainer(row) == true) || (!key)) { // Catch Error Produced by selecting a row with no key. 142 | return; 143 | } 144 | let item = CustomXUL.find(key); 145 | if (item) { 146 | this.window.document.getElementById("KeybinderCustomXUL_Edit").setAttribute("disabled", false); 147 | this.window.document.getElementById("KeybinderCustomXUL_Delete").setAttribute("disabled", false); 148 | } 149 | this.window.document.getElementById("KeybinderCustomXUL_Add").setAttribute("disabled", false); 150 | }, 151 | 152 | _validate: function(c, type, field = false) { 153 | if (!!field) { 154 | switch (type) { 155 | case "key": 156 | return (/^[a-zA-Z0-9-_]+$/).test(c); 157 | break; 158 | case "label": 159 | return (/^[^*{}()\[\]\\\/\"\'\`\´^#]+$/).test(c); 160 | break; 161 | case "command": 162 | return (/^[a-zA-Z0-9-_:]+$/).test(c); 163 | break; 164 | default: 165 | return false; 166 | } 167 | } 168 | else { 169 | if (c.length > 1) { return true } 170 | switch (type) { 171 | case "key": 172 | return (/^[a-zA-Z0-9-_]$/).test(c); 173 | break; 174 | case "label": 175 | return (/^[^*{}()\[\]\\\/\"\'\`\´^#]$/).test(c); 176 | break; 177 | case "command": 178 | return (/^[a-zA-Z0-9-_:]$/).test(c); 179 | break; 180 | default: 181 | return false; 182 | } 183 | } 184 | }, 185 | 186 | _onKeyPress: function(event) { 187 | if (this.element.editingColumn) { return } 188 | if (("Enter" == event.key) || ("VK_ENTER" == event.keycode) || ("VK_RETURN" == event.keycode)) { 189 | event.preventDefault(); 190 | event.stopPropagation(); 191 | this.window.document.getElementById("KeybinderCustomXUL_Edit").click(); 192 | } 193 | else if (("Delete" == event.key) || ("VK_DELETE" == event.keycode)) { 194 | event.preventDefault(); 195 | event.stopPropagation(); 196 | this.window.document.getElementById("KeybinderCustomXUL_Delete").click(); 197 | } 198 | }, 199 | 200 | _onKeyDown: function(event) { 201 | if (this.element.editingColumn) { 202 | switch (this.element.editingColumn.id) { 203 | 204 | case "KeybinderCustomXUL_NameCol": 205 | if (true != this._validate(event.key,"key",false)) { event.preventDefault(); event.stopPropagation(); return; } 206 | break; 207 | 208 | case "KeybinderCustomXUL_LabelCol": 209 | if (true != this._validate(event.key,"label",false)) { event.preventDefault(); event.stopPropagation(); return; } 210 | break; 211 | 212 | case "KeybinderCustomXUL_ActionCol": 213 | if (true != this._validate(event.key,"command",false)) { event.preventDefault(); event.stopPropagation(); return; } 214 | break; 215 | 216 | default: 217 | return; 218 | } 219 | } 220 | if (("Enter" == event.key) || ("VK_ENTER" == event.keycode) || ("VK_RETURN" == event.keycode)) { 221 | event.preventDefault(); 222 | event.stopPropagation(); 223 | if (this.element.editingColumn) { 224 | let row = this.element.editingRow; 225 | let column = this.element.editingColumn; 226 | let id = column.id; 227 | 228 | let rowKey = this.element.view.getCellValue(row,this.element.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); 229 | 230 | let mapItem = CustomXUL.allCustomKeys().get(rowKey); 231 | 232 | let mapItemKey = mapItem.key; 233 | let mapItemLabel = mapItem.label; 234 | let mapItemCommand = mapItem.command; 235 | 236 | switch (this.element.editingColumn.id) { 237 | 238 | case "KeybinderCustomXUL_NameCol": 239 | let filter = this.window.document.getElementById("KeybinderCustomXUL_Filter"); 240 | mapItemKey = this.element.inputField.value; 241 | if (CustomXUL.find(mapItemKey)) { 242 | if (mapItemKey != rowKey) { 243 | prompterService.alert(this.window, _("conflictWarning.title"), _("customXulConflict.text")); // Break in case name entered already exists, but only display a warning if it would have resulted in a duplicate. 244 | this.element.startEditing(row,column); 245 | return 246 | } 247 | break; 248 | } 249 | CustomXUL.allCustomKeys().delete(rowKey); 250 | CustomXUL.allCustomKeys().set(mapItemKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 251 | 252 | if (!!filter.value) { 253 | filter.value = mapItemKey; 254 | this.element.view = new customXULTreeView(mapItemKey,true,this.element,true); // Let's make sure to avoid possible errors if rows were filtered. 255 | } 256 | else { this.element.view = new customXULTreeView(mapItemKey,true,this.element,false) } 257 | break; 258 | 259 | case "KeybinderCustomXUL_LabelCol": 260 | mapItemLabel = this.element.inputField.value; 261 | CustomXUL.allCustomKeys().set(rowKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 262 | break; 263 | 264 | case "KeybinderCustomXUL_ActionCol": 265 | mapItemCommand = this.element.inputField.value; 266 | let mainWindow = windowMediator.getMostRecentWindow("navigator:browser"); 267 | if (!mainWindow.document.querySelector(`command[id="${mapItemCommand}"]`)) { 268 | prompterService.alert(this.window, _("conflictWarning.title"),_("customXulBadCommand.text")); 269 | this.element.startEditing(row,column); 270 | return 271 | } 272 | 273 | CustomXUL.allCustomKeys().set(rowKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 274 | break; 275 | 276 | default: 277 | return; 278 | } 279 | 280 | this.element.stopEditing(true); 281 | 282 | this.window.setTimeout(function (tree) { tree.startEditing(tree.currentIndex,column.getNext()) },0, this.element); 283 | if (!this.element.editingColumn) { 284 | this.element.focus(); //Restore focus so we can navigate the list with the keyboard arrows. 285 | this._onSelect(event); 286 | } 287 | } 288 | 289 | CustomXUL.storeKeys(); // Array updating code here. 290 | 291 | } 292 | else if (("VK_ESCAPE" == event.keycode) || ("Escape" == event.key)) { 293 | event.stopPropagation(); 294 | event.preventDefault(); 295 | this._onBlur(event,this.element.editingColumn,this.element.inputField,true); 296 | } 297 | }, 298 | _onPaste: function (event) { 299 | let data = event.clipboardData; 300 | let asText = data.getData("text"); 301 | 302 | switch (this.element.editingColumn.id) { 303 | 304 | case "KeybinderCustomXUL_NameCol": 305 | if (true != this._validate(asText,"key",true)) { event.preventDefault(); event.stopPropagation(); return; } 306 | break; 307 | 308 | case "KeybinderCustomXUL_LabelCol": 309 | if (true != this._validate(asText,"label",true)) { event.preventDefault(); event.stopPropagation(); return; } 310 | break; 311 | 312 | case "KeybinderCustomXUL_ActionCol": 313 | if (true != this._validate(asText,"command",true)) { event.preventDefault(); event.stopPropagation(); return; } 314 | break; 315 | 316 | default: 317 | return; 318 | } 319 | 320 | }, 321 | _onBlur: function (event, target, inputField = this.element.inputField, pressedEsc) { 322 | let field = inputField; 323 | switch (target.id) { 324 | 325 | case "KeybinderCustomXUL_NameCol": 326 | if (true != this._validate(field.value,"key",true)) { event.preventDefault(); event.stopPropagation(); return; } 327 | break; 328 | 329 | case "KeybinderCustomXUL_LabelCol": 330 | if (true != this._validate(field.value,"label",true)) { event.preventDefault(); event.stopPropagation(); return; } 331 | break; 332 | 333 | case "KeybinderCustomXUL_ActionCol": 334 | if (true != this._validate(field.value,"command",true)) { event.preventDefault(); event.stopPropagation(); return; } 335 | break; 336 | 337 | default: 338 | return; 339 | } 340 | 341 | if (!!pressedEsc) { 342 | this.element.stopEditing(false); 343 | this.element.focus(); 344 | this._onSelect(event); 345 | return 346 | } 347 | }, 348 | 349 | _onChange: function (event, target) { 350 | if (!target) { return } 351 | let field = this.element.inputField; 352 | switch (target.id) { 353 | 354 | case "KeybinderCustomXUL_NameCol": 355 | if (true != this._validate(field.value,"key",true)) { event.preventDefault(); event.stopPropagation(); return; } 356 | break; 357 | 358 | case "KeybinderCustomXUL_LabelCol": 359 | if (true != this._validate(field.value,"label",true)) { event.preventDefault(); event.stopPropagation(); return; } 360 | break; 361 | 362 | case "KeybinderCustomXUL_ActionCol": 363 | if (true != this._validate(field.value,"command",true)) { event.preventDefault(); event.stopPropagation(); return; } 364 | break; 365 | 366 | default: 367 | return; 368 | } 369 | let row = this.element.currentIndex; 370 | 371 | let rowKey = this.element.view.getCellValue(row,this.element.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); 372 | 373 | let mapItem = CustomXUL.allCustomKeys().get(rowKey); 374 | 375 | let mapItemKey = mapItem.key; 376 | let mapItemLabel = mapItem.label; 377 | let mapItemCommand = mapItem.command; 378 | 379 | switch (target.id) { 380 | 381 | case "KeybinderCustomXUL_NameCol": 382 | let filter = this.window.document.getElementById("KeybinderCustomXUL_Filter"); 383 | mapItemKey = this.element.inputField.value; 384 | if (CustomXUL.find(mapItemKey)) { 385 | if (mapItemKey != rowKey) { 386 | prompterService.alert(this.window, _("conflictWarning.title"), _("customXulConflict.text")); // Break in case name entered already exists, but only display a warning if it would have resulted in a duplicate. 387 | this.element.startEditing(row,target); 388 | return 389 | } 390 | break; 391 | } 392 | CustomXUL.allCustomKeys().delete(rowKey); 393 | CustomXUL.allCustomKeys().set(mapItemKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 394 | 395 | if (!!filter.value) { 396 | filter.value = mapItemKey; 397 | this.element.view = new customXULTreeView(mapItemKey,true,this.element,true); // Let's make sure to avoid possible errors if rows were filtered. 398 | } 399 | else { this.element.view = new customXULTreeView(mapItemKey,true,this.element,false) } 400 | break; 401 | 402 | case "KeybinderCustomXUL_LabelCol": 403 | mapItemLabel = this.element.inputField.value; 404 | CustomXUL.allCustomKeys().set(rowKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 405 | break; 406 | 407 | case "KeybinderCustomXUL_ActionCol": 408 | mapItemCommand = this.element.inputField.value; 409 | let mainWindow = windowMediator.getMostRecentWindow("navigator:browser"); 410 | if (!mainWindow.document.querySelector(`command[id="${mapItemCommand}"]`)) { 411 | prompterService.alert(this.window, _("conflictWarning.title"),_("customXulBadCommand.text")); 412 | this.element.startEditing(row,target); 413 | return 414 | } 415 | CustomXUL.allCustomKeys().set(rowKey, new CustomXUL.CustomXUL({key:mapItemKey, label:mapItemLabel, command:mapItemCommand})); 416 | break; 417 | 418 | default: 419 | return; 420 | } 421 | if (!this.element.editingColumn) { 422 | this.element.focus(); //Restore focus so we can navigate the list with the keyboard arrows. 423 | this._onSelect(event); 424 | } 425 | CustomXUL.storeKeys(); // Array updating code here. 426 | }, 427 | 428 | toElement: function() { 429 | return this.element; 430 | } 431 | }; --------------------------------------------------------------------------------