(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@lumino/commands'), require('@lumino/coreutils'), require('@lumino/widgets')) :
    typeof define === 'function' && define.amd ? define(['exports', '@lumino/commands', '@lumino/coreutils', '@lumino/widgets'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.lumino_application = {}, global.lumino_commands, global.lumino_coreutils, global.lumino_widgets));
}(this, (function (exports, commands, coreutils, widgets) { '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.
    ***************************************************************************** */

    function __awaiter(thisArg, _arguments, P, generator) {
        function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
        return new (P || (P = Promise))(function (resolve, reject) {
            function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
            function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
            function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
            step((generator = generator.apply(thisArg, _arguments || [])).next());
        });
    }

    function __generator(thisArg, body) {
        var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
        return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
        function verb(n) { return function (v) { return step([n, v]); }; }
        function step(op) {
            if (f) throw new TypeError("Generator is already executing.");
            while (_) try {
                if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
                if (y = 0, t) op = [op[0] & 2, t.value];
                switch (op[0]) {
                    case 0: case 1: t = op; break;
                    case 4: _.label++; return { value: op[1], done: false };
                    case 5: _.label++; y = op[1]; op = [0]; continue;
                    case 7: op = _.ops.pop(); _.trys.pop(); continue;
                    default:
                        if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                        if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                        if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                        if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                        if (t[2]) _.ops.pop();
                        _.trys.pop(); continue;
                }
                op = body.call(thisArg, _);
            } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
            if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
        }
    }

    /** @deprecated */
    function __spreadArrays() {
        for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
        for (var r = Array(s), k = 0, i = 0; i < il; i++)
            for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
                r[k] = a[j];
        return r;
    }

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * The namespace for array-specific algorithms.
     */
    var ArrayExt;
    (function (ArrayExt) {
        /**
         * Find the index of the first occurrence of a value in an array.
         *
         * @param array - The array-like object to search.
         *
         * @param value - The value to locate in the array. Values are
         *   compared using strict `===` equality.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the first occurrence of the value, or `-1`
         *   if the value is not found.
         *
         * #### Notes
         * If `stop < start` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = ['one', 'two', 'three', 'four', 'one'];
         * ArrayExt.firstIndexOf(data, 'red');        // -1
         * ArrayExt.firstIndexOf(data, 'one');        // 0
         * ArrayExt.firstIndexOf(data, 'one', 1);     // 4
         * ArrayExt.firstIndexOf(data, 'two', 2);     // -1
         * ArrayExt.firstIndexOf(data, 'two', 2, 1);  // 1
         * ```
         */
        function firstIndexOf(array, value, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return -1;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var span;
            if (stop < start) {
                span = stop + 1 + (n - start);
            }
            else {
                span = stop - start + 1;
            }
            for (var i = 0; i < span; ++i) {
                var j = (start + i) % n;
                if (array[j] === value) {
                    return j;
                }
            }
            return -1;
        }
        ArrayExt.firstIndexOf = firstIndexOf;
        /**
         * Find the index of the last occurrence of a value in an array.
         *
         * @param array - The array-like object to search.
         *
         * @param value - The value to locate in the array. Values are
         *   compared using strict `===` equality.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the last occurrence of the value, or `-1`
         *   if the value is not found.
         *
         * #### Notes
         * If `start < stop` the search will wrap at the front of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = ['one', 'two', 'three', 'four', 'one'];
         * ArrayExt.lastIndexOf(data, 'red');        // -1
         * ArrayExt.lastIndexOf(data, 'one');        // 4
         * ArrayExt.lastIndexOf(data, 'one', 1);     // 0
         * ArrayExt.lastIndexOf(data, 'two', 0);     // -1
         * ArrayExt.lastIndexOf(data, 'two', 0, 1);  // 1
         * ```
         */
        function lastIndexOf(array, value, start, stop) {
            if (start === void 0) { start = -1; }
            if (stop === void 0) { stop = 0; }
            var n = array.length;
            if (n === 0) {
                return -1;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var span;
            if (start < stop) {
                span = start + 1 + (n - stop);
            }
            else {
                span = start - stop + 1;
            }
            for (var i = 0; i < span; ++i) {
                var j = (start - i + n) % n;
                if (array[j] === value) {
                    return j;
                }
            }
            return -1;
        }
        ArrayExt.lastIndexOf = lastIndexOf;
        /**
         * Find the index of the first value which matches a predicate.
         *
         * @param array - The array-like object to search.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the first matching value, or `-1` if no
         *   matching value is found.
         *
         * #### Notes
         * If `stop < start` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [1, 2, 3, 4, 3, 2, 1];
         * ArrayExt.findFirstIndex(data, isEven);       // 1
         * ArrayExt.findFirstIndex(data, isEven, 4);    // 5
         * ArrayExt.findFirstIndex(data, isEven, 6);    // -1
         * ArrayExt.findFirstIndex(data, isEven, 6, 5); // 1
         * ```
         */
        function findFirstIndex(array, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return -1;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var span;
            if (stop < start) {
                span = stop + 1 + (n - start);
            }
            else {
                span = stop - start + 1;
            }
            for (var i = 0; i < span; ++i) {
                var j = (start + i) % n;
                if (fn(array[j], j)) {
                    return j;
                }
            }
            return -1;
        }
        ArrayExt.findFirstIndex = findFirstIndex;
        /**
         * Find the index of the last value which matches a predicate.
         *
         * @param object - The array-like object to search.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the last matching value, or `-1` if no
         *   matching value is found.
         *
         * #### Notes
         * If `start < stop` the search will wrap at the front of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [1, 2, 3, 4, 3, 2, 1];
         * ArrayExt.findLastIndex(data, isEven);        // 5
         * ArrayExt.findLastIndex(data, isEven, 4);     // 3
         * ArrayExt.findLastIndex(data, isEven, 0);     // -1
         * ArrayExt.findLastIndex(data, isEven, 0, 1);  // 5
         * ```
         */
        function findLastIndex(array, fn, start, stop) {
            if (start === void 0) { start = -1; }
            if (stop === void 0) { stop = 0; }
            var n = array.length;
            if (n === 0) {
                return -1;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var d;
            if (start < stop) {
                d = start + 1 + (n - stop);
            }
            else {
                d = start - stop + 1;
            }
            for (var i = 0; i < d; ++i) {
                var j = (start - i + n) % n;
                if (fn(array[j], j)) {
                    return j;
                }
            }
            return -1;
        }
        ArrayExt.findLastIndex = findLastIndex;
        /**
         * Find the first value which matches a predicate.
         *
         * @param array - The array-like object to search.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The first matching value, or `undefined` if no matching
         *   value is found.
         *
         * #### Notes
         * If `stop < start` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [1, 2, 3, 4, 3, 2, 1];
         * ArrayExt.findFirstValue(data, isEven);       // 2
         * ArrayExt.findFirstValue(data, isEven, 2);    // 4
         * ArrayExt.findFirstValue(data, isEven, 6);    // undefined
         * ArrayExt.findFirstValue(data, isEven, 6, 5); // 2
         * ```
         */
        function findFirstValue(array, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var index = findFirstIndex(array, fn, start, stop);
            return index !== -1 ? array[index] : undefined;
        }
        ArrayExt.findFirstValue = findFirstValue;
        /**
         * Find the last value which matches a predicate.
         *
         * @param object - The array-like object to search.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The last matching value, or `undefined` if no matching
         *   value is found.
         *
         * #### Notes
         * If `start < stop` the search will wrap at the front of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [1, 2, 3, 4, 3, 2, 1];
         * ArrayExt.findLastValue(data, isEven);        // 2
         * ArrayExt.findLastValue(data, isEven, 4);     // 4
         * ArrayExt.findLastValue(data, isEven, 0);     // undefined
         * ArrayExt.findLastValue(data, isEven, 0, 1);  // 2
         * ```
         */
        function findLastValue(array, fn, start, stop) {
            if (start === void 0) { start = -1; }
            if (stop === void 0) { stop = 0; }
            var index = findLastIndex(array, fn, start, stop);
            return index !== -1 ? array[index] : undefined;
        }
        ArrayExt.findLastValue = findLastValue;
        /**
         * Find the index of the first element which compares `>=` to a value.
         *
         * @param array - The sorted array-like object to search.
         *
         * @param value - The value to locate in the array.
         *
         * @param fn - The 3-way comparison function to apply to the values.
         *   It should return `< 0` if an element is less than a value, `0` if
         *   an element is equal to a value, or `> 0` if an element is greater
         *   than a value.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the first element which compares `>=` to the
         *   value, or `length` if there is no such element. If the computed
         *   index for `stop` is less than `start`, then the computed index
         *   for `start` is returned.
         *
         * #### Notes
         * The array must already be sorted in ascending order according to
         * the comparison function.
         *
         * #### Complexity
         * Logarithmic.
         *
         * #### Undefined Behavior
         * Searching a range which is not sorted in ascending order.
         *
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function numberCmp(a: number, b: number): number {
         *   return a - b;
         * }
         *
         * let data = [0, 3, 4, 7, 7, 9];
         * ArrayExt.lowerBound(data, 0, numberCmp);   // 0
         * ArrayExt.lowerBound(data, 6, numberCmp);   // 3
         * ArrayExt.lowerBound(data, 7, numberCmp);   // 3
         * ArrayExt.lowerBound(data, -1, numberCmp);  // 0
         * ArrayExt.lowerBound(data, 10, numberCmp);  // 6
         * ```
         */
        function lowerBound(array, value, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return 0;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var begin = start;
            var span = stop - start + 1;
            while (span > 0) {
                var half = span >> 1;
                var middle = begin + half;
                if (fn(array[middle], value) < 0) {
                    begin = middle + 1;
                    span -= half + 1;
                }
                else {
                    span = half;
                }
            }
            return begin;
        }
        ArrayExt.lowerBound = lowerBound;
        /**
         * Find the index of the first element which compares `>` than a value.
         *
         * @param array - The sorted array-like object to search.
         *
         * @param value - The value to locate in the array.
         *
         * @param fn - The 3-way comparison function to apply to the values.
         *   It should return `< 0` if an element is less than a value, `0` if
         *   an element is equal to a value, or `> 0` if an element is greater
         *   than a value.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the first element which compares `>` than the
         *   value, or `length` if there is no such element. If the computed
         *   index for `stop` is less than `start`, then the computed index
         *   for `start` is returned.
         *
         * #### Notes
         * The array must already be sorted in ascending order according to
         * the comparison function.
         *
         * #### Complexity
         * Logarithmic.
         *
         * #### Undefined Behavior
         * Searching a range which is not sorted in ascending order.
         *
         * A `start` or `stop` which is non-integral.
         *
         * Modifying the length of the array while searching.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function numberCmp(a: number, b: number): number {
         *   return a - b;
         * }
         *
         * let data = [0, 3, 4, 7, 7, 9];
         * ArrayExt.upperBound(data, 0, numberCmp);   // 1
         * ArrayExt.upperBound(data, 6, numberCmp);   // 3
         * ArrayExt.upperBound(data, 7, numberCmp);   // 5
         * ArrayExt.upperBound(data, -1, numberCmp);  // 0
         * ArrayExt.upperBound(data, 10, numberCmp);  // 6
         * ```
         */
        function upperBound(array, value, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return 0;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var begin = start;
            var span = stop - start + 1;
            while (span > 0) {
                var half = span >> 1;
                var middle = begin + half;
                if (fn(array[middle], value) > 0) {
                    span = half;
                }
                else {
                    begin = middle + 1;
                    span -= half + 1;
                }
            }
            return begin;
        }
        ArrayExt.upperBound = upperBound;
        /**
         * Test whether two arrays are shallowly equal.
         *
         * @param a - The first array-like object to compare.
         *
         * @param b - The second array-like object to compare.
         *
         * @param fn - The comparison function to apply to the elements. It
         *   should return `true` if the elements are "equal". The default
         *   compares elements using strict `===` equality.
         *
         * @returns Whether the two arrays are shallowly equal.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * Modifying the length of the arrays while comparing.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let d1 = [0, 3, 4, 7, 7, 9];
         * let d2 = [0, 3, 4, 7, 7, 9];
         * let d3 = [42];
         * ArrayExt.shallowEqual(d1, d2);  // true
         * ArrayExt.shallowEqual(d2, d3);  // false
         * ```
         */
        function shallowEqual(a, b, fn) {
            // Check for object identity first.
            if (a === b) {
                return true;
            }
            // Bail early if the lengths are different.
            if (a.length !== b.length) {
                return false;
            }
            // Compare each element for equality.
            for (var i = 0, n = a.length; i < n; ++i) {
                if (fn ? !fn(a[i], b[i]) : a[i] !== b[i]) {
                    return false;
                }
            }
            // The array are shallowly equal.
            return true;
        }
        ArrayExt.shallowEqual = shallowEqual;
        /**
         * Create a slice of an array subject to an optional step.
         *
         * @param array - The array-like object of interest.
         *
         * @param options - The options for configuring the slice.
         *
         * @returns A new array with the specified values.
         *
         * @throws An exception if the slice `step` is `0`.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start`, `stop`, or `step` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 3, 4, 7, 7, 9];
         * ArrayExt.slice(data);                         // [0, 3, 4, 7, 7, 9]
         * ArrayExt.slice(data, { start: 2 });           // [4, 7, 7, 9]
         * ArrayExt.slice(data, { start: 0, stop: 4 });  // [0, 3, 4, 7]
         * ArrayExt.slice(data, { step: 2 });            // [0, 4, 7]
         * ArrayExt.slice(data, { step: -1 });           // [9, 7, 7, 4, 3, 0]
         * ```
         */
        function slice(array, options) {
            if (options === void 0) { options = {}; }
            // Extract the options.
            var start = options.start, stop = options.stop, step = options.step;
            // Set up the `step` value.
            if (step === undefined) {
                step = 1;
            }
            // Validate the step size.
            if (step === 0) {
                throw new Error('Slice `step` cannot be zero.');
            }
            // Look up the length of the array.
            var n = array.length;
            // Set up the `start` value.
            if (start === undefined) {
                start = step < 0 ? n - 1 : 0;
            }
            else if (start < 0) {
                start = Math.max(start + n, step < 0 ? -1 : 0);
            }
            else if (start >= n) {
                start = step < 0 ? n - 1 : n;
            }
            // Set up the `stop` value.
            if (stop === undefined) {
                stop = step < 0 ? -1 : n;
            }
            else if (stop < 0) {
                stop = Math.max(stop + n, step < 0 ? -1 : 0);
            }
            else if (stop >= n) {
                stop = step < 0 ? n - 1 : n;
            }
            // Compute the slice length.
            var length;
            if ((step < 0 && stop >= start) || (step > 0 && start >= stop)) {
                length = 0;
            }
            else if (step < 0) {
                length = Math.floor((stop - start + 1) / step + 1);
            }
            else {
                length = Math.floor((stop - start - 1) / step + 1);
            }
            // Compute the sliced result.
            var result = [];
            for (var i = 0; i < length; ++i) {
                result[i] = array[start + i * step];
            }
            // Return the result.
            return result;
        }
        ArrayExt.slice = slice;
        /**
         * Move an element in an array from one index to another.
         *
         * @param array - The mutable array-like object of interest.
         *
         * @param fromIndex - The index of the element to move. Negative
         *   values are taken as an offset from the end of the array.
         *
         * @param toIndex - The target index of the element. Negative
         *   values are taken as an offset from the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `fromIndex` or `toIndex` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from from '@lumino/algorithm';
         *
         * let data = [0, 1, 2, 3, 4];
         * ArrayExt.move(data, 1, 2);  // [0, 2, 1, 3, 4]
         * ArrayExt.move(data, 4, 2);  // [0, 2, 4, 1, 3]
         * ```
         */
        function move(array, fromIndex, toIndex) {
            var n = array.length;
            if (n <= 1) {
                return;
            }
            if (fromIndex < 0) {
                fromIndex = Math.max(0, fromIndex + n);
            }
            else {
                fromIndex = Math.min(fromIndex, n - 1);
            }
            if (toIndex < 0) {
                toIndex = Math.max(0, toIndex + n);
            }
            else {
                toIndex = Math.min(toIndex, n - 1);
            }
            if (fromIndex === toIndex) {
                return;
            }
            var value = array[fromIndex];
            var d = fromIndex < toIndex ? 1 : -1;
            for (var i = fromIndex; i !== toIndex; i += d) {
                array[i] = array[i + d];
            }
            array[toIndex] = value;
        }
        ArrayExt.move = move;
        /**
         * Reverse an array in-place.
         *
         * @param array - The mutable array-like object of interest.
         *
         * @param start - The index of the first element in the range to be
         *   reversed, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   reversed, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or  `stop` index which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 1, 2, 3, 4];
         * ArrayExt.reverse(data, 1, 3);  // [0, 3, 2, 1, 4]
         * ArrayExt.reverse(data, 3);     // [0, 3, 2, 4, 1]
         * ArrayExt.reverse(data);        // [1, 4, 2, 3, 0]
         * ```
         */
        function reverse(array, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n <= 1) {
                return;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            while (start < stop) {
                var a = array[start];
                var b = array[stop];
                array[start++] = b;
                array[stop--] = a;
            }
        }
        ArrayExt.reverse = reverse;
        /**
         * Rotate the elements of an array in-place.
         *
         * @param array - The mutable array-like object of interest.
         *
         * @param delta - The amount of rotation to apply to the elements. A
         *   positive value will rotate the elements to the left. A negative
         *   value will rotate the elements to the right.
         *
         * @param start - The index of the first element in the range to be
         *   rotated, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   rotated, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `delta`, `start`, or `stop` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 1, 2, 3, 4];
         * ArrayExt.rotate(data, 2);        // [2, 3, 4, 0, 1]
         * ArrayExt.rotate(data, -2);       // [0, 1, 2, 3, 4]
         * ArrayExt.rotate(data, 10);       // [0, 1, 2, 3, 4]
         * ArrayExt.rotate(data, 9);        // [4, 0, 1, 2, 3]
         * ArrayExt.rotate(data, 2, 1, 3);  // [4, 2, 0, 1, 3]
         * ```
         */
        function rotate(array, delta, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n <= 1) {
                return;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            if (start >= stop) {
                return;
            }
            var length = stop - start + 1;
            if (delta > 0) {
                delta = delta % length;
            }
            else if (delta < 0) {
                delta = ((delta % length) + length) % length;
            }
            if (delta === 0) {
                return;
            }
            var pivot = start + delta;
            reverse(array, start, pivot - 1);
            reverse(array, pivot, stop);
            reverse(array, start, stop);
        }
        ArrayExt.rotate = rotate;
        /**
         * Fill an array with a static value.
         *
         * @param array - The mutable array-like object to fill.
         *
         * @param value - The static value to use to fill the array.
         *
         * @param start - The index of the first element in the range to be
         *   filled, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   filled, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * #### Notes
         * If `stop < start` the fill will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * A `start` or `stop` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = ['one', 'two', 'three', 'four'];
         * ArrayExt.fill(data, 'r');        // ['r', 'r', 'r', 'r']
         * ArrayExt.fill(data, 'g', 1);     // ['r', 'g', 'g', 'g']
         * ArrayExt.fill(data, 'b', 2, 3);  // ['r', 'g', 'b', 'b']
         * ArrayExt.fill(data, 'z', 3, 1);  // ['z', 'z', 'b', 'z']
         * ```
         */
        function fill(array, value, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var span;
            if (stop < start) {
                span = stop + 1 + (n - start);
            }
            else {
                span = stop - start + 1;
            }
            for (var i = 0; i < span; ++i) {
                array[(start + i) % n] = value;
            }
        }
        ArrayExt.fill = fill;
        /**
         * Insert a value into an array at a specific index.
         *
         * @param array - The array of interest.
         *
         * @param index - The index at which to insert the value. Negative
         *   values are taken as an offset from the end of the array.
         *
         * @param value - The value to set at the specified index.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * An `index` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 1, 2];
         * ArrayExt.insert(data, 0, -1);  // [-1, 0, 1, 2]
         * ArrayExt.insert(data, 2, 12);  // [-1, 0, 12, 1, 2]
         * ArrayExt.insert(data, -1, 7);  // [-1, 0, 12, 1, 7, 2]
         * ArrayExt.insert(data, 6, 19);  // [-1, 0, 12, 1, 7, 2, 19]
         * ```
         */
        function insert(array, index, value) {
            var n = array.length;
            if (index < 0) {
                index = Math.max(0, index + n);
            }
            else {
                index = Math.min(index, n);
            }
            for (var i = n; i > index; --i) {
                array[i] = array[i - 1];
            }
            array[index] = value;
        }
        ArrayExt.insert = insert;
        /**
         * Remove and return a value at a specific index in an array.
         *
         * @param array - The array of interest.
         *
         * @param index - The index of the value to remove. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The value at the specified index, or `undefined` if the
         *   index is out of range.
         *
         * #### Complexity
         * Linear.
         *
         * #### Undefined Behavior
         * An `index` which is non-integral.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 12, 23, 39, 14, 12, 75];
         * ArrayExt.removeAt(data, 2);   // 23
         * ArrayExt.removeAt(data, -2);  // 12
         * ArrayExt.removeAt(data, 10);  // undefined;
         * ```
         */
        function removeAt(array, index) {
            var n = array.length;
            if (index < 0) {
                index += n;
            }
            if (index < 0 || index >= n) {
                return undefined;
            }
            var value = array[index];
            for (var i = index + 1; i < n; ++i) {
                array[i - 1] = array[i];
            }
            array.length = n - 1;
            return value;
        }
        ArrayExt.removeAt = removeAt;
        /**
         * Remove the first occurrence of a value from an array.
         *
         * @param array - The array of interest.
         *
         * @param value - The value to remove from the array. Values are
         *   compared using strict `===` equality.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the removed value, or `-1` if the value
         *   is not contained in the array.
         *
         * #### Notes
         * If `stop < start` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 12, 23, 39, 14, 12, 75];
         * ArrayExt.removeFirstOf(data, 12);        // 1
         * ArrayExt.removeFirstOf(data, 17);        // -1
         * ArrayExt.removeFirstOf(data, 39, 3);     // -1
         * ArrayExt.removeFirstOf(data, 39, 3, 2);  // 2
         * ```
         */
        function removeFirstOf(array, value, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var index = firstIndexOf(array, value, start, stop);
            if (index !== -1) {
                removeAt(array, index);
            }
            return index;
        }
        ArrayExt.removeFirstOf = removeFirstOf;
        /**
         * Remove the last occurrence of a value from an array.
         *
         * @param array - The array of interest.
         *
         * @param value - The value to remove from the array. Values are
         *   compared using strict `===` equality.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The index of the removed value, or `-1` if the value
         *   is not contained in the array.
         *
         * #### Notes
         * If `start < stop` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [0, 12, 23, 39, 14, 12, 75];
         * ArrayExt.removeLastOf(data, 12);        // 5
         * ArrayExt.removeLastOf(data, 17);        // -1
         * ArrayExt.removeLastOf(data, 39, 2);     // -1
         * ArrayExt.removeLastOf(data, 39, 2, 3);  // 3
         * ```
         */
        function removeLastOf(array, value, start, stop) {
            if (start === void 0) { start = -1; }
            if (stop === void 0) { stop = 0; }
            var index = lastIndexOf(array, value, start, stop);
            if (index !== -1) {
                removeAt(array, index);
            }
            return index;
        }
        ArrayExt.removeLastOf = removeLastOf;
        /**
         * Remove all occurrences of a value from an array.
         *
         * @param array - The array of interest.
         *
         * @param value - The value to remove from the array. Values are
         *   compared using strict `===` equality.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The number of elements removed from the array.
         *
         * #### Notes
         * If `stop < start` the search will conceptually wrap at the end of
         * the array, however the array will be traversed front-to-back.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * let data = [14, 12, 23, 39, 14, 12, 19, 14];
         * ArrayExt.removeAllOf(data, 12);        // 2
         * ArrayExt.removeAllOf(data, 17);        // 0
         * ArrayExt.removeAllOf(data, 14, 1, 4);  // 1
         * ```
         */
        function removeAllOf(array, value, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return 0;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var count = 0;
            for (var i = 0; i < n; ++i) {
                if (start <= stop && i >= start && i <= stop && array[i] === value) {
                    count++;
                }
                else if (stop < start &&
                    (i <= stop || i >= start) &&
                    array[i] === value) {
                    count++;
                }
                else if (count > 0) {
                    array[i - count] = array[i];
                }
            }
            if (count > 0) {
                array.length = n - count;
            }
            return count;
        }
        ArrayExt.removeAllOf = removeAllOf;
        /**
         * Remove the first occurrence of a value which matches a predicate.
         *
         * @param array - The array of interest.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The removed `{ index, value }`, which will be `-1` and
         *   `undefined` if the value is not contained in the array.
         *
         * #### Notes
         * If `stop < start` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [0, 12, 23, 39, 14, 12, 75];
         * ArrayExt.removeFirstWhere(data, isEven);     // { index: 0, value: 0 }
         * ArrayExt.removeFirstWhere(data, isEven, 2);  // { index: 3, value: 14 }
         * ArrayExt.removeFirstWhere(data, isEven, 4);  // { index: -1, value: undefined }
         * ```
         */
        function removeFirstWhere(array, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var value;
            var index = findFirstIndex(array, fn, start, stop);
            if (index !== -1) {
                value = removeAt(array, index);
            }
            return { index: index, value: value };
        }
        ArrayExt.removeFirstWhere = removeFirstWhere;
        /**
         * Remove the last occurrence of a value which matches a predicate.
         *
         * @param array - The array of interest.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The removed `{ index, value }`, which will be `-1` and
         *   `undefined` if the value is not contained in the array.
         *
         * #### Notes
         * If `start < stop` the search will wrap at the end of the array.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * let data = [0, 12, 23, 39, 14, 12, 75];
         * ArrayExt.removeLastWhere(data, isEven);        // { index: 5, value: 12 }
         * ArrayExt.removeLastWhere(data, isEven, 2);     // { index: 1, value: 12 }
         * ArrayExt.removeLastWhere(data, isEven, 2, 1);  // { index: -1, value: undefined }
         * ```
         */
        function removeLastWhere(array, fn, start, stop) {
            if (start === void 0) { start = -1; }
            if (stop === void 0) { stop = 0; }
            var value;
            var index = findLastIndex(array, fn, start, stop);
            if (index !== -1) {
                value = removeAt(array, index);
            }
            return { index: index, value: value };
        }
        ArrayExt.removeLastWhere = removeLastWhere;
        /**
         * Remove all occurrences of values which match a predicate.
         *
         * @param array - The array of interest.
         *
         * @param fn - The predicate function to apply to the values.
         *
         * @param start - The index of the first element in the range to be
         *   searched, inclusive. The default value is `0`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @param stop - The index of the last element in the range to be
         *   searched, inclusive. The default value is `-1`. Negative values
         *   are taken as an offset from the end of the array.
         *
         * @returns The number of elements removed from the array.
         *
         * #### Notes
         * If `stop < start` the search will conceptually wrap at the end of
         * the array, however the array will be traversed front-to-back.
         *
         * #### Complexity
         * Linear.
         *
         * #### Example
         * ```typescript
         * import { ArrayExt } from '@lumino/algorithm';
         *
         * function isEven(value: number): boolean {
         *   return value % 2 === 0;
         * }
         *
         * function isNegative(value: number): boolean {
         *   return value < 0;
         * }
         *
         * let data = [0, 12, -13, -9, 23, 39, 14, -15, 12, 75];
         * ArrayExt.removeAllWhere(data, isEven);            // 4
         * ArrayExt.removeAllWhere(data, isNegative, 0, 3);  // 2
         * ```
         */
        function removeAllWhere(array, fn, start, stop) {
            if (start === void 0) { start = 0; }
            if (stop === void 0) { stop = -1; }
            var n = array.length;
            if (n === 0) {
                return 0;
            }
            if (start < 0) {
                start = Math.max(0, start + n);
            }
            else {
                start = Math.min(start, n - 1);
            }
            if (stop < 0) {
                stop = Math.max(0, stop + n);
            }
            else {
                stop = Math.min(stop, n - 1);
            }
            var count = 0;
            for (var i = 0; i < n; ++i) {
                if (start <= stop && i >= start && i <= stop && fn(array[i], i)) {
                    count++;
                }
                else if (stop < start && (i <= stop || i >= start) && fn(array[i], i)) {
                    count++;
                }
                else if (count > 0) {
                    array[i - count] = array[i];
                }
            }
            if (count > 0) {
                array.length = n - count;
            }
            return count;
        }
        ArrayExt.removeAllWhere = removeAllWhere;
    })(ArrayExt || (ArrayExt = {}));

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * Create an iterator for an iterable object.
     *
     * @param object - The iterable or array-like object of interest.
     *
     * @returns A new iterator for the given object.
     *
     * #### Notes
     * This function allows iteration algorithms to operate on user-defined
     * iterable types and builtin array-like objects in a uniform fashion.
     */
    function iter(object) {
        var it;
        if (typeof object.iter === 'function') {
            it = object.iter();
        }
        else {
            it = new ArrayIterator(object);
        }
        return it;
    }
    /**
     * Invoke a function for each value in an iterable.
     *
     * @param object - The iterable or array-like object of interest.
     *
     * @param fn - The callback function to invoke for each value.
     *
     * #### Notes
     * Iteration can be terminated early by returning `false` from the
     * callback function.
     *
     * #### Complexity
     * Linear.
     *
     * #### Example
     * ```typescript
     * import { each } from '@lumino/algorithm';
     *
     * let data = [5, 7, 0, -2, 9];
     *
     * each(data, value => { console.log(value); });
     * ```
     */
    function each(object, fn) {
        var index = 0;
        var it = iter(object);
        var value;
        while ((value = it.next()) !== undefined) {
            if (fn(value, index++) === false) {
                return;
            }
        }
    }
    /**
     * An iterator for an array-like object.
     *
     * #### Notes
     * This iterator can be used for any builtin JS array-like object.
     */
    var ArrayIterator = /** @class */ (function () {
        /**
         * Construct a new array iterator.
         *
         * @param source - The array-like object of interest.
         */
        function ArrayIterator(source) {
            this._index = 0;
            this._source = source;
        }
        /**
         * Get an iterator over the object's values.
         *
         * @returns An iterator which yields the object's values.
         */
        ArrayIterator.prototype.iter = function () {
            return this;
        };
        /**
         * Create an independent clone of the iterator.
         *
         * @returns A new independent clone of the iterator.
         */
        ArrayIterator.prototype.clone = function () {
            var result = new ArrayIterator(this._source);
            result._index = this._index;
            return result;
        };
        /**
         * Get the next value from the iterator.
         *
         * @returns The next value from the iterator, or `undefined`.
         */
        ArrayIterator.prototype.next = function () {
            if (this._index >= this._source.length) {
                return undefined;
            }
            return this._source[this._index++];
        };
        return ArrayIterator;
    }());
    /**
     * Find the index of the first value which matches a predicate.
     *
     * @param object - The iterable or array-like object to search.
     *
     * @param fn - The predicate function to apply to the values.
     *
     * @returns The index of the first matching value, or `-1` if no
     *   matching value is found.
     *
     * #### Complexity
     * Linear.
     *
     * #### Example
     * ```typescript
     * import { findIndex } from '@lumino/algorithm';
     *
     * interface IAnimal { species: string, name: string };
     *
     * function isCat(value: IAnimal): boolean {
     *   return value.species === 'cat';
     * }
     *
     * let data: IAnimal[] = [
     *   { species: 'dog', name: 'spot' },
     *   { species: 'cat', name: 'fluffy' },
     *   { species: 'alligator', name: 'pocho' }
     * ];
     *
     * findIndex(data, isCat);  // 1
     * ```
     */
    function findIndex(object, fn) {
        var index = 0;
        var it = iter(object);
        var value;
        while ((value = it.next()) !== undefined) {
            if (fn(value, index++)) {
                return index - 1;
            }
        }
        return -1;
    }
    /**
     * The namespace for the module implementation details.
     */
    var Private$1;
    (function (Private) {
        /**
         * Compute the effective length of a range.
         *
         * @param start - The starting value for the range, inclusive.
         *
         * @param stop - The stopping value for the range, exclusive.
         *
         * @param step - The distance between each value.
         *
         * @returns The number of steps need to traverse the range.
         */
        function rangeLength(start, stop, step) {
            if (step === 0) {
                return Infinity;
            }
            if (start > stop && step > 0) {
                return 0;
            }
            if (start < stop && step < 0) {
                return 0;
            }
            return Math.ceil((stop - start) / step);
        }
        Private.rangeLength = rangeLength;
    })(Private$1 || (Private$1 = {}));

    // Copyright (c) Jupyter Development Team.
    /**
     * Topologically sort an iterable of edges.
     *
     * @param edges - The iterable or array-like object of edges to sort.
     *   An edge is represented as a 2-tuple of `[fromNode, toNode]`.
     *
     * @returns The topologically sorted array of nodes.
     *
     * #### Notes
     * If a cycle is present in the graph, the cycle will be ignored and
     * the return value will be only approximately sorted.
     *
     * #### Example
     * ```typescript
     * import { topologicSort } from '@lumino/algorithm';
     *
     * let data = [
     *   ['d', 'e'],
     *   ['c', 'd'],
     *   ['a', 'b'],
     *   ['b', 'c']
     * ];
     *
     * topologicSort(data);  // ['a', 'b', 'c', 'd', 'e']
     * ```
     */
    function topologicSort(edges) {
        // Setup the shared sorting state.
        var sorted = [];
        var visited = new Set();
        var graph = new Map();
        // Add the edges to the graph.
        each(edges, addEdge);
        // Visit each node in the graph.
        graph.forEach(function (v, k) {
            visit(k);
        });
        // Return the sorted results.
        return sorted;
        // Add an edge to the graph.
        function addEdge(edge) {
            var fromNode = edge[0], toNode = edge[1];
            var children = graph.get(toNode);
            if (children) {
                children.push(fromNode);
            }
            else {
                graph.set(toNode, [fromNode]);
            }
        }
        // Recursively visit the node.
        function visit(node) {
            if (visited.has(node)) {
                return;
            }
            visited.add(node);
            var children = graph.get(node);
            if (children) {
                children.forEach(visit);
            }
            sorted.push(node);
        }
    }

    // Copyright (c) Jupyter Development Team.
    // Distributed under the terms of the Modified BSD License.
    /*-----------------------------------------------------------------------------
    | Copyright (c) 2014-2017, PhosphorJS Contributors
    |
    | Distributed under the terms of the BSD 3-Clause License.
    |
    | The full license is in the file LICENSE, distributed with this software.
    |----------------------------------------------------------------------------*/
    /**
     * The namespace for string-specific algorithms.
     */
    var StringExt;
    (function (StringExt) {
        /**
         * Find the indices of characters in a source text.
         *
         * @param source - The source text which should be searched.
         *
         * @param query - The characters to locate in the source text.
         *
         * @param start - The index to start the search.
         *
         * @returns The matched indices, or `null` if there is no match.
         *
         * #### Complexity
         * Linear on `sourceText`.
         *
         * #### Notes
         * In order for there to be a match, all of the characters in `query`
         * **must** appear in `source` in the order given by `query`.
         *
         * Characters are matched using strict `===` equality.
         */
        function findIndices(source, query, start) {
            if (start === void 0) { start = 0; }
            var indices = new Array(query.length);
            for (var i = 0, j = start, n = query.length; i < n; ++i, ++j) {
                j = source.indexOf(query[i], j);
                if (j === -1) {
                    return null;
                }
                indices[i] = j;
            }
            return indices;
        }
        StringExt.findIndices = findIndices;
        /**
         * A string matcher which uses a sum-of-squares algorithm.
         *
         * @param source - The source text which should be searched.
         *
         * @param query - The characters to locate in the source text.
         *
         * @param start - The index to start the search.
         *
         * @returns The match result, or `null` if there is no match.
         *   A lower `score` represents a stronger match.
         *
         * #### Complexity
         * Linear on `sourceText`.
         *
         * #### Notes
         * This scoring algorithm uses a sum-of-squares approach to determine
         * the score. In order for there to be a match, all of the characters
         * in `query` **must** appear in `source` in order. The index of each
         * matching character is squared and added to the score. This means
         * that early and consecutive character matches are preferred, while
         * late matches are heavily penalized.
         */
        function matchSumOfSquares(source, query, start) {
            if (start === void 0) { start = 0; }
            var indices = findIndices(source, query, start);
            if (!indices) {
                return null;
            }
            var score = 0;
            for (var i = 0, n = indices.length; i < n; ++i) {
                var j = indices[i] - start;
                score += j * j;
            }
            return { score: score, indices: indices };
        }
        StringExt.matchSumOfSquares = matchSumOfSquares;
        /**
         * A string matcher which uses a sum-of-deltas algorithm.
         *
         * @param source - The source text which should be searched.
         *
         * @param query - The characters to locate in the source text.
         *
         * @param start - The index to start the search.
         *
         * @returns The match result, or `null` if there is no match.
         *   A lower `score` represents a stronger match.
         *
         * #### Complexity
         * Linear on `sourceText`.
         *
         * #### Notes
         * This scoring algorithm uses a sum-of-deltas approach to determine
         * the score. In order for there to be a match, all of the characters
         * in `query` **must** appear in `source` in order. The delta between
         * the indices are summed to create the score. This means that groups
         * of matched characters are preferred, while fragmented matches are
         * penalized.
         */
        function matchSumOfDeltas(source, query, start) {
            if (start === void 0) { start = 0; }
            var indices = findIndices(source, query, start);
            if (!indices) {
                return null;
            }
            var score = 0;
            var last = start - 1;
            for (var i = 0, n = indices.length; i < n; ++i) {
                var j = indices[i];
                score += j - last - 1;
                last = j;
            }
            return { score: score, indices: indices };
        }
        StringExt.matchSumOfDeltas = matchSumOfDeltas;
        /**
         * Highlight the matched characters of a source text.
         *
         * @param source - The text which should be highlighted.
         *
         * @param indices - The indices of the matched characters. They must
         *   appear in increasing order and must be in bounds of the source.
         *
         * @param fn - The function to apply to the matched chunks.
         *
         * @returns An array of unmatched and highlighted chunks.
         */
        function highlight(source, indices, fn) {
            // Set up the result array.
            var result = [];
            // Set up the counter variables.
            var k = 0;
            var last = 0;
            var n = indices.length;
            // Iterator over each index.
            while (k < n) {
                // Set up the chunk indices.
                var i = indices[k];
                var j = indices[k];
                // Advance the right chunk index until it's non-contiguous.
                while (++k < n && indices[k] === j + 1) {
                    j++;
                }
                // Extract the unmatched text.
                if (last < i) {
                    result.push(source.slice(last, i));
                }
                // Extract and highlight the matched text.
                if (i < j + 1) {
                    result.push(fn(source.slice(i, j + 1)));
                }
                // Update the last visited index.
                last = j + 1;
            }
            // Extract any remaining unmatched text.
            if (last < source.length) {
                result.push(source.slice(last));
            }
            // Return the highlighted result.
            return result;
        }
        StringExt.highlight = highlight;
        /**
         * A 3-way string comparison function.
         *
         * @param a - The first string of interest.
         *
         * @param b - The second string of interest.
         *
         * @returns `-1` if `a < b`, else `1` if `a > b`, else `0`.
         */
        function cmp(a, b) {
            return a < b ? -1 : a > b ? 1 : 0;
        }
        StringExt.cmp = cmp;
    })(StringExt || (StringExt = {}));

    /**
     * A class for creating pluggable applications.
     *
     * @typeparam T - The type of the application shell.
     *
     * #### Notes
     * The `Application` class is useful when creating large, complex
     * UI applications with the ability to be safely extended by third
     * party code via plugins.
     */
    var Application = /** @class */ (function () {
        /**
         * Construct a new application.
         *
         * @param options - The options for creating the application.
         */
        function Application(options) {
            this._started = false;
            this._pluginMap = Private.createPluginMap();
            this._serviceMap = Private.createServiceMap();
            this._delegate = new coreutils.PromiseDelegate();
            // Initialize the application state.
            this.commands = new commands.CommandRegistry();
            this.contextMenu = new widgets.ContextMenu({
                commands: this.commands,
                renderer: options.contextMenuRenderer
            });
            this.shell = options.shell;
        }
        Object.defineProperty(Application.prototype, "started", {
            /**
             * A promise which resolves after the application has started.
             *
             * #### Notes
             * This promise will resolve after the `start()` method is called,
             * when all the bootstrapping and shell mounting work is complete.
             */
            get: function () {
                return this._delegate.promise;
            },
            enumerable: true,
            configurable: true
        });
        /**
         * Get a plugin description.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @returns The plugin description.
         */
        Application.prototype.getPluginDescription = function (id) {
            var plugin = this._pluginMap[id];
            return plugin ? plugin.description || '' : '';
        };
        /**
         * Test whether a plugin is registered with the application.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @returns `true` if the plugin is registered, `false` otherwise.
         */
        Application.prototype.hasPlugin = function (id) {
            return id in this._pluginMap;
        };
        /**
         * Test whether a plugin is activated with the application.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @returns `true` if the plugin is activated, `false` otherwise.
         */
        Application.prototype.isPluginActivated = function (id) {
            var plugin = this._pluginMap[id];
            return plugin ? plugin.activated : false;
        };
        /**
         * List the IDs of the plugins registered with the application.
         *
         * @returns A new array of the registered plugin IDs.
         */
        Application.prototype.listPlugins = function () {
            return Object.keys(this._pluginMap);
        };
        /**
         * Register a plugin with the application.
         *
         * @param plugin - The plugin to register.
         *
         * #### Notes
         * An error will be thrown if a plugin with the same ID is already
         * registered, or if the plugin has a circular dependency.
         *
         * If the plugin provides a service which has already been provided
         * by another plugin, the new service will override the old service.
         */
        Application.prototype.registerPlugin = function (plugin) {
            // Throw an error if the plugin id is already registered.
            if (plugin.id in this._pluginMap) {
                throw new Error("Plugin '" + plugin.id + "' is already registered.");
            }
            // Create the normalized plugin data.
            var data = Private.createPluginData(plugin);
            // Ensure the plugin does not cause a cyclic dependency.
            Private.ensureNoCycle(data, this._pluginMap, this._serviceMap);
            // Add the service token to the service map.
            if (data.provides) {
                this._serviceMap.set(data.provides, data.id);
            }
            // Add the plugin to the plugin map.
            this._pluginMap[data.id] = data;
        };
        /**
         * Register multiple plugins with the application.
         *
         * @param plugins - The plugins to register.
         *
         * #### Notes
         * This calls `registerPlugin()` for each of the given plugins.
         */
        Application.prototype.registerPlugins = function (plugins) {
            for (var _i = 0, plugins_1 = plugins; _i < plugins_1.length; _i++) {
                var plugin = plugins_1[_i];
                this.registerPlugin(plugin);
            }
        };
        /**
         * Deregister a plugin with the application.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @param force - Whether to deregister the plugin even if it is active.
         */
        Application.prototype.deregisterPlugin = function (id, force) {
            var plugin = this._pluginMap[id];
            if (!plugin) {
                return;
            }
            if (plugin.activated && !force) {
                throw new Error("Plugin '" + id + "' is still active.");
            }
            delete this._pluginMap[id];
        };
        /**
         * Activate the plugin with the given ID.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @returns A promise which resolves when the plugin is activated
         *   or rejects with an error if it cannot be activated.
         */
        Application.prototype.activatePlugin = function (id) {
            return __awaiter(this, void 0, void 0, function () {
                var plugin, required, optional;
                var _this = this;
                return __generator(this, function (_a) {
                    plugin = this._pluginMap[id];
                    if (!plugin) {
                        return [2 /*return*/, Promise.reject(new Error("Plugin '" + id + "' is not registered."))];
                    }
                    // Resolve immediately if the plugin is already activated.
                    if (plugin.activated) {
                        return [2 /*return*/];
                    }
                    // Return the pending resolver promise if it exists.
                    if (plugin.promise) {
                        return [2 /*return*/, plugin.promise];
                    }
                    required = plugin.requires.map(function (t) { return _this.resolveRequiredService(t); });
                    optional = plugin.optional.map(function (t) { return _this.resolveOptionalService(t); });
                    // Setup the resolver promise for the plugin.
                    plugin.promise = Promise.all(__spreadArrays(required, optional))
                        .then(function (services) { return plugin.activate.apply(undefined, __spreadArrays([_this], services)); })
                        .then(function (service) {
                        plugin.service = service;
                        plugin.activated = true;
                        plugin.promise = null;
                    })
                        .catch(function (error) {
                        plugin.promise = null;
                        throw error;
                    });
                    // Return the pending resolver promise.
                    return [2 /*return*/, plugin.promise];
                });
            });
        };
        /**
         * Deactivate the plugin and its downstream dependents if and only if the
         * plugin and its dependents all support `deactivate`.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @returns A list of IDs of downstream plugins deactivated with this one.
         */
        Application.prototype.deactivatePlugin = function (id) {
            return __awaiter(this, void 0, void 0, function () {
                var plugin, manifest, downstream, _i, downstream_1, plugin_1, _a, downstream_2, plugin_2, services;
                var _this = this;
                return __generator(this, function (_b) {
                    switch (_b.label) {
                        case 0:
                            plugin = this._pluginMap[id];
                            if (!plugin) {
                                throw new ReferenceError("Plugin '" + id + "' is not registered.");
                            }
                            // Bail early if the plugin is not activated.
                            if (!plugin.activated) {
                                return [2 /*return*/, []];
                            }
                            // Check that this plugin can deactivate.
                            if (!plugin.deactivate) {
                                throw new TypeError("Plugin '" + id + "'#deactivate() method missing");
                            }
                            manifest = Private.findDependents(id, this._pluginMap, this._serviceMap);
                            downstream = manifest.map(function (id) { return _this._pluginMap[id]; });
                            // Check that all downstream plugins can deactivate.
                            for (_i = 0, downstream_1 = downstream; _i < downstream_1.length; _i++) {
                                plugin_1 = downstream_1[_i];
                                if (!plugin_1.deactivate) {
                                    throw new TypeError("Plugin " + plugin_1.id + "#deactivate() method missing (depends on " + id + ")");
                                }
                            }
                            _a = 0, downstream_2 = downstream;
                            _b.label = 1;
                        case 1:
                            if (!(_a < downstream_2.length)) return [3 /*break*/, 4];
                            plugin_2 = downstream_2[_a];
                            services = __spreadArrays(plugin_2.requires, plugin_2.optional).map(function (service) {
                                var id = _this._serviceMap.get(service);
                                return id ? _this._pluginMap[id].service : null;
                            });
                            // Await deactivation so the next plugins only receive active services.
                            return [4 /*yield*/, plugin_2.deactivate.apply(plugin_2, __spreadArrays([this], services))];
                        case 2:
                            // Await deactivation so the next plugins only receive active services.
                            _b.sent();
                            plugin_2.service = null;
                            plugin_2.activated = false;
                            _b.label = 3;
                        case 3:
                            _a++;
                            return [3 /*break*/, 1];
                        case 4:
                            // Remove plugin ID and return manifest of deactivated plugins.
                            manifest.pop();
                            return [2 /*return*/, manifest];
                    }
                });
            });
        };
        /**
         * Resolve a required service of a given type.
         *
         * @param token - The token for the service type of interest.
         *
         * @returns A promise which resolves to an instance of the requested
         *   service, or rejects with an error if it cannot be resolved.
         *
         * #### Notes
         * Services are singletons. The same instance will be returned each
         * time a given service token is resolved.
         *
         * If the plugin which provides the service has not been activated,
         * resolving the service will automatically activate the plugin.
         *
         * User code will not typically call this method directly. Instead,
         * the required services for the user's plugins will be resolved
         * automatically when the plugin is activated.
         */
        Application.prototype.resolveRequiredService = function (token) {
            return __awaiter(this, void 0, void 0, function () {
                var id, plugin;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            id = this._serviceMap.get(token);
                            if (!id) {
                                return [2 /*return*/, Promise.reject(new Error("No provider for: " + token.name + "."))];
                            }
                            plugin = this._pluginMap[id];
                            if (!!plugin.activated) return [3 /*break*/, 2];
                            return [4 /*yield*/, this.activatePlugin(id)];
                        case 1:
                            _a.sent();
                            _a.label = 2;
                        case 2: return [2 /*return*/, plugin.service];
                    }
                });
            });
        };
        /**
         * Resolve an optional service of a given type.
         *
         * @param token - The token for the service type of interest.
         *
         * @returns A promise which resolves to an instance of the requested
         *   service, or `null` if it cannot be resolved.
         *
         * #### Notes
         * Services are singletons. The same instance will be returned each
         * time a given service token is resolved.
         *
         * If the plugin which provides the service has not been activated,
         * resolving the service will automatically activate the plugin.
         *
         * User code will not typically call this method directly. Instead,
         * the optional services for the user's plugins will be resolved
         * automatically when the plugin is activated.
         */
        Application.prototype.resolveOptionalService = function (token) {
            return __awaiter(this, void 0, void 0, function () {
                var id, plugin, reason_1;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            id = this._serviceMap.get(token);
                            if (!id) {
                                return [2 /*return*/, null];
                            }
                            plugin = this._pluginMap[id];
                            if (!!plugin.activated) return [3 /*break*/, 4];
                            _a.label = 1;
                        case 1:
                            _a.trys.push([1, 3, , 4]);
                            return [4 /*yield*/, this.activatePlugin(id)];
                        case 2:
                            _a.sent();
                            return [3 /*break*/, 4];
                        case 3:
                            reason_1 = _a.sent();
                            console.error(reason_1);
                            return [2 /*return*/, null];
                        case 4: return [2 /*return*/, plugin.service];
                    }
                });
            });
        };
        /**
         * Start the application.
         *
         * @param options - The options for starting the application.
         *
         * @returns A promise which resolves when all bootstrapping work
         *   is complete and the shell is mounted to the DOM.
         *
         * #### Notes
         * This should be called once by the application creator after all
         * initial plugins have been registered.
         *
         * If a plugin fails to the load, the error will be logged and the
         * other valid plugins will continue to be loaded.
         *
         * Bootstrapping the application consists of the following steps:
         * 1. Activate the startup plugins
         * 2. Wait for those plugins to activate
         * 3. Attach the shell widget to the DOM
         * 4. Add the application event listeners
         */
        Application.prototype.start = function (options) {
            var _this = this;
            if (options === void 0) { options = {}; }
            // Return immediately if the application is already started.
            if (this._started) {
                return this._delegate.promise;
            }
            // Mark the application as started;
            this._started = true;
            // Parse the host ID for attaching the shell.
            var hostID = options.hostID || '';
            // Collect the ids of the startup plugins.
            var startups = Private.collectStartupPlugins(this._pluginMap, options);
            // Generate the activation promises.
            var promises = startups.map(function (id) {
                return _this.activatePlugin(id).catch(function (error) {
                    console.error("Plugin '" + id + "' failed to activate.");
                    console.error(error);
                });
            });
            // Wait for the plugins to activate, then finalize startup.
            Promise.all(promises).then(function () {
                _this.attachShell(hostID);
                _this.addEventListeners();
                _this._delegate.resolve(undefined);
            });
            // Return the pending delegate promise.
            return this._delegate.promise;
        };
        /**
         * Handle the DOM events for the application.
         *
         * @param event - The DOM event sent to the application.
         *
         * #### Notes
         * This method implements the DOM `EventListener` interface and is
         * called in response to events registered for the application. It
         * should not be called directly by user code.
         */
        Application.prototype.handleEvent = function (event) {
            switch (event.type) {
                case 'resize':
                    this.evtResize(event);
                    break;
                case 'keydown':
                    this.evtKeydown(event);
                    break;
                case 'contextmenu':
                    this.evtContextMenu(event);
                    break;
            }
        };
        /**
         * Attach the application shell to the DOM.
         *
         * @param id - The ID of the host node for the shell, or `''`.
         *
         * #### Notes
         * If the ID is not provided, the document body will be the host.
         *
         * A subclass may reimplement this method as needed.
         */
        Application.prototype.attachShell = function (id) {
            widgets.Widget.attach(this.shell, (id && document.getElementById(id)) || document.body);
        };
        /**
         * Add the application event listeners.
         *
         * #### Notes
         * The default implementation of this method adds listeners for
         * `'keydown'` and `'resize'` events.
         *
         * A subclass may reimplement this method as needed.
         */
        Application.prototype.addEventListeners = function () {
            document.addEventListener('contextmenu', this);
            document.addEventListener('keydown', this, true);
            window.addEventListener('resize', this);
        };
        /**
         * A method invoked on a document `'keydown'` event.
         *
         * #### Notes
         * The default implementation of this method invokes the key down
         * processing method of the application command registry.
         *
         * A subclass may reimplement this method as needed.
         */
        Application.prototype.evtKeydown = function (event) {
            this.commands.processKeydownEvent(event);
        };
        /**
         * A method invoked on a document `'contextmenu'` event.
         *
         * #### Notes
         * The default implementation of this method opens the application
         * `contextMenu` at the current mouse position.
         *
         * If the application context menu has no matching content *or* if
         * the shift key is pressed, the default browser context menu will
         * be opened instead.
         *
         * A subclass may reimplement this method as needed.
         */
        Application.prototype.evtContextMenu = function (event) {
            if (event.shiftKey) {
                return;
            }
            if (this.contextMenu.open(event)) {
                event.preventDefault();
                event.stopPropagation();
            }
        };
        /**
         * A method invoked on a window `'resize'` event.
         *
         * #### Notes
         * The default implementation of this method updates the shell.
         *
         * A subclass may reimplement this method as needed.
         */
        Application.prototype.evtResize = function (event) {
            this.shell.update();
        };
        return Application;
    }());
    /**
     * The namespace for the module implementation details.
     */
    var Private;
    (function (Private) {
        /**
         * Create a new plugin map.
         */
        function createPluginMap() {
            return Object.create(null);
        }
        Private.createPluginMap = createPluginMap;
        /**
         * Create a new service map.
         */
        function createServiceMap() {
            return new Map();
        }
        Private.createServiceMap = createServiceMap;
        /**
         * Create a normalized plugin data object for the given plugin.
         */
        function createPluginData(plugin) {
            return {
                id: plugin.id,
                description: plugin.description || '',
                service: null,
                promise: null,
                activated: false,
                activate: plugin.activate,
                deactivate: plugin.deactivate || null,
                provides: plugin.provides || null,
                autoStart: plugin.autoStart || false,
                requires: plugin.requires ? plugin.requires.slice() : [],
                optional: plugin.optional ? plugin.optional.slice() : []
            };
        }
        Private.createPluginData = createPluginData;
        /**
         * Ensure no cycle is present in the plugin resolution graph.
         *
         * If a cycle is detected, an error will be thrown.
         */
        function ensureNoCycle(plugin, plugins, services) {
            var dependencies = __spreadArrays(plugin.requires, plugin.optional);
            var visit = function (token) {
                if (token === plugin.provides) {
                    return true;
                }
                var id = services.get(token);
                if (!id) {
                    return false;
                }
                var visited = plugins[id];
                var dependencies = __spreadArrays(visited.requires, visited.optional);
                if (dependencies.length === 0) {
                    return false;
                }
                trace.push(id);
                if (dependencies.some(visit)) {
                    return true;
                }
                trace.pop();
                return false;
            };
            // Bail early if there cannot be a cycle.
            if (!plugin.provides || dependencies.length === 0) {
                return;
            }
            // Setup a stack to trace service resolution.
            var trace = [plugin.id];
            // Throw an exception if a cycle is present.
            if (dependencies.some(visit)) {
                throw new Error("Cycle detected: " + trace.join(' -> ') + ".");
            }
        }
        Private.ensureNoCycle = ensureNoCycle;
        /**
         * Find dependents in deactivation order.
         *
         * @param id - The ID of the plugin of interest.
         *
         * @param plugins - The map containing all plugins.
         *
         * @param services - The map containing all services.
         *
         * @returns A list of dependent plugin IDs in order of deactivation
         *
         * #### Notes
         * The final item of the returned list is always the plugin of interest.
         */
        function findDependents(id, plugins, services) {
            var edges = new Array();
            var add = function (id) {
                var plugin = plugins[id];
                // FIXME In the case of missing optional dependencies, we may consider
                // deactivating and reactivating the plugin without the missing service.
                var dependencies = __spreadArrays(plugin.requires, plugin.optional);
                edges.push.apply(edges, dependencies.reduce(function (acc, dep) {
                    var service = services.get(dep);
                    if (service) {
                        // An edge is oriented from dependent to provider.
                        acc.push([id, service]);
                    }
                    return acc;
                }, []));
            };
            for (var id_1 in plugins) {
                add(id_1);
            }
            // Filter edges
            // - Get all packages that dependent on the package to be deactivated
            var newEdges = edges.filter(function (edge) { return edge[1] === id; });
            var oldSize = 0;
            while (newEdges.length > oldSize) {
                var previousSize = newEdges.length;
                // Get all packages that dependent on packages that will be deactivated
                var packagesOfInterest = newEdges
                    .map(function (edge) { return edge[0]; })
                    .reduce(function (agg, value) {
                    if (agg.indexOf(value) == -1) {
                        agg.push(value);
                    }
                    return agg;
                }, []);
                var _loop_1 = function (poi) {
                    edges
                        .filter(function (edge) { return edge[1] === poi; })
                        .forEach(function (edge) {
                        // We check it is not already included to deal with circular dependencies
                        if (newEdges.indexOf(edge) == -1) {
                            newEdges.push(edge);
                        }
                    });
                };
                for (var _i = 0, packagesOfInterest_1 = packagesOfInterest; _i < packagesOfInterest_1.length; _i++) {
                    var poi = packagesOfInterest_1[_i];
                    _loop_1(poi);
                }
                oldSize = previousSize;
            }
            var sorted = topologicSort(newEdges);
            var index = findIndex(sorted, function (candidate) { return candidate === id; });
            if (index === -1) {
                return [id];
            }
            return sorted.slice(0, index + 1);
        }
        Private.findDependents = findDependents;
        /**
         * Collect the IDs of the plugins to activate on startup.
         */
        function collectStartupPlugins(pluginMap, options) {
            // Create a map to hold the plugin IDs.
            var collection = new Map();
            // Collect the auto-start plugins.
            for (var id in pluginMap) {
                if (pluginMap[id].autoStart) {
                    collection.set(id, true);
                }
            }
            // Add the startup plugins.
            if (options.startPlugins) {
                for (var _i = 0, _a = options.startPlugins; _i < _a.length; _i++) {
                    var id = _a[_i];
                    collection.set(id, true);
                }
            }
            // Remove the ignored plugins.
            if (options.ignorePlugins) {
                for (var _b = 0, _c = options.ignorePlugins; _b < _c.length; _b++) {
                    var id = _c[_b];
                    collection.delete(id);
                }
            }
            // Return the collected startup plugins.
            return Array.from(collection.keys());
        }
        Private.collectStartupPlugins = collectStartupPlugins;
    })(Private || (Private = {}));

    exports.Application = Application;

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

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