UNPKG

@launchmenu/core

Version:

An environment for visual keyboard controlled applets

366 lines 30.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GlobalKeyHandler = void 0; const electron_1 = require("electron"); const electronAcceleratorKeyMapping_1 = require("./electronAcceleratorKeyMapping"); const primaryGlobalShortcutKeys_1 = require("./primaryGlobalShortcutKeys"); const node_global_key_listener_1 = require("node-global-key-listener"); const nodeGlobalKeyListenerMapping_1 = require("./nodeGlobalKeyListenerMapping"); const keys_1 = require("../keyIdentifiers/keys"); const model_react_1 = require("model-react"); const isPlatform_1 = require("../../utils/platform/isPlatform"); /** A class that can be used for registering keyboard shortcuts. Should be used as a singleton obtained from LaunchMenu */ class GlobalKeyHandler { /** * Creates a new instance of the global key handler * @param useElectronListener A data retriever to determine whether to force use electron's listener */ constructor(useElectronListener = () => true) { this.keyListeners = []; this.electronListeners = {}; // Track whether the handler is disposed in order to not register new handlers when it is this.silentError = true; // TODO: May want to change this in the future and/or make it configurable (but it currently happens on every reload, so silent error is preferable) this.disposed = false; this.currentUseElectronListener = false; this.shortcutListeners = []; try { this.advancedManager = new node_global_key_listener_1.GlobalKeyboardListener(); } catch (e) { console.error(e); } this.useElectronListener = useElectronListener; this.useElectronListenerObserver = this.setupShortcutMethodObserver(); } /** * Adds a global key listeners that listens to all events * @param callback The key press callback * @returns A function that can be invoked to remove the listener */ addListener(callback) { var _a; if (this.disposed) { if (this.silentError) return () => { }; throw new Error("Handler already disposed"); } if (!this.advancedManager) throw new Error("Global key listeners are not supported on this platform"); this.keyListeners.push(callback); // If this is the first listener, add it to key hook if (this.keyListeners.length == 1) { this.invokeListeners = (event, held) => { if (this.currentUseElectronListener) return; const ev = this.convertKeyEvent(event, held); if (!ev) return; let stopPropagation = false; let stopImmediatePropagation = false; for (let listener of this.keyListeners) { const res = listener(ev); if (typeof res == "object") { if (res.stopPropagation) stopPropagation = true; if (res.stopImmediatePropagation) { stopImmediatePropagation = true; break; } } else if (res) stopPropagation = true; } return { stopImmediatePropagation, stopPropagation, }; }; (_a = this.advancedManager) === null || _a === void 0 ? void 0 : _a.addListener(this.invokeListeners); } // Return a function to remove the listener return () => { var _a; const index = this.keyListeners.indexOf(callback); if (index != -1) this.keyListeners.splice(index, 1); // Remove the key hook listener if no listeners remain if (this.keyListeners.length == 0 && this.invokeListeners) { (_a = this.advancedManager) === null || _a === void 0 ? void 0 : _a.removeListener(this.invokeListeners); } }; } /** * Converts a global key event to the format as used by LM * @param event The event to convert * @param held The keys that are currently held * @returns The LM event */ convertKeyEvent(event, held) { if (!event.name) return undefined; const key = nodeGlobalKeyListenerMapping_1.nodeGlobalKeyListenerMapping[event.name]; if (!key) return undefined; return { key, rawcode: event.name, type: event.state == "UP" ? "keyup" : "keydown", altKey: held["LEFT ALT"] ? "left" : held["RIGHT ALT"] ? "right" : undefined, ctrlKey: held["LEFT CTRL"] ? "left" : held["RIGHT CTRL"] ? "right" : undefined, metaKey: held["LEFT META"] ? "left" : held["RIGHT META"] ? "right" : undefined, shiftKey: held["LEFT SHIFT"] ? "left" : held["RIGHT SHIFT"] ? "right" : undefined, }; } /** * Checks whether global key listeners are supported on the current OS/environment * @param hook The hook to subscribe to changes * @returns Whether listeners are supported */ areListenersSupported(hook) { return (!this.useElectronListener(hook) && !!this.advancedManager && (!isPlatform_1.isPlatform("mac") || electron_1.remote.systemPreferences.isTrustedAccessibilityClient(false))); } /** * Sets up an observer that takes care of moving the shortcut listeners if the setting changed */ setupShortcutMethodObserver() { return new model_react_1.Observer(h => !this.areListenersSupported(h)).listen(useElectron => { if (this.currentUseElectronListener != useElectron) { this.currentUseElectronListener = useElectron; // Dispose all the old listeners const allListeners = this.shortcutListeners; this.shortcutListeners = []; allListeners.forEach(({ dispose }) => dispose()); // Unregister all listeners allListeners.forEach(bundle => { const newDispose = this.addShortcut(bundle.shortcut, bundle.callback); // Make sure that the original dispose method can still be used (since this was returned from the original addShortcut callback) bundle.dispose = newDispose; }); // Add or remove the global listener if (this.advancedManager && this.invokeListeners) { if (useElectron) { this.advancedManager.removeListener(this.invokeListeners); this.advancedManager.kill(); } else { this.advancedManager.addListener(this.invokeListeners); } } } }, true); } /** * Adds a global shortcut * @param shortcut The keypattern to listen for * @param callback The callback to trigger when the event is fired * @returns A function that can be invoked to remove the shortcut */ addShortcut(shortcut, callback) { if (this.disposed) { if (this.silentError) return () => { }; throw new Error("Handler already disposed"); } const invalid = this.isShortcutInvalid(shortcut); if (invalid) throw invalid[0].error; // Use one of the two shortcut methods let dispose; if (!this.areListenersSupported()) dispose = this.addElectronShortcut(shortcut, callback); else dispose = this.addCustomShortcut(shortcut, callback); // Setup a callback to dispose all data associated to a shortcut const fullDispose = () => { const index = this.shortcutListeners.indexOf(bundle); if (index != -1) this.shortcutListeners.splice(index, 1); dispose(); }; const bundle = { shortcut, callback, dispose: fullDispose }; this.shortcutListeners.push(bundle); // Note that bundle's fullDispose method can be changed throughout its lifetime, the bundle object mutates (to support dynamic `useElectronListener`) return () => bundle.dispose(); } /** * Checks whether the given keypattern is valid as a global shortcut or not * @param shortcut The key pattern to check * @returns False if the pattern is valid, or the patterns and errors if invalid */ isShortcutInvalid(shortcut) { const invalid = this.getElectronAccelerators(shortcut) .map((res, index) => ({ pattern: shortcut.patterns[index], error: res })) .filter((res) => typeof res.error != "string"); return invalid.length > 0 ? invalid : false; } /** * Adds a global shortcut using the node-global-key-listener package * @param shortcut The key pattern to listen for * @param callback The callback to trigger when the event is fired * @returns A function that can be invoked to remove the shortcut */ addCustomShortcut(shortcut, callback) { // Create a format that's faster to compare with const keys = {}; shortcut.patterns.forEach(({ pattern }, i) => { const primaryKey = pattern.find(key => ![ "altLeft", "altRight", "controlLeft", "controlRight", "shiftLeft", "shiftRight", "metaLeft", "metaRight", "alt", "ctrl", "shift", "meta", ].includes(key)); function getState(pattern, left, right, either) { const includesLeft = pattern.includes(left); const includesRight = pattern.includes(right); const includesEither = pattern.includes(either); return [ ...(includesLeft || includesEither ? ["left"] : []), ...(includesRight || includesEither ? ["right"] : []), ...(!includesLeft && !includesRight && !includesEither ? [undefined] : []), ]; } if (primaryKey) { Object.entries(keys_1.keyIdMapping) .filter(([id, name]) => id == primaryKey || name == primaryKey) .forEach(([id]) => { var _a; if (!keys[id]) keys[id] = []; (_a = keys[id]) === null || _a === void 0 ? void 0 : _a.push({ altKey: getState(pattern, "altLeft", "altRight", "alt"), ctrlKey: getState(pattern, "controlLeft", "controlRight", "ctrl"), shiftKey: getState(pattern, "shiftLeft", "shiftRight", "shift"), metaKey: getState(pattern, "metaLeft", "metaRight", "meta"), }); }); } }); // Create the listener const listener = event => { if (event.type == "keyup") return; const modifiers = keys[event.key]; if (modifiers != undefined) { const matches = modifiers.some(modifiers => modifiers.altKey.includes(event.altKey) && modifiers.ctrlKey.includes(event.ctrlKey) && modifiers.shiftKey.includes(event.shiftKey) && modifiers.metaKey.includes(event.metaKey)); if (matches) { setTimeout(callback); // Prevents reaching timeout (apart from when node is already busy while triggering the shortcut :/) return true; } } }; return this.addListener(listener); } /** * Adds a global shortcut using electron's shortcut system * @param shortcut The key pattern to listen for * @param callback The callback to trigger when the event is fired * @returns A function that can be invoked to remove the shortcut */ addElectronShortcut(shortcut, callback) { const accelerators = this.getElectronAccelerators(shortcut).filter((n) => typeof n == "string"); // Register each accelerator accelerators.forEach(accelerator => { if (!this.electronListeners[accelerator]) { const listeners = []; this.electronListeners[accelerator] = listeners; const invoker = () => listeners.forEach(listener => listener()); electron_1.remote.globalShortcut.register(accelerator, invoker); } this.electronListeners[accelerator].push(callback); }); // Return a function to remove the listeners return () => { accelerators.forEach(accelerator => { const listeners = this.electronListeners[accelerator]; if (!listeners) return; const index = listeners.indexOf(callback); if (index != -1) { listeners.splice(index, 1); if (listeners.length == 0) { electron_1.remote.globalShortcut.unregister(accelerator); delete this.electronListeners[accelerator]; } } }); }; } /** * Retrieves the electron accelerator string if valid, or an error object otherwise * @param shortcut The key pattern shortcut * @returns The accelerator string or error object */ getElectronAccelerators(shortcut) { return shortcut.patterns.map(({ allowExtra, pattern, type }) => { var _a; // Check the extra pattern event data if ((_a = allowExtra === null || allowExtra === void 0 ? void 0 : allowExtra.length) !== null && _a !== void 0 ? _a : 0 > 0) return new Error("Global shortcuts can't have extra keys"); if (type != "down") return new Error("Global shortcuts can only listen for key up events"); // Check the pattern itself const { error } = pattern.reduce(({ error, charCount }, key) => { if (error) return { error, charCount }; if (primaryGlobalShortcutKeys_1.primaryGlobalShortcutKeys.includes(key)) { charCount += 1; if (charCount > 1) error = new Error("Global shortcuts can only contain 1 primary key"); } return { error, charCount }; }, { error: null, charCount: 0 }); if (error) return error; // Obtain the shortcut return pattern .map(key => { var _a; return (_a = electronAcceleratorKeyMapping_1.electronAcceleratorKeyMapping[key]) !== null && _a !== void 0 ? _a : key; }) .join("+"); }); } /** * Disposes all listeners */ destroy() { this.disposed = true; this.useElectronListenerObserver.destroy(); if (this.advancedManager) { if (this.invokeListeners) this.advancedManager.removeListener(this.invokeListeners); this.keyListeners = []; this.advancedManager.kill(); } for (let shortcut in this.electronListeners) electron_1.remote.globalShortcut.unregister(shortcut); this.electronListeners = {}; } } exports.GlobalKeyHandler = GlobalKeyHandler; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2xvYmFsS2V5SGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9rZXlIYW5kbGVyL2dsb2JhbEtleUhhbmRsZXIvR2xvYmFsS2V5SGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx1Q0FBZ0M7QUFHaEMsbUZBQThFO0FBQzlFLDJFQUFzRTtBQUN0RSx1RUFLa0M7QUFFbEMsaUZBQTRFO0FBRzVFLGlEQUFpRTtBQUVqRSw2Q0FBZ0U7QUFDaEUsZ0VBQTJEO0FBRTNELDBIQUEwSDtBQUMxSCxNQUFhLGdCQUFnQjtJQXFCekI7OztPQUdHO0lBQ0gsWUFBbUIsc0JBQStDLEdBQUcsRUFBRSxDQUFDLElBQUk7UUFyQmxFLGlCQUFZLEdBQXlCLEVBQUUsQ0FBQztRQUN4QyxzQkFBaUIsR0FBbUMsRUFBRSxDQUFDO1FBRWpFLHlGQUF5RjtRQUMvRSxnQkFBVyxHQUFHLElBQUksQ0FBQyxDQUFDLG9KQUFvSjtRQUN4SyxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBS2pCLCtCQUEwQixHQUFZLEtBQUssQ0FBQztRQUM1QyxzQkFBaUIsR0FJckIsRUFBRSxDQUFDO1FBT0wsSUFBSTtZQUNBLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxpREFBc0IsRUFBRSxDQUFDO1NBQ3ZEO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDUixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ3BCO1FBQ0QsSUFBSSxDQUFDLG1CQUFtQixHQUFHLG1CQUFtQixDQUFDO1FBQy9DLElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLENBQUMsMkJBQTJCLEVBQUUsQ0FBQztJQUMxRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFdBQVcsQ0FBQyxRQUE0Qjs7UUFDM0MsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2YsSUFBSSxJQUFJLENBQUMsV0FBVztnQkFBRSxPQUFPLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQztZQUN0QyxNQUFNLElBQUksS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUM7U0FDL0M7UUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWU7WUFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyx5REFBeUQsQ0FBQyxDQUFDO1FBRS9FLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBRWpDLG9EQUFvRDtRQUNwRCxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxJQUFJLENBQUMsRUFBRTtZQUMvQixJQUFJLENBQUMsZUFBZSxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFO2dCQUNuQyxJQUFJLElBQUksQ0FBQywwQkFBMEI7b0JBQUUsT0FBTztnQkFFNUMsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7Z0JBQzdDLElBQUksQ0FBQyxFQUFFO29CQUFFLE9BQU87Z0JBRWhCLElBQUksZUFBZSxHQUFHLEtBQUssQ0FBQztnQkFDNUIsSUFBSSx3QkFBd0IsR0FBRyxLQUFLLENBQUM7Z0JBQ3JDLEtBQUssSUFBSSxRQUFRLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtvQkFDcEMsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDO29CQUN6QixJQUFJLE9BQU8sR0FBRyxJQUFJLFFBQVEsRUFBRTt3QkFDeEIsSUFBSSxHQUFHLENBQUMsZUFBZTs0QkFBRSxlQUFlLEdBQUcsSUFBSSxDQUFDO3dCQUNoRCxJQUFJLEdBQUcsQ0FBQyx3QkFBd0IsRUFBRTs0QkFDOUIsd0JBQXdCLEdBQUcsSUFBSSxDQUFDOzRCQUNoQyxNQUFNO3lCQUNUO3FCQUNKO3lCQUFNLElBQUksR0FBRzt3QkFBRSxlQUFlLEdBQUcsSUFBSSxDQUFDO2lCQUMxQztnQkFFRCxPQUFPO29CQUNILHdCQUF3QjtvQkFDeEIsZUFBZTtpQkFDbEIsQ0FBQztZQUNOLENBQUMsQ0FBQztZQUNGLE1BQUEsSUFBSSxDQUFDLGVBQWUsMENBQUUsV0FBVyxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUU7U0FDM0Q7UUFFRCwyQ0FBMkM7UUFDM0MsT0FBTyxHQUFHLEVBQUU7O1lBQ1IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDbEQsSUFBSSxLQUFLLElBQUksQ0FBQyxDQUFDO2dCQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQztZQUVwRCxzREFBc0Q7WUFDdEQsSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRTtnQkFDdkQsTUFBQSxJQUFJLENBQUMsZUFBZSwwQ0FBRSxjQUFjLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRTthQUM5RDtRQUNMLENBQUMsQ0FBQztJQUNOLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNPLGVBQWUsQ0FDckIsS0FBVyxFQUNYLElBRUM7UUFFRCxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUk7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUNsQyxNQUFNLEdBQUcsR0FBRywyREFBNEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDckQsSUFBSSxDQUFDLEdBQUc7WUFBRSxPQUFPLFNBQVMsQ0FBQztRQUUzQixPQUFPO1lBQ0gsR0FBRztZQUNILE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSTtZQUNuQixJQUFJLEVBQUUsS0FBSyxDQUFDLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUztZQUMvQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxTQUFTO1lBQzNFLE9BQU8sRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDO2dCQUN0QixDQUFDLENBQUMsTUFBTTtnQkFDUixDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQztvQkFDcEIsQ0FBQyxDQUFDLE9BQU87b0JBQ1QsQ0FBQyxDQUFDLFNBQVM7WUFDZixPQUFPLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQztnQkFDdEIsQ0FBQyxDQUFDLE1BQU07Z0JBQ1IsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUM7b0JBQ3BCLENBQUMsQ0FBQyxPQUFPO29CQUNULENBQUMsQ0FBQyxTQUFTO1lBQ2YsUUFBUSxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUM7Z0JBQ3hCLENBQUMsQ0FBQyxNQUFNO2dCQUNSLENBQUMsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDO29CQUNyQixDQUFDLENBQUMsT0FBTztvQkFDVCxDQUFDLENBQUMsU0FBUztTQUNsQixDQUFDO0lBQ04sQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxxQkFBcUIsQ0FBQyxJQUFnQjtRQUN6QyxPQUFPLENBQ0gsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDO1lBQy9CLENBQUMsQ0FBQyxJQUFJLENBQUMsZUFBZTtZQUN0QixDQUFDLENBQUMsdUJBQVUsQ0FBQyxLQUFLLENBQUM7Z0JBQ2YsaUJBQU0sQ0FBQyxpQkFBaUIsQ0FBQyw0QkFBNEIsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUNwRSxDQUFDO0lBQ04sQ0FBQztJQUVEOztPQUVHO0lBQ08sMkJBQTJCO1FBQ2pDLE9BQU8sSUFBSSxzQkFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQUU7WUFDMUUsSUFBSSxJQUFJLENBQUMsMEJBQTBCLElBQUksV0FBVyxFQUFFO2dCQUNoRCxJQUFJLENBQUMsMEJBQTBCLEdBQUcsV0FBVyxDQUFDO2dCQUU5QyxnQ0FBZ0M7Z0JBQ2hDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQztnQkFDNUMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLEVBQUUsQ0FBQztnQkFDNUIsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUMsT0FBTyxFQUFDLEVBQUUsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7Z0JBRS9DLDJCQUEyQjtnQkFDM0IsWUFBWSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtvQkFDMUIsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztvQkFDdEUsZ0lBQWdJO29CQUNoSSxNQUFNLENBQUMsT0FBTyxHQUFHLFVBQVUsQ0FBQztnQkFDaEMsQ0FBQyxDQUFDLENBQUM7Z0JBRUgsb0NBQW9DO2dCQUNwQyxJQUFJLElBQUksQ0FBQyxlQUFlLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRTtvQkFDOUMsSUFBSSxXQUFXLEVBQUU7d0JBQ2IsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDO3dCQUMxRCxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksRUFBRSxDQUFDO3FCQUMvQjt5QkFBTTt3QkFDSCxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7cUJBQzFEO2lCQUNKO2FBQ0o7UUFDTCxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDYixDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxXQUFXLENBQUMsUUFBb0IsRUFBRSxRQUFvQjtRQUN6RCxJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUU7WUFDZixJQUFJLElBQUksQ0FBQyxXQUFXO2dCQUFFLE9BQU8sR0FBRyxFQUFFLEdBQUUsQ0FBQyxDQUFDO1lBQ3RDLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLENBQUMsQ0FBQztTQUMvQztRQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNqRCxJQUFJLE9BQU87WUFBRSxNQUFNLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7UUFFcEMsc0NBQXNDO1FBQ3RDLElBQUksT0FBbUIsQ0FBQztRQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFO1lBQzdCLE9BQU8sR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxDQUFDOztZQUN0RCxPQUFPLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUUxRCxnRUFBZ0U7UUFDaEUsTUFBTSxXQUFXLEdBQUcsR0FBRyxFQUFFO1lBQ3JCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDckQsSUFBSSxLQUFLLElBQUksQ0FBQyxDQUFDO2dCQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3pELE9BQU8sRUFBRSxDQUFDO1FBQ2QsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxNQUFNLEdBQUcsRUFBQyxRQUFRLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUMsQ0FBQztRQUMxRCxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRXBDLHFKQUFxSjtRQUNySixPQUFPLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLGlCQUFpQixDQUNwQixRQUFvQjtRQUdwQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsdUJBQXVCLENBQUMsUUFBUSxDQUFDO2FBQ2pELEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxLQUFLLEVBQUUsR0FBRyxFQUFDLENBQUMsQ0FBQzthQUN0RSxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQWlCLEVBQUUsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxLQUFLLElBQUksUUFBUSxDQUFDLENBQUM7UUFDbEUsT0FBTyxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDaEQsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ08saUJBQWlCLENBQUMsUUFBb0IsRUFBRSxRQUFvQjtRQVNsRSxnREFBZ0Q7UUFDaEQsTUFBTSxJQUFJLEdBQW1DLEVBQUUsQ0FBQztRQUVoRCxRQUFRLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUMsT0FBTyxFQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDdkMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FDM0IsR0FBRyxDQUFDLEVBQUUsQ0FDRixDQUNJO2dCQUNJLFNBQVM7Z0JBQ1QsVUFBVTtnQkFDVixhQUFhO2dCQUNiLGNBQWM7Z0JBQ2QsV0FBVztnQkFDWCxZQUFZO2dCQUNaLFVBQVU7Z0JBQ1YsV0FBVztnQkFDWCxLQUFLO2dCQUNMLE1BQU07Z0JBQ04sT0FBTztnQkFDUCxNQUFNO2FBRWIsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQ3RCLENBQUM7WUFFRixTQUFTLFFBQVEsQ0FDYixPQUFzQixFQUN0QixJQUFZLEVBQ1osS0FBYSxFQUNiLE1BQWdCO2dCQUVoQixNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUM1QyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUM5QyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUNoRCxPQUFPO29CQUNILEdBQUcsQ0FBQyxZQUFZLElBQUksY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7b0JBQzVELEdBQUcsQ0FBQyxhQUFhLElBQUksY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO29CQUM5RCxHQUFHLENBQUMsQ0FBQyxZQUFZLElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxjQUFjO3dCQUNsRCxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7d0JBQ2IsQ0FBQyxDQUFDLEVBQUUsQ0FBQztpQkFDWixDQUFDO1lBQ04sQ0FBQztZQUVELElBQUksVUFBVSxFQUFFO2dCQUNaLE1BQU0sQ0FBQyxPQUFPLENBQUMsbUJBQVksQ0FBQztxQkFDdkIsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLEVBQUUsSUFBSSxVQUFVLElBQUksSUFBSSxJQUFJLFVBQVUsQ0FBQztxQkFDOUQsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFOztvQkFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQVksQ0FBQzt3QkFBRSxJQUFJLENBQUMsRUFBWSxDQUFDLEdBQUcsRUFBRSxDQUFDO29CQUNqRCxNQUFBLElBQUksQ0FBQyxFQUFZLENBQUMsMENBQUUsSUFBSSxDQUFDO3dCQUNyQixNQUFNLEVBQUUsUUFBUSxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQzt3QkFDdkQsT0FBTyxFQUFFLFFBQVEsQ0FDYixPQUFPLEVBQ1AsYUFBYSxFQUNiLGNBQWMsRUFDZCxNQUFNLENBQ1Q7d0JBQ0QsUUFBUSxFQUFFLFFBQVEsQ0FDZCxPQUFPLEVBQ1AsV0FBVyxFQUNYLFlBQVksRUFDWixPQUFPLENBQ1Y7d0JBQ0QsT0FBTyxFQUFFLFFBQVEsQ0FBQyxPQUFPLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxNQUFNLENBQUM7cUJBQzlELEVBQUU7Z0JBQ1AsQ0FBQyxDQUFDLENBQUM7YUFDVjtRQUNMLENBQUMsQ0FBQyxDQUFDO1FBRUgsc0JBQXNCO1FBQ3RCLE1BQU0sUUFBUSxHQUF1QixLQUFLLENBQUMsRUFBRTtZQUN6QyxJQUFJLEtBQUssQ0FBQyxJQUFJLElBQUksT0FBTztnQkFBRSxPQUFPO1lBRWxDLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDbEMsSUFBSSxTQUFTLElBQUksU0FBUyxFQUFFO2dCQUN4QixNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsSUFBSSxDQUMxQixTQUFTLENBQUMsRUFBRSxDQUNSLFNBQVMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUM7b0JBQ3ZDLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUM7b0JBQ3pDLFNBQVMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUM7b0JBQzNDLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FDaEQsQ0FBQztnQkFDRixJQUFJLE9BQU8sRUFBRTtvQkFDVCxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxvR0FBb0c7b0JBQzFILE9BQU8sSUFBSSxDQUFDO2lCQUNmO2FBQ0o7UUFDTCxDQUFDLENBQUM7UUFDRixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ08sbUJBQW1CLENBQ3pCLFFBQW9CLEVBQ3BCLFFBQW9CO1FBRXBCLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQzlELENBQUMsQ0FBQyxFQUFlLEVBQUUsQ0FBQyxPQUFPLENBQUMsSUFBSSxRQUFRLENBQzNDLENBQUM7UUFFRiw0QkFBNEI7UUFDNUIsWUFBWSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRTtZQUMvQixJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxFQUFFO2dCQUN0QyxNQUFNLFNBQVMsR0FBbUIsRUFBRSxDQUFDO2dCQUNyQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLEdBQUcsU0FBUyxDQUFDO2dCQUVoRCxNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDaEUsaUJBQU0sQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQzthQUN4RDtZQUNELElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdkQsQ0FBQyxDQUFDLENBQUM7UUFFSCw0Q0FBNEM7UUFDNUMsT0FBTyxHQUFHLEVBQUU7WUFDUixZQUFZLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFO2dCQUMvQixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsaUJBQWlCLENBQUMsV0FBVyxDQUFDLENBQUM7Z0JBQ3RELElBQUksQ0FBQyxTQUFTO29CQUFFLE9BQU87Z0JBQ3ZCLE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzFDLElBQUksS0FBSyxJQUFJLENBQUMsQ0FBQyxFQUFFO29CQUNiLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDO29CQUMzQixJQUFJLFNBQVMsQ0FBQyxNQUFNLElBQUksQ0FBQyxFQUFFO3dCQUN2QixpQkFBTSxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUM7d0JBQzlDLE9BQU8sSUFBSSxDQUFDLGlCQUFpQixDQUFDLFdBQVcsQ0FBQyxDQUFDO3FCQUM5QztpQkFDSjtZQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDO0lBQ04sQ0FBQztJQUVEOzs7O09BSUc7SUFDTyx1QkFBdUIsQ0FBQyxRQUFvQjtRQUNsRCxPQUFPLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBQyxFQUFFLEVBQUU7O1lBQ3pELHFDQUFxQztZQUNyQyxVQUFJLFVBQVUsYUFBVixVQUFVLHVCQUFWLFVBQVUsQ0FBRSxNQUFNLG1DQUFJLENBQUMsR0FBRyxDQUFDO2dCQUMzQixPQUFPLElBQUksS0FBSyxDQUFDLHdDQUF3QyxDQUFDLENBQUM7WUFDL0QsSUFBSSxJQUFJLElBQUksTUFBTTtnQkFDZCxPQUFPLElBQUksS0FBSyxDQUFDLG9EQUFvRCxDQUFDLENBQUM7WUFFM0UsMkJBQTJCO1lBQzNCLE1BQU0sRUFBQyxLQUFLLEVBQUMsR0FBRyxPQUFPLENBQUMsTUFBTSxDQUMxQixDQUFDLEVBQUMsS0FBSyxFQUFFLFNBQVMsRUFBQyxFQUFFLEdBQUcsRUFBRSxFQUFFO2dCQUN4QixJQUFJLEtBQUs7b0JBQUUsT0FBTyxFQUFDLEtBQUssRUFBRSxTQUFTLEVBQUMsQ0FBQztnQkFDckMsSUFBSSxxREFBeUIsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUU7b0JBQ3pDLFNBQVMsSUFBSSxDQUFDLENBQUM7b0JBQ2YsSUFBSSxTQUFTLEdBQUcsQ0FBQzt3QkFDYixLQUFLLEdBQUcsSUFBSSxLQUFLLENBQ2IsaURBQWlELENBQ3BELENBQUM7aUJBQ1Q7Z0JBQ0QsT0FBTyxFQUFDLEtBQUssRUFBRSxTQUFTLEVBQUMsQ0FBQztZQUM5QixDQUFDLEVBQ0QsRUFBQyxLQUFLLEVBQUUsSUFBb0IsRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFDLENBQzlDLENBQUM7WUFDRixJQUFJLEtBQUs7Z0JBQUUsT0FBTyxLQUFLLENBQUM7WUFFeEIsc0JBQXNCO1lBQ3RCLE9BQU8sT0FBTztpQkFDVCxHQUFHLENBQ0EsR0FBRyxDQUFDLEVBQUUsd0JBQ0YsNkRBQTZCLENBQ3pCLEdBQWlELENBQ3BELG1DQUFJLEdBQUcsR0FBQSxDQUNmO2lCQUNBLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7T0FFRztJQUNJLE9BQU87UUFDVixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUNyQixJQUFJLENBQUMsMkJBQTJCLENBQUMsT0FBTyxFQUFFLENBQUM7UUFFM0MsSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFO1lBQ3RCLElBQUksSUFBSSxDQUFDLGVBQWU7Z0JBQ3BCLElBQUksQ0FBQyxlQUFlLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUM5RCxJQUFJLENBQUMsWUFBWSxHQUFHLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksRUFBRSxDQUFDO1NBQy9CO1FBRUQsS0FBSyxJQUFJLFFBQVEsSUFBSSxJQUFJLENBQUMsaUJBQWlCO1lBQ3ZDLGlCQUFNLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMvQyxJQUFJLENBQUMsaUJBQWlCLEdBQUcsRUFBRSxDQUFDO0lBQ2hDLENBQUM7Q0FDSjtBQWpiRCw0Q0FpYkMifQ==