/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** * Helper to listen for keyboard events described in .properties file. * * let shortcuts = new KeyShortcuts({ * window * }); * shortcuts.on("Ctrl+F", event => { * // `event` is the KeyboardEvent which relates to the key shortcuts * }); * * @param DOMWindow window * The window object of the document to listen events from. * @param DOMElement target * Optional DOM Element on which we should listen events from. * If omitted, we listen for all events fired on `window`.
*/ function KeyShortcuts({ window, target }) { this.window = window; this.target = target || window; this.keys = new Map(); this.eventEmitter = new EventEmitter(); this.target.addEventListener("keydown", this);
}
/* * Parse an electron-like key string and return a normalized object which * allow efficient match on DOM key event. The normalized object matches DOM * API. * * @param String str * The shortcut string to parse, following this document: * https://github.com/electron/electron/blob/master/docs/api/accelerator.md
*/
KeyShortcuts.parseElectronKey = function (str) { // If a localized string is found but has no value in the properties file, // getStr will return `null`. See Bug 1569572. if (typeof str !== "string") {
console.error("Invalid key passed to parseElectronKey, stacktrace below");
console.trace();
returnnull;
}
const modifiers = str.split("+");
let key = modifiers.pop();
const shortcut = {
ctrl: false,
meta: false,
alt: false,
shift: false, // Set for character keys
key: undefined, // Set for non-character keys
keyCode: undefined,
}; for (const mod of modifiers) { if (mod === "Alt") {
shortcut.alt = true;
} elseif (["Command", "Cmd"].includes(mod)) {
shortcut.meta = true;
} elseif (["CommandOrControl", "CmdOrCtrl"].includes(mod)) { if (isOSX) {
shortcut.meta = true;
} else {
shortcut.ctrl = true;
}
} elseif (["Control", "Ctrl"].includes(mod)) {
shortcut.ctrl = true;
} elseif (mod === "Shift") {
shortcut.shift = true;
} else {
console.error("Unsupported modifier:", mod, "from key:", str); returnnull;
}
}
// Plus is a special case. It's a character key and shouldn't be matched // against a keycode as it is only accessible via Shift/Capslock if (key === "Plus") {
key = "+";
}
if (typeof key === "string" && key.length === 1) { if (shortcut.alt) { // When Alt is involved, some platforms (macOS) give different printable characters // for the `key` value, like `®` for the key `R`. In this case, prefer matching by // `keyCode` instead.
shortcut.keyCode = KeyCodes[`DOM_VK_${key.toUpperCase()}`];
shortcut.keyCodeString = key;
} else { // Match any single character
shortcut.key = key.toLowerCase();
}
} elseif (key in ElectronKeysMapping) { // Maps the others manually to DOM API DOM_VK_*
key = ElectronKeysMapping[key];
shortcut.keyCode = KeyCodes[key]; // Used only to stringify the shortcut
shortcut.keyCodeString = key;
shortcut.key = key;
} else {
console.error("Unsupported key:", key); returnnull;
}
return shortcut;
};
KeyShortcuts.stringify = function (shortcut) { if (shortcut === null) { // parseElectronKey might return null in several situations. return"";
}
const list = []; if (shortcut.alt) {
list.push("Alt");
} if (shortcut.ctrl) {
list.push("Ctrl");
} if (shortcut.meta) {
list.push("Cmd");
} if (shortcut.shift) {
list.push("Shift");
}
let key; if (shortcut.key) {
key = shortcut.key.toUpperCase();
} else {
key = shortcut.keyCodeString;
}
list.push(key); return list.join("+");
};
doesEventMatchShortcut(event, shortcut) { if (shortcut.meta != event.metaKey) { returnfalse;
} if (shortcut.ctrl != event.ctrlKey) { returnfalse;
} if (shortcut.alt != event.altKey) { returnfalse;
} if (shortcut.shift != event.shiftKey) { // Check the `keyCode` to see whether it's a character (see also Bug 1493646) constchar = String.fromCharCode(event.keyCode);
let isAlphabetical = char.length == 1 && char.match(/[a-zA-Z]/);
// Shift is a special modifier, it may implicitly be required if the expected key // is a special character accessible via shift. if (!isAlphabetical) {
isAlphabetical = event.key && event.key.match(/[a-zA-Z]/);
}
if (shortcut.keyCode) { return event.keyCode == shortcut.keyCode;
} elseif (event.key in ElectronKeysMapping) { return ElectronKeysMapping[event.key] === shortcut.key;
}
// get the key from the keyCode if key is not provided. const key = event.key || String.fromCharCode(event.keyCode);
// For character keys, we match if the final character is the expected one. // But for digits we also accept indirect match to please azerty keyboard, // which requires Shift to be pressed to get digits. return (
key.toLowerCase() == shortcut.key ||
(shortcut.key.match(/[0-9]/) &&
event.keyCode == shortcut.key.charCodeAt(0))
);
},
handleEvent(event) { for (const [key, shortcut] of this.keys) { if (this.doesEventMatchShortcut(event, shortcut)) { this.eventEmitter.emit(key, event);
}
}
},
on(key, listener) { if (typeof listener !== "function") { thrownew Error( "KeyShortcuts.on() expects a function as " + "second argument"
);
} if (!this.keys.has(key)) { const shortcut = KeyShortcuts.parseElectronKey(key); // The key string is wrong and we were unable to compute the key shortcut if (!shortcut) { return;
} this.keys.set(key, shortcut);
} this.eventEmitter.on(key, listener);
},
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.