(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@lumino/algorithm'), require('@lumino/coreutils'), require('@lumino/disposable'), require('@lumino/domutils'), require('@lumino/keyboard'), require('@lumino/signaling')) :
    typeof define === 'function' && define.amd ? define(['exports', '@lumino/algorithm', '@lumino/coreutils', '@lumino/disposable', '@lumino/domutils', '@lumino/keyboard', '@lumino/signaling'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.lumino_commands = {}, global.lumino_algorithm, global.lumino_coreutils, global.lumino_disposable, global.lumino_domutils, global.lumino_keyboard, global.lumino_signaling));
}(this, (function (exports, algorithm, coreutils, disposable, domutils, keyboard, signaling) { 'use strict';

    /*! *****************************************************************************
    Copyright (c) Microsoft Corporation.

    Permission to use, copy, modify, and/or distribute this software for any
    purpose with or without fee is hereby granted.

    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
    PERFORMANCE OF THIS SOFTWARE.
    ***************************************************************************** */

    var __assign = function() {
        __assign = Object.assign || function __assign(t) {
            for (var s, i = 1, n = arguments.length; i < n; i++) {
                s = arguments[i];
                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
            }
            return t;
        };
        return __assign.apply(this, arguments);
    };

    /**
     * An object which manages a collection of commands.
     *
     * #### Notes
     * A command registry can be used to populate a variety of action-based
     * widgets, such as command palettes, menus, and toolbars.
     */
    exports.CommandRegistry = /** @class */ (function () {
        function CommandRegistry() {
            this._timerID = 0;
            this._replaying = false;
            this._keystrokes = [];
            this._keydownEvents = [];
            this._keyBindings = [];
            this._exactKeyMatch = null;
            this._commands = Object.create(null);
            this._commandChanged = new signaling.Signal(this);
            this._commandExecuted = new signaling.Signal(this);
            this._keyBindingChanged = new signaling.Signal(this);
        }
        Object.defineProperty(CommandRegistry.prototype, "commandChanged", {
            /**
             * A signal emitted when a command has changed.
             *
             * #### Notes
             * This signal is useful for visual representations of commands which
             * need to refresh when the state of a relevant command has changed.
             */
            get: function () {
                return this._commandChanged;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(CommandRegistry.prototype, "commandExecuted", {
            /**
             * A signal emitted when a command has executed.
             *
             * #### Notes
             * Care should be taken when consuming this signal. The command system is used
             * by many components for many user actions. Handlers registered with this
             * signal must return quickly to ensure the overall application remains responsive.
             */
            get: function () {
                return this._commandExecuted;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(CommandRegistry.prototype, "keyBindingChanged", {
            /**
             * A signal emitted when a key binding is changed.
             */
            get: function () {
                return this._keyBindingChanged;
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(CommandRegistry.prototype, "keyBindings", {
            /**
             * A read-only array of the key bindings in the registry.
             */
            get: function () {
                return this._keyBindings;
            },
            enumerable: true,
            configurable: true
        });
        /**
         * List the ids of the registered commands.
         *
         * @returns A new array of the registered command ids.
         */
        CommandRegistry.prototype.listCommands = function () {
            return Object.keys(this._commands);
        };
        /**
         * Test whether a specific command is registered.
         *
         * @param id - The id of the command of interest.
         *
         * @returns `true` if the command is registered, `false` otherwise.
         */
        CommandRegistry.prototype.hasCommand = function (id) {
            return id in this._commands;
        };
        /**
         * Add a command to the registry.
         *
         * @param id - The unique id of the command.
         *
         * @param options - The options for the command.
         *
         * @returns A disposable which will remove the command.
         *
         * @throws An error if the given `id` is already registered.
         */
        CommandRegistry.prototype.addCommand = function (id, options) {
            var _this = this;
            // Throw an error if the id is already registered.
            if (id in this._commands) {
                throw new Error("Command '" + id + "' already registered.");
            }
            // Add the command to the registry.
            this._commands[id] = Private.createCommand(options);
            // Emit the `commandChanged` signal.
            this._commandChanged.emit({ id: id, type: 'added' });
            // Return a disposable which will remove the command.
            return new disposable.DisposableDelegate(function () {
                // Remove the command from the registry.
                delete _this._commands[id];
                // Emit the `commandChanged` signal.
                _this._commandChanged.emit({ id: id, type: 'removed' });
            });
        };
        /**
         * Notify listeners that the state of a command has changed.
         *
         * @param id - The id of the command which has changed. If more than
         *   one command has changed, this argument should be omitted.
         *
         * @throws An error if the given `id` is not registered.
         *
         * #### Notes
         * This method should be called by the command author whenever the
         * application state changes such that the results of the command
         * metadata functions may have changed.
         *
         * This will cause the `commandChanged` signal to be emitted.
         */
        CommandRegistry.prototype.notifyCommandChanged = function (id) {
            if (id !== undefined && !(id in this._commands)) {
                throw new Error("Command '" + id + "' is not registered.");
            }
            this._commandChanged.emit({ id: id, type: id ? 'changed' : 'many-changed' });
        };
        /**
         * Get the description for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @returns The description for the command.
         */
        CommandRegistry.prototype.describedBy = function (id) {
            var cmd = this._commands[id];
            return cmd ? cmd.describedBy || { args: null } : { args: null };
        };
        /**
         * Get the display label for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The display label for the command, or an empty string
         *   if the command is not registered.
         */
        CommandRegistry.prototype.label = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.label.call(undefined, args) : '';
        };
        /**
         * Get the mnemonic index for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The mnemonic index for the command, or `-1` if the
         *   command is not registered.
         */
        CommandRegistry.prototype.mnemonic = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.mnemonic.call(undefined, args) : -1;
        };
        /**
         * Get the icon renderer for a specific command.
         *
         * DEPRECATED: if set to a string value, the .icon field will
         * function as an alias for the .iconClass field, for backwards
         * compatibility. In the future when this is removed, the default
         * return type will become undefined.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The icon renderer for the command, or
         *   an empty string if the command is not registered.
         */
        CommandRegistry.prototype.icon = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd
                ? cmd.icon.call(undefined, args)
                : /* <DEPRECATED> */ '' /* </DEPRECATED> */ /* <FUTURE> undefined </FUTURE> */;
        };
        /**
         * Get the icon class for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The icon class for the command, or an empty string if
         *   the command is not registered.
         */
        CommandRegistry.prototype.iconClass = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.iconClass.call(undefined, args) : '';
        };
        /**
         * Get the icon label for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The icon label for the command, or an empty string if
         *   the command is not registered.
         */
        CommandRegistry.prototype.iconLabel = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.iconLabel.call(undefined, args) : '';
        };
        /**
         * Get the short form caption for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The caption for the command, or an empty string if the
         *   command is not registered.
         */
        CommandRegistry.prototype.caption = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.caption.call(undefined, args) : '';
        };
        /**
         * Get the usage help text for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The usage text for the command, or an empty string if
         *   the command is not registered.
         */
        CommandRegistry.prototype.usage = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.usage.call(undefined, args) : '';
        };
        /**
         * Get the extra class name for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The class name for the command, or an empty string if
         *   the command is not registered.
         */
        CommandRegistry.prototype.className = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.className.call(undefined, args) : '';
        };
        /**
         * Get the dataset for a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns The dataset for the command, or an empty dataset if
         *   the command is not registered.
         */
        CommandRegistry.prototype.dataset = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.dataset.call(undefined, args) : {};
        };
        /**
         * Test whether a specific command is enabled.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns A boolean indicating whether the command is enabled,
         *   or `false` if the command is not registered.
         */
        CommandRegistry.prototype.isEnabled = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.isEnabled.call(undefined, args) : false;
        };
        /**
         * Test whether a specific command is toggled.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns A boolean indicating whether the command is toggled,
         *   or `false` if the command is not registered.
         */
        CommandRegistry.prototype.isToggled = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.isToggled.call(undefined, args) : false;
        };
        /**
         * Test whether a specific command is toggleable.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns A boolean indicating whether the command is toggleable,
         *   or `false` if the command is not registered.
         */
        CommandRegistry.prototype.isToggleable = function (id, args) {
            var cmd = this._commands[id];
            return cmd ? cmd.isToggleable : false;
        };
        /**
         * Test whether a specific command is visible.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns A boolean indicating whether the command is visible,
         *   or `false` if the command is not registered.
         */
        CommandRegistry.prototype.isVisible = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            var cmd = this._commands[id];
            return cmd ? cmd.isVisible.call(undefined, args) : false;
        };
        /**
         * Execute a specific command.
         *
         * @param id - The id of the command of interest.
         *
         * @param args - The arguments for the command.
         *
         * @returns A promise which resolves with the result of the command.
         *
         * #### Notes
         * The promise will reject if the command throws an exception,
         * or if the command is not registered.
         */
        CommandRegistry.prototype.execute = function (id, args) {
            if (args === void 0) { args = coreutils.JSONExt.emptyObject; }
            // Reject if the command is not registered.
            var cmd = this._commands[id];
            if (!cmd) {
                return Promise.reject(new Error("Command '" + id + "' not registered."));
            }
            // Execute the command and reject if an exception is thrown.
            var value;
            try {
                value = cmd.execute.call(undefined, args);
            }
            catch (err) {
                value = Promise.reject(err);
            }
            // Create the return promise which resolves the result.
            var result = Promise.resolve(value);
            // Emit the command executed signal.
            this._commandExecuted.emit({ id: id, args: args, result: result });
            // Return the result promise to the caller.
            return result;
        };
        /**
         * Add a key binding to the registry.
         *
         * @param options - The options for creating the key binding.
         *
         * @returns A disposable which removes the added key binding.
         *
         * #### Notes
         * If multiple key bindings are registered for the same sequence, the
         * binding with the highest selector specificity is executed first. A
         * tie is broken by using the most recently added key binding.
         *
         * Ambiguous key bindings are resolved with a timeout. As an example,
         * suppose two key bindings are registered: one with the key sequence
         * `['Ctrl D']`, and another with `['Ctrl D', 'Ctrl W']`. If the user
         * presses `Ctrl D`, the first binding cannot be immediately executed
         * since the user may intend to complete the chord with `Ctrl W`. For
         * such cases, a timer is used to allow the chord to be completed. If
         * the chord is not completed before the timeout, the first binding
         * is executed.
         */
        CommandRegistry.prototype.addKeyBinding = function (options) {
            var _this = this;
            // Create the binding for the given options.
            var binding = Private.createKeyBinding(options);
            // Add the key binding to the bindings array.
            this._keyBindings.push(binding);
            // Emit the `bindingChanged` signal.
            this._keyBindingChanged.emit({ binding: binding, type: 'added' });
            // Return a disposable which will remove the binding.
            return new disposable.DisposableDelegate(function () {
                // Remove the binding from the array.
                algorithm.ArrayExt.removeFirstOf(_this._keyBindings, binding);
                // Emit the `bindingChanged` signal.
                _this._keyBindingChanged.emit({ binding: binding, type: 'removed' });
            });
        };
        /**
         * Process a `'keydown'` event and invoke a matching key binding.
         *
         * @param event - The event object for a `'keydown'` event.
         *
         * #### Notes
         * This should be called in response to a `'keydown'` event in order
         * to invoke the command for the best matching key binding.
         *
         * The registry **does not** install its own listener for `'keydown'`
         * events. This allows the application full control over the nodes
         * and phase for which the registry processes `'keydown'` events.
         *
         * When the keydown event is processed, if the event target or any of its
         * ancestor nodes has a `data-lm-suppress-shortcuts` attribute, its keydown
         * events will not invoke commands.
         */
        CommandRegistry.prototype.processKeydownEvent = function (event) {
            // Bail immediately if playing back keystrokes.
            if (this._replaying || CommandRegistry.isModifierKeyPressed(event)) {
                return;
            }
            // Get the normalized keystroke for the event.
            var keystroke = CommandRegistry.keystrokeForKeydownEvent(event);
            // If the keystroke is not valid for the keyboard layout, replay
            // any suppressed events and clear the pending state.
            if (!keystroke) {
                this._replayKeydownEvents();
                this._clearPendingState();
                return;
            }
            // Add the keystroke to the current key sequence.
            this._keystrokes.push(keystroke);
            // Find the exact and partial matches for the key sequence.
            var _a = Private.matchKeyBinding(this._keyBindings, this._keystrokes, event), exact = _a.exact, partial = _a.partial;
            // If there is no exact match and no partial match, replay
            // any suppressed events and clear the pending state.
            if (!exact && !partial) {
                this._replayKeydownEvents();
                this._clearPendingState();
                return;
            }
            // Stop propagation of the event. If there is only a partial match,
            // the event will be replayed if a final exact match never occurs.
            event.preventDefault();
            event.stopPropagation();
            // If there is an exact match but no partial match, the exact match
            // can be dispatched immediately. The pending state is cleared so
            // the next key press starts from the default state.
            if (exact && !partial) {
                this._executeKeyBinding(exact);
                this._clearPendingState();
                return;
            }
            // If there is both an exact match and a partial match, the exact
            // match is stored for future dispatch in case the timer expires
            // before a more specific match is triggered.
            if (exact) {
                this._exactKeyMatch = exact;
            }
            // Store the event for possible playback in the future.
            this._keydownEvents.push(event);
            // (Re)start the timer to dispatch the most recent exact match
            // in case the partial match fails to result in an exact match.
            this._startTimer();
        };
        /**
         * Start or restart the pending timeout.
         */
        CommandRegistry.prototype._startTimer = function () {
            var _this = this;
            this._clearTimer();
            this._timerID = window.setTimeout(function () {
                _this._onPendingTimeout();
            }, Private.CHORD_TIMEOUT);
        };
        /**
         * Clear the pending timeout.
         */
        CommandRegistry.prototype._clearTimer = function () {
            if (this._timerID !== 0) {
                clearTimeout(this._timerID);
                this._timerID = 0;
            }
        };
        /**
         * Replay the keydown events which were suppressed.
         */
        CommandRegistry.prototype._replayKeydownEvents = function () {
            if (this._keydownEvents.length === 0) {
                return;
            }
            this._replaying = true;
            this._keydownEvents.forEach(Private.replayKeyEvent);
            this._replaying = false;
        };
        /**
         * Execute the command for the given key binding.
         *
         * If the command is missing or disabled, a warning will be logged.
         */
        CommandRegistry.prototype._executeKeyBinding = function (binding) {
            var command = binding.command, args = binding.args;
            if (!this.hasCommand(command) || !this.isEnabled(command, args)) {
                var word = this.hasCommand(command) ? 'enabled' : 'registered';
                var keys = binding.keys.join(', ');
                var msg1 = "Cannot execute key binding '" + keys + "':";
                var msg2 = "command '" + command + "' is not " + word + ".";
                console.warn(msg1 + " " + msg2);
                return;
            }
            this.execute(command, args);
        };
        /**
         * Clear the internal pending state.
         */
        CommandRegistry.prototype._clearPendingState = function () {
            this._clearTimer();
            this._exactKeyMatch = null;
            this._keystrokes.length = 0;
            this._keydownEvents.length = 0;
        };
        /**
         * Handle the partial match timeout.
         */
        CommandRegistry.prototype._onPendingTimeout = function () {
            this._timerID = 0;
            if (this._exactKeyMatch) {
                this._executeKeyBinding(this._exactKeyMatch);
            }
            else {
                this._replayKeydownEvents();
            }
            this._clearPendingState();
        };
        return CommandRegistry;
    }());
    /**
     * The namespace for the `CommandRegistry` class statics.
     */
    (function (CommandRegistry) {
        /**
         * Parse a keystroke into its constituent components.
         *
         * @param keystroke - The keystroke of interest.
         *
         * @returns The parsed components of the keystroke.
         *
         * #### Notes
         * The keystroke should be of the form:
         *   `[<modifier 1> [<modifier 2> [<modifier N> ]]]<primary key>`
         *
         * The supported modifiers are: `Accel`, `Alt`, `Cmd`, `Ctrl`, and
         * `Shift`. The `Accel` modifier is translated to `Cmd` on Mac and
         * `Ctrl` on all other platforms.
         *
         * The parsing is tolerant and will not throw exceptions. Notably:
         *   - Duplicate modifiers are ignored.
         *   - Extra primary keys are ignored.
         *   - The order of modifiers and primary key is irrelevant.
         *   - The keystroke parts should be separated by whitespace.
         *   - The keystroke is case sensitive.
         */
        function parseKeystroke(keystroke) {
            var key = '';
            var alt = false;
            var cmd = false;
            var ctrl = false;
            var shift = false;
            for (var _i = 0, _a = keystroke.split(/\s+/); _i < _a.length; _i++) {
                var token = _a[_i];
                if (token === 'Accel') {
                    if (domutils.Platform.IS_MAC) {
                        cmd = true;
                    }
                    else {
                        ctrl = true;
                    }
                }
                else if (token === 'Alt') {
                    alt = true;
                }
                else if (token === 'Cmd') {
                    cmd = true;
                }
                else if (token === 'Ctrl') {
                    ctrl = true;
                }
                else if (token === 'Shift') {
                    shift = true;
                }
                else if (token.length > 0) {
                    key = token;
                }
            }
            return { cmd: cmd, ctrl: ctrl, alt: alt, shift: shift, key: key };
        }
        CommandRegistry.parseKeystroke = parseKeystroke;
        /**
         * Normalize a keystroke into a canonical representation.
         *
         * @param keystroke - The keystroke of interest.
         *
         * @returns The normalized representation of the keystroke.
         *
         * #### Notes
         * This normalizes the keystroke by removing duplicate modifiers and
         * extra primary keys, and assembling the parts in a canonical order.
         *
         * The `Cmd` modifier is ignored on non-Mac platforms.
         */
        function normalizeKeystroke(keystroke) {
            var mods = '';
            var parts = parseKeystroke(keystroke);
            if (parts.ctrl) {
                mods += 'Ctrl ';
            }
            if (parts.alt) {
                mods += 'Alt ';
            }
            if (parts.shift) {
                mods += 'Shift ';
            }
            if (parts.cmd && domutils.Platform.IS_MAC) {
                mods += 'Cmd ';
            }
            return mods + parts.key;
        }
        CommandRegistry.normalizeKeystroke = normalizeKeystroke;
        /**
         * Get the platform-specific normalized keys for an options object.
         *
         * @param options - The options for the key binding.
         *
         * @returns Array of combined, normalized keys.
         */
        function normalizeKeys(options) {
            var keys;
            if (domutils.Platform.IS_WIN) {
                keys = options.winKeys || options.keys;
            }
            else if (domutils.Platform.IS_MAC) {
                keys = options.macKeys || options.keys;
            }
            else {
                keys = options.linuxKeys || options.keys;
            }
            return keys.map(normalizeKeystroke);
        }
        CommandRegistry.normalizeKeys = normalizeKeys;
        /**
         * Format keystrokes for display on the local system.
         *
         * If a list of keystrokes is provided, it will be displayed as
         * a comma-separated string
         *
         * @param keystroke The keystrokes to format
         * @returns The keystrokes representation
         */
        function formatKeystroke(keystroke) {
            return typeof keystroke === 'string'
                ? formatSingleKey(keystroke)
                : keystroke.map(formatSingleKey).join(', ');
            function formatSingleKey(key) {
                var mods = [];
                var separator = domutils.Platform.IS_MAC ? ' ' : '+';
                var parts = parseKeystroke(key);
                if (parts.ctrl) {
                    mods.push('Ctrl');
                }
                if (parts.alt) {
                    mods.push('Alt');
                }
                if (parts.shift) {
                    mods.push('Shift');
                }
                if (domutils.Platform.IS_MAC && parts.cmd) {
                    mods.push('Cmd');
                }
                mods.push(parts.key);
                return mods.map(Private.formatKey).join(separator);
            }
        }
        CommandRegistry.formatKeystroke = formatKeystroke;
        /**
         * Check if `'keydown'` event is caused by pressing a modifier key that should be ignored.
         *
         * @param event - The event object for a `'keydown'` event.
         *
         * @returns `true` if modifier key was pressed, `false` otherwise.
         */
        function isModifierKeyPressed(event) {
            var layout = keyboard.getKeyboardLayout();
            var key = layout.keyForKeydownEvent(event);
            return layout.isModifierKey(key);
        }
        CommandRegistry.isModifierKeyPressed = isModifierKeyPressed;
        /**
         * Create a normalized keystroke for a `'keydown'` event.
         *
         * @param event - The event object for a `'keydown'` event.
         *
         * @returns A normalized keystroke, or an empty string if the event
         *   does not represent a valid keystroke for the given layout.
         */
        function keystrokeForKeydownEvent(event) {
            var layout = keyboard.getKeyboardLayout();
            var key = layout.keyForKeydownEvent(event);
            if (!key || layout.isModifierKey(key)) {
                return '';
            }
            var mods = [];
            if (event.ctrlKey) {
                mods.push('Ctrl');
            }
            if (event.altKey) {
                mods.push('Alt');
            }
            if (event.shiftKey) {
                mods.push('Shift');
            }
            if (event.metaKey && domutils.Platform.IS_MAC) {
                mods.push('Cmd');
            }
            mods.push(key);
            return mods.join(' ');
        }
        CommandRegistry.keystrokeForKeydownEvent = keystrokeForKeydownEvent;
    })(exports.CommandRegistry || (exports.CommandRegistry = {}));
    /**
     * The namespace for the module implementation details.
     */
    var Private;
    (function (Private) {
        /**
         * The timeout in ms for triggering a key binding chord.
         */
        Private.CHORD_TIMEOUT = 1000;
        /**
         * Create a normalized command from an options object.
         */
        function createCommand(options) {
            var icon;
            var iconClass;
            /* <DEPRECATED> */
            if (!options.icon || typeof options.icon === 'string') {
                // alias icon to iconClass
                iconClass = asFunc(options.iconClass || options.icon, emptyStringFunc);
                icon = iconClass;
            }
            else {
                /* /<DEPRECATED> */
                iconClass = asFunc(options.iconClass, emptyStringFunc);
                icon = asFunc(options.icon, undefinedFunc);
                /* <DEPRECATED> */
            }
            /* </DEPRECATED> */
            return {
                execute: options.execute,
                describedBy: __assign({ args: null }, options.describedBy),
                label: asFunc(options.label, emptyStringFunc),
                mnemonic: asFunc(options.mnemonic, negativeOneFunc),
                icon: icon,
                iconClass: iconClass,
                iconLabel: asFunc(options.iconLabel, emptyStringFunc),
                caption: asFunc(options.caption, emptyStringFunc),
                usage: asFunc(options.usage, emptyStringFunc),
                className: asFunc(options.className, emptyStringFunc),
                dataset: asFunc(options.dataset, emptyDatasetFunc),
                isEnabled: options.isEnabled || trueFunc,
                isToggled: options.isToggled || falseFunc,
                isToggleable: options.isToggleable || !!options.isToggled,
                isVisible: options.isVisible || trueFunc
            };
        }
        Private.createCommand = createCommand;
        /**
         * Create a key binding object from key binding options.
         */
        function createKeyBinding(options) {
            return {
                keys: exports.CommandRegistry.normalizeKeys(options),
                selector: validateSelector(options),
                command: options.command,
                args: options.args || coreutils.JSONExt.emptyObject
            };
        }
        Private.createKeyBinding = createKeyBinding;
        /**
         * Find the key bindings which match a key sequence.
         *
         * This returns a match result which contains the best exact matching
         * binding, and a flag which indicates if there are partial matches.
         */
        function matchKeyBinding(bindings, keys, event) {
            // The current best exact match.
            var exact = null;
            // Whether a partial match has been found.
            var partial = false;
            // The match distance for the exact match.
            var distance = Infinity;
            // The specificity for the exact match.
            var specificity = 0;
            // Iterate over the bindings and search for the best match.
            for (var i = 0, n = bindings.length; i < n; ++i) {
                // Lookup the current binding.
                var binding = bindings[i];
                // Check whether the key binding sequence is a match.
                var sqm = matchSequence(binding.keys, keys);
                // If there is no match, the binding is ignored.
                if (sqm === 0 /* None */) {
                    continue;
                }
                // If it is a partial match and no other partial match has been
                // found, ensure the selector matches and set the partial flag.
                if (sqm === 2 /* Partial */) {
                    if (!partial && targetDistance(binding.selector, event) !== -1) {
                        partial = true;
                    }
                    continue;
                }
                // Ignore the match if the selector doesn't match, or if the
                // matched node is farther away than the current best match.
                var td = targetDistance(binding.selector, event);
                if (td === -1 || td > distance) {
                    continue;
                }
                // Get the specificity for the selector.
                var sp = domutils.Selector.calculateSpecificity(binding.selector);
                // Update the best match if this match is stronger.
                if (!exact || td < distance || sp >= specificity) {
                    exact = binding;
                    distance = td;
                    specificity = sp;
                }
            }
            // Return the match result.
            return { exact: exact, partial: partial };
        }
        Private.matchKeyBinding = matchKeyBinding;
        /**
         * Replay a keyboard event.
         *
         * This synthetically dispatches a clone of the keyboard event.
         */
        function replayKeyEvent(event) {
            event.target.dispatchEvent(cloneKeyboardEvent(event));
        }
        Private.replayKeyEvent = replayKeyEvent;
        function formatKey(key) {
            if (domutils.Platform.IS_MAC) {
                return MAC_DISPLAY.hasOwnProperty(key) ? MAC_DISPLAY[key] : key;
            }
            else {
                return WIN_DISPLAY.hasOwnProperty(key) ? WIN_DISPLAY[key] : key;
            }
        }
        Private.formatKey = formatKey;
        var MAC_DISPLAY = {
            Backspace: '⌫',
            Tab: '⇥',
            Enter: '↩',
            Shift: '⇧',
            Ctrl: '⌃',
            Alt: '⌥',
            Escape: '⎋',
            PageUp: '⇞',
            PageDown: '⇟',
            End: '↘',
            Home: '↖',
            ArrowLeft: '←',
            ArrowUp: '↑',
            ArrowRight: '→',
            ArrowDown: '↓',
            Delete: '⌦',
            Cmd: '⌘'
        };
        var WIN_DISPLAY = {
            Escape: 'Esc',
            PageUp: 'Page Up',
            PageDown: 'Page Down',
            ArrowLeft: 'Left',
            ArrowUp: 'Up',
            ArrowRight: 'Right',
            ArrowDown: 'Down',
            Delete: 'Del'
        };
        /**
         * A singleton empty string function.
         */
        var emptyStringFunc = function () { return ''; };
        /**
         * A singleton `-1` number function
         */
        var negativeOneFunc = function () { return -1; };
        /**
         * A singleton true boolean function.
         */
        var trueFunc = function () { return true; };
        /**
         * A singleton false boolean function.
         */
        var falseFunc = function () { return false; };
        /**
         * A singleton empty dataset function.
         */
        var emptyDatasetFunc = function () { return ({}); };
        /**
         * A singleton undefined function
         */
        var undefinedFunc = function () { return undefined; };
        /**
         * Cast a value or command func to a command func.
         */
        function asFunc(value, dfault) {
            if (value === undefined) {
                return dfault;
            }
            if (typeof value === 'function') {
                return value;
            }
            return function () { return value; };
        }
        /**
         * Validate the selector for an options object.
         *
         * This returns the validated selector, or throws if the selector is
         * invalid or contains commas.
         */
        function validateSelector(options) {
            if (options.selector.indexOf(',') !== -1) {
                throw new Error("Selector cannot contain commas: " + options.selector);
            }
            if (!domutils.Selector.isValid(options.selector)) {
                throw new Error("Invalid selector: " + options.selector);
            }
            return options.selector;
        }
        /**
         * Test whether a key binding sequence matches a key sequence.
         *
         * Returns a `SequenceMatch` value indicating the type of match.
         */
        function matchSequence(bindKeys, userKeys) {
            if (bindKeys.length < userKeys.length) {
                return 0 /* None */;
            }
            for (var i = 0, n = userKeys.length; i < n; ++i) {
                if (bindKeys[i] !== userKeys[i]) {
                    return 0 /* None */;
                }
            }
            if (bindKeys.length > userKeys.length) {
                return 2 /* Partial */;
            }
            return 1 /* Exact */;
        }
        /**
         * Find the distance from the target node to the first matching node.
         *
         * This traverses the event path from `target` to `currentTarget` and
         * computes the distance from `target` to the first node which matches
         * the CSS selector. If no match is found, `-1` is returned.
         */
        function targetDistance(selector, event) {
            var targ = event.target;
            var curr = event.currentTarget;
            for (var dist = 0; targ !== null; targ = targ.parentElement, ++dist) {
                if (targ.hasAttribute('data-lm-suppress-shortcuts')) {
                    return -1;
                }
                /* <DEPRECATED> */
                if (targ.hasAttribute('data-p-suppress-shortcuts')) {
                    return -1;
                }
                /* </DEPRECATED> */
                if (domutils.Selector.matches(targ, selector)) {
                    return dist;
                }
                if (targ === curr) {
                    return -1;
                }
            }
            return -1;
        }
        /**
         * Clone a keyboard event.
         */
        function cloneKeyboardEvent(event) {
            // A custom event is required because Chrome nulls out the
            // `keyCode` field in user-generated `KeyboardEvent` types.
            var clone = document.createEvent('Event');
            var bubbles = event.bubbles || true;
            var cancelable = event.cancelable || true;
            clone.initEvent(event.type || 'keydown', bubbles, cancelable);
            clone.key = event.key || '';
            clone.keyCode = event.keyCode || 0;
            clone.which = event.keyCode || 0;
            clone.ctrlKey = event.ctrlKey || false;
            clone.altKey = event.altKey || false;
            clone.shiftKey = event.shiftKey || false;
            clone.metaKey = event.metaKey || false;
            clone.view = event.view || window;
            return clone;
        }
    })(Private || (Private = {}));

    Object.defineProperty(exports, '__esModule', { value: true });

})));
//# sourceMappingURL=index.js.map
