├── 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 |
--------------------------------------------------------------------------------
/chrome/content/customXulKeys.xul:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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 | };
--------------------------------------------------------------------------------