"use strict";
/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
(self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] = self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] || []).push([["vendors-node_modules_jupyterlab_debugger_lib_panels_variables_gridpanel_js"],{

/***/ "../node_modules/@jupyterlab/debugger/lib/panels/variables/gridpanel.js":
/*!******************************************************************************!*\
  !*** ../node_modules/@jupyterlab/debugger/lib/panels/variables/gridpanel.js ***!
  \******************************************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   Grid: () => (/* binding */ Grid),\n/* harmony export */   GridModel: () => (/* binding */ GridModel)\n/* harmony export */ });\n/* harmony import */ var _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lumino/datagrid */ \"../node_modules/@lumino/datagrid/dist/index.es6.js\");\n/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lumino/signaling */ \"webpack/sharing/consume/default/@lumino/signaling/@lumino/signaling\");\n/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_lumino_signaling__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lumino/widgets */ \"webpack/sharing/consume/default/@lumino/widgets/@lumino/widgets\");\n/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_lumino_widgets__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _jupyterlab_translation__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @jupyterlab/translation */ \"webpack/sharing/consume/default/@jupyterlab/translation/@jupyterlab/translation\");\n/* harmony import */ var _jupyterlab_translation__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_jupyterlab_translation__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var ___WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../ */ \"../node_modules/@jupyterlab/debugger/lib/debugger.js\");\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n\n\n\n\n\n/**\n * A class wrapping the underlying variables datagrid.\n */\nclass Grid extends _lumino_widgets__WEBPACK_IMPORTED_MODULE_2__.Panel {\n    /**\n     * Instantiate a new VariablesGrid.\n     *\n     * @param options The instantiation options for a VariablesGrid.\n     */\n    constructor(options) {\n        super();\n        const { commands, model, themeManager } = options;\n        this.model = model;\n        const dataModel = new GridModel(options.translator);\n        const grid = new _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.DataGrid();\n        const mouseHandler = new Private.MouseHandler();\n        mouseHandler.doubleClicked.connect((_, hit) => commands.execute(___WEBPACK_IMPORTED_MODULE_4__.Debugger.CommandIDs.inspectVariable, {\n            variableReference: dataModel.getVariableReference(hit.row),\n            name: dataModel.getVariableName(hit.row)\n        }));\n        mouseHandler.selected.connect((_, hit) => {\n            const { row } = hit;\n            this.model.selectedVariable = {\n                name: dataModel.getVariableName(row),\n                value: dataModel.data('body', row, 1),\n                type: dataModel.data('body', row, 2),\n                variablesReference: dataModel.getVariableReference(row)\n            };\n        });\n        grid.dataModel = dataModel;\n        grid.keyHandler = new _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.BasicKeyHandler();\n        grid.mouseHandler = mouseHandler;\n        grid.selectionModel = new _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.BasicSelectionModel({\n            dataModel\n        });\n        grid.stretchLastColumn = true;\n        grid.node.style.height = '100%';\n        this._grid = grid;\n        // Compute the grid's styles based on the current theme.\n        if (themeManager) {\n            themeManager.themeChanged.connect(this._updateStyles, this);\n        }\n        this.addWidget(grid);\n    }\n    /**\n     * Set the variable filter list.\n     *\n     * @param filter The variable filter to apply.\n     */\n    set filter(filter) {\n        this._grid.dataModel.filter = filter;\n        this.update();\n    }\n    /**\n     * Set the scope for the variables data model.\n     *\n     * @param scope The scopes for the variables\n     */\n    set scope(scope) {\n        this._grid.dataModel.scope = scope;\n        this.update();\n    }\n    /**\n     * Get the data model for the data grid.\n     */\n    get dataModel() {\n        return this._grid.dataModel;\n    }\n    /**\n     * Handle `after-attach` messages.\n     *\n     * @param message - The `after-attach` message.\n     */\n    onAfterAttach(message) {\n        super.onAfterAttach(message);\n        this._updateStyles();\n    }\n    /**\n     * Update the computed style for the data grid on theme change.\n     */\n    _updateStyles() {\n        const { style, textRenderer } = Private.computeStyle();\n        this._grid.cellRenderers.update({}, textRenderer);\n        this._grid.style = style;\n    }\n}\n/**\n * A data grid model for variables.\n */\nclass GridModel extends _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.DataModel {\n    /**\n     * Create gird model\n     * @param translator optional translator\n     */\n    constructor(translator) {\n        super();\n        this._filter = new Set();\n        this._scope = '';\n        this._data = {\n            name: [],\n            type: [],\n            value: [],\n            variablesReference: []\n        };\n        this._trans = (translator || _jupyterlab_translation__WEBPACK_IMPORTED_MODULE_3__.nullTranslator).load('jupyterlab');\n    }\n    /**\n     * The variable filter list.\n     */\n    get filter() {\n        return this._filter;\n    }\n    set filter(filter) {\n        this._filter = filter;\n    }\n    /**\n     * The current scope for the variables.\n     */\n    get scope() {\n        return this._scope;\n    }\n    set scope(scope) {\n        this._scope = scope;\n    }\n    /**\n     * Get the row count for a particular region in the data grid.\n     *\n     * @param region The datagrid region.\n     */\n    rowCount(region) {\n        return region === 'body' ? this._data.name.length : 1;\n    }\n    /**\n     * Get the column count for a particular region in the data grid.\n     *\n     * @param region The datagrid region.\n     */\n    columnCount(region) {\n        return region === 'body' ? 2 : 1;\n    }\n    /**\n     * Get the data count for a particular region, row and column in the data grid.\n     *\n     * @param region The datagrid region.\n     * @param row The datagrid row\n     * @param column The datagrid column\n     */\n    data(region, row, column) {\n        if (region === 'row-header') {\n            return this._data.name[row];\n        }\n        if (region === 'column-header') {\n            return column === 1 ? this._trans.__('Value') : this._trans.__('Type');\n        }\n        if (region === 'corner-header') {\n            return this._trans.__('Name');\n        }\n        return column === 1 ? this._data.value[row] : this._data.type[row];\n    }\n    /**\n     * Get the variable reference for a given row\n     *\n     * @param row The row in the datagrid.\n     */\n    getVariableReference(row) {\n        return this._data.variablesReference[row];\n    }\n    /**\n     * Get the variable name for a given row\n     *\n     * @param row The row in the datagrid.\n     */\n    getVariableName(row) {\n        return this._data.name[row];\n    }\n    /**\n     * Set the datagrid model data from the list of variables.\n     *\n     * @param scopes The list of variables.\n     */\n    setData(scopes) {\n        var _a, _b;\n        this._clearData();\n        this.emitChanged({\n            type: 'model-reset'\n        });\n        const scope = (_a = scopes.find(scope => scope.name === this._scope)) !== null && _a !== void 0 ? _a : scopes[0];\n        const variables = (_b = scope === null || scope === void 0 ? void 0 : scope.variables) !== null && _b !== void 0 ? _b : [];\n        const filtered = variables.filter(variable => variable.name && !this._filter.has(variable.name));\n        filtered.forEach((variable, index) => {\n            var _a;\n            this._data.name[index] = variable.name;\n            this._data.type[index] = (_a = variable.type) !== null && _a !== void 0 ? _a : '';\n            this._data.value[index] = variable.value;\n            this._data.variablesReference[index] = variable.variablesReference;\n        });\n        this.emitChanged({\n            type: 'rows-inserted',\n            region: 'body',\n            index: 1,\n            span: filtered.length\n        });\n    }\n    /**\n     * Clear all the data in the datagrid.\n     */\n    _clearData() {\n        this._data = {\n            name: [],\n            type: [],\n            value: [],\n            variablesReference: []\n        };\n    }\n}\n/**\n * A namespace for private data.\n */\nvar Private;\n(function (Private) {\n    /**\n     * Create a color palette element.\n     */\n    function createPalette() {\n        const div = document.createElement('div');\n        div.className = 'jp-DebuggerVariables-colorPalette';\n        div.innerHTML = `\n        <div class=\"jp-mod-void\"></div>\n        <div class=\"jp-mod-background\"></div>\n        <div class=\"jp-mod-header-background\"></div>\n        <div class=\"jp-mod-grid-line\"></div>\n        <div class=\"jp-mod-header-grid-line\"></div>\n        <div class=\"jp-mod-selection\"></div>\n        <div class=\"jp-mod-text\"></div>\n      `;\n        return div;\n    }\n    /**\n     * Compute the style and renderer for a data grid.\n     */\n    function computeStyle() {\n        const palette = createPalette();\n        document.body.appendChild(palette);\n        let node;\n        node = palette.querySelector('.jp-mod-void');\n        const voidColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-background');\n        const backgroundColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-header-background');\n        const headerBackgroundColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-grid-line');\n        const gridLineColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-header-grid-line');\n        const headerGridLineColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-selection');\n        const selectionFillColor = getComputedStyle(node).color;\n        node = palette.querySelector('.jp-mod-text');\n        const textColor = getComputedStyle(node).color;\n        document.body.removeChild(palette);\n        return {\n            style: {\n                voidColor,\n                backgroundColor,\n                headerBackgroundColor,\n                gridLineColor,\n                headerGridLineColor,\n                rowBackgroundColor: (i) => i % 2 === 0 ? voidColor : backgroundColor,\n                selectionFillColor\n            },\n            textRenderer: new _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.TextRenderer({\n                font: '12px sans-serif',\n                textColor,\n                backgroundColor: '',\n                verticalAlignment: 'center',\n                horizontalAlignment: 'left'\n            })\n        };\n    }\n    Private.computeStyle = computeStyle;\n    /**\n     * A custom click handler to handle clicks on the variables grid.\n     */\n    class MouseHandler extends _lumino_datagrid__WEBPACK_IMPORTED_MODULE_0__.BasicMouseHandler {\n        constructor() {\n            super(...arguments);\n            this._doubleClicked = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_1__.Signal(this);\n            this._selected = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_1__.Signal(this);\n        }\n        /**\n         * A signal emitted when the variables grid is double clicked.\n         */\n        get doubleClicked() {\n            return this._doubleClicked;\n        }\n        /**\n         * A signal emitted when the variables grid received mouse down or context menu event.\n         */\n        get selected() {\n            return this._selected;\n        }\n        /**\n         * Dispose of the resources held by the mouse handler.\n         */\n        dispose() {\n            if (this.isDisposed) {\n                return;\n            }\n            _lumino_signaling__WEBPACK_IMPORTED_MODULE_1__.Signal.disconnectSender(this);\n            super.dispose();\n        }\n        /**\n         * Handle a mouse double-click event.\n         *\n         * @param grid The datagrid clicked.\n         * @param event The mouse event.\n         */\n        onMouseDoubleClick(grid, event) {\n            const hit = grid.hitTest(event.clientX, event.clientY);\n            this._doubleClicked.emit(hit);\n        }\n        /**\n         * Handle the mouse down event for the data grid.\n         *\n         * @param grid - The data grid of interest.\n         *\n         * @param event - The mouse down event of interest.\n         */\n        onMouseDown(grid, event) {\n            // Unpack the event.\n            let { clientX, clientY } = event;\n            // Hit test the grid.\n            let hit = grid.hitTest(clientX, clientY);\n            this._selected.emit(hit);\n            // Propagate event to Lumino DataGrid BasicMouseHandler.\n            super.onMouseDown(grid, event);\n        }\n        /**\n         * Handle the context menu event for the data grid.\n         *\n         * @param grid - The data grid of interest.\n         *\n         * @param event - The context menu event of interest.\n         */\n        onContextMenu(grid, event) {\n            // Unpack the event.\n            let { clientX, clientY } = event;\n            // Hit test the grid.\n            let hit = grid.hitTest(clientX, clientY);\n            this._selected.emit(hit);\n        }\n    }\n    Private.MouseHandler = MouseHandler;\n})(Private || (Private = {}));\n//# sourceMappingURL=gridpanel.js.map\n\n//# sourceURL=webpack://_JUPYTERLAB.CORE_OUTPUT/../node_modules/@jupyterlab/debugger/lib/panels/variables/gridpanel.js?");

/***/ }),

/***/ "../node_modules/@lumino/datagrid/dist/index.es6.js":
/*!**********************************************************!*\
  !*** ../node_modules/@lumino/datagrid/dist/index.es6.js ***!
  \**********************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   AsyncCellRenderer: () => (/* binding */ AsyncCellRenderer),\n/* harmony export */   BasicKeyHandler: () => (/* binding */ BasicKeyHandler),\n/* harmony export */   BasicMouseHandler: () => (/* binding */ BasicMouseHandler),\n/* harmony export */   BasicSelectionModel: () => (/* binding */ BasicSelectionModel),\n/* harmony export */   BooleanCellEditor: () => (/* binding */ BooleanCellEditor),\n/* harmony export */   CellEditor: () => (/* binding */ CellEditor),\n/* harmony export */   CellEditorController: () => (/* binding */ CellEditorController),\n/* harmony export */   CellGroup: () => (/* binding */ CellGroup),\n/* harmony export */   CellRenderer: () => (/* binding */ CellRenderer),\n/* harmony export */   DataGrid: () => (/* binding */ DataGrid),\n/* harmony export */   DataModel: () => (/* binding */ DataModel),\n/* harmony export */   DateCellEditor: () => (/* binding */ DateCellEditor),\n/* harmony export */   DynamicOptionCellEditor: () => (/* binding */ DynamicOptionCellEditor),\n/* harmony export */   GraphicsContext: () => (/* binding */ GraphicsContext),\n/* harmony export */   HyperlinkRenderer: () => (/* binding */ HyperlinkRenderer),\n/* harmony export */   ImageRenderer: () => (/* binding */ ImageRenderer),\n/* harmony export */   InputCellEditor: () => (/* binding */ InputCellEditor),\n/* harmony export */   IntegerCellEditor: () => (/* binding */ IntegerCellEditor),\n/* harmony export */   IntegerInputValidator: () => (/* binding */ IntegerInputValidator),\n/* harmony export */   JSONModel: () => (/* binding */ JSONModel),\n/* harmony export */   MutableDataModel: () => (/* binding */ MutableDataModel),\n/* harmony export */   NumberCellEditor: () => (/* binding */ NumberCellEditor),\n/* harmony export */   NumberInputValidator: () => (/* binding */ NumberInputValidator),\n/* harmony export */   OptionCellEditor: () => (/* binding */ OptionCellEditor),\n/* harmony export */   PassInputValidator: () => (/* binding */ PassInputValidator),\n/* harmony export */   RendererMap: () => (/* binding */ RendererMap),\n/* harmony export */   SectionList: () => (/* binding */ SectionList),\n/* harmony export */   SelectionModel: () => (/* binding */ SelectionModel),\n/* harmony export */   TextCellEditor: () => (/* binding */ TextCellEditor),\n/* harmony export */   TextInputValidator: () => (/* binding */ TextInputValidator),\n/* harmony export */   TextRenderer: () => (/* binding */ TextRenderer),\n/* harmony export */   resolveOption: () => (/* binding */ resolveOption)\n/* harmony export */ });\n/* harmony import */ var _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @lumino/domutils */ \"webpack/sharing/consume/default/@lumino/domutils/@lumino/domutils\");\n/* harmony import */ var _lumino_domutils__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @lumino/keyboard */ \"webpack/sharing/consume/default/@lumino/keyboard/@lumino/keyboard\");\n/* harmony import */ var _lumino_keyboard__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @lumino/dragdrop */ \"webpack/sharing/consume/default/@lumino/dragdrop/@lumino/dragdrop\");\n/* harmony import */ var _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @lumino/algorithm */ \"webpack/sharing/consume/default/@lumino/algorithm/@lumino/algorithm\");\n/* harmony import */ var _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @lumino/signaling */ \"webpack/sharing/consume/default/@lumino/signaling/@lumino/signaling\");\n/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_lumino_signaling__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @lumino/widgets */ \"webpack/sharing/consume/default/@lumino/widgets/@lumino/widgets\");\n/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_lumino_widgets__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @lumino/messaging */ \"webpack/sharing/consume/default/@lumino/messaging/@lumino/messaging\");\n/* harmony import */ var _lumino_messaging__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_lumino_messaging__WEBPACK_IMPORTED_MODULE_6__);\n/* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @lumino/coreutils */ \"webpack/sharing/consume/default/@lumino/coreutils/@lumino/coreutils\");\n/* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(_lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__);\n\n\n\n\n\n\n\n\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * A basic implementation of a data grid key handler.\n *\n * #### Notes\n * This class may be subclassed and customized as needed.\n */\nclass BasicKeyHandler {\n    constructor() {\n        this._disposed = false;\n    }\n    /**\n     * Whether the key handler is disposed.\n     */\n    get isDisposed() {\n        return this._disposed;\n    }\n    /**\n     * Dispose of the resources held by the key handler.\n     */\n    dispose() {\n        this._disposed = true;\n    }\n    /**\n     * Handle the key down event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keydown event of interest.\n     *\n     * #### Notes\n     * This will not be called if the mouse button is pressed.\n     */\n    onKeyDown(grid, event) {\n        // if grid is editable and cell selection available, start cell editing\n        // on key press (letters, numbers and space only)\n        if (grid.editable &&\n            grid.selectionModel.cursorRow !== -1 &&\n            grid.selectionModel.cursorColumn !== -1) {\n            const input = String.fromCharCode(event.keyCode);\n            if (/[a-zA-Z0-9-_ ]/.test(input)) {\n                const row = grid.selectionModel.cursorRow;\n                const column = grid.selectionModel.cursorColumn;\n                const cell = {\n                    grid: grid,\n                    row: row,\n                    column: column\n                };\n                grid.editorController.edit(cell);\n                if ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event) === 'Space') {\n                    event.stopPropagation();\n                    event.preventDefault();\n                }\n                return;\n            }\n        }\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'ArrowLeft':\n                this.onArrowLeft(grid, event);\n                break;\n            case 'ArrowRight':\n                this.onArrowRight(grid, event);\n                break;\n            case 'ArrowUp':\n                this.onArrowUp(grid, event);\n                break;\n            case 'ArrowDown':\n                this.onArrowDown(grid, event);\n                break;\n            case 'PageUp':\n                this.onPageUp(grid, event);\n                break;\n            case 'PageDown':\n                this.onPageDown(grid, event);\n                break;\n            case 'Escape':\n                this.onEscape(grid, event);\n                break;\n            case 'Delete':\n                this.onDelete(grid, event);\n                break;\n            case 'C':\n                this.onKeyC(grid, event);\n                break;\n            case 'Enter':\n                if (grid.selectionModel) {\n                    grid.moveCursor(event.shiftKey ? 'up' : 'down');\n                    grid.scrollToCursor();\n                }\n                break;\n            case 'Tab':\n                if (grid.selectionModel) {\n                    grid.moveCursor(event.shiftKey ? 'left' : 'right');\n                    grid.scrollToCursor();\n                    event.stopPropagation();\n                    event.preventDefault();\n                }\n                break;\n        }\n    }\n    /**\n     * Handle the `'ArrowLeft'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onArrowLeft(grid, event) {\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Fetch the modifier flags.\n        let shift = event.shiftKey;\n        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);\n        // Handle no model with the accel modifier.\n        if (!model && accel) {\n            grid.scrollTo(0, grid.scrollY);\n            return;\n        }\n        // Handle no model and no modifier. (ignore shift)\n        if (!model) {\n            grid.scrollByStep('left');\n            return;\n        }\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Handle the row selection mode with accel key.\n        if (mode === 'row' && accel) {\n            grid.scrollTo(0, grid.scrollY);\n            return;\n        }\n        // Handle the row selection mode with no modifier. (ignore shift)\n        if (mode === 'row') {\n            grid.scrollByStep('left');\n            return;\n        }\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Dispatch based on the modifier keys.\n        if (accel && shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 - 1 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (accel) {\n            r1 = r;\n            r2 = r;\n            c1 = 0;\n            c2 = 0;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        else {\n            r1 = r;\n            r2 = r;\n            c1 = c - 1;\n            c2 = c - 1;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        if (shift || mode === 'column') {\n            grid.scrollToColumn(cs.c2);\n        }\n        else {\n            grid.scrollToCursor();\n        }\n    }\n    /**\n     * Handle the `'ArrowRight'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onArrowRight(grid, event) {\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Fetch the modifier flags.\n        let shift = event.shiftKey;\n        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);\n        // Handle no model with the accel modifier.\n        if (!model && accel) {\n            grid.scrollTo(grid.maxScrollX, grid.scrollY);\n            return;\n        }\n        // Handle no model and no modifier. (ignore shift)\n        if (!model) {\n            grid.scrollByStep('right');\n            return;\n        }\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Handle the row selection model with accel key.\n        if (mode === 'row' && accel) {\n            grid.scrollTo(grid.maxScrollX, grid.scrollY);\n            return;\n        }\n        // Handle the row selection mode with no modifier. (ignore shift)\n        if (mode === 'row') {\n            grid.scrollByStep('right');\n            return;\n        }\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Dispatch based on the modifier keys.\n        if (accel && shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = Infinity;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 + 1 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (accel) {\n            r1 = r;\n            r2 = r;\n            c1 = Infinity;\n            c2 = Infinity;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        else {\n            r1 = r;\n            r2 = r;\n            c1 = c + 1;\n            c2 = c + 1;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        if (shift || mode === 'column') {\n            grid.scrollToColumn(cs.c2);\n        }\n        else {\n            grid.scrollToCursor();\n        }\n    }\n    /**\n     * Handle the `'ArrowUp'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onArrowUp(grid, event) {\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Fetch the modifier flags.\n        let shift = event.shiftKey;\n        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);\n        // Handle no model with the accel modifier.\n        if (!model && accel) {\n            grid.scrollTo(grid.scrollX, 0);\n            return;\n        }\n        // Handle no model and no modifier. (ignore shift)\n        if (!model) {\n            grid.scrollByStep('up');\n            return;\n        }\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Handle the column selection mode with accel key.\n        if (mode === 'column' && accel) {\n            grid.scrollTo(grid.scrollX, 0);\n            return;\n        }\n        // Handle the column selection mode with no modifier. (ignore shift)\n        if (mode === 'column') {\n            grid.scrollByStep('up');\n            return;\n        }\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Dispatch based on the modifier keys.\n        if (accel && shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 - 1 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (accel) {\n            r1 = 0;\n            r2 = 0;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        else {\n            r1 = r - 1;\n            r2 = r - 1;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        if (shift || mode === 'row') {\n            grid.scrollToRow(cs.r2);\n        }\n        else {\n            grid.scrollToCursor();\n        }\n    }\n    /**\n     * Handle the `'ArrowDown'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onArrowDown(grid, event) {\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Fetch the modifier flags.\n        let shift = event.shiftKey;\n        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);\n        // Handle no model with the accel modifier.\n        if (!model && accel) {\n            grid.scrollTo(grid.scrollX, grid.maxScrollY);\n            return;\n        }\n        // Handle no model and no modifier. (ignore shift)\n        if (!model) {\n            grid.scrollByStep('down');\n            return;\n        }\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Handle the column selection mode with accel key.\n        if (mode === 'column' && accel) {\n            grid.scrollTo(grid.scrollX, grid.maxScrollY);\n            return;\n        }\n        // Handle the column selection mode with no modifier. (ignore shift)\n        if (mode === 'column') {\n            grid.scrollByStep('down');\n            return;\n        }\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Dispatch based on the modifier keys.\n        if (accel && shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = Infinity;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (shift) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 + 1 : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else if (accel) {\n            r1 = Infinity;\n            r2 = Infinity;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        else {\n            r1 = r + 1;\n            r2 = r + 1;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c1;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        if (shift || mode === 'row') {\n            grid.scrollToRow(cs.r2);\n        }\n        else {\n            grid.scrollToCursor();\n        }\n    }\n    /**\n     * Handle the `'PageUp'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onPageUp(grid, event) {\n        // Ignore the event if the accel key is pressed.\n        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {\n            return;\n        }\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Scroll by page if there is no selection model.\n        if (!model || model.selectionMode === 'column') {\n            grid.scrollByPage('up');\n            return;\n        }\n        // Get the normal number of cells in the page height.\n        let n = Math.floor(grid.pageHeight / grid.defaultSizes.rowHeight);\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Select or resize as needed.\n        if (event.shiftKey) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 - n : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else {\n            r1 = cs ? cs.r1 - n : 0;\n            r2 = r1;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        grid.scrollToRow(cs.r2);\n    }\n    /**\n     * Handle the `'PageDown'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onPageDown(grid, event) {\n        // Ignore the event if the accel key is pressed.\n        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {\n            return;\n        }\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Scroll by page if there is no selection model.\n        if (!model || model.selectionMode === 'column') {\n            grid.scrollByPage('down');\n            return;\n        }\n        // Get the normal number of cells in the page height.\n        let n = Math.floor(grid.pageHeight / grid.defaultSizes.rowHeight);\n        // Fetch the cursor and selection.\n        let r = model.cursorRow;\n        let c = model.cursorColumn;\n        let cs = model.currentSelection();\n        // Set up the selection variables.\n        let r1;\n        let r2;\n        let c1;\n        let c2;\n        let cr;\n        let cc;\n        let clear;\n        // Select or resize as needed.\n        if (event.shiftKey) {\n            r1 = cs ? cs.r1 : 0;\n            r2 = cs ? cs.r2 + n : 0;\n            c1 = cs ? cs.c1 : 0;\n            c2 = cs ? cs.c2 : 0;\n            cr = r;\n            cc = c;\n            clear = 'current';\n        }\n        else {\n            r1 = cs ? cs.r1 + n : 0;\n            r2 = r1;\n            c1 = c;\n            c2 = c;\n            cr = r1;\n            cc = c;\n            clear = 'all';\n        }\n        // Create the new selection.\n        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid appropriately.\n        grid.scrollToRow(cs.r2);\n    }\n    /**\n     * Handle the `'Escape'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onEscape(grid, event) {\n        if (grid.selectionModel) {\n            grid.selectionModel.clear();\n        }\n    }\n    /**\n     * Handle the `'Delete'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onDelete(grid, event) {\n        if (grid.editable && !grid.selectionModel.isEmpty) {\n            const dataModel = grid.dataModel;\n            // Fetch the max row and column.\n            let maxRow = dataModel.rowCount('body') - 1;\n            let maxColumn = dataModel.columnCount('body') - 1;\n            for (let s of grid.selectionModel.selections()) {\n                // Clamp the cell to the model bounds.\n                let sr1 = Math.max(0, Math.min(s.r1, maxRow));\n                let sc1 = Math.max(0, Math.min(s.c1, maxColumn));\n                let sr2 = Math.max(0, Math.min(s.r2, maxRow));\n                let sc2 = Math.max(0, Math.min(s.c2, maxColumn));\n                for (let r = sr1; r <= sr2; ++r) {\n                    for (let c = sc1; c <= sc2; ++c) {\n                        dataModel.setData('body', r, c, null);\n                    }\n                }\n            }\n        }\n    }\n    /**\n     * Handle the `'C'` key press for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The keyboard event of interest.\n     */\n    onKeyC(grid, event) {\n        // Bail early if the modifiers aren't correct for copy.\n        if (event.shiftKey || !_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {\n            return;\n        }\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Copy the current selection to the clipboard.\n        grid.copyToClipboard();\n    }\n}\n\n/**\n * An object which renders the cells of a data grid.\n *\n * #### Notes\n * If the predefined cell renderers are insufficient for a particular\n * use case, a custom cell renderer can be defined which derives from\n * this class.\n *\n * The data grid renders cells in column-major order, by region. The\n * region order is: body, row header, column header, corner header.\n */\nclass CellRenderer {\n}\n/**\n * The namespace for the `CellRenderer` class statics.\n */\n(function (CellRenderer) {\n    /**\n     * Resolve a config option for a cell renderer.\n     *\n     * @param option - The config option to resolve.\n     *\n     * @param config - The cell config object.\n     *\n     * @returns The resolved value for the option.\n     */\n    function resolveOption(option, config) {\n        return typeof option === 'function'\n            ? option(config)\n            : option;\n    }\n    CellRenderer.resolveOption = resolveOption;\n})(CellRenderer || (CellRenderer = {}));\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * A cell renderer which renders data values as text.\n */\nclass TextRenderer extends CellRenderer {\n    /**\n     * Construct a new text renderer.\n     *\n     * @param options - The options for initializing the renderer.\n     */\n    constructor(options = {}) {\n        super();\n        this.font = options.font || '12px sans-serif';\n        this.textColor = options.textColor || '#000000';\n        this.backgroundColor = options.backgroundColor || '';\n        this.verticalAlignment = options.verticalAlignment || 'center';\n        this.horizontalAlignment = options.horizontalAlignment || 'left';\n        this.horizontalPadding = options.horizontalPadding || 8;\n        this.format = options.format || TextRenderer.formatGeneric();\n        this.elideDirection = options.elideDirection || 'none';\n        this.wrapText = options.wrapText || false;\n    }\n    /**\n     * Paint the content for a cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    paint(gc, config) {\n        this.drawBackground(gc, config);\n        this.drawText(gc, config);\n    }\n    /**\n     * Draw the background for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawBackground(gc, config) {\n        // Resolve the background color for the cell.\n        let color = CellRenderer.resolveOption(this.backgroundColor, config);\n        // Bail if there is no background color to draw.\n        if (!color) {\n            return;\n        }\n        // Fill the cell with the background color.\n        gc.fillStyle = color;\n        gc.fillRect(config.x, config.y, config.width, config.height);\n    }\n    /**\n     * Get the full text to be rendered by the cell.\n     */\n    getText(config) {\n        return this.format(config);\n    }\n    /**\n     * Draw the text for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawText(gc, config) {\n        // Resolve the font for the cell.\n        let font = CellRenderer.resolveOption(this.font, config);\n        // Bail if there is no font to draw.\n        if (!font) {\n            return;\n        }\n        // Resolve the text color for the cell.\n        let color = CellRenderer.resolveOption(this.textColor, config);\n        // Bail if there is no text color to draw.\n        if (!color) {\n            return;\n        }\n        // Format the cell value to text.\n        let text = this.getText(config);\n        // Bail if there is no text to draw.\n        if (!text) {\n            return;\n        }\n        // Resolve the vertical and horizontal alignment.\n        let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config);\n        let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config);\n        // Resolve the elision direction\n        let elideDirection = CellRenderer.resolveOption(this.elideDirection, config);\n        // Resolve the text wrapping flag\n        let wrapText = CellRenderer.resolveOption(this.wrapText, config);\n        // Compute the padded text box height for the specified alignment.\n        let boxHeight = config.height - (vAlign === 'center' ? 1 : 2);\n        // Bail if the text box has no effective size.\n        if (boxHeight <= 0) {\n            return;\n        }\n        // Compute the text height for the gc font.\n        let textHeight = TextRenderer.measureFontHeight(font);\n        // Set up the text position variables.\n        let textX;\n        let textY;\n        let boxWidth;\n        // Compute the Y position for the text.\n        switch (vAlign) {\n            case 'top':\n                textY = config.y + 2 + textHeight;\n                break;\n            case 'center':\n                textY = config.y + config.height / 2 + textHeight / 2;\n                break;\n            case 'bottom':\n                textY = config.y + config.height - 2;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Compute the X position for the text.\n        switch (hAlign) {\n            case 'left':\n                textX = config.x + this.horizontalPadding;\n                boxWidth = config.width - 14;\n                break;\n            case 'center':\n                textX = config.x + config.width / 2;\n                boxWidth = config.width;\n                break;\n            case 'right':\n                textX = config.x + config.width - this.horizontalPadding;\n                boxWidth = config.width - 14;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Clip the cell if the text is taller than the text box height.\n        if (textHeight > boxHeight) {\n            gc.beginPath();\n            gc.rect(config.x, config.y, config.width, config.height - 1);\n            gc.clip();\n        }\n        // Set the gc state.\n        gc.font = font;\n        gc.fillStyle = color;\n        gc.textAlign = hAlign;\n        gc.textBaseline = 'bottom';\n        // Terminate call here if we're not eliding or wrapping text\n        if (elideDirection === 'none' && !wrapText) {\n            gc.fillText(text, textX, textY);\n            return;\n        }\n        // The current text width in pixels.\n        let textWidth = gc.measureText(text).width;\n        // Apply text wrapping if enabled.\n        if (wrapText && textWidth > boxWidth) {\n            // Make sure box clipping happens.\n            gc.beginPath();\n            gc.rect(config.x, config.y, config.width, config.height - 1);\n            gc.clip();\n            // Split column name to words based on\n            // whitespace preceding a word boundary.\n            // \"Hello  world\" --> [\"Hello  \", \"world\"]\n            const wordsInColumn = text.split(/\\s(?=\\b)/);\n            // Y-coordinate offset for any additional lines\n            let curY = textY;\n            let textInCurrentLine = wordsInColumn.shift();\n            // Single word. Applying text wrap on word by splitting\n            // it into characters and fitting the maximum number of\n            // characters possible per line (box width).\n            if (wordsInColumn.length === 0) {\n                let curLineTextWidth = gc.measureText(textInCurrentLine).width;\n                while (curLineTextWidth > boxWidth && textInCurrentLine !== '') {\n                    // Iterating from the end of the string until we find a\n                    // substring (0,i) which has a width less than the box width.\n                    for (let i = textInCurrentLine.length; i > 0; i--) {\n                        const curSubString = textInCurrentLine.substring(0, i);\n                        const curSubStringWidth = gc.measureText(curSubString).width;\n                        if (curSubStringWidth < boxWidth || curSubString.length === 1) {\n                            // Found a substring which has a width less than the current\n                            // box width. Rendering that substring on the current line\n                            // and setting the remainder of the parent string as the next\n                            // string to iterate on for the next line.\n                            const nextLineText = textInCurrentLine.substring(i, textInCurrentLine.length);\n                            textInCurrentLine = nextLineText;\n                            curLineTextWidth = gc.measureText(textInCurrentLine).width;\n                            gc.fillText(curSubString, textX, curY);\n                            curY += textHeight;\n                            // No need to continue iterating after we identified\n                            // an index to break the string on.\n                            break;\n                        }\n                    }\n                }\n            }\n            // Multiple words in column header. Fitting maximum\n            // number of words possible per line (box width).\n            else {\n                while (wordsInColumn.length !== 0) {\n                    // Processing the next word in the queue.\n                    const curWord = wordsInColumn.shift();\n                    // Joining that word with the existing text for\n                    // the current line.\n                    const incrementedText = [textInCurrentLine, curWord].join(' ');\n                    const incrementedTextWidth = gc.measureText(incrementedText).width;\n                    if (incrementedTextWidth > boxWidth) {\n                        // If the newly combined text has a width larger than\n                        // the box width, we render the line before the current\n                        // word was added. We set the current word as the next\n                        // line.\n                        gc.fillText(textInCurrentLine, textX, curY);\n                        curY += textHeight;\n                        textInCurrentLine = curWord;\n                    }\n                    else {\n                        // The combined text hasd a width less than the box width. We\n                        // set the the current line text to be the new combined text.\n                        textInCurrentLine = incrementedText;\n                    }\n                }\n            }\n            gc.fillText(textInCurrentLine, textX, curY);\n            // Terminating the call here as we don't want\n            // to apply text eliding when wrapping is active.\n            return;\n        }\n        // Elide text that is too long\n        const elide = '\\u2026';\n        // Loop until text width fits box or only one character remains\n        while (textWidth > boxWidth && text.length > 1) {\n            // Convert text string to array for dealing with astral symbols\n            const textArr = [...text];\n            if (elideDirection === 'right') {\n                // If text width is substantially bigger, take half the string\n                if (textArr.length > 4 && textWidth >= 2 * boxWidth) {\n                    text =\n                        textArr.slice(0, Math.floor(textArr.length / 2 + 1)).join('') +\n                            elide;\n                }\n                else {\n                    // Otherwise incrementally remove the last character\n                    text = textArr.slice(0, textArr.length - 2).join('') + elide;\n                }\n            }\n            else {\n                // If text width is substantially bigger, take half the string\n                if (textArr.length > 4 && textWidth >= 2 * boxWidth) {\n                    text = elide + textArr.slice(Math.floor(textArr.length / 2)).join('');\n                }\n                else {\n                    // Otherwise incrementally remove the last character\n                    text = elide + textArr.slice(2).join('');\n                }\n            }\n            // Measure new text width\n            textWidth = gc.measureText(text).width;\n        }\n        // Draw the text for the cell.\n        gc.fillText(text, textX, textY);\n    }\n}\n/**\n * The namespace for the `TextRenderer` class statics.\n */\n(function (TextRenderer) {\n    /**\n     * Create a generic text format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new generic text format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `String()` to coerce any value\n     * to a string.\n     */\n    function formatGeneric(options = {}) {\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return String(value);\n        };\n    }\n    TextRenderer.formatGeneric = formatGeneric;\n    /**\n     * Create a fixed decimal format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new fixed decimal format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `Number()` and `toFixed()` to\n     * coerce values.\n     *\n     * The `formatIntlNumber()` formatter is more flexible, but slower.\n     */\n    function formatFixed(options = {}) {\n        let digits = options.digits;\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return Number(value).toFixed(digits);\n        };\n    }\n    TextRenderer.formatFixed = formatFixed;\n    /**\n     * Create a significant figure format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new significant figure format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `Number()` and `toPrecision()`\n     * to coerce values.\n     *\n     * The `formatIntlNumber()` formatter is more flexible, but slower.\n     */\n    function formatPrecision(options = {}) {\n        let digits = options.digits;\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return Number(value).toPrecision(digits);\n        };\n    }\n    TextRenderer.formatPrecision = formatPrecision;\n    /**\n     * Create a scientific notation format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new scientific notation format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `Number()` and `toExponential()`\n     * to coerce values.\n     *\n     * The `formatIntlNumber()` formatter is more flexible, but slower.\n     */\n    function formatExponential(options = {}) {\n        let digits = options.digits;\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return Number(value).toExponential(digits);\n        };\n    }\n    TextRenderer.formatExponential = formatExponential;\n    /**\n     * Create an international number format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new international number format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `Intl.NumberFormat` object to\n     * coerce values.\n     *\n     * This is the most flexible (but slowest) number formatter.\n     */\n    function formatIntlNumber(options = {}) {\n        let missing = options.missing || '';\n        let nft = new Intl.NumberFormat(options.locales, options.options);\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return nft.format(value);\n        };\n    }\n    TextRenderer.formatIntlNumber = formatIntlNumber;\n    /**\n     * Create a date format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new date format function.\n     *\n     * #### Notes\n     * This formatter uses `Date.toDateString()` to format the values.\n     *\n     * If a value is not a `Date` object, `new Date(value)` is used to\n     * coerce the value to a date.\n     *\n     * The `formatIntlDateTime()` formatter is more flexible, but slower.\n     */\n    function formatDate(options = {}) {\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            if (value instanceof Date) {\n                return value.toDateString();\n            }\n            return new Date(value).toDateString();\n        };\n    }\n    TextRenderer.formatDate = formatDate;\n    /**\n     * Create a time format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new time format function.\n     *\n     * #### Notes\n     * This formatter uses `Date.toTimeString()` to format the values.\n     *\n     * If a value is not a `Date` object, `new Date(value)` is used to\n     * coerce the value to a date.\n     *\n     * The `formatIntlDateTime()` formatter is more flexible, but slower.\n     */\n    function formatTime(options = {}) {\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            if (value instanceof Date) {\n                return value.toTimeString();\n            }\n            return new Date(value).toTimeString();\n        };\n    }\n    TextRenderer.formatTime = formatTime;\n    /**\n     * Create an ISO datetime format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new ISO datetime format function.\n     *\n     * #### Notes\n     * This formatter uses `Date.toISOString()` to format the values.\n     *\n     * If a value is not a `Date` object, `new Date(value)` is used to\n     * coerce the value to a date.\n     *\n     * The `formatIntlDateTime()` formatter is more flexible, but slower.\n     */\n    function formatISODateTime(options = {}) {\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            if (value instanceof Date) {\n                return value.toISOString();\n            }\n            return new Date(value).toISOString();\n        };\n    }\n    TextRenderer.formatISODateTime = formatISODateTime;\n    /**\n     * Create a UTC datetime format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new UTC datetime format function.\n     *\n     * #### Notes\n     * This formatter uses `Date.toUTCString()` to format the values.\n     *\n     * If a value is not a `Date` object, `new Date(value)` is used to\n     * coerce the value to a date.\n     *\n     * The `formatIntlDateTime()` formatter is more flexible, but slower.\n     */\n    function formatUTCDateTime(options = {}) {\n        let missing = options.missing || '';\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            if (value instanceof Date) {\n                return value.toUTCString();\n            }\n            return new Date(value).toUTCString();\n        };\n    }\n    TextRenderer.formatUTCDateTime = formatUTCDateTime;\n    /**\n     * Create an international datetime format function.\n     *\n     * @param options - The options for creating the format function.\n     *\n     * @returns A new international datetime format function.\n     *\n     * #### Notes\n     * This formatter uses the builtin `Intl.DateTimeFormat` object to\n     * coerce values.\n     *\n     * This is the most flexible (but slowest) datetime formatter.\n     */\n    function formatIntlDateTime(options = {}) {\n        let missing = options.missing || '';\n        let dtf = new Intl.DateTimeFormat(options.locales, options.options);\n        return ({ value }) => {\n            if (value === null || value === undefined) {\n                return missing;\n            }\n            return dtf.format(value);\n        };\n    }\n    TextRenderer.formatIntlDateTime = formatIntlDateTime;\n    /**\n     * Measure the height of a font.\n     *\n     * @param font - The CSS font string of interest.\n     *\n     * @returns The height of the font bounding box.\n     *\n     * #### Notes\n     * This function uses a temporary DOM node to measure the text box\n     * height for the specified font. The first call for a given font\n     * will incur a DOM reflow, but the return value is cached, so any\n     * subsequent call for the same font will return the cached value.\n     */\n    function measureFontHeight(font) {\n        // Look up the cached font height.\n        let height = Private$6.fontHeightCache[font];\n        // Return the cached font height if it exists.\n        if (height !== undefined) {\n            return height;\n        }\n        // Normalize the font.\n        Private$6.fontMeasurementGC.font = font;\n        let normFont = Private$6.fontMeasurementGC.font;\n        // Set the font on the measurement node.\n        Private$6.fontMeasurementNode.style.font = normFont;\n        // Add the measurement node to the document.\n        document.body.appendChild(Private$6.fontMeasurementNode);\n        // Measure the node height.\n        height = Private$6.fontMeasurementNode.offsetHeight;\n        // Remove the measurement node from the document.\n        document.body.removeChild(Private$6.fontMeasurementNode);\n        // Cache the measured height for the font and norm font.\n        Private$6.fontHeightCache[font] = height;\n        Private$6.fontHeightCache[normFont] = height;\n        // Return the measured height.\n        return height;\n    }\n    TextRenderer.measureFontHeight = measureFontHeight;\n})(TextRenderer || (TextRenderer = {}));\n/**\n * The namespace for the module implementation details.\n */\nvar Private$6;\n(function (Private) {\n    /**\n     * A cache of measured font heights.\n     */\n    Private.fontHeightCache = Object.create(null);\n    /**\n     * The DOM node used for font height measurement.\n     */\n    Private.fontMeasurementNode = (() => {\n        let node = document.createElement('div');\n        node.style.position = 'absolute';\n        node.style.top = '-99999px';\n        node.style.left = '-99999px';\n        node.style.visibility = 'hidden';\n        node.textContent = 'M';\n        return node;\n    })();\n    /**\n     * The GC used for font measurement.\n     */\n    Private.fontMeasurementGC = (() => {\n        let canvas = document.createElement('canvas');\n        canvas.width = 0;\n        canvas.height = 0;\n        return canvas.getContext('2d');\n    })();\n})(Private$6 || (Private$6 = {}));\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * A cell renderer which renders data values as text.\n */\nclass HyperlinkRenderer extends TextRenderer {\n    /**\n     * Construct a new text renderer.\n     *\n     * @param options - The options for initializing the renderer.\n     */\n    constructor(options = {}) {\n        // Set default parameters before passing over the super.\n        options.textColor = options.textColor || 'navy';\n        options.font = options.font || 'bold 12px sans-serif';\n        super(options);\n        this.url = options.url;\n        this.urlName = options.urlName;\n    }\n    /**\n     * Get the full text to be rendered by the cell.\n     */\n    getText(config) {\n        let urlName = CellRenderer.resolveOption(this.urlName, config);\n        // If we have a friendly URL name, use that.\n        if (urlName) {\n            return this.format({\n                ...config,\n                value: urlName\n            });\n        }\n        // Otherwise use the raw value attribute.\n        return this.format(config);\n    }\n    /**\n     * Draw the text for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawText(gc, config) {\n        // Resolve the font for the cell.\n        let font = CellRenderer.resolveOption(this.font, config);\n        // Bail if there is no font to draw.\n        if (!font) {\n            return;\n        }\n        // Resolve the text color for the cell.\n        let color = CellRenderer.resolveOption(this.textColor, config);\n        // Bail if there is no text color to draw.\n        if (!color) {\n            return;\n        }\n        let text = this.getText(config);\n        // Bail if there is no text to draw.\n        if (!text) {\n            return;\n        }\n        // Resolve the vertical and horizontal alignment.\n        let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config);\n        let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config);\n        // Resolve the elision direction\n        let elideDirection = CellRenderer.resolveOption(this.elideDirection, config);\n        // Resolve the text wrapping flag\n        let wrapText = CellRenderer.resolveOption(this.wrapText, config);\n        // Compute the padded text box height for the specified alignment.\n        let boxHeight = config.height - (vAlign === 'center' ? 1 : 2);\n        // Bail if the text box has no effective size.\n        if (boxHeight <= 0) {\n            return;\n        }\n        // Compute the text height for the gc font.\n        let textHeight = HyperlinkRenderer.measureFontHeight(font);\n        // Set up the text position variables.\n        let textX;\n        let textY;\n        let boxWidth;\n        // Compute the Y position for the text.\n        switch (vAlign) {\n            case 'top':\n                textY = config.y + 2 + textHeight;\n                break;\n            case 'center':\n                textY = config.y + config.height / 2 + textHeight / 2;\n                break;\n            case 'bottom':\n                textY = config.y + config.height - 2;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Compute the X position for the text.\n        switch (hAlign) {\n            case 'left':\n                textX = config.x + 8;\n                boxWidth = config.width - 14;\n                break;\n            case 'center':\n                textX = config.x + config.width / 2;\n                boxWidth = config.width;\n                break;\n            case 'right':\n                textX = config.x + config.width - 8;\n                boxWidth = config.width - 14;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Clip the cell if the text is taller than the text box height.\n        if (textHeight > boxHeight) {\n            gc.beginPath();\n            gc.rect(config.x, config.y, config.width, config.height - 1);\n            gc.clip();\n        }\n        // Set the gc state.\n        gc.font = font;\n        gc.fillStyle = color;\n        gc.textAlign = hAlign;\n        gc.textBaseline = 'bottom';\n        // Terminate call here if we're not eliding or wrapping text\n        if (elideDirection === 'none' && !wrapText) {\n            gc.fillText(text, textX, textY);\n            return;\n        }\n        // The current text width in pixels.\n        let textWidth = gc.measureText(text).width;\n        // Apply text wrapping if enabled.\n        if (wrapText && textWidth > boxWidth) {\n            // Make sure box clipping happens.\n            gc.beginPath();\n            gc.rect(config.x, config.y, config.width, config.height - 1);\n            gc.clip();\n            // Split column name to words based on\n            // whitespace preceding a word boundary.\n            // \"Hello  world\" --> [\"Hello  \", \"world\"]\n            const wordsInColumn = text.split(/\\s(?=\\b)/);\n            // Y-coordinate offset for any additional lines\n            let curY = textY;\n            let textInCurrentLine = wordsInColumn.shift();\n            // Single word. Applying text wrap on word by splitting\n            // it into characters and fitting the maximum number of\n            // characters possible per line (box width).\n            if (wordsInColumn.length === 0) {\n                let curLineTextWidth = gc.measureText(textInCurrentLine).width;\n                while (curLineTextWidth > boxWidth && textInCurrentLine !== '') {\n                    // Iterating from the end of the string until we find a\n                    // substring (0,i) which has a width less than the box width.\n                    for (let i = textInCurrentLine.length; i > 0; i--) {\n                        const curSubString = textInCurrentLine.substring(0, i);\n                        const curSubStringWidth = gc.measureText(curSubString).width;\n                        if (curSubStringWidth < boxWidth || curSubString.length === 1) {\n                            // Found a substring which has a width less than the current\n                            // box width. Rendering that substring on the current line\n                            // and setting the remainder of the parent string as the next\n                            // string to iterate on for the next line.\n                            const nextLineText = textInCurrentLine.substring(i, textInCurrentLine.length);\n                            textInCurrentLine = nextLineText;\n                            curLineTextWidth = gc.measureText(textInCurrentLine).width;\n                            gc.fillText(curSubString, textX, curY);\n                            curY += textHeight;\n                            // No need to continue iterating after we identified\n                            // an index to break the string on.\n                            break;\n                        }\n                    }\n                }\n            }\n            // Multiple words in column header. Fitting maximum\n            // number of words possible per line (box width).\n            else {\n                while (wordsInColumn.length !== 0) {\n                    // Processing the next word in the queue.\n                    const curWord = wordsInColumn.shift();\n                    // Joining that word with the existing text for\n                    // the current line.\n                    const incrementedText = [textInCurrentLine, curWord].join(' ');\n                    const incrementedTextWidth = gc.measureText(incrementedText).width;\n                    if (incrementedTextWidth > boxWidth) {\n                        // If the newly combined text has a width larger than\n                        // the box width, we render the line before the current\n                        // word was added. We set the current word as the next\n                        // line.\n                        gc.fillText(textInCurrentLine, textX, curY);\n                        curY += textHeight;\n                        textInCurrentLine = curWord;\n                    }\n                    else {\n                        // The combined text hasd a width less than the box width. We\n                        // set the the current line text to be the new combined text.\n                        textInCurrentLine = incrementedText;\n                    }\n                }\n            }\n            gc.fillText(textInCurrentLine, textX, curY);\n            // Terminating the call here as we don't want\n            // to apply text eliding when wrapping is active.\n            return;\n        }\n        // Elide text that is too long\n        let elide = '\\u2026';\n        // Compute elided text\n        if (elideDirection === 'right') {\n            while (textWidth > boxWidth && text.length > 1) {\n                if (text.length > 4 && textWidth >= 2 * boxWidth) {\n                    // If text width is substantially bigger, take half the string\n                    text = text.substring(0, text.length / 2 + 1) + elide;\n                }\n                else {\n                    // Otherwise incrementally remove the last character\n                    text = text.substring(0, text.length - 2) + elide;\n                }\n                textWidth = gc.measureText(text).width;\n            }\n        }\n        else {\n            while (textWidth > boxWidth && text.length > 1) {\n                if (text.length > 4 && textWidth >= 2 * boxWidth) {\n                    // If text width is substantially bigger, take half the string\n                    text = elide + text.substring(text.length / 2);\n                }\n                else {\n                    // Otherwise incrementally remove the last character\n                    text = elide + text.substring(2);\n                }\n                textWidth = gc.measureText(text).width;\n            }\n        }\n        // Draw the text for the cell.\n        gc.fillText(text, textX, textY);\n    }\n}\n\n/*\n * Copyright (c) Jupyter Development Team.\n * Distributed under the terms of the Modified BSD License.\n */\n/**\n * A collection of helper functions relating to merged cell groups\n */\nvar CellGroup;\n(function (CellGroup) {\n    /**\n     * Checks if two cell-groups are intersecting\n     * in the given axis.\n     * @param group1\n     * @param group2\n     * @param axis\n     */\n    function areCellGroupsIntersectingAtAxis(group1, group2, axis) {\n        if (axis === 'row') {\n            return ((group1.r1 >= group2.r1 && group1.r1 <= group2.r2) ||\n                (group1.r2 >= group2.r1 && group1.r2 <= group2.r2) ||\n                (group2.r1 >= group1.r1 && group2.r1 <= group1.r2) ||\n                (group2.r2 >= group1.r1 && group2.r2 <= group1.r2));\n        }\n        return ((group1.c1 >= group2.c1 && group1.c1 <= group2.c2) ||\n            (group1.c2 >= group2.c1 && group1.c2 <= group2.c2) ||\n            (group2.c1 >= group1.c1 && group2.c1 <= group1.c2) ||\n            (group2.c2 >= group1.c1 && group2.c2 <= group1.c2));\n    }\n    CellGroup.areCellGroupsIntersectingAtAxis = areCellGroupsIntersectingAtAxis;\n    /**\n     * Checks if cell-groups are intersecting.\n     * @param group1\n     * @param group2\n     */\n    function areCellGroupsIntersecting(group1, group2) {\n        return (((group1.r1 >= group2.r1 && group1.r1 <= group2.r2) ||\n            (group1.r2 >= group2.r1 && group1.r2 <= group2.r2) ||\n            (group2.r1 >= group1.r1 && group2.r1 <= group1.r2) ||\n            (group2.r2 >= group1.r1 && group2.r2 <= group1.r2)) &&\n            ((group1.c1 >= group2.c1 && group1.c1 <= group2.c2) ||\n                (group1.c2 >= group2.c1 && group1.c2 <= group2.c2) ||\n                (group2.c1 >= group1.c1 && group2.c1 <= group1.c2) ||\n                (group2.c2 >= group1.c1 && group2.c2 <= group1.c2)));\n    }\n    CellGroup.areCellGroupsIntersecting = areCellGroupsIntersecting;\n    /**\n     * Retrieves the index of the cell-group to which\n     * the cell at the given row, column belongs.\n     * @param dataModel\n     * @param rgn\n     * @param row\n     * @param column\n     */\n    function getGroupIndex(dataModel, rgn, row, column) {\n        const numGroups = dataModel.groupCount(rgn);\n        for (let i = 0; i < numGroups; i++) {\n            const group = dataModel.group(rgn, i);\n            if (row >= group.r1 &&\n                row <= group.r2 &&\n                column >= group.c1 &&\n                column <= group.c2) {\n                return i;\n            }\n        }\n        return -1;\n    }\n    CellGroup.getGroupIndex = getGroupIndex;\n    /**\n     * Returns a cell-group for the given row/index coordinates.\n     * @param dataModel\n     * @param rgn\n     * @param row\n     * @param column\n     */\n    function getGroup(dataModel, rgn, row, column) {\n        const groupIndex = getGroupIndex(dataModel, rgn, row, column);\n        if (groupIndex === -1) {\n            return null;\n        }\n        return dataModel.group(rgn, groupIndex);\n    }\n    CellGroup.getGroup = getGroup;\n    /**\n     * Returns all cell groups which belong to\n     * a given cell cell region.\n     * @param dataModel\n     * @param rgn\n     */\n    function getCellGroupsAtRegion(dataModel, rgn) {\n        let groupsAtRegion = [];\n        const numGroups = dataModel.groupCount(rgn);\n        for (let i = 0; i < numGroups; i++) {\n            const group = dataModel.group(rgn, i);\n            groupsAtRegion.push(group);\n        }\n        return groupsAtRegion;\n    }\n    CellGroup.getCellGroupsAtRegion = getCellGroupsAtRegion;\n    /**\n     * Calculates and returns a merged cell-group from\n     * two cell-group objects.\n     * @param groups\n     */\n    function joinCellGroups(groups) {\n        let startRow = Number.MAX_VALUE;\n        let endRow = Number.MIN_VALUE;\n        let startColumn = Number.MAX_VALUE;\n        let endColumn = Number.MIN_VALUE;\n        for (const group of groups) {\n            startRow = Math.min(startRow, group.r1);\n            endRow = Math.max(endRow, group.r2);\n            startColumn = Math.min(startColumn, group.c1);\n            endColumn = Math.max(endColumn, group.c2);\n        }\n        return { r1: startRow, r2: endRow, c1: startColumn, c2: endColumn };\n    }\n    CellGroup.joinCellGroups = joinCellGroups;\n    /**\n     * Merges a cell group with other cells groups in the\n     * same region if they intersect.\n     * @param dataModel the data model of the grid.\n     * @param group the target cell group.\n     * @param region the region of the cell group.\n     * @returns a new cell group after merging has happened.\n     */\n    function joinCellGroupWithMergedCellGroups(dataModel, group, region) {\n        let joinedGroup = { ...group };\n        const mergedCellGroups = getCellGroupsAtRegion(dataModel, region);\n        for (let g = 0; g < mergedCellGroups.length; g++) {\n            const mergedGroup = mergedCellGroups[g];\n            if (areCellGroupsIntersecting(joinedGroup, mergedGroup)) {\n                joinedGroup = joinCellGroups([joinedGroup, mergedGroup]);\n            }\n        }\n        return joinedGroup;\n    }\n    CellGroup.joinCellGroupWithMergedCellGroups = joinCellGroupWithMergedCellGroups;\n    /**\n     * Retrieves a list of cell groups intersecting at\n     * a given row.\n     * @param dataModel data model of the grid.\n     * @param rgn the cell region.\n     * @param row the target row to look for intersections at.\n     * @returns all cell groups intersecting with the row.\n     */\n    function getCellGroupsAtRow(dataModel, rgn, row) {\n        let groupsAtRow = [];\n        const numGroups = dataModel.groupCount(rgn);\n        for (let i = 0; i < numGroups; i++) {\n            const group = dataModel.group(rgn, i);\n            if (row >= group.r1 && row <= group.r2) {\n                groupsAtRow.push(group);\n            }\n        }\n        return groupsAtRow;\n    }\n    CellGroup.getCellGroupsAtRow = getCellGroupsAtRow;\n    /**\n     * Retrieves a list of cell groups intersecting at\n     * a given column.\n     * @param dataModel data model of the grid.\n     * @param rgn the cell region.\n     * @param column the target column to look for intersections at.\n     * @returns all cell groups intersecting with the column.\n     */\n    function getCellGroupsAtColumn(dataModel, rgn, column) {\n        let groupsAtColumn = [];\n        const numGroups = dataModel.groupCount(rgn);\n        for (let i = 0; i < numGroups; i++) {\n            const group = dataModel.group(rgn, i);\n            if (column >= group.c1 && column <= group.c2) {\n                groupsAtColumn.push(group);\n            }\n        }\n        return groupsAtColumn;\n    }\n    CellGroup.getCellGroupsAtColumn = getCellGroupsAtColumn;\n    /**\n     * Merges a target cell group with any cell groups\n     * it intersects with at a given row or column.\n     * @param dataModel data model of the grid.\n     * @param regions list of cell regions.\n     * @param axis row or column.\n     * @param group the target cell group.\n     * @returns a new merged cell group.\n     */\n    function joinCellGroupsIntersectingAtAxis(dataModel, regions, axis, group) {\n        let groupsAtAxis = [];\n        if (axis === 'row') {\n            for (const region of regions) {\n                for (let r = group.r1; r <= group.r2; r++) {\n                    groupsAtAxis = groupsAtAxis.concat(CellGroup.getCellGroupsAtRow(dataModel, region, r));\n                }\n            }\n        }\n        else {\n            for (const region of regions) {\n                for (let c = group.c1; c <= group.c2; c++) {\n                    groupsAtAxis = groupsAtAxis.concat(CellGroup.getCellGroupsAtColumn(dataModel, region, c));\n                }\n            }\n        }\n        let mergedGroupAtAxis = CellGroup.joinCellGroups(groupsAtAxis);\n        if (groupsAtAxis.length > 0) {\n            let mergedCellGroups = [];\n            for (const region of regions) {\n                mergedCellGroups = mergedCellGroups.concat(CellGroup.getCellGroupsAtRegion(dataModel, region));\n            }\n            for (let g = 0; g < mergedCellGroups.length; g++) {\n                const group = mergedCellGroups[g];\n                if (CellGroup.areCellGroupsIntersectingAtAxis(mergedGroupAtAxis, group, axis)) {\n                    mergedGroupAtAxis = CellGroup.joinCellGroups([\n                        group,\n                        mergedGroupAtAxis\n                    ]);\n                    mergedCellGroups.splice(g, 1);\n                    g = 0;\n                }\n            }\n        }\n        return mergedGroupAtAxis;\n    }\n    CellGroup.joinCellGroupsIntersectingAtAxis = joinCellGroupsIntersectingAtAxis;\n})(CellGroup || (CellGroup = {}));\n\n/**\n * A basic implementation of a data grid mouse handler.\n *\n * #### Notes\n * This class may be subclassed and customized as needed.\n */\nclass BasicMouseHandler {\n    constructor() {\n        this._disposed = false;\n        this._pressData = null;\n    }\n    /**\n     * Dispose of the resources held by the mouse handler.\n     */\n    dispose() {\n        // Bail early if the handler is already disposed.\n        if (this._disposed) {\n            return;\n        }\n        // Release any held resources.\n        this.release();\n        // Mark the handler as disposed.\n        this._disposed = true;\n    }\n    /**\n     * Whether the mouse handler is disposed.\n     */\n    get isDisposed() {\n        return this._disposed;\n    }\n    /**\n     * Release the resources held by the handler.\n     */\n    release() {\n        // Bail early if the is no press data.\n        if (!this._pressData) {\n            return;\n        }\n        // Clear the autoselect timeout.\n        if (this._pressData.type === 'select') {\n            this._pressData.timeout = -1;\n        }\n        // Clear the press data.\n        this._pressData.override.dispose();\n        this._pressData = null;\n    }\n    /**\n     * Handle the mouse hover event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse hover event of interest.\n     */\n    onMouseHover(grid, event) {\n        // Hit test the grid.\n        let hit = grid.hitTest(event.clientX, event.clientY);\n        // Get the resize handle for the hit test.\n        let handle = Private$5.resizeHandleForHitTest(hit);\n        // Fetch the cursor for the handle.\n        let cursor = this.cursorForHandle(handle);\n        // Hyperlink logic.\n        const config = Private$5.createCellConfigObject(grid, hit);\n        if (config) {\n            // Retrieve renderer for hovered cell.\n            const renderer = grid.cellRenderers.get(config);\n            if (renderer instanceof HyperlinkRenderer) {\n                cursor = this.cursorForHandle('hyperlink');\n            }\n        }\n        // Update the viewport cursor based on the part.\n        grid.viewport.node.style.cursor = cursor;\n        // TODO support user-defined hover items\n    }\n    /**\n     * Handle the mouse leave event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse hover event of interest.\n     */\n    onMouseLeave(grid, event) {\n        // TODO support user-defined hover popups.\n        // Clear the viewport cursor.\n        grid.viewport.node.style.cursor = '';\n    }\n    /**\n     * Handle the mouse down event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse down event of interest.\n     */\n    onMouseDown(grid, event) {\n        // Unpack the event.\n        let { clientX, clientY } = event;\n        // Hit test the grid.\n        let hit = grid.hitTest(clientX, clientY);\n        // Unpack the hit test.\n        const { region, row, column } = hit;\n        // Bail if the hit test is on an uninteresting region.\n        if (region === 'void') {\n            return;\n        }\n        // Fetch the modifier flags.\n        let shift = event.shiftKey;\n        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);\n        // Hyperlink logic.\n        if (grid) {\n            // Create cell config object.\n            const config = Private$5.createCellConfigObject(grid, hit);\n            // Retrieve cell renderer.\n            let renderer = grid.cellRenderers.get(config);\n            // Only process hyperlink renderers.\n            if (renderer instanceof HyperlinkRenderer) {\n                // Use the url param if it exists.\n                let url = CellRenderer.resolveOption(renderer.url, config);\n                // Otherwise assume cell value is the URL.\n                if (!url) {\n                    const format = TextRenderer.formatGeneric();\n                    url = format(config);\n                }\n                // Open the hyperlink only if user hit Ctrl+Click.\n                if (accel) {\n                    window.open(url);\n                    // Reset cursor default after clicking\n                    const cursor = this.cursorForHandle('none');\n                    grid.viewport.node.style.cursor = cursor;\n                    // Not applying selections if navigating away.\n                    return;\n                }\n            }\n        }\n        // If the hit test is the body region, the only option is select.\n        if (region === 'body') {\n            // Fetch the selection model.\n            let model = grid.selectionModel;\n            // Bail early if there is no selection model.\n            if (!model) {\n                return;\n            }\n            // Override the document cursor.\n            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor('default');\n            // Set up the press data.\n            this._pressData = {\n                type: 'select',\n                region,\n                row,\n                column,\n                override,\n                localX: -1,\n                localY: -1,\n                timeout: -1\n            };\n            // Set up the selection variables.\n            let r1;\n            let c1;\n            let r2;\n            let c2;\n            let cursorRow;\n            let cursorColumn;\n            let clear;\n            // Accel == new selection, keep old selections.\n            if (accel) {\n                r1 = row;\n                r2 = row;\n                c1 = column;\n                c2 = column;\n                cursorRow = row;\n                cursorColumn = column;\n                clear = 'none';\n            }\n            else if (shift) {\n                r1 = model.cursorRow;\n                r2 = row;\n                c1 = model.cursorColumn;\n                c2 = column;\n                cursorRow = model.cursorRow;\n                cursorColumn = model.cursorColumn;\n                clear = 'current';\n            }\n            else {\n                r1 = row;\n                r2 = row;\n                c1 = column;\n                c2 = column;\n                cursorRow = row;\n                cursorColumn = column;\n                clear = 'all';\n            }\n            // Make the selection.\n            model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });\n            // Done.\n            return;\n        }\n        // Otherwise, the hit test is on a header region.\n        // Convert the hit test into a part.\n        let handle = Private$5.resizeHandleForHitTest(hit);\n        // Fetch the cursor for the handle.\n        let cursor = this.cursorForHandle(handle);\n        // Handle horizontal resize.\n        if (handle === 'left' || handle === 'right') {\n            // Set up the resize data type.\n            const type = 'column-resize';\n            // Determine the column region.\n            let rgn = region === 'column-header' ? 'body' : 'row-header';\n            // Determine the section index.\n            let index = handle === 'left' ? column - 1 : column;\n            // Fetch the section size.\n            let size = grid.columnSize(rgn, index);\n            // Override the document cursor.\n            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor(cursor);\n            // Create the temporary press data.\n            this._pressData = { type, region: rgn, index, size, clientX, override };\n            // Done.\n            return;\n        }\n        // Handle vertical resize\n        if (handle === 'top' || handle === 'bottom') {\n            // Set up the resize data type.\n            const type = 'row-resize';\n            // Determine the row region.\n            let rgn = region === 'row-header' ? 'body' : 'column-header';\n            // Determine the section index.\n            let index = handle === 'top' ? row - 1 : row;\n            // Fetch the section size.\n            let size = grid.rowSize(rgn, index);\n            // Override the document cursor.\n            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor(cursor);\n            // Create the temporary press data.\n            this._pressData = { type, region: rgn, index, size, clientY, override };\n            // Done.\n            return;\n        }\n        // Otherwise, the only option is select.\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Bail if there is no selection model.\n        if (!model) {\n            return;\n        }\n        // Override the document cursor.\n        let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor('default');\n        // Set up the press data.\n        this._pressData = {\n            type: 'select',\n            region,\n            row,\n            column,\n            override,\n            localX: -1,\n            localY: -1,\n            timeout: -1\n        };\n        // Set up the selection variables.\n        let r1;\n        let c1;\n        let r2;\n        let c2;\n        let cursorRow;\n        let cursorColumn;\n        let clear;\n        // Compute the selection based on the pressed region.\n        if (region === 'corner-header') {\n            r1 = 0;\n            r2 = Infinity;\n            c1 = 0;\n            c2 = Infinity;\n            cursorRow = accel ? 0 : shift ? model.cursorRow : 0;\n            cursorColumn = accel ? 0 : shift ? model.cursorColumn : 0;\n            clear = accel ? 'none' : shift ? 'current' : 'all';\n        }\n        else if (region === 'row-header') {\n            r1 = accel ? row : shift ? model.cursorRow : row;\n            r2 = row;\n            const selectionGroup = { r1: r1, c1: 0, r2: r2, c2: 0 };\n            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['row-header', 'body'], 'row', selectionGroup);\n            // Check if there are any merges\n            if (joinedGroup.r1 != Number.MAX_VALUE) {\n                r1 = joinedGroup.r1;\n                r2 = joinedGroup.r2;\n            }\n            c1 = 0;\n            c2 = Infinity;\n            cursorRow = accel ? row : shift ? model.cursorRow : row;\n            cursorColumn = accel ? 0 : shift ? model.cursorColumn : 0;\n            clear = accel ? 'none' : shift ? 'current' : 'all';\n        }\n        else if (region === 'column-header') {\n            r1 = 0;\n            r2 = Infinity;\n            c1 = accel ? column : shift ? model.cursorColumn : column;\n            c2 = column;\n            const selectionGroup = { r1: 0, c1: c1, r2: 0, c2: c2 };\n            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['column-header', 'body'], 'column', selectionGroup);\n            // Check if there are any merges\n            if (joinedGroup.c1 != Number.MAX_VALUE) {\n                c1 = joinedGroup.c1;\n                c2 = joinedGroup.c2;\n            }\n            cursorRow = accel ? 0 : shift ? model.cursorRow : 0;\n            cursorColumn = accel ? column : shift ? model.cursorColumn : column;\n            clear = accel ? 'none' : shift ? 'current' : 'all';\n        }\n        else {\n            r1 = accel ? row : shift ? model.cursorRow : row;\n            r2 = row;\n            c1 = accel ? column : shift ? model.cursorColumn : column;\n            c2 = column;\n            cursorRow = accel ? row : shift ? model.cursorRow : row;\n            cursorColumn = accel ? column : shift ? model.cursorColumn : column;\n            clear = accel ? 'none' : shift ? 'current' : 'all';\n        }\n        // Make the selection.\n        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });\n    }\n    /**\n     * Handle the mouse move event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse move event of interest.\n     */\n    onMouseMove(grid, event) {\n        // Fetch the press data.\n        const data = this._pressData;\n        // Bail early if there is no press data.\n        if (!data) {\n            return;\n        }\n        // Handle a row resize.\n        if (data.type === 'row-resize') {\n            let dy = event.clientY - data.clientY;\n            grid.resizeRow(data.region, data.index, data.size + dy);\n            return;\n        }\n        // Handle a column resize.\n        if (data.type === 'column-resize') {\n            let dx = event.clientX - data.clientX;\n            grid.resizeColumn(data.region, data.index, data.size + dx);\n            return;\n        }\n        // Otherwise, it's a select.\n        // Mouse moves during a corner header press are a no-op.\n        if (data.region === 'corner-header') {\n            return;\n        }\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Bail early if the selection model was removed.\n        if (!model) {\n            return;\n        }\n        // Map to local coordinates.\n        let { lx, ly } = grid.mapToLocal(event.clientX, event.clientY);\n        // Update the local mouse coordinates in the press data.\n        data.localX = lx;\n        data.localY = ly;\n        // Fetch the grid geometry.\n        let hw = grid.headerWidth;\n        let hh = grid.headerHeight;\n        let vpw = grid.viewportWidth;\n        let vph = grid.viewportHeight;\n        let sx = grid.scrollX;\n        let sy = grid.scrollY;\n        let msx = grid.maxScrollY;\n        let msy = grid.maxScrollY;\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Set up the timeout variable.\n        let timeout = -1;\n        // Compute the timemout based on hit region and mouse position.\n        if (data.region === 'row-header' || mode === 'row') {\n            if (ly < hh && sy > 0) {\n                timeout = Private$5.computeTimeout(hh - ly);\n            }\n            else if (ly >= vph && sy < msy) {\n                timeout = Private$5.computeTimeout(ly - vph);\n            }\n        }\n        else if (data.region === 'column-header' || mode === 'column') {\n            if (lx < hw && sx > 0) {\n                timeout = Private$5.computeTimeout(hw - lx);\n            }\n            else if (lx >= vpw && sx < msx) {\n                timeout = Private$5.computeTimeout(lx - vpw);\n            }\n        }\n        else {\n            if (lx < hw && sx > 0) {\n                timeout = Private$5.computeTimeout(hw - lx);\n            }\n            else if (lx >= vpw && sx < msx) {\n                timeout = Private$5.computeTimeout(lx - vpw);\n            }\n            else if (ly < hh && sy > 0) {\n                timeout = Private$5.computeTimeout(hh - ly);\n            }\n            else if (ly >= vph && sy < msy) {\n                timeout = Private$5.computeTimeout(ly - vph);\n            }\n        }\n        // Update or initiate the autoselect if needed.\n        if (timeout >= 0) {\n            if (data.timeout < 0) {\n                data.timeout = timeout;\n                setTimeout(() => {\n                    Private$5.autoselect(grid, data);\n                }, timeout);\n            }\n            else {\n                data.timeout = timeout;\n            }\n            return;\n        }\n        // Otherwise, clear the autoselect timeout.\n        data.timeout = -1;\n        // Map the position to virtual coordinates.\n        let { vx, vy } = grid.mapToVirtual(event.clientX, event.clientY);\n        // Clamp the coordinates to the limits.\n        vx = Math.max(0, Math.min(vx, grid.bodyWidth - 1));\n        vy = Math.max(0, Math.min(vy, grid.bodyHeight - 1));\n        // Set up the selection variables.\n        let r1;\n        let c1;\n        let r2;\n        let c2;\n        let cursorRow = model.cursorRow;\n        let cursorColumn = model.cursorColumn;\n        let clear = 'current';\n        // Compute the selection based pressed region.\n        if (data.region === 'row-header' || mode === 'row') {\n            r1 = data.row;\n            r2 = grid.rowAt('body', vy);\n            const selectionGroup = { r1: r1, c1: 0, r2: r2, c2: 0 };\n            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['row-header', 'body'], 'row', selectionGroup);\n            // Check if there are any merges\n            if (joinedGroup.r1 != Number.MAX_VALUE) {\n                r1 = Math.min(r1, joinedGroup.r1);\n                r2 = Math.max(r2, joinedGroup.r2);\n            }\n            c1 = 0;\n            c2 = Infinity;\n        }\n        else if (data.region === 'column-header' || mode === 'column') {\n            r1 = 0;\n            r2 = Infinity;\n            c1 = data.column;\n            c2 = grid.columnAt('body', vx);\n            const selectionGroup = { r1: 0, c1: c1, r2: 0, c2: c2 };\n            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['column-header', 'body'], 'column', selectionGroup);\n            // Check if there are any merges\n            if (joinedGroup.c1 != Number.MAX_VALUE) {\n                c1 = joinedGroup.c1;\n                c2 = joinedGroup.c2;\n            }\n        }\n        else {\n            r1 = cursorRow;\n            r2 = grid.rowAt('body', vy);\n            c1 = cursorColumn;\n            c2 = grid.columnAt('body', vx);\n        }\n        // Make the selection.\n        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });\n    }\n    /**\n     * Handle the mouse up event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse up event of interest.\n     */\n    onMouseUp(grid, event) {\n        this.release();\n    }\n    /**\n     * Handle the mouse double click event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The mouse up event of interest.\n     */\n    onMouseDoubleClick(grid, event) {\n        if (!grid.dataModel) {\n            this.release();\n            return;\n        }\n        // Unpack the event.\n        let { clientX, clientY } = event;\n        // Hit test the grid.\n        let hit = grid.hitTest(clientX, clientY);\n        // Unpack the hit test.\n        let { region, row, column } = hit;\n        if (region === 'void') {\n            this.release();\n            return;\n        }\n        if (region === 'column-header' || region === 'corner-header') {\n            // Convert the hit test into a part.\n            const handle = Private$5.resizeHandleForHitTest(hit);\n            if (handle === 'left' || handle === 'right') {\n                let colIndex = handle === 'left' ? column - 1 : column;\n                let colRegion = region === 'column-header' ? 'body' : 'row-header';\n                if (colIndex < 0) {\n                    if (region === 'column-header') {\n                        // If the column is -1, it means we are in the corner header\n                        colIndex = grid.dataModel.columnCount('row-header') - 1;\n                        colRegion = 'row-header';\n                    }\n                    else {\n                        // If we are on the left edge of the row header, do nothing\n                        return;\n                    }\n                }\n                grid.resizeColumn(colRegion, colIndex, null);\n            }\n        }\n        if (region === 'body') {\n            if (grid.editable) {\n                const cell = {\n                    grid: grid,\n                    row: row,\n                    column: column\n                };\n                grid.editorController.edit(cell);\n            }\n        }\n        this.release();\n    }\n    /**\n     * Handle the context menu event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The context menu event of interest.\n     */\n    onContextMenu(grid, event) {\n        // TODO support user-defined context menus\n    }\n    /**\n     * Handle the wheel event for the data grid.\n     *\n     * @param grid - The data grid of interest.\n     *\n     * @param event - The wheel event of interest.\n     */\n    onWheel(grid, event) {\n        // Bail if a mouse press is in progress.\n        if (this._pressData) {\n            return;\n        }\n        // Extract the delta X and Y movement.\n        let dx = event.deltaX;\n        let dy = event.deltaY;\n        // Convert the delta values to pixel values.\n        switch (event.deltaMode) {\n            case 0: // DOM_DELTA_PIXEL\n                break;\n            case 1: {\n                // DOM_DELTA_LINE\n                let ds = grid.defaultSizes;\n                dx *= ds.columnWidth;\n                dy *= ds.rowHeight;\n                break;\n            }\n            case 2: // DOM_DELTA_PAGE\n                dx *= grid.pageWidth;\n                dy *= grid.pageHeight;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Only scroll and stop the event propagation if needed.\n        if (\n        // Scrolling left and not reached min already\n        (dx < 0 && grid.scrollX !== 0) ||\n            // Scrolling right and not reached max already\n            (dx > 0 && grid.scrollX !== grid.maxScrollX) ||\n            // Scrolling top and not reached min already\n            (dy < 0 && grid.scrollY !== 0) ||\n            // Scrolling down and not reached max already\n            (dy > 0 && grid.scrollY !== grid.maxScrollY)) {\n            event.preventDefault();\n            event.stopPropagation();\n            // Scroll by the desired amount.\n            grid.scrollBy(dx, dy);\n        }\n    }\n    /**\n     * Convert a resize handle into a cursor.\n     */\n    cursorForHandle(handle) {\n        return Private$5.cursorMap[handle];\n    }\n    /**\n     * Get the current pressData\n     */\n    get pressData() {\n        return this._pressData;\n    }\n}\n/**\n * The namespace for the module implementation details.\n */\nvar Private$5;\n(function (Private) {\n    /**\n     * Creates a CellConfig object from a hit region.\n     */\n    function createCellConfigObject(grid, hit) {\n        const { region, row, column } = hit;\n        // Terminate call if region is void.\n        if (region === 'void') {\n            return undefined;\n        }\n        // Augment hit region params with value and metadata.\n        const value = grid.dataModel.data(region, row, column);\n        const metadata = grid.dataModel.metadata(region, row, column);\n        // Create cell config object to retrieve cell renderer.\n        const config = {\n            ...hit,\n            value: value,\n            metadata: metadata\n        };\n        return config;\n    }\n    Private.createCellConfigObject = createCellConfigObject;\n    /**\n     * Get the resize handle for a grid hit test.\n     */\n    function resizeHandleForHitTest(hit) {\n        // Fetch the row and column.\n        let r = hit.row;\n        let c = hit.column;\n        // Fetch the leading and trailing sizes.\n        let lw = hit.x;\n        let lh = hit.y;\n        let tw = hit.width - hit.x;\n        let th = hit.height - hit.y;\n        // Set up the result variable.\n        let result;\n        // Dispatch based on hit test region.\n        switch (hit.region) {\n            case 'corner-header':\n                if (c > 0 && lw <= 5) {\n                    result = 'left';\n                }\n                else if (tw <= 6) {\n                    result = 'right';\n                }\n                else if (r > 0 && lh <= 5) {\n                    result = 'top';\n                }\n                else if (th <= 6) {\n                    result = 'bottom';\n                }\n                else {\n                    result = 'none';\n                }\n                break;\n            case 'column-header':\n                if (c > 0 && lw <= 5) {\n                    result = 'left';\n                }\n                else if (tw <= 6) {\n                    result = 'right';\n                }\n                else if (r > 0 && lh <= 5) {\n                    result = 'top';\n                }\n                else if (th <= 6) {\n                    result = 'bottom';\n                }\n                else {\n                    result = 'none';\n                }\n                break;\n            case 'row-header':\n                if (c > 0 && lw <= 5) {\n                    result = 'left';\n                }\n                else if (tw <= 6) {\n                    result = 'right';\n                }\n                else if (r > 0 && lh <= 5) {\n                    result = 'top';\n                }\n                else if (th <= 6) {\n                    result = 'bottom';\n                }\n                else {\n                    result = 'none';\n                }\n                break;\n            case 'body':\n                result = 'none';\n                break;\n            case 'void':\n                result = 'none';\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Return the result.\n        return result;\n    }\n    Private.resizeHandleForHitTest = resizeHandleForHitTest;\n    /**\n     * A timer callback for the autoselect loop.\n     *\n     * @param grid - The datagrid of interest.\n     *\n     * @param data - The select data of interest.\n     */\n    function autoselect(grid, data) {\n        // Bail early if the timeout has been reset.\n        if (data.timeout < 0) {\n            return;\n        }\n        // Fetch the selection model.\n        let model = grid.selectionModel;\n        // Bail early if the selection model has been removed.\n        if (!model) {\n            return;\n        }\n        // Fetch the current selection.\n        let cs = model.currentSelection();\n        // Bail early if there is no current selection.\n        if (!cs) {\n            return;\n        }\n        // Fetch local X and Y coordinates of the mouse.\n        let lx = data.localX;\n        let ly = data.localY;\n        // Set up the selection variables.\n        let r1 = cs.r1;\n        let c1 = cs.c1;\n        let r2 = cs.r2;\n        let c2 = cs.c2;\n        let cursorRow = model.cursorRow;\n        let cursorColumn = model.cursorColumn;\n        let clear = 'current';\n        // Fetch the grid geometry.\n        let hw = grid.headerWidth;\n        let hh = grid.headerHeight;\n        let vpw = grid.viewportWidth;\n        let vph = grid.viewportHeight;\n        // Fetch the selection mode.\n        let mode = model.selectionMode;\n        // Update the selection based on the hit region.\n        if (data.region === 'row-header' || mode === 'row') {\n            r2 += ly <= hh ? -1 : ly >= vph ? 1 : 0;\n        }\n        else if (data.region === 'column-header' || mode === 'column') {\n            c2 += lx <= hw ? -1 : lx >= vpw ? 1 : 0;\n        }\n        else {\n            r2 += ly <= hh ? -1 : ly >= vph ? 1 : 0;\n            c2 += lx <= hw ? -1 : lx >= vpw ? 1 : 0;\n        }\n        // Update the current selection.\n        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });\n        // Re-fetch the current selection.\n        cs = model.currentSelection();\n        // Bail if there is no selection.\n        if (!cs) {\n            return;\n        }\n        // Scroll the grid based on the hit region.\n        if (data.region === 'row-header' || mode === 'row') {\n            grid.scrollToRow(cs.r2);\n        }\n        else if (data.region === 'column-header' || mode == 'column') {\n            grid.scrollToColumn(cs.c2);\n        }\n        else if (mode === 'cell') {\n            grid.scrollToCell(cs.r2, cs.c2);\n        }\n        // Schedule the next call with the current timeout.\n        setTimeout(() => {\n            autoselect(grid, data);\n        }, data.timeout);\n    }\n    Private.autoselect = autoselect;\n    /**\n     * Compute the scroll timeout for the given delta distance.\n     *\n     * @param delta - The delta pixels from the origin.\n     *\n     * @returns The scaled timeout in milliseconds.\n     */\n    function computeTimeout(delta) {\n        return 5 + 120 * (1 - Math.min(128, Math.abs(delta)) / 128);\n    }\n    Private.computeTimeout = computeTimeout;\n    /**\n     * A mapping of resize handle to cursor.\n     */\n    Private.cursorMap = {\n        top: 'ns-resize',\n        left: 'ew-resize',\n        right: 'ew-resize',\n        bottom: 'ns-resize',\n        hyperlink: 'pointer',\n        none: 'default'\n    };\n})(Private$5 || (Private$5 = {}));\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * A base class for creating data grid selection models.\n *\n * #### Notes\n * If the predefined selection models are insufficient for a particular\n * use case, a custom model can be defined which derives from this class.\n */\nclass SelectionModel {\n    /**\n     * Construct a new selection model.\n     *\n     * @param options - The options for initializing the model.\n     */\n    constructor(options) {\n        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);\n        this._selectionMode = 'cell';\n        this.dataModel = options.dataModel;\n        this._selectionMode = options.selectionMode || 'cell';\n        this.dataModel.changed.connect(this.onDataModelChanged, this);\n    }\n    /**\n     * A signal emitted when the selection model has changed.\n     */\n    get changed() {\n        return this._changed;\n    }\n    /**\n     * Get the selection mode for the model.\n     */\n    get selectionMode() {\n        return this._selectionMode;\n    }\n    /**\n     * Set the selection mode for the model.\n     *\n     * #### Notes\n     * This will clear the selection model.\n     */\n    set selectionMode(value) {\n        // Bail early if the mode does not change.\n        if (this._selectionMode === value) {\n            return;\n        }\n        // Update the internal mode.\n        this._selectionMode = value;\n        // Clear the current selections.\n        this.clear();\n    }\n    /**\n     * Test whether any selection intersects a row.\n     *\n     * @param index - The row index of interest.\n     *\n     * @returns Whether any selection intersects the row.\n     *\n     * #### Notes\n     * This method may be reimplemented in a subclass.\n     */\n    isRowSelected(index) {\n        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsRow(s, index));\n    }\n    /**\n     * Test whether any selection intersects a column.\n     *\n     * @param index - The column index of interest.\n     *\n     * @returns Whether any selection intersects the column.\n     *\n     * #### Notes\n     * This method may be reimplemented in a subclass.\n     */\n    isColumnSelected(index) {\n        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsColumn(s, index));\n    }\n    /**\n     * Test whether any selection intersects a cell.\n     *\n     * @param row - The row index of interest.\n     *\n     * @param column - The column index of interest.\n     *\n     * @returns Whether any selection intersects the cell.\n     *\n     * #### Notes\n     * This method may be reimplemented in a subclass.\n     */\n    isCellSelected(row, column) {\n        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsCell(s, row, column));\n    }\n    /**\n     * A signal handler for the data model `changed` signal.\n     *\n     * @param args - The arguments for the signal.\n     *\n     * #### Notes\n     * Selection model implementations should update their selections\n     * in a manner that is relevant for the changes to the data model.\n     *\n     * The default implementation of this method is a no-op.\n     */\n    onDataModelChanged(sender, args) {\n        // pass\n    }\n    /**\n     * Emit the `changed` signal for the selection model.\n     *\n     * #### Notes\n     * Subclasses should call this method whenever the selection model\n     * has changed so that attached data grids can update themselves.\n     */\n    emitChanged() {\n        this._changed.emit(undefined);\n    }\n}\n/**\n * The namespace for the module implementation details.\n */\nvar Private$4;\n(function (Private) {\n    /**\n     * Test whether a selection contains a given row.\n     */\n    function containsRow(selection, row) {\n        let { r1, r2 } = selection;\n        return (row >= r1 && row <= r2) || (row >= r2 && row <= r1);\n    }\n    Private.containsRow = containsRow;\n    /**\n     * Test whether a selection contains a given column.\n     */\n    function containsColumn(selection, column) {\n        let { c1, c2 } = selection;\n        return (column >= c1 && column <= c2) || (column >= c2 && column <= c1);\n    }\n    Private.containsColumn = containsColumn;\n    /**\n     * Test whether a selection contains a given cell.\n     */\n    function containsCell(selection, row, column) {\n        return containsRow(selection, row) && containsColumn(selection, column);\n    }\n    Private.containsCell = containsCell;\n})(Private$4 || (Private$4 = {}));\n\n/**\n * A basic selection model implementation.\n *\n * #### Notes\n * This selection model is sufficient for most use cases where\n * structural knowledge of the data source is *not* required.\n */\nclass BasicSelectionModel extends SelectionModel {\n    constructor() {\n        super(...arguments);\n        this._cursorRow = -1;\n        this._cursorColumn = -1;\n        this._cursorRectIndex = -1;\n        this._selections = [];\n    }\n    /**\n     * Whether the selection model is empty.\n     */\n    get isEmpty() {\n        return this._selections.length === 0;\n    }\n    /**\n     * The row index of the cursor.\n     */\n    get cursorRow() {\n        return this._cursorRow;\n    }\n    /**\n     * The column index of the cursor.\n     */\n    get cursorColumn() {\n        return this._cursorColumn;\n    }\n    /**\n     * Move cursor down/up/left/right while making sure it remains\n     * within the bounds of selected rectangles\n     *\n     * @param direction - The direction of the movement.\n     */\n    moveCursorWithinSelections(direction) {\n        // Bail early if there are no selections or no existing cursor\n        if (this.isEmpty || this.cursorRow === -1 || this._cursorColumn === -1) {\n            return;\n        }\n        // Bail early if only single cell is selected\n        const firstSelection = this._selections[0];\n        if (this._selections.length === 1 &&\n            firstSelection.r1 === firstSelection.r2 &&\n            firstSelection.c1 === firstSelection.c2) {\n            return;\n        }\n        // start from last selection rectangle\n        if (this._cursorRectIndex === -1) {\n            this._cursorRectIndex = this._selections.length - 1;\n        }\n        let cursorRect = this._selections[this._cursorRectIndex];\n        const dr = direction === 'down' ? 1 : direction === 'up' ? -1 : 0;\n        const dc = direction === 'right' ? 1 : direction === 'left' ? -1 : 0;\n        let newRow = this._cursorRow + dr;\n        let newColumn = this._cursorColumn + dc;\n        const r1 = Math.min(cursorRect.r1, cursorRect.r2);\n        const r2 = Math.max(cursorRect.r1, cursorRect.r2);\n        const c1 = Math.min(cursorRect.c1, cursorRect.c2);\n        const c2 = Math.max(cursorRect.c1, cursorRect.c2);\n        const moveToNextRect = () => {\n            this._cursorRectIndex =\n                (this._cursorRectIndex + 1) % this._selections.length;\n            cursorRect = this._selections[this._cursorRectIndex];\n            newRow = Math.min(cursorRect.r1, cursorRect.r2);\n            newColumn = Math.min(cursorRect.c1, cursorRect.c2);\n        };\n        const moveToPreviousRect = () => {\n            this._cursorRectIndex =\n                this._cursorRectIndex === 0\n                    ? this._selections.length - 1\n                    : this._cursorRectIndex - 1;\n            cursorRect = this._selections[this._cursorRectIndex];\n            newRow = Math.max(cursorRect.r1, cursorRect.r2);\n            newColumn = Math.max(cursorRect.c1, cursorRect.c2);\n        };\n        if (newRow > r2) {\n            newRow = r1;\n            newColumn += 1;\n            if (newColumn > c2) {\n                moveToNextRect();\n            }\n        }\n        else if (newRow < r1) {\n            newRow = r2;\n            newColumn -= 1;\n            if (newColumn < c1) {\n                moveToPreviousRect();\n            }\n        }\n        else if (newColumn > c2) {\n            newColumn = c1;\n            newRow += 1;\n            if (newRow > r2) {\n                moveToNextRect();\n            }\n        }\n        else if (newColumn < c1) {\n            newColumn = c2;\n            newRow -= 1;\n            if (newRow < r1) {\n                moveToPreviousRect();\n            }\n        }\n        this._cursorRow = newRow;\n        this._cursorColumn = newColumn;\n        // Emit the changed signal.\n        this.emitChanged();\n    }\n    /**\n     * Get the current selection in the selection model.\n     *\n     * @returns The current selection or `null`.\n     *\n     * #### Notes\n     * This is the selection which holds the cursor.\n     */\n    currentSelection() {\n        return this._selections[this._selections.length - 1] || null;\n    }\n    /**\n     * Get an iterator of the selections in the model.\n     *\n     * @returns A new iterator of the current selections.\n     *\n     * #### Notes\n     * The data grid will render the selections in order.\n     */\n    *selections() {\n        yield* this._selections;\n    }\n    /**\n     * Select the specified cells.\n     *\n     * @param args - The arguments for the selection.\n     */\n    select(args) {\n        // Fetch the current row and column counts;\n        let rowCount = this.dataModel.rowCount('body');\n        let columnCount = this.dataModel.columnCount('body');\n        // Bail early if there is no content.\n        if (rowCount <= 0 || columnCount <= 0) {\n            return;\n        }\n        // Unpack the arguments.\n        let { r1, c1, r2, c2, cursorRow, cursorColumn, clear } = args;\n        // Clear the necessary selections.\n        if (clear === 'all') {\n            this._selections.length = 0;\n        }\n        else if (clear === 'current') {\n            this._selections.pop();\n        }\n        // Clamp to the data model bounds.\n        r1 = Math.max(0, Math.min(r1, rowCount - 1));\n        r2 = Math.max(0, Math.min(r2, rowCount - 1));\n        c1 = Math.max(0, Math.min(c1, columnCount - 1));\n        c2 = Math.max(0, Math.min(c2, columnCount - 1));\n        // Indicate if a row/column has already been selected.\n        let alreadySelected = false;\n        // Handle the selection mode.\n        if (this.selectionMode === 'row') {\n            c1 = 0;\n            c2 = columnCount - 1;\n            alreadySelected =\n                this._selections.filter(selection => selection.r1 === r1).length !== 0;\n            // Remove from selections if already selected.\n            this._selections = alreadySelected\n                ? this._selections.filter(selection => selection.r1 !== r1)\n                : this._selections;\n        }\n        else if (this.selectionMode === 'column') {\n            r1 = 0;\n            r2 = rowCount - 1;\n            alreadySelected =\n                this._selections.filter(selection => selection.c1 === c1).length !== 0;\n            // Remove from selections if already selected.\n            this._selections = alreadySelected\n                ? this._selections.filter(selection => selection.c1 !== c1)\n                : this._selections;\n        }\n        // Alias the cursor row and column.\n        let cr = cursorRow;\n        let cc = cursorColumn;\n        // Compute the new cursor location.\n        if (cr < 0 || (cr < r1 && cr < r2) || (cr > r1 && cr > r2)) {\n            cr = r1;\n        }\n        if (cc < 0 || (cc < c1 && cc < c2) || (cc > c1 && cc > c2)) {\n            cc = c1;\n        }\n        // Update the cursor.\n        this._cursorRow = cr;\n        this._cursorColumn = cc;\n        this._cursorRectIndex = this._selections.length;\n        // Add the new selection if it wasn't already selected.\n        if (!alreadySelected) {\n            this._selections.push({ r1, c1, r2, c2 });\n        }\n        // Emit the changed signal.\n        this.emitChanged();\n    }\n    /**\n     * Clear all selections in the selection model.\n     */\n    clear() {\n        // Bail early if there are no selections.\n        if (this._selections.length === 0) {\n            return;\n        }\n        // Reset the internal state.\n        this._cursorRow = -1;\n        this._cursorColumn = -1;\n        this._cursorRectIndex = -1;\n        this._selections.length = 0;\n        // Emit the changed signal.\n        this.emitChanged();\n    }\n    /**\n     * A signal handler for the data model `changed` signal.\n     *\n     * @param args - The arguments for the signal.\n     */\n    onDataModelChanged(sender, args) {\n        // Bail early if the model has no current selections.\n        if (this._selections.length === 0) {\n            return;\n        }\n        // Bail early if the cells have changed in place.\n        if (args.type === 'cells-changed') {\n            return;\n        }\n        // Bail early if there is no change to the row or column count.\n        if (args.type === 'rows-moved' || args.type === 'columns-moved') {\n            return;\n        }\n        // Fetch the last row and column index.\n        let lr = sender.rowCount('body') - 1;\n        let lc = sender.columnCount('body') - 1;\n        // Bail early if the data model is empty.\n        if (lr < 0 || lc < 0) {\n            this._selections.length = 0;\n            this.emitChanged();\n            return;\n        }\n        // Fetch the selection mode.\n        let mode = this.selectionMode;\n        // Set up the assignment index variable.\n        let j = 0;\n        // Iterate over the current selections.\n        for (let i = 0, n = this._selections.length; i < n; ++i) {\n            // Unpack the selection.\n            let { r1, c1, r2, c2 } = this._selections[i];\n            // Skip the selection if it will disappear.\n            if ((lr < r1 && lr < r2) || (lc < c1 && lc < c2)) {\n                continue;\n            }\n            // Modify the bounds based on the selection mode.\n            if (mode === 'row') {\n                r1 = Math.max(0, Math.min(r1, lr));\n                r2 = Math.max(0, Math.min(r2, lr));\n                c1 = 0;\n                c2 = lc;\n            }\n            else if (mode === 'column') {\n                r1 = 0;\n                r2 = lr;\n                c1 = Math.max(0, Math.min(c1, lc));\n                c2 = Math.max(0, Math.min(c2, lc));\n            }\n            else {\n                r1 = Math.max(0, Math.min(r1, lr));\n                r2 = Math.max(0, Math.min(r2, lr));\n                c1 = Math.max(0, Math.min(c1, lc));\n                c2 = Math.max(0, Math.min(c2, lc));\n            }\n            // Assign the modified selection to the array.\n            this._selections[j++] = { r1, c1, r2, c2 };\n        }\n        // Remove the stale selections.\n        this._selections.length = j;\n        // Emit the changed signal.\n        this.emitChanged();\n    }\n}\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2023, Lumino Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * An object which renders the cells of a data grid asynchronously.\n *\n * #### Notes\n * For performance reason, the datagrid only paints cells synchronously,\n * though if your cell renderer inherits from AsyncCellRenderer, you will\n * be able to do some asynchronous work prior to painting the cell.\n * See `ImageRenderer` for an example of an asynchronous renderer.\n */\nclass AsyncCellRenderer extends CellRenderer {\n}\n\n/*\n * Copyright (c) Jupyter Development Team.\n * Distributed under the terms of the Modified BSD License.\n */\n// default validation error message\nconst DEFAULT_INVALID_INPUT_MESSAGE = 'Invalid input!';\n/**\n * A cell input validator object which always returns valid.\n */\nclass PassInputValidator {\n    /**\n     * Validate cell input.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param value - The cell value input.\n     *\n     * @returns An object with validation result.\n     */\n    validate(cell, value) {\n        return { valid: true };\n    }\n}\n/**\n * Text cell input validator.\n */\nclass TextInputValidator {\n    constructor() {\n        /**\n         * Minimum text length\n         *\n         * The default is Number.NaN, meaning no minimum constraint\n         */\n        this.minLength = Number.NaN;\n        /**\n         * Maximum text length\n         *\n         * The default is Number.NaN, meaning no maximum constraint\n         */\n        this.maxLength = Number.NaN;\n        /**\n         * Required text pattern as regular expression\n         *\n         * The default is null, meaning no pattern constraint\n         */\n        this.pattern = null;\n    }\n    /**\n     * Validate cell input.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param value - The cell value input.\n     *\n     * @returns An object with validation result.\n     */\n    validate(cell, value) {\n        if (value === null) {\n            return { valid: true };\n        }\n        if (typeof value !== 'string') {\n            return {\n                valid: false,\n                message: 'Input must be valid text'\n            };\n        }\n        if (!isNaN(this.minLength) && value.length < this.minLength) {\n            return {\n                valid: false,\n                message: `Text length must be greater than ${this.minLength}`\n            };\n        }\n        if (!isNaN(this.maxLength) && value.length > this.maxLength) {\n            return {\n                valid: false,\n                message: `Text length must be less than ${this.maxLength}`\n            };\n        }\n        if (this.pattern && !this.pattern.test(value)) {\n            return {\n                valid: false,\n                message: `Text doesn't match the required pattern`\n            };\n        }\n        return { valid: true };\n    }\n}\n/**\n * Integer cell input validator.\n */\nclass IntegerInputValidator {\n    constructor() {\n        /**\n         * Minimum value\n         *\n         * The default is Number.NaN, meaning no minimum constraint\n         */\n        this.min = Number.NaN;\n        /**\n         * Maximum value\n         *\n         * The default is Number.NaN, meaning no maximum constraint\n         */\n        this.max = Number.NaN;\n    }\n    /**\n     * Validate cell input.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param value - The cell value input.\n     *\n     * @returns An object with validation result.\n     */\n    validate(cell, value) {\n        if (value === null) {\n            return { valid: true };\n        }\n        if (isNaN(value) || value % 1 !== 0) {\n            return {\n                valid: false,\n                message: 'Input must be valid integer'\n            };\n        }\n        if (!isNaN(this.min) && value < this.min) {\n            return {\n                valid: false,\n                message: `Input must be greater than ${this.min}`\n            };\n        }\n        if (!isNaN(this.max) && value > this.max) {\n            return {\n                valid: false,\n                message: `Input must be less than ${this.max}`\n            };\n        }\n        return { valid: true };\n    }\n}\n/**\n * Real number cell input validator.\n */\nclass NumberInputValidator {\n    constructor() {\n        /**\n         * Minimum value\n         *\n         * The default is Number.NaN, meaning no minimum constraint\n         */\n        this.min = Number.NaN;\n        /**\n         * Maximum value\n         *\n         * The default is Number.NaN, meaning no maximum constraint\n         */\n        this.max = Number.NaN;\n    }\n    /**\n     * Validate cell input.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param value - The cell value input.\n     *\n     * @returns An object with validation result.\n     */\n    validate(cell, value) {\n        if (value === null) {\n            return { valid: true };\n        }\n        if (isNaN(value)) {\n            return {\n                valid: false,\n                message: 'Input must be valid number'\n            };\n        }\n        if (!isNaN(this.min) && value < this.min) {\n            return {\n                valid: false,\n                message: `Input must be greater than ${this.min}`\n            };\n        }\n        if (!isNaN(this.max) && value > this.max) {\n            return {\n                valid: false,\n                message: `Input must be less than ${this.max}`\n            };\n        }\n        return { valid: true };\n    }\n}\n/**\n * An abstract base class that provides the most of the functionality\n * needed by a cell editor. All of the built-in cell editors\n * for various cell types are derived from this base class. Custom cell editors\n * can be easily implemented by extending this class.\n */\nclass CellEditor {\n    /**\n     * Construct a new cell editor.\n     */\n    constructor() {\n        /**\n         * A signal emitted when input changes.\n         */\n        this.inputChanged = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);\n        /**\n         * Notification popup used to show validation error messages.\n         */\n        this.validityNotification = null;\n        /**\n         * Whether the cell editor is disposed.\n         */\n        this._disposed = false;\n        /**\n         * Whether the value input is valid.\n         */\n        this._validInput = true;\n        /**\n         * Grid wheel event handler.\n         */\n        this._gridWheelEventHandler = null;\n        this.inputChanged.connect(() => {\n            this.validate();\n        });\n    }\n    /**\n     * Whether the cell editor is disposed.\n     */\n    get isDisposed() {\n        return this._disposed;\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this._disposed) {\n            return;\n        }\n        if (this._gridWheelEventHandler) {\n            this.cell.grid.node.removeEventListener('wheel', this._gridWheelEventHandler);\n            this._gridWheelEventHandler = null;\n        }\n        this._closeValidityNotification();\n        this._disposed = true;\n        this.cell.grid.node.removeChild(this.viewportOccluder);\n    }\n    /**\n     * Start editing the cell.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param options - The cell editing options.\n     */\n    edit(cell, options) {\n        this.cell = cell;\n        this.onCommit = options && options.onCommit;\n        this.onCancel = options && options.onCancel;\n        this.validator =\n            options && options.validator\n                ? options.validator\n                : this.createValidatorBasedOnType();\n        this._gridWheelEventHandler = () => {\n            this._closeValidityNotification();\n            this.updatePosition();\n        };\n        cell.grid.node.addEventListener('wheel', this._gridWheelEventHandler);\n        this._addContainer();\n        this.updatePosition();\n        this.startEditing();\n    }\n    /**\n     * Cancel editing the cell.\n     */\n    cancel() {\n        if (this._disposed) {\n            return;\n        }\n        this.dispose();\n        if (this.onCancel) {\n            this.onCancel();\n        }\n    }\n    /**\n     * Whether the value input is valid.\n     */\n    get validInput() {\n        return this._validInput;\n    }\n    /**\n     * Validate the cell input. Shows validation error notification when input is invalid.\n     */\n    validate() {\n        let value;\n        try {\n            value = this.getInput();\n        }\n        catch (error) {\n            console.log(`Input error: ${error.message}`);\n            this.setValidity(false, error.message || DEFAULT_INVALID_INPUT_MESSAGE);\n            return;\n        }\n        if (this.validator) {\n            const result = this.validator.validate(this.cell, value);\n            if (result.valid) {\n                this.setValidity(true);\n            }\n            else {\n                this.setValidity(false, result.message || DEFAULT_INVALID_INPUT_MESSAGE);\n            }\n        }\n        else {\n            this.setValidity(true);\n        }\n    }\n    /**\n     * Set validity flag.\n     *\n     * @param valid - Whether the input is valid.\n     *\n     * @param message - Notification message to show.\n     *\n     * If message is set to empty string (which is the default)\n     * existing notification popup is removed if any.\n     */\n    setValidity(valid, message = '') {\n        this._validInput = valid;\n        this._closeValidityNotification();\n        if (valid) {\n            this.editorContainer.classList.remove('lm-mod-invalid');\n        }\n        else {\n            this.editorContainer.classList.add('lm-mod-invalid');\n            // show a notification popup\n            if (message !== '') {\n                this.validityNotification = new CellEditor.Notification({\n                    target: this.editorContainer,\n                    message: message,\n                    placement: 'bottom',\n                    timeout: 5000\n                });\n                this.validityNotification.show();\n            }\n        }\n    }\n    /**\n     * Create and return a cell input validator based on configuration of the\n     * cell being edited. If no suitable validator can be found, it returns undefined.\n     */\n    createValidatorBasedOnType() {\n        const cell = this.cell;\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        switch (metadata && metadata.type) {\n            case 'string':\n                {\n                    const validator = new TextInputValidator();\n                    if (typeof metadata.format === 'string') {\n                        const format = metadata.format;\n                        switch (format) {\n                            case 'email':\n                                validator.pattern = new RegExp('^([a-z0-9_.-]+)@([da-z.-]+).([a-z.]{2,6})$');\n                                break;\n                            case 'uuid':\n                                validator.pattern = new RegExp('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}');\n                                break;\n                        }\n                    }\n                    if (metadata.constraint) {\n                        if (metadata.constraint.minLength !== undefined) {\n                            validator.minLength = metadata.constraint.minLength;\n                        }\n                        if (metadata.constraint.maxLength !== undefined) {\n                            validator.maxLength = metadata.constraint.maxLength;\n                        }\n                        if (typeof metadata.constraint.pattern === 'string') {\n                            validator.pattern = new RegExp(metadata.constraint.pattern);\n                        }\n                    }\n                    return validator;\n                }\n            case 'number':\n                {\n                    const validator = new NumberInputValidator();\n                    if (metadata.constraint) {\n                        if (metadata.constraint.minimum !== undefined) {\n                            validator.min = metadata.constraint.minimum;\n                        }\n                        if (metadata.constraint.maximum !== undefined) {\n                            validator.max = metadata.constraint.maximum;\n                        }\n                    }\n                    return validator;\n                }\n            case 'integer':\n                {\n                    const validator = new IntegerInputValidator();\n                    if (metadata.constraint) {\n                        if (metadata.constraint.minimum !== undefined) {\n                            validator.min = metadata.constraint.minimum;\n                        }\n                        if (metadata.constraint.maximum !== undefined) {\n                            validator.max = metadata.constraint.maximum;\n                        }\n                    }\n                    return validator;\n                }\n        }\n        return undefined;\n    }\n    /**\n     * Compute cell rectangle and return with other cell properties.\n     */\n    getCellInfo(cell) {\n        const { grid, row, column } = cell;\n        let data, columnX, rowY, width, height;\n        const cellGroup = CellGroup.getGroup(grid.dataModel, 'body', row, column);\n        if (cellGroup) {\n            columnX =\n                grid.headerWidth -\n                    grid.scrollX +\n                    grid.columnOffset('body', cellGroup.c1);\n            rowY =\n                grid.headerHeight - grid.scrollY + grid.rowOffset('body', cellGroup.r1);\n            width = 0;\n            height = 0;\n            for (let r = cellGroup.r1; r <= cellGroup.r2; r++) {\n                height += grid.rowSize('body', r);\n            }\n            for (let c = cellGroup.c1; c <= cellGroup.c2; c++) {\n                width += grid.columnSize('body', c);\n            }\n            data = grid.dataModel.data('body', cellGroup.r1, cellGroup.c1);\n        }\n        else {\n            columnX =\n                grid.headerWidth - grid.scrollX + grid.columnOffset('body', column);\n            rowY = grid.headerHeight - grid.scrollY + grid.rowOffset('body', row);\n            width = grid.columnSize('body', column);\n            height = grid.rowSize('body', row);\n            data = grid.dataModel.data('body', row, column);\n        }\n        return {\n            grid: grid,\n            row: row,\n            column: column,\n            data: data,\n            x: columnX,\n            y: rowY,\n            width: width,\n            height: height\n        };\n    }\n    /**\n     * Reposition cell editor by moving viewport occluder and cell editor container.\n     */\n    updatePosition() {\n        const grid = this.cell.grid;\n        const cellInfo = this.getCellInfo(this.cell);\n        const headerHeight = grid.headerHeight;\n        const headerWidth = grid.headerWidth;\n        this.viewportOccluder.style.top = headerHeight + 'px';\n        this.viewportOccluder.style.left = headerWidth + 'px';\n        this.viewportOccluder.style.width = grid.viewportWidth - headerWidth + 'px';\n        this.viewportOccluder.style.height =\n            grid.viewportHeight - headerHeight + 'px';\n        this.viewportOccluder.style.position = 'absolute';\n        this.editorContainer.style.left = cellInfo.x - 1 - headerWidth + 'px';\n        this.editorContainer.style.top = cellInfo.y - 1 - headerHeight + 'px';\n        this.editorContainer.style.width = cellInfo.width + 1 + 'px';\n        this.editorContainer.style.height = cellInfo.height + 1 + 'px';\n        this.editorContainer.style.visibility = 'visible';\n        this.editorContainer.style.position = 'absolute';\n    }\n    /**\n     * Commit the edited value.\n     *\n     * @param cursorMovement - Cursor move direction based on keys pressed to end the edit.\n     *\n     * @returns true on valid input, false otherwise.\n     */\n    commit(cursorMovement = 'none') {\n        this.validate();\n        if (!this._validInput) {\n            return false;\n        }\n        let value;\n        try {\n            value = this.getInput();\n        }\n        catch (error) {\n            console.log(`Input error: ${error.message}`);\n            return false;\n        }\n        this.dispose();\n        if (this.onCommit) {\n            this.onCommit({\n                cell: this.cell,\n                value: value,\n                cursorMovement: cursorMovement\n            });\n        }\n        return true;\n    }\n    /**\n     * Create container elements needed to prevent editor widget overflow\n     * beyond viewport and to position cell editor widget.\n     */\n    _addContainer() {\n        this.viewportOccluder = document.createElement('div');\n        this.viewportOccluder.className = 'lm-DataGrid-cellEditorOccluder';\n        this.cell.grid.node.appendChild(this.viewportOccluder);\n        this.editorContainer = document.createElement('div');\n        this.editorContainer.className = 'lm-DataGrid-cellEditorContainer';\n        this.viewportOccluder.appendChild(this.editorContainer);\n        // update mouse event pass-through state based on input validity\n        this.editorContainer.addEventListener('mouseleave', (event) => {\n            this.viewportOccluder.style.pointerEvents = this._validInput\n                ? 'none'\n                : 'auto';\n        });\n        this.editorContainer.addEventListener('mouseenter', (event) => {\n            this.viewportOccluder.style.pointerEvents = 'none';\n        });\n    }\n    /**\n     * Remove validity notification popup.\n     */\n    _closeValidityNotification() {\n        if (this.validityNotification) {\n            this.validityNotification.close();\n            this.validityNotification = null;\n        }\n    }\n}\n/**\n * Abstract base class with shared functionality\n * for cell editors which use HTML Input widget as editor.\n */\nclass InputCellEditor extends CellEditor {\n    /**\n     * Handle the DOM events for the editor.\n     *\n     * @param event - The DOM event sent to the editor.\n     */\n    handleEvent(event) {\n        switch (event.type) {\n            case 'keydown':\n                this._onKeyDown(event);\n                break;\n            case 'blur':\n                this._onBlur(event);\n                break;\n            case 'input':\n                this._onInput(event);\n                break;\n        }\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this.isDisposed) {\n            return;\n        }\n        this._unbindEvents();\n        super.dispose();\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        this.createWidget();\n        const cell = this.cell;\n        const cellInfo = this.getCellInfo(cell);\n        this.input.value = this.deserialize(cellInfo.data);\n        this.editorContainer.appendChild(this.input);\n        this.input.focus();\n        this.input.select();\n        this.bindEvents();\n    }\n    deserialize(value) {\n        if (value === null || value === undefined) {\n            return '';\n        }\n        return value.toString();\n    }\n    createWidget() {\n        const input = document.createElement('input');\n        input.classList.add('lm-DataGrid-cellEditorWidget');\n        input.classList.add('lm-DataGrid-cellEditorInput');\n        input.spellcheck = false;\n        input.type = this.inputType;\n        this.input = input;\n    }\n    bindEvents() {\n        this.input.addEventListener('keydown', this);\n        this.input.addEventListener('blur', this);\n        this.input.addEventListener('input', this);\n    }\n    _unbindEvents() {\n        this.input.removeEventListener('keydown', this);\n        this.input.removeEventListener('blur', this);\n        this.input.removeEventListener('input', this);\n    }\n    _onKeyDown(event) {\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'Enter':\n                this.commit(event.shiftKey ? 'up' : 'down');\n                break;\n            case 'Tab':\n                this.commit(event.shiftKey ? 'left' : 'right');\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'Escape':\n                this.cancel();\n                break;\n        }\n    }\n    _onBlur(event) {\n        if (this.isDisposed) {\n            return;\n        }\n        if (!this.commit()) {\n            event.preventDefault();\n            event.stopPropagation();\n            this.input.focus();\n        }\n    }\n    _onInput(event) {\n        this.inputChanged.emit(void 0);\n    }\n}\n/**\n * Cell editor for text cells.\n */\nclass TextCellEditor extends InputCellEditor {\n    constructor() {\n        super(...arguments);\n        this.inputType = 'text';\n    }\n    /**\n     * Return the current text input entered.\n     */\n    getInput() {\n        return this.input.value;\n    }\n}\n/**\n * Cell editor for real number cells.\n */\nclass NumberCellEditor extends InputCellEditor {\n    constructor() {\n        super(...arguments);\n        this.inputType = 'number';\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        super.startEditing();\n        this.input.step = 'any';\n        const cell = this.cell;\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        const constraint = metadata.constraint;\n        if (constraint) {\n            if (constraint.minimum) {\n                this.input.min = constraint.minimum;\n            }\n            if (constraint.maximum) {\n                this.input.max = constraint.maximum;\n            }\n        }\n    }\n    /**\n     * Return the current number input entered. This method throws exception\n     * if input is invalid.\n     */\n    getInput() {\n        let value = this.input.value;\n        if (value.trim() === '') {\n            return null;\n        }\n        const floatValue = parseFloat(value);\n        if (isNaN(floatValue)) {\n            throw new Error('Invalid input');\n        }\n        return floatValue;\n    }\n}\n/**\n * Cell editor for integer cells.\n */\nclass IntegerCellEditor extends InputCellEditor {\n    constructor() {\n        super(...arguments);\n        this.inputType = 'number';\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        super.startEditing();\n        this.input.step = '1';\n        const cell = this.cell;\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        const constraint = metadata.constraint;\n        if (constraint) {\n            if (constraint.minimum) {\n                this.input.min = constraint.minimum;\n            }\n            if (constraint.maximum) {\n                this.input.max = constraint.maximum;\n            }\n        }\n    }\n    /**\n     * Return the current integer input entered. This method throws exception\n     * if input is invalid.\n     */\n    getInput() {\n        let value = this.input.value;\n        if (value.trim() === '') {\n            return null;\n        }\n        let intValue = parseInt(value);\n        if (isNaN(intValue)) {\n            throw new Error('Invalid input');\n        }\n        return intValue;\n    }\n}\n/**\n * Cell editor for date cells.\n */\nclass DateCellEditor extends CellEditor {\n    /**\n     * Handle the DOM events for the editor.\n     *\n     * @param event - The DOM event sent to the editor.\n     */\n    handleEvent(event) {\n        switch (event.type) {\n            case 'keydown':\n                this._onKeyDown(event);\n                break;\n            case 'blur':\n                this._onBlur(event);\n                break;\n        }\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this.isDisposed) {\n            return;\n        }\n        this._unbindEvents();\n        super.dispose();\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        this._createWidget();\n        const cell = this.cell;\n        const cellInfo = this.getCellInfo(cell);\n        this._input.value = this._deserialize(cellInfo.data);\n        this.editorContainer.appendChild(this._input);\n        this._input.focus();\n        this._bindEvents();\n    }\n    /**\n     * Return the current date input entered.\n     */\n    getInput() {\n        return this._input.value;\n    }\n    _deserialize(value) {\n        if (value === null || value === undefined) {\n            return '';\n        }\n        return value.toString();\n    }\n    _createWidget() {\n        const input = document.createElement('input');\n        input.type = 'date';\n        input.pattern = 'd{4}-d{2}-d{2}';\n        input.classList.add('lm-DataGrid-cellEditorWidget');\n        input.classList.add('lm-DataGrid-cellEditorInput');\n        this._input = input;\n    }\n    _bindEvents() {\n        this._input.addEventListener('keydown', this);\n        this._input.addEventListener('blur', this);\n    }\n    _unbindEvents() {\n        this._input.removeEventListener('keydown', this);\n        this._input.removeEventListener('blur', this);\n    }\n    _onKeyDown(event) {\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'Enter':\n                this.commit(event.shiftKey ? 'up' : 'down');\n                break;\n            case 'Tab':\n                this.commit(event.shiftKey ? 'left' : 'right');\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'Escape':\n                this.cancel();\n                break;\n        }\n    }\n    _onBlur(event) {\n        if (this.isDisposed) {\n            return;\n        }\n        if (!this.commit()) {\n            event.preventDefault();\n            event.stopPropagation();\n            this._input.focus();\n        }\n    }\n}\n/**\n * Cell editor for boolean cells.\n */\nclass BooleanCellEditor extends CellEditor {\n    /**\n     * Handle the DOM events for the editor.\n     *\n     * @param event - The DOM event sent to the editor.\n     */\n    handleEvent(event) {\n        switch (event.type) {\n            case 'keydown':\n                this._onKeyDown(event);\n                break;\n            case 'mousedown':\n                // fix focus loss problem in Safari and Firefox\n                this._input.focus();\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'blur':\n                this._onBlur(event);\n                break;\n        }\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this.isDisposed) {\n            return;\n        }\n        this._unbindEvents();\n        super.dispose();\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        this._createWidget();\n        const cell = this.cell;\n        const cellInfo = this.getCellInfo(cell);\n        this._input.checked = this._deserialize(cellInfo.data);\n        this.editorContainer.appendChild(this._input);\n        this._input.focus();\n        this._bindEvents();\n    }\n    /**\n     * Return the current boolean input entered.\n     */\n    getInput() {\n        return this._input.checked;\n    }\n    _deserialize(value) {\n        if (value === null || value === undefined) {\n            return false;\n        }\n        return value == true;\n    }\n    _createWidget() {\n        const input = document.createElement('input');\n        input.classList.add('lm-DataGrid-cellEditorWidget');\n        input.classList.add('lm-DataGrid-cellEditorCheckbox');\n        input.type = 'checkbox';\n        input.spellcheck = false;\n        this._input = input;\n    }\n    _bindEvents() {\n        this._input.addEventListener('keydown', this);\n        this._input.addEventListener('mousedown', this);\n        this._input.addEventListener('blur', this);\n    }\n    _unbindEvents() {\n        this._input.removeEventListener('keydown', this);\n        this._input.removeEventListener('mousedown', this);\n        this._input.removeEventListener('blur', this);\n    }\n    _onKeyDown(event) {\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'Enter':\n                this.commit(event.shiftKey ? 'up' : 'down');\n                break;\n            case 'Tab':\n                this.commit(event.shiftKey ? 'left' : 'right');\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'Escape':\n                this.cancel();\n                break;\n        }\n    }\n    _onBlur(event) {\n        if (this.isDisposed) {\n            return;\n        }\n        if (!this.commit()) {\n            event.preventDefault();\n            event.stopPropagation();\n            this._input.focus();\n        }\n    }\n}\n/**\n * Cell editor for option cells.\n *\n * It supports multiple option selection. If cell metadata contains\n * type attribute 'array', then it behaves as a multi select.\n * In that case cell data is expected to be list of string values.\n */\nclass OptionCellEditor extends CellEditor {\n    constructor() {\n        super(...arguments);\n        this._isMultiSelect = false;\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this.isDisposed) {\n            return;\n        }\n        super.dispose();\n        if (this._isMultiSelect) {\n            document.body.removeChild(this._select);\n        }\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        const cell = this.cell;\n        const cellInfo = this.getCellInfo(cell);\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        this._isMultiSelect = metadata.type === 'array';\n        this._createWidget();\n        if (this._isMultiSelect) {\n            this._select.multiple = true;\n            const values = this._deserialize(cellInfo.data);\n            for (let i = 0; i < this._select.options.length; ++i) {\n                const option = this._select.options.item(i);\n                option.selected = values.indexOf(option.value) !== -1;\n            }\n            document.body.appendChild(this._select);\n        }\n        else {\n            this._select.value = this._deserialize(cellInfo.data);\n            this.editorContainer.appendChild(this._select);\n        }\n        this._select.focus();\n        this._bindEvents();\n        this.updatePosition();\n    }\n    /**\n     * Return the current option input.\n     */\n    getInput() {\n        if (this._isMultiSelect) {\n            const input = [];\n            for (let i = 0; i < this._select.selectedOptions.length; ++i) {\n                input.push(this._select.selectedOptions.item(i).value);\n            }\n            return input;\n        }\n        else {\n            return this._select.value;\n        }\n    }\n    /**\n     * Reposition cell editor.\n     */\n    updatePosition() {\n        super.updatePosition();\n        if (!this._isMultiSelect) {\n            return;\n        }\n        const cellInfo = this.getCellInfo(this.cell);\n        this._select.style.position = 'absolute';\n        const editorContainerRect = this.editorContainer.getBoundingClientRect();\n        this._select.style.left = editorContainerRect.left + 'px';\n        this._select.style.top = editorContainerRect.top + cellInfo.height + 'px';\n        this._select.style.width = editorContainerRect.width + 'px';\n        this._select.style.maxHeight = '60px';\n        this.editorContainer.style.visibility = 'hidden';\n    }\n    _deserialize(value) {\n        if (value === null || value === undefined) {\n            return '';\n        }\n        if (this._isMultiSelect) {\n            const values = [];\n            if (Array.isArray(value)) {\n                for (let item of value) {\n                    values.push(item.toString());\n                }\n            }\n            return values;\n        }\n        else {\n            return value.toString();\n        }\n    }\n    _createWidget() {\n        const cell = this.cell;\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        const items = metadata.constraint.enum;\n        const select = document.createElement('select');\n        select.classList.add('lm-DataGrid-cellEditorWidget');\n        for (let item of items) {\n            const option = document.createElement('option');\n            option.value = item;\n            option.text = item;\n            select.appendChild(option);\n        }\n        this._select = select;\n    }\n    _bindEvents() {\n        this._select.addEventListener('keydown', this._onKeyDown.bind(this));\n        this._select.addEventListener('blur', this._onBlur.bind(this));\n    }\n    _onKeyDown(event) {\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'Enter':\n                this.commit(event.shiftKey ? 'up' : 'down');\n                break;\n            case 'Tab':\n                this.commit(event.shiftKey ? 'left' : 'right');\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'Escape':\n                this.cancel();\n                break;\n        }\n    }\n    _onBlur(event) {\n        if (this.isDisposed) {\n            return;\n        }\n        if (!this.commit()) {\n            event.preventDefault();\n            event.stopPropagation();\n            this._select.focus();\n        }\n    }\n}\n/**\n * Cell editor for option cells whose value can be any value\n * from set of pre-defined options or values that can be input by user.\n */\nclass DynamicOptionCellEditor extends CellEditor {\n    /**\n     * Handle the DOM events for the editor.\n     *\n     * @param event - The DOM event sent to the editor.\n     */\n    handleEvent(event) {\n        switch (event.type) {\n            case 'keydown':\n                this._onKeyDown(event);\n                break;\n            case 'blur':\n                this._onBlur(event);\n                break;\n        }\n    }\n    /**\n     * Dispose of the resources held by cell editor.\n     */\n    dispose() {\n        if (this.isDisposed) {\n            return;\n        }\n        this._unbindEvents();\n        super.dispose();\n    }\n    /**\n     * Start editing the cell.\n     */\n    startEditing() {\n        this._createWidget();\n        const cell = this.cell;\n        const cellInfo = this.getCellInfo(cell);\n        this._input.value = this._deserialize(cellInfo.data);\n        this.editorContainer.appendChild(this._input);\n        this._input.focus();\n        this._input.select();\n        this._bindEvents();\n    }\n    /**\n     * Return the current option input.\n     */\n    getInput() {\n        return this._input.value;\n    }\n    _deserialize(value) {\n        if (value === null || value === undefined) {\n            return '';\n        }\n        return value.toString();\n    }\n    _createWidget() {\n        const cell = this.cell;\n        const grid = cell.grid;\n        const dataModel = grid.dataModel;\n        const rowCount = dataModel.rowCount('body');\n        const listId = 'cell-editor-list';\n        const list = document.createElement('datalist');\n        list.id = listId;\n        const input = document.createElement('input');\n        input.classList.add('lm-DataGrid-cellEditorWidget');\n        input.classList.add('lm-DataGrid-cellEditorInput');\n        const valueSet = new Set();\n        for (let r = 0; r < rowCount; ++r) {\n            const data = dataModel.data('body', r, cell.column);\n            if (data) {\n                valueSet.add(data);\n            }\n        }\n        valueSet.forEach((value) => {\n            const option = document.createElement('option');\n            option.value = value;\n            option.text = value;\n            list.appendChild(option);\n        });\n        this.editorContainer.appendChild(list);\n        input.setAttribute('list', listId);\n        this._input = input;\n    }\n    _bindEvents() {\n        this._input.addEventListener('keydown', this);\n        this._input.addEventListener('blur', this);\n    }\n    _unbindEvents() {\n        this._input.removeEventListener('keydown', this);\n        this._input.removeEventListener('blur', this);\n    }\n    _onKeyDown(event) {\n        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {\n            case 'Enter':\n                this.commit(event.shiftKey ? 'up' : 'down');\n                break;\n            case 'Tab':\n                this.commit(event.shiftKey ? 'left' : 'right');\n                event.stopPropagation();\n                event.preventDefault();\n                break;\n            case 'Escape':\n                this.cancel();\n                break;\n        }\n    }\n    _onBlur(event) {\n        if (this.isDisposed) {\n            return;\n        }\n        if (!this.commit()) {\n            event.preventDefault();\n            event.stopPropagation();\n            this._input.focus();\n        }\n    }\n}\n/**\n * The namespace for the `CellEditor` class statics.\n */\n(function (CellEditor) {\n    /**\n     * A widget which implements a notification popup.\n     */\n    class Notification extends _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget {\n        /**\n         * Construct a new notification.\n         *\n         * @param options - The options for initializing the notification.\n         */\n        constructor(options) {\n            super({ node: Notification.createNode() });\n            this._message = '';\n            this.addClass('lm-DataGrid-notification');\n            this.setFlag(_lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.Flag.DisallowLayout);\n            this._target = options.target;\n            this._message = options.message || '';\n            this._placement = options.placement || 'bottom';\n            _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.attach(this, document.body);\n            if (options.timeout && options.timeout > 0) {\n                setTimeout(() => {\n                    this.close();\n                }, options.timeout);\n            }\n        }\n        /**\n         * Handle the DOM events for the notification.\n         *\n         * @param event - The DOM event sent to the notification.\n         *\n         * #### Notes\n         * This method implements the DOM `EventListener` interface and is\n         * called in response to events on the notification's DOM node.\n         *\n         * This should not be called directly by user code.\n         */\n        handleEvent(event) {\n            switch (event.type) {\n                case 'mousedown':\n                    this._evtMouseDown(event);\n                    break;\n                case 'contextmenu':\n                    event.preventDefault();\n                    event.stopPropagation();\n                    break;\n            }\n        }\n        /**\n         * Get the placement of the notification.\n         */\n        get placement() {\n            return this._placement;\n        }\n        /**\n         * Set the placement of the notification.\n         */\n        set placement(value) {\n            // Do nothing if the placement does not change.\n            if (this._placement === value) {\n                return;\n            }\n            // Update the internal placement.\n            this._placement = value;\n            // Schedule an update for notification.\n            this.update();\n        }\n        /**\n         * Get the current value of the message.\n         */\n        get message() {\n            return this._message;\n        }\n        /**\n         * Set the current value of the message.\n         *\n         */\n        set message(value) {\n            // Do nothing if the value does not change.\n            if (this._message === value) {\n                return;\n            }\n            // Update the internal value.\n            this._message = value;\n            // Schedule an update for notification.\n            this.update();\n        }\n        /**\n         * Get the node presenting the message.\n         */\n        get messageNode() {\n            return this.node.getElementsByClassName('lm-DataGrid-notificationMessage')[0];\n        }\n        /**\n         * A method invoked on a 'before-attach' message.\n         */\n        onBeforeAttach(msg) {\n            this.node.addEventListener('mousedown', this);\n            this.update();\n        }\n        /**\n         * A method invoked on an 'after-detach' message.\n         */\n        onAfterDetach(msg) {\n            this.node.removeEventListener('mousedown', this);\n        }\n        /**\n         * A method invoked on an 'update-request' message.\n         */\n        onUpdateRequest(msg) {\n            const targetRect = this._target.getBoundingClientRect();\n            const style = this.node.style;\n            switch (this._placement) {\n                case 'bottom':\n                    style.left = targetRect.left + 'px';\n                    style.top = targetRect.bottom + 'px';\n                    break;\n                case 'top':\n                    style.left = targetRect.left + 'px';\n                    style.height = targetRect.top + 'px';\n                    style.top = '0';\n                    style.alignItems = 'flex-end';\n                    style.justifyContent = 'flex-end';\n                    break;\n                case 'left':\n                    style.left = '0';\n                    style.width = targetRect.left + 'px';\n                    style.top = targetRect.top + 'px';\n                    style.alignItems = 'flex-end';\n                    style.justifyContent = 'flex-end';\n                    break;\n                case 'right':\n                    style.left = targetRect.right + 'px';\n                    style.top = targetRect.top + 'px';\n                    break;\n            }\n            this.messageNode.innerHTML = this._message;\n        }\n        /**\n         * Handle the `'mousedown'` event for the notification.\n         */\n        _evtMouseDown(event) {\n            // Do nothing if it's not a left mouse press.\n            if (event.button !== 0) {\n                return;\n            }\n            event.preventDefault();\n            event.stopPropagation();\n            this.close();\n        }\n    }\n    CellEditor.Notification = Notification;\n    /**\n     * The namespace for the `Notification` class statics.\n     */\n    (function (Notification) {\n        /**\n         * Create the DOM node for notification.\n         */\n        function createNode() {\n            const node = document.createElement('div');\n            const container = document.createElement('div');\n            container.className = 'lm-DataGrid-notificationContainer';\n            const message = document.createElement('span');\n            message.className = 'lm-DataGrid-notificationMessage';\n            container.appendChild(message);\n            node.appendChild(container);\n            return node;\n        }\n        Notification.createNode = createNode;\n    })(Notification = CellEditor.Notification || (CellEditor.Notification = {}));\n})(CellEditor || (CellEditor = {}));\n\n/*\n * Copyright (c) Jupyter Development Team.\n * Distributed under the terms of the Modified BSD License.\n */\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * Resolve a config option for a cell editor.\n *\n * @param option - The config option to resolve.\n *\n * @param config - The cell config object.\n *\n * @returns The resolved value for the option.\n */\nfunction resolveOption(option, config) {\n    return typeof option === 'function'\n        ? option(config)\n        : option;\n}\n/**\n * An object which manages cell editing. It stores editor overrides,\n * decides which editor to use for a cell, makes sure there is only one editor active.\n */\nclass CellEditorController {\n    constructor() {\n        // active cell editor\n        this._editor = null;\n        // active cell being edited\n        this._cell = null;\n        // cell editor overrides based on cell data type identifier\n        this._typeBasedOverrides = new Map();\n        // cell editor overrides based on partial metadata match\n        this._metadataBasedOverrides = new Map();\n    }\n    /**\n     * Override cell editor for the cells matching the identifier.\n     *\n     * @param identifier - Cell identifier to use when matching cells.\n     * if identifier is a CellDataType, then cell matching is done using data type of the cell,\n     * if identifier is a Metadata, then partial match of the cell metadata with identifier is used for match,\n     * if identifier is 'default' then override is used as default editor when no other editor is found suitable\n     *\n     * @param editor - The cell editor to use or resolver to use to get an editor for matching cells.\n     */\n    setEditor(identifier, editor) {\n        if (typeof identifier === 'string') {\n            this._typeBasedOverrides.set(identifier, editor);\n        }\n        else {\n            const key = this._metadataIdentifierToKey(identifier);\n            this._metadataBasedOverrides.set(key, [identifier, editor]);\n        }\n    }\n    /**\n     * Start editing a cell.\n     *\n     * @param cell - The object holding cell configuration data.\n     *\n     * @param options - The cell editing options.\n     */\n    edit(cell, options) {\n        const grid = cell.grid;\n        if (!grid.editable) {\n            console.error('Grid cannot be edited!');\n            return false;\n        }\n        this.cancel();\n        this._cell = cell;\n        options = options || {};\n        options.onCommit = options.onCommit || this._onCommit.bind(this);\n        options.onCancel = options.onCancel || this._onCancel.bind(this);\n        // if an editor is passed in with options, then use it for editing\n        if (options.editor) {\n            this._editor = options.editor;\n            options.editor.edit(cell, options);\n            return true;\n        }\n        // choose an editor based on overrides / cell data type\n        const editor = this._getEditor(cell);\n        if (editor) {\n            this._editor = editor;\n            editor.edit(cell, options);\n            return true;\n        }\n        return false;\n    }\n    /**\n     * Cancel editing.\n     */\n    cancel() {\n        if (this._editor) {\n            this._editor.cancel();\n            this._editor = null;\n        }\n        this._cell = null;\n    }\n    _onCommit(response) {\n        const cell = this._cell;\n        if (!cell) {\n            return;\n        }\n        const grid = cell.grid;\n        const dataModel = grid.dataModel;\n        let row = cell.row;\n        let column = cell.column;\n        const cellGroup = CellGroup.getGroup(grid.dataModel, 'body', row, column);\n        if (cellGroup) {\n            row = cellGroup.r1;\n            column = cellGroup.c1;\n        }\n        dataModel.setData('body', row, column, response.value);\n        grid.viewport.node.focus();\n        if (response.cursorMovement !== 'none') {\n            grid.moveCursor(response.cursorMovement);\n            grid.scrollToCursor();\n        }\n    }\n    _onCancel() {\n        if (!this._cell) {\n            return;\n        }\n        this._cell.grid.viewport.node.focus();\n    }\n    _getDataTypeKey(cell) {\n        const metadata = cell.grid.dataModel\n            ? cell.grid.dataModel.metadata('body', cell.row, cell.column)\n            : null;\n        if (!metadata) {\n            return 'default';\n        }\n        let key = '';\n        if (metadata) {\n            key = metadata.type;\n        }\n        if (metadata.constraint && metadata.constraint.enum) {\n            if (metadata.constraint.enum === 'dynamic') {\n                key += ':dynamic-option';\n            }\n            else {\n                key += ':option';\n            }\n        }\n        return key;\n    }\n    _objectToKey(object) {\n        let str = '';\n        for (let key in object) {\n            const value = object[key];\n            if (typeof value === 'object') {\n                str += `${key}:${this._objectToKey(value)}`;\n            }\n            else {\n                str += `[${key}:${value}]`;\n            }\n        }\n        return str;\n    }\n    _metadataIdentifierToKey(metadata) {\n        return this._objectToKey(metadata);\n    }\n    _metadataMatchesIdentifier(metadata, identifier) {\n        for (let key in identifier) {\n            if (!metadata.hasOwnProperty(key)) {\n                return false;\n            }\n            const identifierValue = identifier[key];\n            const metadataValue = metadata[key];\n            if (typeof identifierValue === 'object') {\n                if (!this._metadataMatchesIdentifier(metadataValue, identifierValue)) {\n                    return false;\n                }\n            }\n            else if (metadataValue !== identifierValue) {\n                return false;\n            }\n        }\n        return true;\n    }\n    _getMetadataBasedEditor(cell) {\n        let editorMatched;\n        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);\n        if (metadata) {\n            this._metadataBasedOverrides.forEach(value => {\n                if (!editorMatched) {\n                    let [identifier, editor] = value;\n                    if (this._metadataMatchesIdentifier(metadata, identifier)) {\n                        editorMatched = resolveOption(editor, cell);\n                    }\n                }\n            });\n        }\n        return editorMatched;\n    }\n    /**\n     * Choose the most appropriate cell editor to use based on overrides / cell data type.\n     *\n     * If no match is found in overrides or based on cell data type, and if cell has a primitive\n     * data type then TextCellEditor is used as default cell editor. If 'default' cell editor\n     * is overridden, then it is used instead of TextCellEditor for default.\n     */\n    _getEditor(cell) {\n        const dtKey = this._getDataTypeKey(cell);\n        // find an editor based on data type based override\n        if (this._typeBasedOverrides.has(dtKey)) {\n            const editor = this._typeBasedOverrides.get(dtKey);\n            return resolveOption(editor, cell);\n        } // find an editor based on metadata match based override\n        else if (this._metadataBasedOverrides.size > 0) {\n            const editor = this._getMetadataBasedEditor(cell);\n            if (editor) {\n                return editor;\n            }\n        }\n        // choose an editor based on data type\n        switch (dtKey) {\n            case 'string':\n                return new TextCellEditor();\n            case 'number':\n                return new NumberCellEditor();\n            case 'integer':\n                return new IntegerCellEditor();\n            case 'boolean':\n                return new BooleanCellEditor();\n            case 'date':\n                return new DateCellEditor();\n            case 'string:option':\n            case 'number:option':\n            case 'integer:option':\n            case 'date:option':\n            case 'array:option':\n                return new OptionCellEditor();\n            case 'string:dynamic-option':\n            case 'number:dynamic-option':\n            case 'integer:dynamic-option':\n            case 'date:dynamic-option':\n                return new DynamicOptionCellEditor();\n        }\n        // if an override exists for 'default', then use it\n        if (this._typeBasedOverrides.has('default')) {\n            const editor = this._typeBasedOverrides.get('default');\n            return resolveOption(editor, cell);\n        }\n        // if cell has a primitive data type then use TextCellEditor\n        const data = cell.grid.dataModel.data('body', cell.row, cell.column);\n        if (!data || typeof data !== 'object') {\n            return new TextCellEditor();\n        }\n        // no suitable editor found for the cell\n        return undefined;\n    }\n}\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * An object which provides the data for a data grid.\n *\n * #### Notes\n * If the predefined data models are insufficient for a particular use\n * case, a custom model can be defined which derives from this class.\n */\nclass DataModel {\n    constructor() {\n        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);\n    }\n    /**\n     * A signal emitted when the data model has changed.\n     */\n    get changed() {\n        return this._changed;\n    }\n    /**\n     * Get the count of merged cell groups pertaining to a given\n     * cell region.\n     * @param region the target cell region.\n     */\n    groupCount(region) {\n        return 0;\n    }\n    /**\n     * Get the metadata for a cell in the data model.\n     *\n     * @param region - The cell region of interest.\n     *\n     * @param row - The row index of the cell of interest.\n     *\n     * @param column - The column index of the cell of interest.\n     *\n     * @returns The metadata for the specified cell.\n     *\n     * #### Notes\n     * The returned metadata should be treated as immutable.\n     *\n     * This method is called often, and so should be efficient.\n     *\n     * The default implementation returns `{}`.\n     */\n    metadata(region, row, column) {\n        return DataModel.emptyMetadata;\n    }\n    /**\n     * Get the merged cell group corresponding to a region and index number.\n     * @param region the cell region of cell group.\n     * @param groupIndex the group index of the cell group.\n     * @returns a cell group.\n     */\n    group(region, groupIndex) {\n        return null;\n    }\n    /**\n     * Emit the `changed` signal for the data model.\n     *\n     * #### Notes\n     * Subclasses should call this method whenever the data model has\n     * changed so that attached data grids can update themselves.\n     */\n    emitChanged(args) {\n        this._changed.emit(args);\n    }\n}\n/**\n * An object which provides the mutable data for a data grid.\n *\n * #### Notes\n * This object is an extension to `DataModel` and it only adds ability to\n * change data for cells.\n */\nclass MutableDataModel extends DataModel {\n}\n/**\n * The namespace for the `DataModel` class statics.\n */\n(function (DataModel) {\n    /**\n     * A singleton empty metadata object.\n     */\n    DataModel.emptyMetadata = Object.freeze({});\n})(DataModel || (DataModel = {}));\n\n/**\n * A thin caching wrapper around a 2D canvas rendering context.\n *\n * #### Notes\n * This class is mostly a transparent wrapper around a canvas rendering\n * context which improves performance when writing context state.\n *\n * For best performance, avoid reading state from the `gc`. Writes are\n * cached based on the previously written value.\n *\n * Unless otherwise specified, the API and semantics of this class are\n * identical to the builtin 2D canvas rendering context:\n * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D\n *\n * The wrapped canvas context should not be manipulated externally\n * until the wrapping `GraphicsContext` object is disposed.\n */\nclass GraphicsContext {\n    /**\n     * Create a new graphics context object.\n     *\n     * @param context - The 2D canvas rendering context to wrap.\n     */\n    constructor(context) {\n        this._disposed = false;\n        this._context = context;\n        this._state = Private$3.State.create(context);\n    }\n    dispose() {\n        // Bail if the gc is already disposed.\n        if (this._disposed) {\n            return;\n        }\n        // Mark the gc as disposed.\n        this._disposed = true;\n        // Pop any unrestored saves.\n        while (this._state.next) {\n            this._state = this._state.next;\n            this._context.restore();\n        }\n    }\n    get isDisposed() {\n        return this._disposed;\n    }\n    get fillStyle() {\n        return this._context.fillStyle;\n    }\n    set fillStyle(value) {\n        if (this._state.fillStyle !== value) {\n            this._state.fillStyle = value;\n            this._context.fillStyle = value;\n        }\n    }\n    get strokeStyle() {\n        return this._context.strokeStyle;\n    }\n    set strokeStyle(value) {\n        if (this._state.strokeStyle !== value) {\n            this._state.strokeStyle = value;\n            this._context.strokeStyle = value;\n        }\n    }\n    get font() {\n        return this._context.font;\n    }\n    set font(value) {\n        if (this._state.font !== value) {\n            this._state.font = value;\n            this._context.font = value;\n        }\n    }\n    get textAlign() {\n        return this._context.textAlign;\n    }\n    set textAlign(value) {\n        if (this._state.textAlign !== value) {\n            this._state.textAlign = value;\n            this._context.textAlign = value;\n        }\n    }\n    get textBaseline() {\n        return this._context.textBaseline;\n    }\n    set textBaseline(value) {\n        if (this._state.textBaseline !== value) {\n            this._state.textBaseline = value;\n            this._context.textBaseline = value;\n        }\n    }\n    get lineCap() {\n        return this._context.lineCap;\n    }\n    set lineCap(value) {\n        if (this._state.lineCap !== value) {\n            this._state.lineCap = value;\n            this._context.lineCap = value;\n        }\n    }\n    get lineDashOffset() {\n        return this._context.lineDashOffset;\n    }\n    set lineDashOffset(value) {\n        if (this._state.lineDashOffset !== value) {\n            this._state.lineDashOffset = value;\n            this._context.lineDashOffset = value;\n        }\n    }\n    get lineJoin() {\n        return this._context.lineJoin;\n    }\n    set lineJoin(value) {\n        if (this._state.lineJoin !== value) {\n            this._state.lineJoin = value;\n            this._context.lineJoin = value;\n        }\n    }\n    get lineWidth() {\n        return this._context.lineWidth;\n    }\n    set lineWidth(value) {\n        if (this._state.lineWidth !== value) {\n            this._state.lineWidth = value;\n            this._context.lineWidth = value;\n        }\n    }\n    get miterLimit() {\n        return this._context.miterLimit;\n    }\n    set miterLimit(value) {\n        if (this._state.miterLimit !== value) {\n            this._state.miterLimit = value;\n            this._context.miterLimit = value;\n        }\n    }\n    get shadowBlur() {\n        return this._context.shadowBlur;\n    }\n    set shadowBlur(value) {\n        if (this._state.shadowBlur !== value) {\n            this._state.shadowBlur = value;\n            this._context.shadowBlur = value;\n        }\n    }\n    get shadowColor() {\n        return this._context.shadowColor;\n    }\n    set shadowColor(value) {\n        if (this._state.shadowColor !== value) {\n            this._state.shadowColor = value;\n            this._context.shadowColor = value;\n        }\n    }\n    get shadowOffsetX() {\n        return this._context.shadowOffsetX;\n    }\n    set shadowOffsetX(value) {\n        if (this._state.shadowOffsetX !== value) {\n            this._state.shadowOffsetX = value;\n            this._context.shadowOffsetX = value;\n        }\n    }\n    get shadowOffsetY() {\n        return this._context.shadowOffsetY;\n    }\n    set shadowOffsetY(value) {\n        if (this._state.shadowOffsetY !== value) {\n            this._state.shadowOffsetY = value;\n            this._context.shadowOffsetY = value;\n        }\n    }\n    get imageSmoothingEnabled() {\n        return this._context.imageSmoothingEnabled;\n    }\n    set imageSmoothingEnabled(value) {\n        if (this._state.imageSmoothingEnabled !== value) {\n            this._state.imageSmoothingEnabled = value;\n            this._context.imageSmoothingEnabled = value;\n        }\n    }\n    get globalAlpha() {\n        return this._context.globalAlpha;\n    }\n    set globalAlpha(value) {\n        if (this._state.globalAlpha !== value) {\n            this._state.globalAlpha = value;\n            this._context.globalAlpha = value;\n        }\n    }\n    get globalCompositeOperation() {\n        return this._context.globalCompositeOperation;\n    }\n    set globalCompositeOperation(value) {\n        if (this._state.globalCompositeOperation !== value) {\n            this._state.globalCompositeOperation = value;\n            this._context.globalCompositeOperation = value;\n        }\n    }\n    getLineDash() {\n        return this._context.getLineDash();\n    }\n    setLineDash(segments) {\n        this._context.setLineDash(segments);\n    }\n    rotate(angle) {\n        this._context.rotate(angle);\n    }\n    scale(x, y) {\n        this._context.scale(x, y);\n    }\n    transform(m11, m12, m21, m22, dx, dy) {\n        this._context.transform(m11, m12, m21, m22, dx, dy);\n    }\n    translate(x, y) {\n        this._context.translate(x, y);\n    }\n    setTransform(m11, m12, m21, m22, dx, dy) {\n        this._context.setTransform(m11, m12, m21, m22, dx, dy);\n    }\n    save() {\n        // Clone an push the current state to the stack.\n        this._state = Private$3.State.push(this._state);\n        // Save the wrapped context state.\n        this._context.save();\n    }\n    restore() {\n        // Bail if there is no state to restore.\n        if (!this._state.next) {\n            return;\n        }\n        // Pop the saved state from the stack.\n        this._state = Private$3.State.pop(this._state);\n        // Restore the wrapped context state.\n        this._context.restore();\n    }\n    beginPath() {\n        return this._context.beginPath();\n    }\n    closePath() {\n        this._context.closePath();\n    }\n    isPointInPath(x, y, fillRule) {\n        let result;\n        if (arguments.length === 2) {\n            result = this._context.isPointInPath(x, y);\n        }\n        else {\n            result = this._context.isPointInPath(x, y, fillRule);\n        }\n        return result;\n    }\n    arc(x, y, radius, startAngle, endAngle, anticlockwise) {\n        if (arguments.length === 5) {\n            this._context.arc(x, y, radius, startAngle, endAngle);\n        }\n        else {\n            this._context.arc(x, y, radius, startAngle, endAngle, anticlockwise);\n        }\n    }\n    arcTo(x1, y1, x2, y2, radius) {\n        this._context.arcTo(x1, y1, x2, y2, radius);\n    }\n    bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {\n        this._context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);\n    }\n    ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {\n        if (arguments.length === 7) {\n            this._context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle);\n        }\n        else {\n            this._context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);\n        }\n    }\n    lineTo(x, y) {\n        this._context.lineTo(x, y);\n    }\n    moveTo(x, y) {\n        this._context.moveTo(x, y);\n    }\n    quadraticCurveTo(cpx, cpy, x, y) {\n        this._context.quadraticCurveTo(cpx, cpy, x, y);\n    }\n    rect(x, y, w, h) {\n        this._context.rect(x, y, w, h);\n    }\n    clip(fillRule) {\n        if (arguments.length === 0) {\n            this._context.clip();\n        }\n        else {\n            this._context.clip(fillRule);\n        }\n    }\n    fill(fillRule) {\n        if (arguments.length === 0) {\n            this._context.fill();\n        }\n        else {\n            this._context.fill(fillRule);\n        }\n    }\n    stroke() {\n        this._context.stroke();\n    }\n    clearRect(x, y, w, h) {\n        return this._context.clearRect(x, y, w, h);\n    }\n    fillRect(x, y, w, h) {\n        this._context.fillRect(x, y, w, h);\n    }\n    fillText(text, x, y, maxWidth) {\n        if (arguments.length === 3) {\n            this._context.fillText(text, x, y);\n        }\n        else {\n            this._context.fillText(text, x, y, maxWidth);\n        }\n    }\n    strokeRect(x, y, w, h) {\n        this._context.strokeRect(x, y, w, h);\n    }\n    strokeText(text, x, y, maxWidth) {\n        if (arguments.length === 3) {\n            this._context.strokeText(text, x, y);\n        }\n        else {\n            this._context.strokeText(text, x, y, maxWidth);\n        }\n    }\n    measureText(text) {\n        return this._context.measureText(text);\n    }\n    createLinearGradient(x0, y0, x1, y1) {\n        return this._context.createLinearGradient(x0, y0, x1, y1);\n    }\n    createRadialGradient(x0, y0, r0, x1, y1, r1) {\n        return this._context.createRadialGradient(x0, y0, r0, x1, y1, r1);\n    }\n    createPattern(image, repetition) {\n        return this._context.createPattern(image, repetition);\n    }\n    createImageData() {\n        // eslint-disable-next-line prefer-spread, prefer-rest-params\n        return this._context.createImageData.apply(this._context, arguments);\n    }\n    getImageData(sx, sy, sw, sh) {\n        return this._context.getImageData(sx, sy, sw, sh);\n    }\n    putImageData() {\n        // eslint-disable-next-line prefer-spread, prefer-rest-params\n        this._context.putImageData.apply(this._context, arguments);\n    }\n    drawImage() {\n        // eslint-disable-next-line prefer-spread, prefer-rest-params\n        this._context.drawImage.apply(this._context, arguments);\n    }\n    drawFocusIfNeeded(element) {\n        this._context.drawFocusIfNeeded(element);\n    }\n}\n/**\n * The namespace for the module implementation details.\n */\nvar Private$3;\n(function (Private) {\n    /**\n     * The index of next valid pool object.\n     */\n    let pi = -1;\n    /**\n     * A state object allocation pool.\n     */\n    const pool = [];\n    /**\n     * An object which holds the state for a gc.\n     */\n    class State {\n        /**\n         * Create a gc state object from a 2D canvas context.\n         */\n        static create(context) {\n            let state = pi < 0 ? new State() : pool[pi--];\n            state.next = null;\n            state.fillStyle = context.fillStyle;\n            state.font = context.font;\n            state.globalAlpha = context.globalAlpha;\n            state.globalCompositeOperation = context.globalCompositeOperation;\n            state.imageSmoothingEnabled = context.imageSmoothingEnabled;\n            state.lineCap = context.lineCap;\n            state.lineDashOffset = context.lineDashOffset;\n            state.lineJoin = context.lineJoin;\n            state.lineWidth = context.lineWidth;\n            state.miterLimit = context.miterLimit;\n            state.shadowBlur = context.shadowBlur;\n            state.shadowColor = context.shadowColor;\n            state.shadowOffsetX = context.shadowOffsetX;\n            state.shadowOffsetY = context.shadowOffsetY;\n            state.strokeStyle = context.strokeStyle;\n            state.textAlign = context.textAlign;\n            state.textBaseline = context.textBaseline;\n            return state;\n        }\n        /**\n         * Clone an existing gc state object and add it to the state stack.\n         */\n        static push(other) {\n            let state = pi < 0 ? new State() : pool[pi--];\n            state.next = other;\n            state.fillStyle = other.fillStyle;\n            state.font = other.font;\n            state.globalAlpha = other.globalAlpha;\n            state.globalCompositeOperation = other.globalCompositeOperation;\n            state.imageSmoothingEnabled = other.imageSmoothingEnabled;\n            state.lineCap = other.lineCap;\n            state.lineDashOffset = other.lineDashOffset;\n            state.lineJoin = other.lineJoin;\n            state.lineWidth = other.lineWidth;\n            state.miterLimit = other.miterLimit;\n            state.shadowBlur = other.shadowBlur;\n            state.shadowColor = other.shadowColor;\n            state.shadowOffsetX = other.shadowOffsetX;\n            state.shadowOffsetY = other.shadowOffsetY;\n            state.strokeStyle = other.strokeStyle;\n            state.textAlign = other.textAlign;\n            state.textBaseline = other.textBaseline;\n            return state;\n        }\n        /**\n         * Pop the next state object and return the current to the pool\n         */\n        static pop(state) {\n            state.fillStyle = '';\n            state.strokeStyle = '';\n            pool[++pi] = state;\n            return state.next;\n        }\n    }\n    Private.State = State;\n})(Private$3 || (Private$3 = {}));\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * A class which manages the mapping of cell renderers.\n */\nclass RendererMap {\n    /**\n     * Construct a new renderer map.\n     *\n     * @param values - The initial values for the map.\n     *\n     * @param fallback - The renderer of last resort.\n     */\n    constructor(values = {}, fallback) {\n        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);\n        this._values = { ...values };\n        this._fallback = fallback || new TextRenderer();\n    }\n    /**\n     * A signal emitted when the renderer map has changed.\n     */\n    get changed() {\n        return this._changed;\n    }\n    /**\n     * Get the cell renderer to use for the given cell config.\n     *\n     * @param config - The cell config of interest.\n     *\n     * @returns The renderer to use for the cell.\n     */\n    get(config) {\n        // Fetch the renderer from the values map.\n        let renderer = this._values[config.region];\n        // Execute a resolver function if necessary.\n        if (typeof renderer === 'function') {\n            try {\n                renderer = renderer(config);\n            }\n            catch (err) {\n                renderer = undefined;\n                console.error(err);\n            }\n        }\n        // Return the renderer or the fallback.\n        return renderer || this._fallback;\n    }\n    /**\n     * Update the renderer map with new values\n     *\n     * @param values - The updated values for the map.\n     *\n     * @param fallback - The renderer of last resort.\n     *\n     * #### Notes\n     * This method always emits the `changed` signal.\n     */\n    update(values = {}, fallback) {\n        this._values = { ...this._values, ...values };\n        this._fallback = fallback || this._fallback;\n        this._changed.emit(undefined);\n    }\n}\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2019, PhosphorJS Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\n/**\n * An object which manages a collection of variable sized sections.\n *\n * #### Notes\n * This class is an implementation detail. It is designed to manage\n * the variable row and column sizes for a data grid. User code will\n * not interact with this class directly.\n */\nclass SectionList {\n    /**\n     * Construct a new section list.\n     *\n     * @param options - The options for initializing the list.\n     */\n    constructor(options) {\n        this._count = 0;\n        this._length = 0;\n        this._sections = [];\n        this._minimumSize = options.minimumSize || 2;\n        this._defaultSize = Math.max(this._minimumSize, Math.floor(options.defaultSize));\n    }\n    /**\n     * The total size of all sections in the list.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    get length() {\n        return this._length;\n    }\n    /**\n     * The total number of sections in the list.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    get count() {\n        return this._count;\n    }\n    /**\n     * Get the minimum size of sections in the list.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    get minimumSize() {\n        return this._minimumSize;\n    }\n    /**\n     * Set the minimum size of sections in the list.\n     *\n     * #### Complexity\n     * Linear on the number of resized sections.\n     */\n    set minimumSize(value) {\n        // Normalize the value.\n        value = Math.max(2, Math.floor(value));\n        // Bail early if the value does not change.\n        if (this._minimumSize === value) {\n            return;\n        }\n        // Update the internal minimum size.\n        this._minimumSize = value;\n        // Update default size if larger than minimum size\n        if (value > this._defaultSize) {\n            this.defaultSize = value;\n        }\n    }\n    /**\n     * Get the default size of sections in the list.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    get defaultSize() {\n        return this._defaultSize;\n    }\n    /**\n     * Set the default size of sections in the list.\n     *\n     * #### Complexity\n     * Linear on the number of resized sections.\n     */\n    set defaultSize(value) {\n        // Normalize the value.\n        value = Math.max(this._minimumSize, Math.floor(value));\n        // Bail early if the value does not change.\n        if (this._defaultSize === value) {\n            return;\n        }\n        // Compute the delta default size.\n        let delta = value - this._defaultSize;\n        // Update the internal default size.\n        this._defaultSize = value;\n        // Update the length.\n        this._length += delta * (this._count - this._sections.length);\n        // Bail early if there are no modified sections.\n        if (this._sections.length === 0) {\n            return;\n        }\n        // Recompute the offsets of the modified sections.\n        for (let i = 0, n = this._sections.length; i < n; ++i) {\n            // Look up the previous and current modified sections.\n            let prev = this._sections[i - 1];\n            let curr = this._sections[i];\n            // Adjust the offset for the current section.\n            if (prev) {\n                let count = curr.index - prev.index - 1;\n                curr.offset = prev.offset + prev.size + count * value;\n            }\n            else {\n                curr.offset = curr.index * value;\n            }\n        }\n    }\n    /**\n     * Clamp a size to the minimum section size\n     *\n     * @param size - The size to clamp.\n     *\n     * @returns The size or the section minimum size, whichever is larger\n     */\n    clampSize(size) {\n        return Math.max(this._minimumSize, Math.floor(size));\n    }\n    /**\n     * Find the index of the section which covers the given offset.\n     *\n     * @param offset - The offset of the section of interest.\n     *\n     * @returns The index of the section which covers the given offset,\n     *   or `-1` if the offset is out of range.\n     *\n     * #### Complexity\n     * Logarithmic on the number of resized sections.\n     */\n    indexOf(offset) {\n        // Bail early if the offset is out of range.\n        if (offset < 0 || offset >= this._length || this._count === 0) {\n            return -1;\n        }\n        // Handle the simple case of no modified sections.\n        if (this._sections.length === 0) {\n            return Math.floor(offset / this._defaultSize);\n        }\n        // Find the modified section for the given offset.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, offset, Private$2.offsetCmp);\n        // Return the index of an exact match.\n        if (i < this._sections.length && this._sections[i].offset <= offset) {\n            return this._sections[i].index;\n        }\n        // Handle the case of no modified sections before the offset.\n        if (i === 0) {\n            return Math.floor(offset / this._defaultSize);\n        }\n        // Compute the index from the previous modified section.\n        let section = this._sections[i - 1];\n        let span = offset - (section.offset + section.size);\n        return section.index + Math.floor(span / this._defaultSize) + 1;\n    }\n    /**\n     * Find the offset of the section at the given index.\n     *\n     * @param index - The index of the section of interest.\n     *\n     * @returns The offset of the section at the given index, or `-1`\n     *   if the index is out of range.\n     *\n     * #### Undefined Behavior\n     * An `index` which is non-integral.\n     *\n     * #### Complexity\n     * Logarithmic on the number of resized sections.\n     */\n    offsetOf(index) {\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= this._count) {\n            return -1;\n        }\n        // Handle the simple case of no modified sections.\n        if (this._sections.length === 0) {\n            return index * this._defaultSize;\n        }\n        // Find the modified section for the given index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Return the offset of an exact match.\n        if (i < this._sections.length && this._sections[i].index === index) {\n            return this._sections[i].offset;\n        }\n        // Handle the case of no modified sections before the index.\n        if (i === 0) {\n            return index * this._defaultSize;\n        }\n        // Compute the offset from the previous modified section.\n        let section = this._sections[i - 1];\n        let span = index - section.index - 1;\n        return section.offset + section.size + span * this._defaultSize;\n    }\n    /**\n     * Find the extent of the section at the given index.\n     *\n     * @param index - The index of the section of interest.\n     *\n     * @returns The extent of the section at the given index, or `-1`\n     *   if the index is out of range.\n     *\n     * #### Undefined Behavior\n     * An `index` which is non-integral.\n     *\n     * #### Complexity\n     * Logarithmic on the number of resized sections.\n     */\n    extentOf(index) {\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= this._count) {\n            return -1;\n        }\n        // Handle the simple case of no modified sections.\n        if (this._sections.length === 0) {\n            return (index + 1) * this._defaultSize - 1;\n        }\n        // Find the modified section for the given index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Return the offset of an exact match.\n        if (i < this._sections.length && this._sections[i].index === index) {\n            return this._sections[i].offset + this._sections[i].size - 1;\n        }\n        // Handle the case of no modified sections before the index.\n        if (i === 0) {\n            return (index + 1) * this._defaultSize - 1;\n        }\n        // Compute the offset from the previous modified section.\n        let section = this._sections[i - 1];\n        let span = index - section.index;\n        return section.offset + section.size + span * this._defaultSize - 1;\n    }\n    /**\n     * Find the size of the section at the given index.\n     *\n     * @param index - The index of the section of interest.\n     *\n     * @returns The size of the section at the given index, or `-1`\n     *   if the index is out of range.\n     *\n     * #### Undefined Behavior\n     * An `index` which is non-integral.\n     *\n     * #### Complexity\n     * Logarithmic on the number of resized sections.\n     */\n    sizeOf(index) {\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= this._count) {\n            return -1;\n        }\n        // Handle the simple case of no modified sections.\n        if (this._sections.length === 0) {\n            return this._defaultSize;\n        }\n        // Find the modified section for the given index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Return the size of an exact match.\n        if (i < this._sections.length && this._sections[i].index === index) {\n            return this._sections[i].size;\n        }\n        // Return the default size for all other cases.\n        return this._defaultSize;\n    }\n    /**\n     * Resize a section in the list.\n     *\n     * @param index - The index of the section to resize. This method\n     *   is a no-op if this value is out of range.\n     *\n     * @param size - The new size of the section. This value will be\n     *   clamped to an integer `>= 0`.\n     *\n     * #### Undefined Behavior\n     * An `index` which is non-integral.\n     *\n     * #### Complexity\n     * Linear on the number of resized sections.\n     */\n    resize(index, size) {\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= this._count) {\n            return;\n        }\n        // Clamp the size to an integer >= minimum size.\n        size = Math.max(this._minimumSize, Math.floor(size));\n        // Find the modified section for the given index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Update or create the modified section as needed.\n        let delta;\n        if (i < this._sections.length && this._sections[i].index === index) {\n            let section = this._sections[i];\n            delta = size - section.size;\n            section.size = size;\n        }\n        else if (i === 0) {\n            let offset = index * this._defaultSize;\n            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.insert(this._sections, i, { index, offset, size });\n            delta = size - this._defaultSize;\n        }\n        else {\n            let section = this._sections[i - 1];\n            let span = index - section.index - 1;\n            let offset = section.offset + section.size + span * this._defaultSize;\n            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.insert(this._sections, i, { index, offset, size });\n            delta = size - this._defaultSize;\n        }\n        // Adjust the length.\n        this._length += delta;\n        // Update all modified sections after the resized section.\n        for (let j = i + 1, n = this._sections.length; j < n; ++j) {\n            this._sections[j].offset += delta;\n        }\n    }\n    /**\n     * Insert sections into the list.\n     *\n     * @param index - The index at which to insert the sections. This\n     *   value will be clamped to the bounds of the list.\n     *\n     * @param count - The number of sections to insert. This method\n     *   is a no-op if this value is `<= 0`.\n     *\n     * #### Undefined Behavior\n     * An `index` or `count` which is non-integral.\n     *\n     * #### Complexity\n     * Linear on the number of resized sections.\n     */\n    insert(index, count) {\n        // Bail early if there are no sections to insert.\n        if (count <= 0) {\n            return;\n        }\n        // Clamp the index to the bounds of the list.\n        index = Math.max(0, Math.min(index, this._count));\n        // Add the new sections to the totals.\n        let span = count * this._defaultSize;\n        this._count += count;\n        this._length += span;\n        // Bail early if there are no modified sections to update.\n        if (this._sections.length === 0) {\n            return;\n        }\n        // Find the modified section for the given index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Update all modified sections after the insert location.\n        for (let n = this._sections.length; i < n; ++i) {\n            let section = this._sections[i];\n            section.index += count;\n            section.offset += span;\n        }\n    }\n    /**\n     * Remove sections from the list.\n     *\n     * @param index - The index of the first section to remove. This\n     *   method is a no-op if this value is out of range.\n     *\n     * @param count - The number of sections to remove. This method\n     *   is a no-op if this value is `<= 0`.\n     *\n     * #### Undefined Behavior\n     * An `index` or `count` which is non-integral.\n     *\n     * #### Complexity\n     * Linear on the number of resized sections.\n     */\n    remove(index, count) {\n        // Bail early if there is nothing to remove.\n        if (index < 0 || index >= this._count || count <= 0) {\n            return;\n        }\n        // Clamp the count to the bounds of the list.\n        count = Math.min(this._count - index, count);\n        // Handle the simple case of no modified sections to update.\n        if (this._sections.length === 0) {\n            this._count -= count;\n            this._length -= count * this._defaultSize;\n            return;\n        }\n        // Handle the simple case of removing all sections.\n        if (count === this._count) {\n            this._length = 0;\n            this._count = 0;\n            this._sections.length = 0;\n            return;\n        }\n        // Find the modified section for the start index.\n        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);\n        // Find the modified section for the end index.\n        let j = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index + count, Private$2.indexCmp);\n        // Remove the relevant modified sections.\n        let removed = this._sections.splice(i, j - i);\n        // Compute the total removed span.\n        let span = (count - removed.length) * this._defaultSize;\n        for (let k = 0, n = removed.length; k < n; ++k) {\n            span += removed[k].size;\n        }\n        // Adjust the totals.\n        this._count -= count;\n        this._length -= span;\n        // Update all modified sections after the removed span.\n        for (let k = i, n = this._sections.length; k < n; ++k) {\n            let section = this._sections[k];\n            section.index -= count;\n            section.offset -= span;\n        }\n    }\n    /**\n     * Move sections within the list.\n     *\n     * @param index - The index of the first section to move. This method\n     *   is a no-op if this value is out of range.\n     *\n     * @param count - The number of sections to move. This method is a\n     *   no-op if this value is `<= 0`.\n     *\n     * @param destination - The destination index for the first section.\n     *   This value will be clamped to the allowable range.\n     *\n     * #### Undefined Behavior\n     * An `index`, `count`, or `destination` which is non-integral.\n     *\n     * #### Complexity\n     * Linear on the number of moved resized sections.\n     */\n    move(index, count, destination) {\n        // Bail early if there is nothing to move.\n        if (index < 0 || index >= this._count || count <= 0) {\n            return;\n        }\n        // Handle the simple case of no modified sections.\n        if (this._sections.length === 0) {\n            return;\n        }\n        // Clamp the move count to the limit.\n        count = Math.min(count, this._count - index);\n        // Clamp the destination index to the limit.\n        destination = Math.min(Math.max(0, destination), this._count - count);\n        // Bail early if there is no effective move.\n        if (index === destination) {\n            return;\n        }\n        // Compute the first affected index.\n        let i1 = Math.min(index, destination);\n        // Look up the first affected modified section.\n        let k1 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, i1, Private$2.indexCmp);\n        // Bail early if there are no affected modified sections.\n        if (k1 === this._sections.length) {\n            return;\n        }\n        // Compute the last affected index.\n        let i2 = Math.max(index + count - 1, destination + count - 1);\n        // Look up the last affected modified section.\n        let k2 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.upperBound(this._sections, i2, Private$2.indexCmp) - 1;\n        // Bail early if there are no affected modified sections.\n        if (k2 < k1) {\n            return;\n        }\n        // Compute the pivot index.\n        let pivot = destination < index ? index : index + count;\n        // Compute the count for each side of the pivot.\n        let count1 = pivot - i1;\n        let count2 = i2 - pivot + 1;\n        // Compute the span for each side of the pivot.\n        let span1 = count1 * this._defaultSize;\n        let span2 = count2 * this._defaultSize;\n        // Adjust the spans for the modified sections.\n        for (let j = k1; j <= k2; ++j) {\n            let section = this._sections[j];\n            if (section.index < pivot) {\n                span1 += section.size - this._defaultSize;\n            }\n            else {\n                span2 += section.size - this._defaultSize;\n            }\n        }\n        // Look up the pivot section.\n        let k3 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, pivot, Private$2.indexCmp);\n        // Rotate the modified sections if needed.\n        if (k1 <= k3 && k3 <= k2) {\n            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.rotate(this._sections, k3 - k1, k1, k2);\n        }\n        // Adjust the modified section indices and offsets.\n        for (let j = k1; j <= k2; ++j) {\n            let section = this._sections[j];\n            if (section.index < pivot) {\n                section.index += count2;\n                section.offset += span2;\n            }\n            else {\n                section.index -= count1;\n                section.offset -= span1;\n            }\n        }\n    }\n    /**\n     * Reset all modified sections to the default size.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    reset() {\n        this._sections.length = 0;\n        this._length = this._count * this._defaultSize;\n    }\n    /**\n     * Remove all sections from the list.\n     *\n     * #### Complexity\n     * Constant.\n     */\n    clear() {\n        this._count = 0;\n        this._length = 0;\n        this._sections.length = 0;\n    }\n}\n/**\n * The namespace for the module implementation details.\n */\nvar Private$2;\n(function (Private) {\n    /**\n     * A comparison function for searching by offset.\n     */\n    function offsetCmp(section, offset) {\n        if (offset < section.offset) {\n            return 1;\n        }\n        if (section.offset + section.size <= offset) {\n            return -1;\n        }\n        return 0;\n    }\n    Private.offsetCmp = offsetCmp;\n    /**\n     * A comparison function for searching by index.\n     */\n    function indexCmp(section, index) {\n        return section.index - index;\n    }\n    Private.indexCmp = indexCmp;\n})(Private$2 || (Private$2 = {}));\n\n/**\n * A widget which implements a high-performance tabular data grid.\n *\n * #### Notes\n * A data grid is implemented as a composition of child widgets. These\n * child widgets are considered an implementation detail. Manipulating\n * the child widgets of a data grid directly is undefined behavior.\n *\n * This class is not designed to be subclassed.\n */\nclass DataGrid extends _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget {\n    /**\n     * Construct a new data grid.\n     *\n     * @param options - The options for initializing the data grid.\n     */\n    constructor(options = {}) {\n        super();\n        this._scrollX = 0;\n        this._scrollY = 0;\n        this._viewportWidth = 0;\n        this._viewportHeight = 0;\n        this._mousedown = false;\n        this._keyHandler = null;\n        this._mouseHandler = null;\n        this._vScrollBarMinWidth = 0;\n        this._hScrollBarMinHeight = 0;\n        this._dpiRatio = Math.ceil(window.devicePixelRatio);\n        this._dataModel = null;\n        this._selectionModel = null;\n        this._editingEnabled = false;\n        this.addClass('lm-DataGrid');\n        // Parse the simple options.\n        this._style = options.style || DataGrid.defaultStyle;\n        this._stretchLastRow = options.stretchLastRow || false;\n        this._stretchLastColumn = options.stretchLastColumn || false;\n        this._headerVisibility = options.headerVisibility || 'all';\n        this._cellRenderers = options.cellRenderers || new RendererMap();\n        this._copyConfig = options.copyConfig || DataGrid.defaultCopyConfig;\n        // Connect to the renderer map changed signal.\n        this._cellRenderers.changed.connect(this._onRenderersChanged, this);\n        // Parse the default sizes.\n        let defaultSizes = options.defaultSizes || DataGrid.defaultSizes;\n        let minimumSizes = options.minimumSizes || DataGrid.minimumSizes;\n        // Set up the sections lists.\n        this._rowSections = new SectionList({\n            defaultSize: defaultSizes.rowHeight,\n            minimumSize: minimumSizes.rowHeight\n        });\n        this._columnSections = new SectionList({\n            defaultSize: defaultSizes.columnWidth,\n            minimumSize: minimumSizes.columnWidth\n        });\n        this._rowHeaderSections = new SectionList({\n            defaultSize: defaultSizes.rowHeaderWidth,\n            minimumSize: minimumSizes.rowHeaderWidth\n        });\n        this._columnHeaderSections = new SectionList({\n            defaultSize: defaultSizes.columnHeaderHeight,\n            minimumSize: minimumSizes.columnHeaderHeight\n        });\n        // Create the canvas, buffer, and overlay objects.\n        this._canvas = Private$1.createCanvas();\n        this._buffer = Private$1.createCanvas();\n        this._overlay = Private$1.createCanvas();\n        // Get the graphics contexts for the canvases.\n        this._canvasGC = this._canvas.getContext('2d');\n        this._bufferGC = this._buffer.getContext('2d');\n        this._overlayGC = this._overlay.getContext('2d');\n        // Set up the on-screen canvas.\n        this._canvas.style.position = 'absolute';\n        this._canvas.style.top = '0px';\n        this._canvas.style.left = '0px';\n        this._canvas.style.width = '0px';\n        this._canvas.style.height = '0px';\n        // Set up the on-screen overlay.\n        this._overlay.style.position = 'absolute';\n        this._overlay.style.top = '0px';\n        this._overlay.style.left = '0px';\n        this._overlay.style.width = '0px';\n        this._overlay.style.height = '0px';\n        // Create the internal widgets for the data grid.\n        this._viewport = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget();\n        this._viewport.node.tabIndex = -1;\n        this._viewport.node.style.outline = 'none';\n        this._vScrollBar = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.ScrollBar({ orientation: 'vertical' });\n        this._hScrollBar = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.ScrollBar({ orientation: 'horizontal' });\n        this._scrollCorner = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget();\n        this._editorController = new CellEditorController();\n        // Add the extra class names to the child widgets.\n        this._viewport.addClass('lm-DataGrid-viewport');\n        this._vScrollBar.addClass('lm-DataGrid-scrollBar');\n        this._hScrollBar.addClass('lm-DataGrid-scrollBar');\n        this._scrollCorner.addClass('lm-DataGrid-scrollCorner');\n        // Add the on-screen canvas to the viewport node.\n        this._viewport.node.appendChild(this._canvas);\n        // Add the on-screen overlay to the viewport node.\n        this._viewport.node.appendChild(this._overlay);\n        // Install the message hooks.\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._viewport, this);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._hScrollBar, this);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._vScrollBar, this);\n        // Hide the scroll bars and corner from the outset.\n        this._vScrollBar.hide();\n        this._hScrollBar.hide();\n        this._scrollCorner.hide();\n        // Connect to the scroll bar signals.\n        this._vScrollBar.thumbMoved.connect(this._onThumbMoved, this);\n        this._hScrollBar.thumbMoved.connect(this._onThumbMoved, this);\n        this._vScrollBar.pageRequested.connect(this._onPageRequested, this);\n        this._hScrollBar.pageRequested.connect(this._onPageRequested, this);\n        this._vScrollBar.stepRequested.connect(this._onStepRequested, this);\n        this._hScrollBar.stepRequested.connect(this._onStepRequested, this);\n        // Set the layout cell config for the child widgets.\n        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._viewport, { row: 0, column: 0 });\n        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._vScrollBar, { row: 0, column: 1 });\n        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._hScrollBar, { row: 1, column: 0 });\n        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._scrollCorner, { row: 1, column: 1 });\n        // Create the layout for the data grid.\n        let layout = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout({\n            rowCount: 2,\n            columnCount: 2,\n            rowSpacing: 0,\n            columnSpacing: 0,\n            fitPolicy: 'set-no-constraint'\n        });\n        // Set the stretch factors for the grid.\n        layout.setRowStretch(0, 1);\n        layout.setRowStretch(1, 0);\n        layout.setColumnStretch(0, 1);\n        layout.setColumnStretch(1, 0);\n        // Add the child widgets to the layout.\n        layout.addWidget(this._viewport);\n        layout.addWidget(this._vScrollBar);\n        layout.addWidget(this._hScrollBar);\n        layout.addWidget(this._scrollCorner);\n        // Install the layout on the data grid.\n        this.layout = layout;\n    }\n    /**\n     * Dispose of the resources held by the widgets.\n     */\n    dispose() {\n        // Release the mouse.\n        this._releaseMouse();\n        // Dispose of the handlers.\n        if (this._keyHandler) {\n            this._keyHandler.dispose();\n        }\n        if (this._mouseHandler) {\n            this._mouseHandler.dispose();\n        }\n        this._keyHandler = null;\n        this._mouseHandler = null;\n        // Clear the models.\n        this._dataModel = null;\n        this._selectionModel = null;\n        // Clear the section lists.\n        this._rowSections.clear();\n        this._columnSections.clear();\n        this._rowHeaderSections.clear();\n        this._columnHeaderSections.clear();\n        // Dispose of the base class.\n        super.dispose();\n    }\n    /**\n     * Get the data model for the data grid.\n     */\n    get dataModel() {\n        return this._dataModel;\n    }\n    /**\n     * Set the data model for the data grid.\n     *\n     * #### Notes\n     * This will automatically remove the current selection model.\n     */\n    set dataModel(value) {\n        // Do nothing if the model does not change.\n        if (this._dataModel === value) {\n            return;\n        }\n        // Release the mouse.\n        this._releaseMouse();\n        // Clear the selection model.\n        this.selectionModel = null;\n        // Disconnect the change handler from the old model.\n        if (this._dataModel) {\n            this._dataModel.changed.disconnect(this._onDataModelChanged, this);\n        }\n        // Connect the change handler for the new model.\n        if (value) {\n            value.changed.connect(this._onDataModelChanged, this);\n        }\n        // Update the internal model reference.\n        this._dataModel = value;\n        // Clear the section lists.\n        this._rowSections.clear();\n        this._columnSections.clear();\n        this._rowHeaderSections.clear();\n        this._columnHeaderSections.clear();\n        // Populate the section lists.\n        if (value) {\n            this._rowSections.insert(0, value.rowCount('body'));\n            this._columnSections.insert(0, value.columnCount('body'));\n            this._rowHeaderSections.insert(0, value.columnCount('row-header'));\n            this._columnHeaderSections.insert(0, value.rowCount('column-header'));\n        }\n        // Reset the scroll position.\n        this._scrollX = 0;\n        this._scrollY = 0;\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Get the selection model for the data grid.\n     */\n    get selectionModel() {\n        return this._selectionModel;\n    }\n    /**\n     * Set the selection model for the data grid.\n     */\n    set selectionModel(value) {\n        // Do nothing if the selection model does not change.\n        if (this._selectionModel === value) {\n            return;\n        }\n        // Release the mouse.\n        this._releaseMouse();\n        // Ensure the data models are a match.\n        if (value && value.dataModel !== this._dataModel) {\n            throw new Error('SelectionModel.dataModel !== DataGrid.dataModel');\n        }\n        // Disconnect the change handler from the old model.\n        if (this._selectionModel) {\n            this._selectionModel.changed.disconnect(this._onSelectionsChanged, this);\n        }\n        // Connect the change handler for the new model.\n        if (value) {\n            value.changed.connect(this._onSelectionsChanged, this);\n        }\n        // Update the internal selection model reference.\n        this._selectionModel = value;\n        // Schedule a repaint of the overlay.\n        this.repaintOverlay();\n    }\n    /**\n     * Get the key handler for the data grid.\n     */\n    get keyHandler() {\n        return this._keyHandler;\n    }\n    /**\n     * Set the key handler for the data grid.\n     */\n    set keyHandler(value) {\n        this._keyHandler = value;\n    }\n    /**\n     * Get the mouse handler for the data grid.\n     */\n    get mouseHandler() {\n        return this._mouseHandler;\n    }\n    /**\n     * Set the mouse handler for the data grid.\n     */\n    set mouseHandler(value) {\n        // Bail early if the mouse handler does not change.\n        if (this._mouseHandler === value) {\n            return;\n        }\n        // Release the mouse.\n        this._releaseMouse();\n        // Update the internal mouse handler.\n        this._mouseHandler = value;\n    }\n    /**\n     * Get the style for the data grid.\n     */\n    get style() {\n        return this._style;\n    }\n    /**\n     * Set the style for the data grid.\n     */\n    set style(value) {\n        // Bail if the style does not change.\n        if (this._style === value) {\n            return;\n        }\n        // Update the internal style.\n        this._style = { ...value };\n        // Schedule a repaint of the content.\n        this.repaintContent();\n        // Schedule a repaint of the overlay.\n        this.repaintOverlay();\n    }\n    /**\n     * Get the cell renderer map for the data grid.\n     */\n    get cellRenderers() {\n        return this._cellRenderers;\n    }\n    /**\n     * Set the cell renderer map for the data grid.\n     */\n    set cellRenderers(value) {\n        // Bail if the renderer map does not change.\n        if (this._cellRenderers === value) {\n            return;\n        }\n        // Disconnect the old map.\n        this._cellRenderers.changed.disconnect(this._onRenderersChanged, this);\n        // Connect the new map.\n        value.changed.connect(this._onRenderersChanged, this);\n        // Update the internal renderer map.\n        this._cellRenderers = value;\n        // Schedule a repaint of the grid content.\n        this.repaintContent();\n    }\n    /**\n     * Get the header visibility for the data grid.\n     */\n    get headerVisibility() {\n        return this._headerVisibility;\n    }\n    /**\n     * Set the header visibility for the data grid.\n     */\n    set headerVisibility(value) {\n        // Bail if the visibility does not change.\n        if (this._headerVisibility === value) {\n            return;\n        }\n        // Update the internal visibility.\n        this._headerVisibility = value;\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Get the default sizes for the various sections of the data grid.\n     */\n    get defaultSizes() {\n        let rowHeight = this._rowSections.defaultSize;\n        let columnWidth = this._columnSections.defaultSize;\n        let rowHeaderWidth = this._rowHeaderSections.defaultSize;\n        let columnHeaderHeight = this._columnHeaderSections.defaultSize;\n        return { rowHeight, columnWidth, rowHeaderWidth, columnHeaderHeight };\n    }\n    /**\n     * Set the default sizes for the various sections of the data grid.\n     */\n    set defaultSizes(value) {\n        // Update the section default sizes.\n        this._rowSections.defaultSize = value.rowHeight;\n        this._columnSections.defaultSize = value.columnWidth;\n        this._rowHeaderSections.defaultSize = value.rowHeaderWidth;\n        this._columnHeaderSections.defaultSize = value.columnHeaderHeight;\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Get the minimum sizes for the various sections of the data grid.\n     */\n    get minimumSizes() {\n        let rowHeight = this._rowSections.minimumSize;\n        let columnWidth = this._columnSections.minimumSize;\n        let rowHeaderWidth = this._rowHeaderSections.minimumSize;\n        let columnHeaderHeight = this._columnHeaderSections.minimumSize;\n        return { rowHeight, columnWidth, rowHeaderWidth, columnHeaderHeight };\n    }\n    /**\n     * Set the minimum sizes for the various sections of the data grid.\n     */\n    set minimumSizes(value) {\n        // Update the section default sizes.\n        this._rowSections.minimumSize = value.rowHeight;\n        this._columnSections.minimumSize = value.columnWidth;\n        this._rowHeaderSections.minimumSize = value.rowHeaderWidth;\n        this._columnHeaderSections.minimumSize = value.columnHeaderHeight;\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Get the copy configuration for the data grid.\n     */\n    get copyConfig() {\n        return this._copyConfig;\n    }\n    /**\n     * Set the copy configuration for the data grid.\n     */\n    set copyConfig(value) {\n        this._copyConfig = value;\n    }\n    /**\n     * Get whether the last row is stretched.\n     */\n    get stretchLastRow() {\n        return this._stretchLastRow;\n    }\n    /**\n     * Set whether the last row is stretched.\n     */\n    set stretchLastRow(value) {\n        // Bail early if the value does not change.\n        if (value === this._stretchLastRow) {\n            return;\n        }\n        // Update the internal value.\n        this._stretchLastRow = value;\n        // Sync the viewport\n        this._syncViewport();\n    }\n    /**\n     * Get whether the last column is stretched.\n     */\n    get stretchLastColumn() {\n        return this._stretchLastColumn;\n    }\n    /**\n     * Set whether the last column is stretched.\n     */\n    set stretchLastColumn(value) {\n        // Bail early if the value does not change.\n        if (value === this._stretchLastColumn) {\n            return;\n        }\n        // Update the internal value.\n        this._stretchLastColumn = value;\n        // Sync the viewport\n        this._syncViewport();\n    }\n    /**\n     * The virtual width of the row headers.\n     */\n    get headerWidth() {\n        if (this._headerVisibility === 'none') {\n            return 0;\n        }\n        if (this._headerVisibility === 'column') {\n            return 0;\n        }\n        return this._rowHeaderSections.length;\n    }\n    /**\n     * The virtual height of the column headers.\n     */\n    get headerHeight() {\n        if (this._headerVisibility === 'none') {\n            return 0;\n        }\n        if (this._headerVisibility === 'row') {\n            return 0;\n        }\n        return this._columnHeaderSections.length;\n    }\n    /**\n     * The virtual width of the grid body.\n     *\n     * #### Notes\n     * This does *not* account for a stretched last column.\n     */\n    get bodyWidth() {\n        return this._columnSections.length;\n    }\n    /**\n     * The virtual height of the grid body.\n     *\n     * #### Notes\n     * This does *not* account for a stretched last row.\n     */\n    get bodyHeight() {\n        return this._rowSections.length;\n    }\n    /**\n     * The virtual width of the entire grid.\n     *\n     * #### Notes\n     * This does *not* account for a stretched last column.\n     */\n    get totalWidth() {\n        return this.headerWidth + this.bodyWidth;\n    }\n    /**\n     * The virtual height of the entire grid.\n     *\n     * #### Notes\n     * This does *not* account for a stretched last row.\n     */\n    get totalHeight() {\n        return this.headerHeight + this.bodyHeight;\n    }\n    /**\n     * The actual width of the viewport.\n     */\n    get viewportWidth() {\n        return this._viewportWidth;\n    }\n    /**\n     * The actual height of the viewport.\n     */\n    get viewportHeight() {\n        return this._viewportHeight;\n    }\n    /**\n     * The width of the visible portion of the grid body.\n     */\n    get pageWidth() {\n        return Math.max(0, this.viewportWidth - this.headerWidth);\n    }\n    /**\n     * The height of the visible portion of the grid body.\n     */\n    get pageHeight() {\n        return Math.max(0, this.viewportHeight - this.headerHeight);\n    }\n    /**\n     * The current scroll X position of the viewport.\n     */\n    get scrollX() {\n        return this._hScrollBar.value;\n    }\n    /**\n     * The current scroll Y position of the viewport.\n     */\n    get scrollY() {\n        return this._vScrollBar.value;\n    }\n    /**\n     * The maximum scroll X position for the grid.\n     */\n    get maxScrollX() {\n        return Math.max(0, this.bodyWidth - this.pageWidth - 1);\n    }\n    /**\n     * The maximum scroll Y position for the grid.\n     */\n    get maxScrollY() {\n        return Math.max(0, this.bodyHeight - this.pageHeight - 1);\n    }\n    /**\n     * The viewport widget for the data grid.\n     */\n    get viewport() {\n        return this._viewport;\n    }\n    /**\n     * The cell editor controller object for the data grid.\n     */\n    get editorController() {\n        return this._editorController;\n    }\n    set editorController(controller) {\n        this._editorController = controller;\n    }\n    /**\n     * Whether the cell editing is enabled for the data grid.\n     */\n    get editingEnabled() {\n        return this._editingEnabled;\n    }\n    set editingEnabled(enabled) {\n        this._editingEnabled = enabled;\n    }\n    /**\n     * Whether the grid cells are editable.\n     *\n     * `editingEnabled` flag must be on and grid must have required\n     * selection model, editor controller and data model properties.\n     */\n    get editable() {\n        return (this._editingEnabled &&\n            this._selectionModel !== null &&\n            this._editorController !== null &&\n            this.dataModel instanceof MutableDataModel);\n    }\n    /**\n     * The rendering context for painting the data grid.\n     */\n    get canvasGC() {\n        return this._canvasGC;\n    }\n    /**\n     * The row sections of the data grid.\n     */\n    get rowSections() {\n        return this._rowSections;\n    }\n    /**\n     * The column sections of the data grid.\n     */\n    get columnSections() {\n        return this._columnSections;\n    }\n    /**\n     * The row header sections of the data grid.\n     */\n    get rowHeaderSections() {\n        return this._rowHeaderSections;\n    }\n    /**\n     * The column header sections of the data grid.\n     */\n    get columnHeaderSections() {\n        return this._columnHeaderSections;\n    }\n    /**\n     * Scroll the grid to the specified row.\n     *\n     * @param row - The row index of the cell.\n     *\n     * #### Notes\n     * This is a no-op if the row is already visible.\n     */\n    scrollToRow(row) {\n        // Fetch the row count.\n        let nr = this._rowSections.count;\n        // Bail early if there is no content.\n        if (nr === 0) {\n            return;\n        }\n        // Floor the row index.\n        row = Math.floor(row);\n        // Clamp the row index.\n        row = Math.max(0, Math.min(row, nr - 1));\n        // Get the virtual bounds of the row.\n        let y1 = this._rowSections.offsetOf(row);\n        let y2 = this._rowSections.extentOf(row);\n        // Get the virtual bounds of the viewport.\n        let vy1 = this._scrollY;\n        let vy2 = this._scrollY + this.pageHeight - 1;\n        // Set up the delta variables.\n        let dy = 0;\n        // Compute the delta Y scroll.\n        if (y1 < vy1) {\n            dy = y1 - vy1 - 10;\n        }\n        else if (y2 > vy2) {\n            dy = y2 - vy2 + 10;\n        }\n        // Bail early if no scroll is needed.\n        if (dy === 0) {\n            return;\n        }\n        // Scroll by the computed delta.\n        this.scrollBy(0, dy);\n    }\n    /**\n     * Scroll the grid to the specified column.\n     *\n     * @param column - The column index of the cell.\n     *\n     * #### Notes\n     * This is a no-op if the column is already visible.\n     */\n    scrollToColumn(column) {\n        // Fetch the column count.\n        let nc = this._columnSections.count;\n        // Bail early if there is no content.\n        if (nc === 0) {\n            return;\n        }\n        // Floor the column index.\n        column = Math.floor(column);\n        // Clamp the column index.\n        column = Math.max(0, Math.min(column, nc - 1));\n        // Get the virtual bounds of the column.\n        let x1 = this._columnSections.offsetOf(column);\n        let x2 = this._columnSections.extentOf(column);\n        // Get the virtual bounds of the viewport.\n        let vx1 = this._scrollX;\n        let vx2 = this._scrollX + this.pageWidth - 1;\n        // Set up the delta variables.\n        let dx = 0;\n        // Compute the delta X scroll.\n        if (x1 < vx1) {\n            dx = x1 - vx1 - 10;\n        }\n        else if (x2 > vx2) {\n            dx = x2 - vx2 + 10;\n        }\n        // Bail early if no scroll is needed.\n        if (dx === 0) {\n            return;\n        }\n        // Scroll by the computed delta.\n        this.scrollBy(dx, 0);\n    }\n    /**\n     * Scroll the grid to the specified cell.\n     *\n     * @param row - The row index of the cell.\n     *\n     * @param column - The column index of the cell.\n     *\n     * #### Notes\n     * This is a no-op if the cell is already visible.\n     */\n    scrollToCell(row, column) {\n        // Fetch the row and column count.\n        let nr = this._rowSections.count;\n        let nc = this._columnSections.count;\n        // Bail early if there is no content.\n        if (nr === 0 || nc === 0) {\n            return;\n        }\n        // Floor the cell index.\n        row = Math.floor(row);\n        column = Math.floor(column);\n        // Clamp the cell index.\n        row = Math.max(0, Math.min(row, nr - 1));\n        column = Math.max(0, Math.min(column, nc - 1));\n        // Get the virtual bounds of the cell.\n        let x1 = this._columnSections.offsetOf(column);\n        let x2 = this._columnSections.extentOf(column);\n        let y1 = this._rowSections.offsetOf(row);\n        let y2 = this._rowSections.extentOf(row);\n        // Get the virtual bounds of the viewport.\n        let vx1 = this._scrollX;\n        let vx2 = this._scrollX + this.pageWidth - 1;\n        let vy1 = this._scrollY;\n        let vy2 = this._scrollY + this.pageHeight - 1;\n        // Set up the delta variables.\n        let dx = 0;\n        let dy = 0;\n        // Compute the delta X scroll.\n        if (x1 < vx1) {\n            dx = x1 - vx1 - 10;\n        }\n        else if (x2 > vx2) {\n            dx = x2 - vx2 + 10;\n        }\n        // Compute the delta Y scroll.\n        if (y1 < vy1) {\n            dy = y1 - vy1 - 10;\n        }\n        else if (y2 > vy2) {\n            dy = y2 - vy2 + 10;\n        }\n        // Bail early if no scroll is needed.\n        if (dx === 0 && dy === 0) {\n            return;\n        }\n        // Scroll by the computed delta.\n        this.scrollBy(dx, dy);\n    }\n    /**\n     * Move cursor down/up/left/right while making sure it remains\n     * within the bounds of selected rectangles\n     *\n     * @param direction - The direction of the movement.\n     */\n    moveCursor(direction) {\n        // Bail early if there is no selection\n        if (!this.dataModel ||\n            !this._selectionModel ||\n            this._selectionModel.isEmpty) {\n            return;\n        }\n        const iter = this._selectionModel.selections();\n        const onlyOne = iter.next() && !iter.next();\n        // if there is a single selection that is a single cell selection\n        // then move the selection and cursor within grid bounds\n        if (onlyOne) {\n            const currentSel = this._selectionModel.currentSelection();\n            if (currentSel.r1 === currentSel.r2 && currentSel.c1 === currentSel.c2) {\n                const dr = direction === 'down' ? 1 : direction === 'up' ? -1 : 0;\n                const dc = direction === 'right' ? 1 : direction === 'left' ? -1 : 0;\n                let newRow = currentSel.r1 + dr;\n                let newColumn = currentSel.c1 + dc;\n                const rowCount = this.dataModel.rowCount('body');\n                const columnCount = this.dataModel.columnCount('body');\n                if (newRow >= rowCount) {\n                    newRow = 0;\n                    newColumn += 1;\n                }\n                else if (newRow === -1) {\n                    newRow = rowCount - 1;\n                    newColumn -= 1;\n                }\n                if (newColumn >= columnCount) {\n                    newColumn = 0;\n                    newRow += 1;\n                    if (newRow >= rowCount) {\n                        newRow = 0;\n                    }\n                }\n                else if (newColumn === -1) {\n                    newColumn = columnCount - 1;\n                    newRow -= 1;\n                    if (newRow === -1) {\n                        newRow = rowCount - 1;\n                    }\n                }\n                this._selectionModel.select({\n                    r1: newRow,\n                    c1: newColumn,\n                    r2: newRow,\n                    c2: newColumn,\n                    cursorRow: newRow,\n                    cursorColumn: newColumn,\n                    clear: 'all'\n                });\n                return;\n            }\n        }\n        // if there are multiple selections, move cursor\n        // within selection rectangles\n        this._selectionModel.moveCursorWithinSelections(direction);\n    }\n    /**\n     * Scroll the grid to the current cursor position.\n     *\n     * #### Notes\n     * This is a no-op if the cursor is already visible or\n     * if there is no selection model installed on the grid.\n     */\n    scrollToCursor() {\n        // Bail early if there is no selection model.\n        if (!this._selectionModel) {\n            return;\n        }\n        // Fetch the cursor row and column.\n        let row = this._selectionModel.cursorRow;\n        let column = this._selectionModel.cursorColumn;\n        // Scroll to the cursor cell.\n        this.scrollToCell(row, column);\n    }\n    /**\n     * Scroll the viewport by the specified amount.\n     *\n     * @param dx - The X scroll amount.\n     *\n     * @param dy - The Y scroll amount.\n     */\n    scrollBy(dx, dy) {\n        this.scrollTo(this.scrollX + dx, this.scrollY + dy);\n    }\n    /**\n     * Scroll the viewport by one page.\n     *\n     * @param dir - The desired direction of the scroll.\n     */\n    scrollByPage(dir) {\n        let dx = 0;\n        let dy = 0;\n        switch (dir) {\n            case 'up':\n                dy = -this.pageHeight;\n                break;\n            case 'down':\n                dy = this.pageHeight;\n                break;\n            case 'left':\n                dx = -this.pageWidth;\n                break;\n            case 'right':\n                dx = this.pageWidth;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        this.scrollTo(this.scrollX + dx, this.scrollY + dy);\n    }\n    /**\n     * Scroll the viewport by one cell-aligned step.\n     *\n     * @param dir - The desired direction of the scroll.\n     */\n    scrollByStep(dir) {\n        let r;\n        let c;\n        let x = this.scrollX;\n        let y = this.scrollY;\n        let rows = this._rowSections;\n        let columns = this._columnSections;\n        switch (dir) {\n            case 'up':\n                r = rows.indexOf(y - 1);\n                y = r < 0 ? y : rows.offsetOf(r);\n                break;\n            case 'down':\n                r = rows.indexOf(y);\n                y = r < 0 ? y : rows.offsetOf(r) + rows.sizeOf(r);\n                break;\n            case 'left':\n                c = columns.indexOf(x - 1);\n                x = c < 0 ? x : columns.offsetOf(c);\n                break;\n            case 'right':\n                c = columns.indexOf(x);\n                x = c < 0 ? x : columns.offsetOf(c) + columns.sizeOf(c);\n                break;\n            default:\n                throw 'unreachable';\n        }\n        this.scrollTo(x, y);\n    }\n    /**\n     * Scroll to the specified offset position.\n     *\n     * @param x - The desired X position.\n     *\n     * @param y - The desired Y position.\n     */\n    scrollTo(x, y) {\n        // Floor and clamp the position to the allowable range.\n        x = Math.max(0, Math.min(Math.floor(x), this.maxScrollX));\n        y = Math.max(0, Math.min(Math.floor(y), this.maxScrollY));\n        // Update the scroll bar values with the desired position.\n        this._hScrollBar.value = x;\n        this._vScrollBar.value = y;\n        // Post a scroll request message to the viewport.\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.ScrollRequest);\n    }\n    /**\n     * Get the row count for a particular region in the data grid.\n     *\n     * @param region - The row region of interest.\n     *\n     * @returns The row count for the specified region.\n     */\n    rowCount(region) {\n        let count;\n        if (region === 'body') {\n            count = this._rowSections.count;\n        }\n        else {\n            count = this._columnHeaderSections.count;\n        }\n        return count;\n    }\n    /**\n     * Get the column count for a particular region in the data grid.\n     *\n     * @param region - The column region of interest.\n     *\n     * @returns The column count for the specified region.\n     */\n    columnCount(region) {\n        let count;\n        if (region === 'body') {\n            count = this._columnSections.count;\n        }\n        else {\n            count = this._rowHeaderSections.count;\n        }\n        return count;\n    }\n    /**\n     * Get the row at a virtual offset in the data grid.\n     *\n     * @param region - The region which holds the row of interest.\n     *\n     * @param offset - The virtual offset of the row of interest.\n     *\n     * @returns The index of the row, or `-1` if the offset is out of range.\n     *\n     * #### Notes\n     * This method accounts for a stretched last row.\n     */\n    rowAt(region, offset) {\n        // Bail early if the offset is negative.\n        if (offset < 0) {\n            return -1;\n        }\n        // Return early for the column header region.\n        if (region === 'column-header') {\n            return this._columnHeaderSections.indexOf(offset);\n        }\n        // Fetch the index.\n        let index = this._rowSections.indexOf(offset);\n        // Return early if the section is found.\n        if (index >= 0) {\n            return index;\n        }\n        // Bail early if the last row is not stretched.\n        if (!this._stretchLastRow) {\n            return -1;\n        }\n        // Fetch the geometry.\n        let bh = this.bodyHeight;\n        let ph = this.pageHeight;\n        // Bail early if no row stretching is required.\n        if (ph <= bh) {\n            return -1;\n        }\n        // Bail early if the offset is out of bounds.\n        if (offset >= ph) {\n            return -1;\n        }\n        // Otherwise, return the last row.\n        return this._rowSections.count - 1;\n    }\n    /**\n     * Get the column at a virtual offset in the data grid.\n     *\n     * @param region - The region which holds the column of interest.\n     *\n     * @param offset - The virtual offset of the column of interest.\n     *\n     * @returns The index of the column, or `-1` if the offset is out of range.\n     *\n     * #### Notes\n     * This method accounts for a stretched last column.\n     */\n    columnAt(region, offset) {\n        if (offset < 0) {\n            return -1;\n        }\n        // Return early for the row header region.\n        if (region === 'row-header') {\n            return this._rowHeaderSections.indexOf(offset);\n        }\n        // Fetch the index.\n        let index = this._columnSections.indexOf(offset);\n        // Return early if the section is found.\n        if (index >= 0) {\n            return index;\n        }\n        // Bail early if the last column is not stretched.\n        if (!this._stretchLastColumn) {\n            return -1;\n        }\n        // Fetch the geometry.\n        let bw = this.bodyWidth;\n        let pw = this.pageWidth;\n        // Bail early if no column stretching is required.\n        if (pw <= bw) {\n            return -1;\n        }\n        // Bail early if the offset is out of bounds.\n        if (offset >= pw) {\n            return -1;\n        }\n        // Otherwise, return the last column.\n        return this._columnSections.count - 1;\n    }\n    /**\n     * Get the offset of a row in the data grid.\n     *\n     * @param region - The region which holds the row of interest.\n     *\n     * @param index - The index of the row of interest.\n     *\n     * @returns The offset of the row, or `-1` if the index is out of range.\n     *\n     * #### Notes\n     * A stretched last row has no effect on the return value.\n     */\n    rowOffset(region, index) {\n        let offset;\n        if (region === 'body') {\n            offset = this._rowSections.offsetOf(index);\n        }\n        else {\n            offset = this._columnHeaderSections.offsetOf(index);\n        }\n        return offset;\n    }\n    /**\n     * Get the offset of a column in the data grid.\n     *\n     * @param region - The region which holds the column of interest.\n     *\n     * @param index - The index of the column of interest.\n     *\n     * @returns The offset of the column, or `-1` if the index is out of range.\n     *\n     * #### Notes\n     * A stretched last column has no effect on the return value.\n     */\n    columnOffset(region, index) {\n        let offset;\n        if (region === 'body') {\n            offset = this._columnSections.offsetOf(index);\n        }\n        else {\n            offset = this._rowHeaderSections.offsetOf(index);\n        }\n        return offset;\n    }\n    /**\n     * Get the size of a row in the data grid.\n     *\n     * @param region - The region which holds the row of interest.\n     *\n     * @param index - The index of the row of interest.\n     *\n     * @returns The size of the row, or `-1` if the index is out of range.\n     *\n     * #### Notes\n     * This method accounts for a stretched last row.\n     */\n    rowSize(region, index) {\n        // Return early for the column header region.\n        if (region === 'column-header') {\n            return this._columnHeaderSections.sizeOf(index);\n        }\n        // Fetch the row size.\n        let size = this._rowSections.sizeOf(index);\n        // Bail early if the index is out of bounds.\n        if (size < 0) {\n            return size;\n        }\n        // Return early if the last row is not stretched.\n        if (!this._stretchLastRow) {\n            return size;\n        }\n        // Return early if its not the last row.\n        if (index < this._rowSections.count - 1) {\n            return size;\n        }\n        // Fetch the geometry.\n        let bh = this.bodyHeight;\n        let ph = this.pageHeight;\n        // Return early if no stretching is needed.\n        if (ph <= bh) {\n            return size;\n        }\n        // Return the adjusted size.\n        return size + (ph - bh);\n    }\n    /**\n     * Get the size of a column in the data grid.\n     *\n     * @param region - The region which holds the column of interest.\n     *\n     * @param index - The index of the column of interest.\n     *\n     * @returns The size of the column, or `-1` if the index is out of range.\n     *\n     * #### Notes\n     * This method accounts for a stretched last column.\n     */\n    columnSize(region, index) {\n        // Return early for the row header region.\n        if (region === 'row-header') {\n            return this._rowHeaderSections.sizeOf(index);\n        }\n        // Fetch the column size.\n        let size = this._columnSections.sizeOf(index);\n        // Bail early if the index is out of bounds.\n        if (size < 0) {\n            return size;\n        }\n        // Return early if the last column is not stretched.\n        if (!this._stretchLastColumn) {\n            return size;\n        }\n        // Return early if its not the last column.\n        if (index < this._columnSections.count - 1) {\n            return size;\n        }\n        // Fetch the geometry.\n        let bw = this.bodyWidth;\n        let pw = this.pageWidth;\n        // Return early if no stretching is needed.\n        if (pw <= bw) {\n            return size;\n        }\n        // Return the adjusted size.\n        return size + (pw - bw);\n    }\n    /**\n     * Resize a row in the data grid.\n     *\n     * @param region - The region which holds the row of interest.\n     *\n     * @param index - The index of the row of interest.\n     *\n     * @param size - The desired size of the row.\n     */\n    resizeRow(region, index, size) {\n        let msg = new Private$1.RowResizeRequest(region, index, size);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);\n    }\n    /**\n     * Resize a column in the data grid.\n     *\n     * @param region - The region which holds the column of interest.\n     *\n     * @param index - The index of the column of interest.\n     *\n     * @param size - The desired size of the column.\n     */\n    resizeColumn(region, index, size) {\n        let msg = new Private$1.ColumnResizeRequest(region, index, size);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);\n    }\n    /**\n     * Reset modified rows to their default size.\n     *\n     * @param region - The row region of interest.\n     */\n    resetRows(region) {\n        switch (region) {\n            case 'all':\n                this._rowSections.reset();\n                this._columnHeaderSections.reset();\n                break;\n            case 'body':\n                this._rowSections.reset();\n                break;\n            case 'column-header':\n                this._columnHeaderSections.reset();\n                break;\n            default:\n                throw 'unreachable';\n        }\n        this.repaintContent();\n        this.repaintOverlay();\n    }\n    /**\n     * Reset modified columns to their default size.\n     *\n     * @param region - The column region of interest.\n     */\n    resetColumns(region) {\n        switch (region) {\n            case 'all':\n                this._columnSections.reset();\n                this._rowHeaderSections.reset();\n                break;\n            case 'body':\n                this._columnSections.reset();\n                break;\n            case 'row-header':\n                this._rowHeaderSections.reset();\n                break;\n            default:\n                throw 'unreachable';\n        }\n        this.repaintContent();\n        this.repaintOverlay();\n    }\n    /**\n     * Auto sizes column-header widths based on their text content.\n     * @param area which area to resize: 'body', 'row-header' or 'all'.\n     * @param padding padding added to resized columns (pixels).\n     * @param numCols specify cap on the number of column resizes (optional).\n     */\n    fitColumnNames(area = 'all', padding = 15, numCols) {\n        // Attempt resizing only if a data model is present.\n        if (this.dataModel) {\n            // Tracking remaining columns to be resized if numCols arg passed.\n            let colsRemaining = numCols === undefined || numCols < 0 ? undefined : numCols;\n            if (area === 'row-header' || area === 'all') {\n                // Respecting any column resize cap, if one has been passed.\n                if (colsRemaining !== undefined) {\n                    const rowColumnCount = this.dataModel.columnCount('row-header');\n                    /*\n                      If we have more row-header columns than columns available\n                      for resize, resize only remaining columns as per allowance\n                      and set remaining resize allowance number to 0.\n                    */\n                    if (colsRemaining - rowColumnCount < 0) {\n                        this._fitRowColumnHeaders(this.dataModel, padding, colsRemaining);\n                        colsRemaining = 0;\n                    }\n                    else {\n                        /*\n                          Otherwise the entire row-header column count can be resized.\n                          Resize all row-header columns and subtract from remaining\n                          column resize allowance.\n                        */\n                        this._fitRowColumnHeaders(this.dataModel, padding, rowColumnCount);\n                        colsRemaining = colsRemaining - rowColumnCount;\n                    }\n                }\n                else {\n                    // No column resize cap passed - resizing all columns.\n                    this._fitRowColumnHeaders(this.dataModel, padding);\n                }\n            }\n            if (area === 'body' || area === 'all') {\n                // Respecting any column resize cap, if one has been passed.\n                if (colsRemaining !== undefined) {\n                    const bodyColumnCount = this.dataModel.columnCount('body');\n                    /*\n                      If we have more body columns than columns available\n                      for resize, resize only remaining columns as per allowance\n                      and set remaining resize allowance number to 0.\n                    */\n                    if (colsRemaining - bodyColumnCount < 0) {\n                        this._fitBodyColumnHeaders(this.dataModel, padding, colsRemaining);\n                    }\n                    else {\n                        /*\n                          Otherwise the entire body column count can be resized.\n                          Resize based on the smallest number between remaining\n                          resize allowance and body column count.\n                        */\n                        this._fitBodyColumnHeaders(this.dataModel, padding, Math.min(colsRemaining, bodyColumnCount));\n                    }\n                }\n                else {\n                    // No column resize cap passed - resizing all columns.\n                    this._fitBodyColumnHeaders(this.dataModel, padding);\n                }\n            }\n        }\n    }\n    /**\n     * Map a client position to local viewport coordinates.\n     *\n     * @param clientX - The client X position of the mouse.\n     *\n     * @param clientY - The client Y position of the mouse.\n     *\n     * @returns The local viewport coordinates for the position.\n     */\n    mapToLocal(clientX, clientY) {\n        // Fetch the viewport rect.\n        let rect = this._viewport.node.getBoundingClientRect();\n        // Extract the rect coordinates.\n        let { left, top } = rect;\n        // Round the rect coordinates for sub-pixel positioning.\n        left = Math.floor(left);\n        top = Math.floor(top);\n        // Convert to local coordinates.\n        let lx = clientX - left;\n        let ly = clientY - top;\n        // Return the local coordinates.\n        return { lx, ly };\n    }\n    /**\n     * Map a client position to virtual grid coordinates.\n     *\n     * @param clientX - The client X position of the mouse.\n     *\n     * @param clientY - The client Y position of the mouse.\n     *\n     * @returns The virtual grid coordinates for the position.\n     */\n    mapToVirtual(clientX, clientY) {\n        // Convert to local coordiates.\n        let { lx, ly } = this.mapToLocal(clientX, clientY);\n        // Convert to virtual coordinates.\n        let vx = lx + this.scrollX - this.headerWidth;\n        let vy = ly + this.scrollY - this.headerHeight;\n        // Return the local coordinates.\n        return { vx, vy };\n    }\n    /**\n     * Hit test the viewport for the given client position.\n     *\n     * @param clientX - The client X position of the mouse.\n     *\n     * @param clientY - The client Y position of the mouse.\n     *\n     * @returns The hit test result, or `null` if the client\n     *   position is out of bounds.\n     *\n     * #### Notes\n     * This method accounts for a stretched last row and/or column.\n     */\n    hitTest(clientX, clientY) {\n        // Convert the mouse position into local coordinates.\n        let { lx, ly } = this.mapToLocal(clientX, clientY);\n        // Fetch the header and body dimensions.\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        let bw = this.bodyWidth;\n        let bh = this.bodyHeight;\n        let ph = this.pageHeight;\n        let pw = this.pageWidth;\n        // Adjust the body width for a stretched last column.\n        if (this._stretchLastColumn && pw > bw) {\n            bw = pw;\n        }\n        // Adjust the body height for a stretched last row.\n        if (this._stretchLastRow && ph > bh) {\n            bh = ph;\n        }\n        // Check for a corner header hit.\n        if (lx >= 0 && lx < hw && ly >= 0 && ly < hh) {\n            // Convert to unscrolled virtual coordinates.\n            let vx = lx;\n            let vy = ly;\n            // Fetch the row and column index.\n            let row = this.rowAt('column-header', vy);\n            let column = this.columnAt('row-header', vx);\n            // Fetch the cell offset position.\n            let ox = this.columnOffset('row-header', column);\n            let oy = this.rowOffset('column-header', row);\n            // Fetch cell width and height.\n            let width = this.columnSize('row-header', column);\n            let height = this.rowSize('column-header', row);\n            // Compute the leading and trailing positions.\n            let x = vx - ox;\n            let y = vy - oy;\n            // Return the hit test result.\n            return { region: 'corner-header', row, column, x, y, width, height };\n        }\n        // Check for a column header hit.\n        if (ly >= 0 && ly < hh && lx >= 0 && lx < hw + bw) {\n            // Convert to unscrolled virtual coordinates.\n            let vx = lx + this._scrollX - hw;\n            let vy = ly;\n            // Fetch the row and column index.\n            let row = this.rowAt('column-header', vy);\n            let column = this.columnAt('body', vx);\n            // Fetch the cell offset position.\n            let ox = this.columnOffset('body', column);\n            let oy = this.rowOffset('column-header', row);\n            // Fetch the cell width and height.\n            let width = this.columnSize('body', column);\n            let height = this.rowSize('column-header', row);\n            // Compute the leading and trailing positions.\n            let x = vx - ox;\n            let y = vy - oy;\n            // Return the hit test result.\n            return { region: 'column-header', row, column, x, y, width, height };\n        }\n        // Check for a row header hit.\n        if (lx >= 0 && lx < hw && ly >= 0 && ly < hh + bh) {\n            // Convert to unscrolled virtual coordinates.\n            let vx = lx;\n            let vy = ly + this._scrollY - hh;\n            // Fetch the row and column index.\n            let row = this.rowAt('body', vy);\n            let column = this.columnAt('row-header', vx);\n            // Fetch the cell offset position.\n            let ox = this.columnOffset('row-header', column);\n            let oy = this.rowOffset('body', row);\n            // Fetch the cell width and height.\n            let width = this.columnSize('row-header', column);\n            let height = this.rowSize('body', row);\n            // Compute the leading and trailing positions.\n            let x = vx - ox;\n            let y = vy - oy;\n            // Return the hit test result.\n            return { region: 'row-header', row, column, x, y, width, height };\n        }\n        // Check for a body hit.\n        if (lx >= hw && lx < hw + bw && ly >= hh && ly < hh + bh) {\n            // Convert to unscrolled virtual coordinates.\n            let vx = lx + this._scrollX - hw;\n            let vy = ly + this._scrollY - hh;\n            // Fetch the row and column index.\n            let row = this.rowAt('body', vy);\n            let column = this.columnAt('body', vx);\n            // Fetch the cell offset position.\n            let ox = this.columnOffset('body', column);\n            let oy = this.rowOffset('body', row);\n            // Fetch the cell width and height.\n            let width = this.columnSize('body', column);\n            let height = this.rowSize('body', row);\n            // Compute the part coordinates.\n            let x = vx - ox;\n            let y = vy - oy;\n            // Return the result.\n            return { region: 'body', row, column, x, y, width, height };\n        }\n        // Otherwise, it's a void space hit.\n        let row = -1;\n        let column = -1;\n        let x = -1;\n        let y = -1;\n        let width = -1;\n        let height = -1;\n        // Return the hit test result.\n        return { region: 'void', row, column, x, y, width, height };\n    }\n    /**\n     * Copy the current selection to the system clipboard.\n     *\n     * #### Notes\n     * The grid must have a data model and a selection model.\n     *\n     * The behavior can be configured via `DataGrid.copyConfig`.\n     */\n    copyToClipboard() {\n        // Fetch the data model.\n        let dataModel = this._dataModel;\n        // Bail early if there is no data model.\n        if (!dataModel) {\n            return;\n        }\n        // Fetch the selection model.\n        let selectionModel = this._selectionModel;\n        // Bail early if there is no selection model.\n        if (!selectionModel) {\n            return;\n        }\n        // Coerce the selections to an array.\n        let selections = Array.from(selectionModel.selections());\n        // Bail early if there are no selections.\n        if (selections.length === 0) {\n            return;\n        }\n        // Alert that multiple selections cannot be copied.\n        if (selections.length > 1) {\n            alert('Cannot copy multiple grid selections.');\n            return;\n        }\n        // Fetch the model counts.\n        let br = dataModel.rowCount('body');\n        let bc = dataModel.columnCount('body');\n        // Bail early if there is nothing to copy.\n        if (br === 0 || bc === 0) {\n            return;\n        }\n        // Unpack the selection.\n        let { r1, c1, r2, c2 } = selections[0];\n        // Clamp the selection to the model bounds.\n        r1 = Math.max(0, Math.min(r1, br - 1));\n        c1 = Math.max(0, Math.min(c1, bc - 1));\n        r2 = Math.max(0, Math.min(r2, br - 1));\n        c2 = Math.max(0, Math.min(c2, bc - 1));\n        // Ensure the limits are well-orderd.\n        if (r2 < r1)\n            [r1, r2] = [r2, r1];\n        if (c2 < c1)\n            [c1, c2] = [c2, c1];\n        // Fetch the header counts.\n        let rhc = dataModel.columnCount('row-header');\n        let chr = dataModel.rowCount('column-header');\n        // Unpack the copy config.\n        let separator = this._copyConfig.separator;\n        let format = this._copyConfig.format;\n        let headers = this._copyConfig.headers;\n        let warningThreshold = this._copyConfig.warningThreshold;\n        // Compute the number of cells to be copied.\n        let rowCount = r2 - r1 + 1;\n        let colCount = c2 - c1 + 1;\n        switch (headers) {\n            case 'none':\n                rhc = 0;\n                chr = 0;\n                break;\n            case 'row':\n                chr = 0;\n                colCount += rhc;\n                break;\n            case 'column':\n                rhc = 0;\n                rowCount += chr;\n                break;\n            case 'all':\n                rowCount += chr;\n                colCount += rhc;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Compute the total cell count.\n        let cellCount = rowCount * colCount;\n        // Allow the user to cancel a large copy request.\n        if (cellCount > warningThreshold) {\n            let msg = `Copying ${cellCount} cells may take a while. Continue?`;\n            if (!window.confirm(msg)) {\n                return;\n            }\n        }\n        // Set up the format args.\n        let args = {\n            region: 'body',\n            row: 0,\n            column: 0,\n            value: null,\n            metadata: {}\n        };\n        // Allocate the array of rows.\n        let rows = new Array(rowCount);\n        // Iterate over the rows.\n        for (let j = 0; j < rowCount; ++j) {\n            // Allocate the array of cells.\n            let cells = new Array(colCount);\n            // Iterate over the columns.\n            for (let i = 0; i < colCount; ++i) {\n                // Set up the format variables.\n                let region;\n                let row;\n                let column;\n                // Populate the format variables.\n                if (j < chr && i < rhc) {\n                    region = 'corner-header';\n                    row = j;\n                    column = i;\n                }\n                else if (j < chr) {\n                    region = 'column-header';\n                    row = j;\n                    column = i - rhc + c1;\n                }\n                else if (i < rhc) {\n                    region = 'row-header';\n                    row = j - chr + r1;\n                    column = i;\n                }\n                else {\n                    region = 'body';\n                    row = j - chr + r1;\n                    column = i - rhc + c1;\n                }\n                // Populate the format args.\n                args.region = region;\n                args.row = row;\n                args.column = column;\n                args.value = dataModel.data(region, row, column);\n                args.metadata = dataModel.metadata(region, row, column);\n                // Format the cell.\n                cells[i] = format(args);\n            }\n            // Save the row of cells.\n            rows[j] = cells;\n        }\n        // Convert the cells into lines.\n        let lines = rows.map(cells => cells.join(separator));\n        // Convert the lines into text.\n        let text = lines.join('\\n');\n        // Copy the text to the clipboard.\n        _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ClipboardExt.copyText(text);\n    }\n    /**\n     * Process a message sent to the widget.\n     *\n     * @param msg - The message sent to the widget.\n     */\n    processMessage(msg) {\n        // Ignore child show/hide messages. The data grid controls the\n        // visibility of its children, and will manually dispatch the\n        // fit-request messages as a result of visibility change.\n        if (msg.type === 'child-shown' || msg.type === 'child-hidden') {\n            return;\n        }\n        // Recompute the scroll bar minimums before the layout refits.\n        if (msg.type === 'fit-request') {\n            let vsbLimits = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ElementExt.sizeLimits(this._vScrollBar.node);\n            let hsbLimits = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ElementExt.sizeLimits(this._hScrollBar.node);\n            this._vScrollBarMinWidth = vsbLimits.minWidth;\n            this._hScrollBarMinHeight = hsbLimits.minHeight;\n        }\n        // Process all other messages as normal.\n        super.processMessage(msg);\n    }\n    /**\n     * Intercept a message sent to a message handler.\n     *\n     * @param handler - The target handler of the message.\n     *\n     * @param msg - The message to be sent to the handler.\n     *\n     * @returns `true` if the message should continue to be processed\n     *   as normal, or `false` if processing should cease immediately.\n     */\n    messageHook(handler, msg) {\n        // Process viewport messages.\n        if (handler === this._viewport) {\n            this._processViewportMessage(msg);\n            return true;\n        }\n        // Process horizontal scroll bar messages.\n        if (handler === this._hScrollBar && msg.type === 'activate-request') {\n            this.activate();\n            return false;\n        }\n        // Process vertical scroll bar messages.\n        if (handler === this._vScrollBar && msg.type === 'activate-request') {\n            this.activate();\n            return false;\n        }\n        // Ignore all other messages.\n        return true;\n    }\n    /**\n     * Handle the DOM events for the data grid.\n     *\n     * @param event - The DOM event sent to the data grid.\n     *\n     * #### Notes\n     * This method implements the DOM `EventListener` interface and is\n     * called in response to events on the data grid's DOM node. It\n     * should not be called directly by user code.\n     */\n    handleEvent(event) {\n        switch (event.type) {\n            case 'keydown':\n                this._evtKeyDown(event);\n                break;\n            case 'mousedown':\n                this._evtMouseDown(event);\n                break;\n            case 'mousemove':\n                this._evtMouseMove(event);\n                break;\n            case 'mouseup':\n                this._evtMouseUp(event);\n                break;\n            case 'dblclick':\n                this._evtMouseDoubleClick(event);\n                break;\n            case 'mouseleave':\n                this._evtMouseLeave(event);\n                break;\n            case 'contextmenu':\n                this._evtContextMenu(event);\n                break;\n            case 'wheel':\n                this._evtWheel(event);\n                break;\n            case 'resize':\n                this._refreshDPI();\n                break;\n        }\n    }\n    /**\n     * A message handler invoked on an `'activate-request'` message.\n     */\n    onActivateRequest(msg) {\n        this.viewport.node.focus({ preventScroll: true });\n    }\n    /**\n     * A message handler invoked on a `'before-attach'` message.\n     */\n    onBeforeAttach(msg) {\n        window.addEventListener('resize', this);\n        this.node.addEventListener('wheel', this);\n        this._viewport.node.addEventListener('keydown', this);\n        this._viewport.node.addEventListener('mousedown', this);\n        this._viewport.node.addEventListener('mousemove', this);\n        this._viewport.node.addEventListener('dblclick', this);\n        this._viewport.node.addEventListener('mouseleave', this);\n        this._viewport.node.addEventListener('contextmenu', this);\n        this.repaintContent();\n        this.repaintOverlay();\n    }\n    /**\n     * A message handler invoked on an `'after-detach'` message.\n     */\n    onAfterDetach(msg) {\n        window.removeEventListener('resize', this);\n        this.node.removeEventListener('wheel', this);\n        this._viewport.node.removeEventListener('keydown', this);\n        this._viewport.node.removeEventListener('mousedown', this);\n        this._viewport.node.removeEventListener('mousemove', this);\n        this._viewport.node.removeEventListener('mouseleave', this);\n        this._viewport.node.removeEventListener('dblclick', this);\n        this._viewport.node.removeEventListener('contextmenu', this);\n        this._releaseMouse();\n    }\n    /**\n     * A message handler invoked on a `'before-show'` message.\n     */\n    onBeforeShow(msg) {\n        this.repaintContent();\n        this.repaintOverlay();\n    }\n    /**\n     * A message handler invoked on a `'resize'` message.\n     */\n    onResize(msg) {\n        if (this._editorController) {\n            this._editorController.cancel();\n        }\n        this._syncScrollState();\n    }\n    /**\n     * Schedule a repaint of all of the grid content.\n     */\n    repaintContent() {\n        let msg = new Private$1.PaintRequest('all', 0, 0, 0, 0);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);\n    }\n    /**\n     * Schedule a repaint of specific grid content.\n     */\n    repaintRegion(region, r1, c1, r2, c2) {\n        let msg = new Private$1.PaintRequest(region, r1, c1, r2, c2);\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);\n    }\n    /**\n     * Schedule a repaint of the overlay.\n     */\n    repaintOverlay() {\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.OverlayPaintRequest);\n    }\n    _getMaxWidthInColumn(index, columnRegion) {\n        const dataModel = this.dataModel;\n        if (!dataModel) {\n            return null;\n        }\n        const columnHeaderRegion = columnRegion == 'row-header' ? 'corner-header' : 'column-header';\n        return Math.max(this._getMaxWidthInArea(dataModel, index, columnHeaderRegion, 'column-header'), this._getMaxWidthInArea(dataModel, index, columnRegion, 'body'));\n    }\n    _getMaxWidthInArea(dataModel, index, region, rowRegion) {\n        const numRows = dataModel.rowCount(rowRegion);\n        // Will only allocate up to 1_000_000 elements otherwise performance can tank.\n        const configs = Array.from({ length: Math.min(numRows, 1000000) }, (_val, idx) => DataGrid._getConfig(dataModel, idx, index, region));\n        // Heuristic: Sort by the length of the text to render and only fully calculate the text width\n        // for the top 100_000 rows by text length\n        if (numRows > 100000) {\n            // Sort by descending length\n            configs.sort(x => -this._getTextToRender(x).length);\n        }\n        let maxWidth = 0;\n        for (let i = 0; i < numRows && i < 100000; ++i) {\n            const textWidth = this._getCellTextWidth(configs[i]);\n            maxWidth = Math.max(maxWidth, textWidth);\n        }\n        return maxWidth;\n    }\n    static _getConfig(dataModel, row, col, location) {\n        return {\n            x: 0,\n            y: 0,\n            width: 0,\n            height: 0,\n            region: location,\n            row: row,\n            column: col,\n            value: DataGrid._getCellValue(dataModel, location, row, col),\n            metadata: DataGrid._getCellMetadata(dataModel, location, row, col)\n        };\n    }\n    _getTextToRender(config) {\n        const renderer = this.cellRenderers.get(config);\n        return renderer.getText(config);\n    }\n    _getCellTextWidth(config) {\n        // Get the renderer for the given cell.\n        const renderer = this.cellRenderers.get(config);\n        // Use the canvas context to measure the cell's text width\n        const gc = this.canvasGC;\n        gc.font = CellRenderer.resolveOption(renderer.font, config);\n        gc.fillStyle = CellRenderer.resolveOption(renderer.textColor, config);\n        gc.textAlign = CellRenderer.resolveOption(renderer.horizontalAlignment, config);\n        gc.textBaseline = 'bottom';\n        const text = this._getTextToRender(config);\n        return gc.measureText(text).width + 2 * renderer.horizontalPadding;\n    }\n    /**\n     * Ensure the canvas is at least the specified size.\n     *\n     * This method will retain the valid canvas content.\n     */\n    _resizeCanvasIfNeeded(width, height) {\n        // Scale the size by the dpi ratio.\n        width = width * this._dpiRatio;\n        height = height * this._dpiRatio;\n        // Compute the maximum canvas size for the given width and height.\n        let maxW = (Math.ceil((width + 1) / 512) + 1) * 512;\n        let maxH = (Math.ceil((height + 1) / 512) + 1) * 512;\n        // Get the current size of the canvas.\n        let curW = this._canvas.width;\n        let curH = this._canvas.height;\n        // Bail early if the canvas size is within bounds.\n        if (curW >= width && curH >= height && curW <= maxW && curH <= maxH) {\n            return;\n        }\n        // Compute the expanded canvas size.\n        let expW = maxW - 512;\n        let expH = maxH - 512;\n        // Set the transforms to the identity matrix.\n        this._canvasGC.setTransform(1, 0, 0, 1, 0, 0);\n        this._bufferGC.setTransform(1, 0, 0, 1, 0, 0);\n        this._overlayGC.setTransform(1, 0, 0, 1, 0, 0);\n        // Resize the buffer if needed.\n        if (curW < width) {\n            this._buffer.width = expW;\n        }\n        else if (curW > maxW) {\n            this._buffer.width = maxW;\n        }\n        // Resize the buffer height if needed.\n        if (curH < height) {\n            this._buffer.height = expH;\n        }\n        else if (curH > maxH) {\n            this._buffer.height = maxH;\n        }\n        // Test whether there is content to blit.\n        let needBlit = curW > 0 && curH > 0 && width > 0 && height > 0;\n        // Copy the valid canvas content into the buffer if needed.\n        if (needBlit) {\n            this._bufferGC.drawImage(this._canvas, 0, 0);\n        }\n        // Resize the canvas width if needed.\n        if (curW < width) {\n            this._canvas.width = expW;\n            this._canvas.style.width = `${expW / this._dpiRatio}px`;\n        }\n        else if (curW > maxW) {\n            this._canvas.width = maxW;\n            this._canvas.style.width = `${maxW / this._dpiRatio}px`;\n        }\n        // Resize the canvas height if needed.\n        if (curH < height) {\n            this._canvas.height = expH;\n            this._canvas.style.height = `${expH / this._dpiRatio}px`;\n        }\n        else if (curH > maxH) {\n            this._canvas.height = maxH;\n            this._canvas.style.height = `${maxH / this._dpiRatio}px`;\n        }\n        // Copy the valid canvas content from the buffer if needed.\n        if (needBlit) {\n            this._canvasGC.drawImage(this._buffer, 0, 0);\n        }\n        // Copy the valid overlay content into the buffer if needed.\n        if (needBlit) {\n            this._bufferGC.drawImage(this._overlay, 0, 0);\n        }\n        // Resize the overlay width if needed.\n        if (curW < width) {\n            this._overlay.width = expW;\n            this._overlay.style.width = `${expW / this._dpiRatio}px`;\n        }\n        else if (curW > maxW) {\n            this._overlay.width = maxW;\n            this._overlay.style.width = `${maxW / this._dpiRatio}px`;\n        }\n        // Resize the overlay height if needed.\n        if (curH < height) {\n            this._overlay.height = expH;\n            this._overlay.style.height = `${expH / this._dpiRatio}px`;\n        }\n        else if (curH > maxH) {\n            this._overlay.height = maxH;\n            this._overlay.style.height = `${maxH / this._dpiRatio}px`;\n        }\n        // Copy the valid overlay content from the buffer if needed.\n        if (needBlit) {\n            this._overlayGC.drawImage(this._buffer, 0, 0);\n        }\n    }\n    /**\n     * Sync the scroll bars and scroll state with the viewport.\n     *\n     * #### Notes\n     * If the visibility of either scroll bar changes, a synchronous\n     * fit-request will be dispatched to the data grid to immediately\n     * resize the viewport.\n     */\n    _syncScrollState() {\n        // Fetch the viewport dimensions.\n        let bw = this.bodyWidth;\n        let bh = this.bodyHeight;\n        let pw = this.pageWidth;\n        let ph = this.pageHeight;\n        // Get the current scroll bar visibility.\n        let hasVScroll = !this._vScrollBar.isHidden;\n        let hasHScroll = !this._hScrollBar.isHidden;\n        // Get the minimum sizes of the scroll bars.\n        let vsw = this._vScrollBarMinWidth;\n        let hsh = this._hScrollBarMinHeight;\n        // Get the page size as if no scroll bars are visible.\n        let apw = pw + (hasVScroll ? vsw : 0);\n        let aph = ph + (hasHScroll ? hsh : 0);\n        // Test whether scroll bars are needed for the adjusted size.\n        let needVScroll = aph < bh - 1;\n        let needHScroll = apw < bw - 1;\n        // Re-test the horizontal scroll if a vertical scroll is needed.\n        if (needVScroll && !needHScroll) {\n            needHScroll = apw - vsw < bw - 1;\n        }\n        // Re-test the vertical scroll if a horizontal scroll is needed.\n        if (needHScroll && !needVScroll) {\n            needVScroll = aph - hsh < bh - 1;\n        }\n        // If the visibility changes, immediately refit the grid.\n        if (needVScroll !== hasVScroll || needHScroll !== hasHScroll) {\n            this._vScrollBar.setHidden(!needVScroll);\n            this._hScrollBar.setHidden(!needHScroll);\n            this._scrollCorner.setHidden(!needVScroll || !needHScroll);\n            _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.sendMessage(this, _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.Msg.FitRequest);\n        }\n        // Update the scroll bar limits.\n        this._vScrollBar.maximum = this.maxScrollY;\n        this._vScrollBar.page = this.pageHeight;\n        this._hScrollBar.maximum = this.maxScrollX;\n        this._hScrollBar.page = this.pageWidth;\n        // Re-clamp the scroll position.\n        this._scrollTo(this._scrollX, this._scrollY);\n    }\n    /**\n     * Sync the viewport to the given scroll position.\n     *\n     * #### Notes\n     * This schedules a full repaint and syncs the scroll state.\n     */\n    _syncViewport() {\n        this.repaintContent();\n        this.repaintOverlay();\n        this._syncScrollState();\n    }\n    /**\n     * Process a message sent to the viewport\n     */\n    _processViewportMessage(msg) {\n        switch (msg.type) {\n            case 'resize':\n                this._onViewportResize(msg);\n                break;\n            case 'scroll-request':\n                this._onViewportScrollRequest(msg);\n                break;\n            case 'paint-request':\n                this._onViewportPaintRequest(msg);\n                break;\n            case 'overlay-paint-request':\n                this._onViewportOverlayPaintRequest(msg);\n                break;\n            case 'row-resize-request':\n                this._onViewportRowResizeRequest(msg);\n                break;\n            case 'column-resize-request':\n                this._onViewportColumnResizeRequest(msg);\n                break;\n        }\n    }\n    /**\n     * A message hook invoked on a viewport `'resize'` message.\n     */\n    _onViewportResize(msg) {\n        // Bail early if the viewport is not visible.\n        if (!this._viewport.isVisible) {\n            return;\n        }\n        // Unpack the message data.\n        let { width, height } = msg;\n        // Measure the viewport node if the dimensions are unknown.\n        if (width === -1) {\n            width = this._viewport.node.offsetWidth;\n        }\n        if (height === -1) {\n            height = this._viewport.node.offsetHeight;\n        }\n        // Round the dimensions to the nearest pixel.\n        width = Math.round(width);\n        height = Math.round(height);\n        // Get the current size of the viewport.\n        let oldWidth = this._viewportWidth;\n        let oldHeight = this._viewportHeight;\n        // Updated internal viewport size.\n        this._viewportWidth = width;\n        this._viewportHeight = height;\n        // Resize the canvas if needed.\n        this._resizeCanvasIfNeeded(width, height);\n        // Bail early if there is nothing to paint.\n        if (width === 0 || height === 0) {\n            return;\n        }\n        // Paint the whole grid if the old size was zero.\n        if (oldWidth === 0 || oldHeight === 0) {\n            this.paintContent(0, 0, width, height);\n            this._paintOverlay();\n            return;\n        }\n        // Paint the right edge as needed.\n        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {\n            let bx = this._columnSections.offsetOf(this._columnSections.count - 1);\n            let x = Math.min(this.headerWidth + bx, oldWidth);\n            this.paintContent(x, 0, width - x, height);\n        }\n        else if (width > oldWidth) {\n            this.paintContent(oldWidth, 0, width - oldWidth + 1, height);\n        }\n        // Paint the bottom edge as needed.\n        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {\n            let by = this._rowSections.offsetOf(this._rowSections.count - 1);\n            let y = Math.min(this.headerHeight + by, oldHeight);\n            this.paintContent(0, y, width, height - y);\n        }\n        else if (height > oldHeight) {\n            this.paintContent(0, oldHeight, width, height - oldHeight + 1);\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n    }\n    /**\n     * A message hook invoked on a viewport `'scroll-request'` message.\n     */\n    _onViewportScrollRequest(msg) {\n        this._scrollTo(this._hScrollBar.value, this._vScrollBar.value);\n    }\n    /**\n     * A message hook invoked on a viewport `'paint-request'` message.\n     */\n    _onViewportPaintRequest(msg) {\n        // Bail early if the viewport is not visible.\n        if (!this._viewport.isVisible) {\n            return;\n        }\n        // Bail early if the viewport has zero area.\n        if (this._viewportWidth === 0 || this._viewportHeight === 0) {\n            return;\n        }\n        // Set up the paint limits.\n        let xMin = 0;\n        let yMin = 0;\n        let xMax = this._viewportWidth - 1;\n        let yMax = this._viewportHeight - 1;\n        // Fetch the scroll position.\n        let sx = this._scrollX;\n        let sy = this._scrollY;\n        // Fetch the header dimensions.\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        // Fetch the section lists.\n        let rs = this._rowSections;\n        let cs = this._columnSections;\n        let rhs = this._rowHeaderSections;\n        let chs = this._columnHeaderSections;\n        // Unpack the message data.\n        let { region, r1, c1, r2, c2 } = msg;\n        // Set up the paint variables.\n        let x1;\n        let y1;\n        let x2;\n        let y2;\n        // Fill the paint variables based on the paint region.\n        switch (region) {\n            case 'all':\n                x1 = xMin;\n                y1 = yMin;\n                x2 = xMax;\n                y2 = yMax;\n                break;\n            case 'body':\n                r1 = Math.max(0, Math.min(r1, rs.count));\n                c1 = Math.max(0, Math.min(c1, cs.count));\n                r2 = Math.max(0, Math.min(r2, rs.count));\n                c2 = Math.max(0, Math.min(c2, cs.count));\n                x1 = cs.offsetOf(c1) - sx + hw;\n                y1 = rs.offsetOf(r1) - sy + hh;\n                x2 = cs.extentOf(c2) - sx + hw;\n                y2 = rs.extentOf(r2) - sy + hh;\n                break;\n            case 'row-header':\n                r1 = Math.max(0, Math.min(r1, rs.count));\n                c1 = Math.max(0, Math.min(c1, rhs.count));\n                r2 = Math.max(0, Math.min(r2, rs.count));\n                c2 = Math.max(0, Math.min(c2, rhs.count));\n                x1 = rhs.offsetOf(c1);\n                y1 = rs.offsetOf(r1) - sy + hh;\n                x2 = rhs.extentOf(c2);\n                y2 = rs.extentOf(r2) - sy + hh;\n                break;\n            case 'column-header':\n                r1 = Math.max(0, Math.min(r1, chs.count));\n                c1 = Math.max(0, Math.min(c1, cs.count));\n                r2 = Math.max(0, Math.min(r2, chs.count));\n                c2 = Math.max(0, Math.min(c2, cs.count));\n                x1 = cs.offsetOf(c1) - sx + hw;\n                y1 = chs.offsetOf(r1);\n                x2 = cs.extentOf(c2) - sx + hw;\n                y2 = chs.extentOf(r2);\n                break;\n            case 'corner-header':\n                r1 = Math.max(0, Math.min(r1, chs.count));\n                c1 = Math.max(0, Math.min(c1, rhs.count));\n                r2 = Math.max(0, Math.min(r2, chs.count));\n                c2 = Math.max(0, Math.min(c2, rhs.count));\n                x1 = rhs.offsetOf(c1);\n                y1 = chs.offsetOf(r1);\n                x2 = rhs.extentOf(c2);\n                y2 = chs.extentOf(r2);\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Bail early if the dirty rect is outside the bounds.\n        if (x2 < xMin || y2 < yMin || x1 > xMax || y1 > yMax) {\n            return;\n        }\n        // Clamp the dirty rect to the paint bounds.\n        x1 = Math.max(xMin, Math.min(x1, xMax));\n        y1 = Math.max(yMin, Math.min(y1, yMax));\n        x2 = Math.max(xMin, Math.min(x2, xMax));\n        y2 = Math.max(yMin, Math.min(y2, yMax));\n        // Paint the content of the dirty rect.\n        this.paintContent(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n    }\n    /**\n     * A message hook invoked on a viewport `'overlay-paint-request'` message.\n     */\n    _onViewportOverlayPaintRequest(msg) {\n        // Bail early if the viewport is not visible.\n        if (!this._viewport.isVisible) {\n            return;\n        }\n        // Bail early if the viewport has zero area.\n        if (this._viewportWidth === 0 || this._viewportHeight === 0) {\n            return;\n        }\n        // Paint the content of the overlay.\n        this._paintOverlay();\n    }\n    /**\n     * A message hook invoked on a viewport `'row-resize-request'` message.\n     */\n    _onViewportRowResizeRequest(msg) {\n        if (msg.region === 'body') {\n            this._resizeRow(msg.index, msg.size);\n        }\n        else {\n            this._resizeColumnHeader(msg.index, msg.size);\n        }\n    }\n    /**\n     * A message hook invoked on a viewport `'column-resize-request'` message.\n     */\n    _onViewportColumnResizeRequest(msg) {\n        if (msg.region === 'body') {\n            this._resizeColumn(msg.index, msg.size);\n        }\n        else {\n            this._resizeRowHeader(msg.index, msg.size);\n        }\n    }\n    /**\n     * Handle the `thumbMoved` signal from a scroll bar.\n     */\n    _onThumbMoved(sender) {\n        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.ScrollRequest);\n    }\n    /**\n     * Handle the `pageRequested` signal from a scroll bar.\n     */\n    _onPageRequested(sender, dir) {\n        if (sender === this._vScrollBar) {\n            this.scrollByPage(dir === 'decrement' ? 'up' : 'down');\n        }\n        else {\n            this.scrollByPage(dir === 'decrement' ? 'left' : 'right');\n        }\n    }\n    /**\n     * Handle the `stepRequested` signal from a scroll bar.\n     */\n    _onStepRequested(sender, dir) {\n        if (sender === this._vScrollBar) {\n            this.scrollByStep(dir === 'decrement' ? 'up' : 'down');\n        }\n        else {\n            this.scrollByStep(dir === 'decrement' ? 'left' : 'right');\n        }\n    }\n    /**\n     * A signal handler for the data model `changed` signal.\n     */\n    _onDataModelChanged(sender, args) {\n        switch (args.type) {\n            case 'rows-inserted':\n                this._onRowsInserted(args);\n                break;\n            case 'columns-inserted':\n                this._onColumnsInserted(args);\n                break;\n            case 'rows-removed':\n                this._onRowsRemoved(args);\n                break;\n            case 'columns-removed':\n                this._onColumnsRemoved(args);\n                break;\n            case 'rows-moved':\n                this._onRowsMoved(args);\n                break;\n            case 'columns-moved':\n                this._onColumnsMoved(args);\n                break;\n            case 'cells-changed':\n                this._onCellsChanged(args);\n                break;\n            case 'model-reset':\n                this._onModelReset(args);\n                break;\n            default:\n                throw 'unreachable';\n        }\n    }\n    /**\n     * A signal handler for the selection model `changed` signal.\n     */\n    _onSelectionsChanged(sender) {\n        this.repaintOverlay();\n    }\n    /**\n     * Handle rows being inserted in the data model.\n     */\n    _onRowsInserted(args) {\n        // Unpack the arg data.\n        let { region, index, span } = args;\n        // Bail early if there are no sections to insert.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._rowSections;\n        }\n        else {\n            list = this._columnHeaderSections;\n        }\n        // Insert the span, maintaining the scroll position as needed.\n        if (this._scrollY === this.maxScrollY && this.maxScrollY > 0) {\n            list.insert(index, span);\n            this._scrollY = this.maxScrollY;\n        }\n        else {\n            list.insert(index, span);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle columns being inserted into the data model.\n     */\n    _onColumnsInserted(args) {\n        // Unpack the arg data.\n        let { region, index, span } = args;\n        // Bail early if there are no sections to insert.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._columnSections;\n        }\n        else {\n            list = this._rowHeaderSections;\n        }\n        // Insert the span, maintaining the scroll position as needed.\n        if (this._scrollX === this.maxScrollX && this.maxScrollX > 0) {\n            list.insert(index, span);\n            this._scrollX = this.maxScrollX;\n        }\n        else {\n            list.insert(index, span);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle rows being removed from the data model.\n     */\n    _onRowsRemoved(args) {\n        // Unpack the arg data.\n        let { region, index, span } = args;\n        // Bail early if there are no sections to remove.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._rowSections;\n        }\n        else {\n            list = this._columnHeaderSections;\n        }\n        // Bail if the index or is invalid\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Remove the span, maintaining the scroll position as needed.\n        if (this._scrollY === this.maxScrollY && this.maxScrollY > 0) {\n            list.remove(index, span);\n            this._scrollY = this.maxScrollY;\n        }\n        else {\n            list.remove(index, span);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle columns being removed from the data model.\n     */\n    _onColumnsRemoved(args) {\n        // Unpack the arg data.\n        let { region, index, span } = args;\n        // Bail early if there are no sections to remove.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._columnSections;\n        }\n        else {\n            list = this._rowHeaderSections;\n        }\n        // Bail if the index or is invalid\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Remove the span, maintaining the scroll position as needed.\n        if (this._scrollX === this.maxScrollX && this.maxScrollX > 0) {\n            list.remove(index, span);\n            this._scrollX = this.maxScrollX;\n        }\n        else {\n            list.remove(index, span);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle rows moving in the data model.\n     */\n    _onRowsMoved(args) {\n        // Unpack the arg data.\n        let { region, index, span, destination } = args;\n        // Bail early if there are no sections to move.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._rowSections;\n        }\n        else {\n            list = this._columnHeaderSections;\n        }\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Clamp the move span to the limit.\n        span = Math.min(span, list.count - index);\n        // Clamp the destination index to the limit.\n        destination = Math.min(Math.max(0, destination), list.count - span);\n        // Bail early if there is no effective move.\n        if (index === destination) {\n            return;\n        }\n        // Compute the first affected index.\n        let r1 = Math.min(index, destination);\n        // Compute the last affected index.\n        let r2 = Math.max(index + span - 1, destination + span - 1);\n        // Move the sections in the list.\n        list.move(index, span, destination);\n        // Schedule a repaint of the dirty cells.\n        if (region === 'body') {\n            this.repaintRegion('body', r1, 0, r2, Infinity);\n            this.repaintRegion('row-header', r1, 0, r2, Infinity);\n        }\n        else {\n            this.repaintRegion('column-header', r1, 0, r2, Infinity);\n            this.repaintRegion('corner-header', r1, 0, r2, Infinity);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle columns moving in the data model.\n     */\n    _onColumnsMoved(args) {\n        // Unpack the arg data.\n        let { region, index, span, destination } = args;\n        // Bail early if there are no sections to move.\n        if (span <= 0) {\n            return;\n        }\n        // Look up the relevant section list.\n        let list;\n        if (region === 'body') {\n            list = this._columnSections;\n        }\n        else {\n            list = this._rowHeaderSections;\n        }\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Clamp the move span to the limit.\n        span = Math.min(span, list.count - index);\n        // Clamp the destination index to the limit.\n        destination = Math.min(Math.max(0, destination), list.count - span);\n        // Bail early if there is no effective move.\n        if (index === destination) {\n            return;\n        }\n        // Move the sections in the list.\n        list.move(index, span, destination);\n        // Compute the first affected index.\n        let c1 = Math.min(index, destination);\n        // Compute the last affected index.\n        let c2 = Math.max(index + span - 1, destination + span - 1);\n        // Schedule a repaint of the dirty cells.\n        if (region === 'body') {\n            this.repaintRegion('body', 0, c1, Infinity, c2);\n            this.repaintRegion('column-header', 0, c1, Infinity, c2);\n        }\n        else {\n            this.repaintRegion('row-header', 0, c1, Infinity, c2);\n            this.repaintRegion('corner-header', 0, c1, Infinity, c2);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * Handle cells changing in the data model.\n     */\n    _onCellsChanged(args) {\n        // Unpack the arg data.\n        let { region, row, column, rowSpan, columnSpan } = args;\n        // Bail early if there are no cells to modify.\n        if (rowSpan <= 0 && columnSpan <= 0) {\n            return;\n        }\n        // Compute the changed cell bounds.\n        let r1 = row;\n        let c1 = column;\n        let r2 = r1 + rowSpan - 1;\n        let c2 = c1 + columnSpan - 1;\n        // Schedule a repaint of the cell content.\n        this.repaintRegion(region, r1, c1, r2, c2);\n    }\n    /**\n     * Handle a full data model reset.\n     */\n    _onModelReset(args) {\n        // Look up the various current section counts.\n        let nr = this._rowSections.count;\n        let nc = this._columnSections.count;\n        let nrh = this._rowHeaderSections.count;\n        let nch = this._columnHeaderSections.count;\n        // Compute the delta count for each region.\n        let dr = this._dataModel.rowCount('body') - nr;\n        let dc = this._dataModel.columnCount('body') - nc;\n        let drh = this._dataModel.columnCount('row-header') - nrh;\n        let dch = this._dataModel.rowCount('column-header') - nch;\n        // Update the row sections, if needed.\n        if (dr > 0) {\n            this._rowSections.insert(nr, dr);\n        }\n        else if (dr < 0) {\n            this._rowSections.remove(nr + dr, -dr);\n        }\n        // Update the column sections, if needed.\n        if (dc > 0) {\n            this._columnSections.insert(nc, dc);\n        }\n        else if (dc < 0) {\n            this._columnSections.remove(nc + dc, -dc);\n        }\n        // Update the row header sections, if needed.\n        if (drh > 0) {\n            this._rowHeaderSections.insert(nrh, drh);\n        }\n        else if (drh < 0) {\n            this._rowHeaderSections.remove(nrh + drh, -drh);\n        }\n        // Update the column header sections, if needed.\n        if (dch > 0) {\n            this._columnHeaderSections.insert(nch, dch);\n        }\n        else if (dch < 0) {\n            this._columnHeaderSections.remove(nch + dch, -dch);\n        }\n        // Sync the viewport.\n        this._syncViewport();\n    }\n    /**\n     * A signal handler for the renderer map `changed` signal.\n     */\n    _onRenderersChanged() {\n        this.repaintContent();\n    }\n    /**\n     * Handle the `'keydown'` event for the data grid.\n     */\n    _evtKeyDown(event) {\n        if (this._mousedown) {\n            event.preventDefault();\n            event.stopPropagation();\n        }\n        else if (this._keyHandler) {\n            this._keyHandler.onKeyDown(this, event);\n        }\n    }\n    /**\n     * Handle the `'mousedown'` event for the data grid.\n     */\n    _evtMouseDown(event) {\n        // Ignore everything except the left mouse button.\n        if (event.button !== 0) {\n            return;\n        }\n        // Activate the grid.\n        this.activate();\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Add the extra document listeners.\n        document.addEventListener('keydown', this, true);\n        document.addEventListener('mouseup', this, true);\n        document.addEventListener('mousedown', this, true);\n        document.addEventListener('mousemove', this, true);\n        document.addEventListener('contextmenu', this, true);\n        // Flip the mousedown flag.\n        this._mousedown = true;\n        // Dispatch to the mouse handler.\n        if (this._mouseHandler) {\n            this._mouseHandler.onMouseDown(this, event);\n        }\n    }\n    /**\n     * Handle the `'mousemove'` event for the data grid.\n     */\n    _evtMouseMove(event) {\n        // Stop the event propagation if the mouse is down.\n        if (this._mousedown) {\n            event.preventDefault();\n            event.stopPropagation();\n        }\n        // Bail if there is no mouse handler.\n        if (!this._mouseHandler) {\n            return;\n        }\n        // Dispatch to the mouse handler.\n        if (this._mousedown) {\n            this._mouseHandler.onMouseMove(this, event);\n        }\n        else {\n            this._mouseHandler.onMouseHover(this, event);\n        }\n    }\n    /**\n     * Handle the `'mouseup'` event for the data grid.\n     */\n    _evtMouseUp(event) {\n        // Ignore everything except the left mouse button.\n        if (event.button !== 0) {\n            return;\n        }\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Dispatch to the mouse handler.\n        if (this._mouseHandler) {\n            this._mouseHandler.onMouseUp(this, event);\n        }\n        // Release the mouse.\n        this._releaseMouse();\n    }\n    /**\n     * Handle the `'dblclick'` event for the data grid.\n     */\n    _evtMouseDoubleClick(event) {\n        // Ignore everything except the left mouse button.\n        if (event.button !== 0) {\n            return;\n        }\n        // Stop the event propagation.\n        event.preventDefault();\n        event.stopPropagation();\n        // Dispatch to the mouse handler.\n        if (this._mouseHandler) {\n            this._mouseHandler.onMouseDoubleClick(this, event);\n        }\n        // Release the mouse.\n        this._releaseMouse();\n    }\n    /**\n     * Handle the `'mouseleave'` event for the data grid.\n     */\n    _evtMouseLeave(event) {\n        if (this._mousedown) {\n            event.preventDefault();\n            event.stopPropagation();\n        }\n        else if (this._mouseHandler) {\n            this._mouseHandler.onMouseLeave(this, event);\n        }\n    }\n    /**\n     * Handle the `'contextmenu'` event for the data grid.\n     */\n    _evtContextMenu(event) {\n        if (this._mousedown) {\n            event.preventDefault();\n            event.stopPropagation();\n        }\n        else if (this._mouseHandler) {\n            this._mouseHandler.onContextMenu(this, event);\n        }\n    }\n    /**\n     * Handle the `'wheel'` event for the data grid.\n     */\n    _evtWheel(event) {\n        // Ignore the event if `accel` is held.\n        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {\n            return;\n        }\n        // Bail early if there is no mouse handler.\n        if (!this._mouseHandler) {\n            return;\n        }\n        // Dispatch to the mouse handler.\n        this._mouseHandler.onWheel(this, event);\n    }\n    /**\n     * Release the mouse grab.\n     */\n    _releaseMouse() {\n        // Clear the mousedown flag.\n        this._mousedown = false;\n        // Relase the mouse handler.\n        if (this._mouseHandler) {\n            this._mouseHandler.release();\n        }\n        // Remove the document listeners.\n        document.removeEventListener('keydown', this, true);\n        document.removeEventListener('mouseup', this, true);\n        document.removeEventListener('mousedown', this, true);\n        document.removeEventListener('mousemove', this, true);\n        document.removeEventListener('contextmenu', this, true);\n    }\n    /**\n     * Refresh the dpi ratio.\n     */\n    _refreshDPI() {\n        // Get the best integral value for the dpi ratio.\n        let dpiRatio = Math.ceil(window.devicePixelRatio);\n        // Bail early if the computed dpi ratio has not changed.\n        if (this._dpiRatio === dpiRatio) {\n            return;\n        }\n        // Update the internal dpi ratio.\n        this._dpiRatio = dpiRatio;\n        // Schedule a repaint of the content.\n        this.repaintContent();\n        // Schedule a repaint of the overlay.\n        this.repaintOverlay();\n        // Update the canvas size for the new dpi ratio.\n        this._resizeCanvasIfNeeded(this._viewportWidth, this._viewportHeight);\n        // Ensure the canvas style is scaled for the new ratio.\n        this._canvas.style.width = `${this._canvas.width / this._dpiRatio}px`;\n        this._canvas.style.height = `${this._canvas.height / this._dpiRatio}px`;\n        // Ensure the overlay style is scaled for the new ratio.\n        this._overlay.style.width = `${this._overlay.width / this._dpiRatio}px`;\n        this._overlay.style.height = `${this._overlay.height / this._dpiRatio}px`;\n    }\n    /**\n     * Resize a row section immediately.\n     */\n    _resizeRow(index, size) {\n        // Look up the target section list.\n        let list = this._rowSections;\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Look up the old size of the section.\n        let oldSize = list.sizeOf(index);\n        // Normalize the new size of the section.\n        let newSize = list.clampSize(size);\n        // Bail early if the size does not change.\n        if (oldSize === newSize) {\n            return;\n        }\n        // Resize the section in the list.\n        list.resize(index, newSize);\n        // Get the current size of the viewport.\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // If there is nothing to paint, sync the scroll state.\n        if (!this._viewport.isVisible || vw === 0 || vh === 0) {\n            this._syncScrollState();\n            return;\n        }\n        // Compute the size delta.\n        let delta = newSize - oldSize;\n        // Look up the column header height.\n        let hh = this.headerHeight;\n        // Compute the viewport offset of the section.\n        let offset = list.offsetOf(index) + hh - this._scrollY;\n        // Bail early if there is nothing to paint.\n        if (hh >= vh || offset >= vh) {\n            this._syncScrollState();\n            return;\n        }\n        // Update the scroll position if the section is not visible.\n        if (offset + oldSize <= hh) {\n            this._scrollY += delta;\n            this._syncScrollState();\n            return;\n        }\n        // Compute the paint origin of the section.\n        let pos = Math.max(hh, offset);\n        // Paint from the section onward if it spans the viewport.\n        if (offset + oldSize >= vh || offset + newSize >= vh) {\n            this.paintContent(0, pos, vw, vh - pos);\n            this._paintOverlay();\n            this._syncScrollState();\n            return;\n        }\n        // Compute the X blit dimensions.\n        let sx = 0;\n        let sw = vw;\n        let dx = 0;\n        // Compute the Y blit dimensions.\n        let sy;\n        let sh;\n        let dy;\n        if (offset + newSize <= hh) {\n            sy = hh - delta;\n            sh = vh - sy;\n            dy = hh;\n        }\n        else {\n            sy = offset + oldSize;\n            sh = vh - sy;\n            dy = sy + delta;\n        }\n        // Blit the valid content to the destination.\n        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);\n        // Repaint the section if needed.\n        if (newSize > 0 && offset + newSize > hh) {\n            this.paintContent(0, pos, vw, offset + newSize - pos);\n        }\n        // Paint the trailing space as needed.\n        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {\n            let r = this._rowSections.count - 1;\n            let y = hh + this._rowSections.offsetOf(r);\n            this.paintContent(0, y, vw, vh - y);\n        }\n        else if (delta < 0) {\n            this.paintContent(0, vh + delta, vw, -delta);\n        }\n        // Repaint merged cells that are intersected by the resized row\n        // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n        for (const rgn of ['body', 'row-header']) {\n            const cellGroups = CellGroup.getCellGroupsAtRow(this.dataModel, rgn, index);\n            let paintRgn = {\n                region: rgn,\n                xMin: 0,\n                xMax: 0,\n                yMin: 0,\n                yMax: 0\n            };\n            let backgroundColor = undefined;\n            switch (rgn) {\n                case 'body':\n                    paintRgn.xMin = this.headerWidth;\n                    paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                    paintRgn.yMin = this.headerHeight;\n                    paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                    backgroundColor = this._style.backgroundColor;\n                    break;\n                case 'row-header':\n                    paintRgn.xMin = 0;\n                    paintRgn.xMax = this.headerWidth;\n                    paintRgn.yMin = this.headerHeight;\n                    paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                    backgroundColor = this._style.headerBackgroundColor;\n                    break;\n            }\n            this._paintMergedCells(cellGroups, paintRgn, backgroundColor);\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n        // Sync the scroll state.\n        this._syncScrollState();\n    }\n    /**\n     * Resize a column section immediately.\n     */\n    _resizeColumn(index, size) {\n        // Look up the target section list.\n        let list = this._columnSections;\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        const adjustedSize = size !== null && size !== void 0 ? size : this._getMaxWidthInColumn(index, 'body');\n        if (!adjustedSize || adjustedSize == 0) {\n            return;\n        }\n        // Look up the old size of the section.\n        let oldSize = list.sizeOf(index);\n        // Normalize the new size of the section.\n        let newSize = list.clampSize(adjustedSize);\n        // Bail early if the size does not change.\n        if (oldSize === newSize) {\n            return;\n        }\n        // Resize the section in the list.\n        list.resize(index, newSize);\n        // Get the current size of the viewport.\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // If there is nothing to paint, sync the scroll state.\n        if (!this._viewport.isVisible || vw === 0 || vh === 0) {\n            this._syncScrollState();\n            return;\n        }\n        // Compute the size delta.\n        let delta = newSize - oldSize;\n        // Look up the row header width.\n        let hw = this.headerWidth;\n        // Compute the viewport offset of the section.\n        let offset = list.offsetOf(index) + hw - this._scrollX;\n        // Bail early if there is nothing to paint.\n        if (hw >= vw || offset >= vw) {\n            this._syncScrollState();\n            return;\n        }\n        // Update the scroll position if the section is not visible.\n        if (offset + oldSize <= hw) {\n            this._scrollX += delta;\n            this._syncScrollState();\n            return;\n        }\n        // Compute the paint origin of the section.\n        let pos = Math.max(hw, offset);\n        // Paint from the section onward if it spans the viewport.\n        if (offset + oldSize >= vw || offset + newSize >= vw) {\n            this.paintContent(pos, 0, vw - pos, vh);\n            this._paintOverlay();\n            this._syncScrollState();\n            return;\n        }\n        // Compute the Y blit dimensions.\n        let sy = 0;\n        let sh = vh;\n        let dy = 0;\n        // Compute the X blit dimensions.\n        let sx;\n        let sw;\n        let dx;\n        if (offset + newSize <= hw) {\n            sx = hw - delta;\n            sw = vw - sx;\n            dx = hw;\n        }\n        else {\n            sx = offset + oldSize;\n            sw = vw - sx;\n            dx = sx + delta;\n        }\n        // Blit the valid content to the destination.\n        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);\n        // Repaint the section if needed.\n        if (newSize > 0 && offset + newSize > hw) {\n            this.paintContent(pos, 0, offset + newSize - pos, vh);\n        }\n        // Paint the trailing space as needed.\n        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {\n            let c = this._columnSections.count - 1;\n            let x = hw + this._columnSections.offsetOf(c);\n            this.paintContent(x, 0, vw - x, vh);\n        }\n        else if (delta < 0) {\n            this.paintContent(vw + delta, 0, -delta, vh);\n        }\n        // Repaint merged cells that are intersected by the resized column\n        // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n        for (const rgn of ['body', 'column-header']) {\n            const cellGroups = CellGroup.getCellGroupsAtColumn(this.dataModel, rgn, index);\n            let paintRgn = {\n                region: rgn,\n                xMin: 0,\n                xMax: 0,\n                yMin: 0,\n                yMax: 0\n            };\n            let backgroundColor = undefined;\n            switch (rgn) {\n                case 'body':\n                    paintRgn.xMin = this.headerWidth;\n                    paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                    paintRgn.yMin = this.headerHeight;\n                    paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                    backgroundColor = this._style.backgroundColor;\n                    break;\n                case 'column-header':\n                    paintRgn.xMin = this.headerWidth;\n                    paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                    paintRgn.yMin = 0;\n                    paintRgn.yMax = this.headerHeight;\n                    backgroundColor = this._style.headerBackgroundColor;\n                    break;\n            }\n            this._paintMergedCells(cellGroups, paintRgn, backgroundColor);\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n        // Sync the scroll state after painting.\n        this._syncScrollState();\n    }\n    /**\n     * Resize a row header section immediately.\n     */\n    _resizeRowHeader(index, size) {\n        // Look up the target section list.\n        let list = this._rowHeaderSections;\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        const adjustedSize = size !== null && size !== void 0 ? size : this._getMaxWidthInColumn(index, 'row-header');\n        if (!adjustedSize || adjustedSize == 0) {\n            return;\n        }\n        // Look up the old size of the section.\n        let oldSize = list.sizeOf(index);\n        // Normalize the new size of the section.\n        let newSize = list.clampSize(adjustedSize);\n        // Bail early if the size does not change.\n        if (oldSize === newSize) {\n            return;\n        }\n        // Resize the section in the list.\n        list.resize(index, newSize);\n        // Get the current size of the viewport.\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // If there is nothing to paint, sync the scroll state.\n        if (!this._viewport.isVisible || vw === 0 || vh === 0) {\n            this._syncScrollState();\n            return;\n        }\n        // Compute the size delta.\n        let delta = newSize - oldSize;\n        // Look up the offset of the section.\n        let offset = list.offsetOf(index);\n        // Bail early if the section is fully outside the viewport.\n        if (offset >= vw) {\n            this._syncScrollState();\n            return;\n        }\n        // Paint the entire tail if the section spans the viewport.\n        if (offset + oldSize >= vw || offset + newSize >= vw) {\n            this.paintContent(offset, 0, vw - offset, vh);\n            this._paintOverlay();\n            this._syncScrollState();\n            return;\n        }\n        // Compute the blit content dimensions.\n        let sx = offset + oldSize;\n        let sy = 0;\n        let sw = vw - sx;\n        let sh = vh;\n        let dx = sx + delta;\n        let dy = 0;\n        // Blit the valid content to the destination.\n        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);\n        // Repaint the header section if needed.\n        if (newSize > 0) {\n            this.paintContent(offset, 0, newSize, vh);\n        }\n        // Paint the trailing space as needed.\n        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {\n            let c = this._columnSections.count - 1;\n            let x = this.headerWidth + this._columnSections.offsetOf(c);\n            this.paintContent(x, 0, vw - x, vh);\n        }\n        else if (delta < 0) {\n            this.paintContent(vw + delta, 0, -delta, vh);\n        }\n        // Repaint merged cells that are intersected by the resized row\n        // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n        for (const rgn of [\n            'corner-header',\n            'row-header'\n        ]) {\n            const cellGroups = CellGroup.getCellGroupsAtColumn(this.dataModel, rgn, index);\n            let paintRgn = {\n                region: rgn,\n                xMin: 0,\n                xMax: 0,\n                yMin: 0,\n                yMax: 0\n            };\n            switch (rgn) {\n                case 'corner-header':\n                    paintRgn.xMin = 0;\n                    paintRgn.xMax = this.headerWidth;\n                    paintRgn.yMin = 0;\n                    paintRgn.yMax = this.headerHeight;\n                    break;\n                case 'row-header':\n                    paintRgn.xMin = 0;\n                    paintRgn.xMax = this.headerWidth;\n                    paintRgn.yMin = this.headerHeight;\n                    paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                    break;\n            }\n            this._paintMergedCells(cellGroups, paintRgn, this._style.headerBackgroundColor);\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n        // Sync the scroll state after painting.\n        this._syncScrollState();\n    }\n    /**\n     * Resize a column header section immediately.\n     */\n    _resizeColumnHeader(index, size) {\n        // Look up the target section list.\n        let list = this._columnHeaderSections;\n        // Bail early if the index is out of range.\n        if (index < 0 || index >= list.count) {\n            return;\n        }\n        // Look up the old size of the section.\n        let oldSize = list.sizeOf(index);\n        // Normalize the new size of the section.\n        let newSize = list.clampSize(size);\n        // Bail early if the size does not change.\n        if (oldSize === newSize) {\n            return;\n        }\n        // Resize the section in the list.\n        list.resize(index, newSize);\n        // Get the current size of the viewport.\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // If there is nothing to paint, sync the scroll state.\n        if (!this._viewport.isVisible || vw === 0 || vh === 0) {\n            this._syncScrollState();\n            return;\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n        // Compute the size delta.\n        let delta = newSize - oldSize;\n        // Look up the offset of the section.\n        let offset = list.offsetOf(index);\n        // Bail early if the section is fully outside the viewport.\n        if (offset >= vh) {\n            this._syncScrollState();\n            return;\n        }\n        // Paint the entire tail if the section spans the viewport.\n        if (offset + oldSize >= vh || offset + newSize >= vh) {\n            this.paintContent(0, offset, vw, vh - offset);\n            this._paintOverlay();\n            this._syncScrollState();\n            return;\n        }\n        // Compute the blit content dimensions.\n        let sx = 0;\n        let sy = offset + oldSize;\n        let sw = vw;\n        let sh = vh - sy;\n        let dx = 0;\n        let dy = sy + delta;\n        // Blit the valid contents to the destination.\n        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);\n        // Repaint the header section if needed.\n        if (newSize > 0) {\n            this.paintContent(0, offset, vw, newSize);\n        }\n        // Paint the trailing space as needed.\n        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {\n            let r = this._rowSections.count - 1;\n            let y = this.headerHeight + this._rowSections.offsetOf(r);\n            this.paintContent(0, y, vw, vh - y);\n        }\n        else if (delta < 0) {\n            this.paintContent(0, vh + delta, vw, -delta);\n        }\n        // Repaint merged cells that are intersected by the resized row\n        // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n        for (const rgn of [\n            'corner-header',\n            'column-header'\n        ]) {\n            const cellGroups = CellGroup.getCellGroupsAtRow(this.dataModel, rgn, index);\n            let paintRgn = {\n                region: rgn,\n                xMin: 0,\n                xMax: 0,\n                yMin: 0,\n                yMax: 0\n            };\n            switch (rgn) {\n                case 'corner-header':\n                    paintRgn.xMin = 0;\n                    paintRgn.xMax = this.headerWidth;\n                    paintRgn.yMin = 0;\n                    paintRgn.yMax = this.headerHeight;\n                    break;\n                case 'column-header':\n                    paintRgn.xMin = this.headerWidth;\n                    paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                    paintRgn.yMin = 0;\n                    paintRgn.yMax = this.headerHeight;\n                    break;\n            }\n            this._paintMergedCells(cellGroups, paintRgn, this._style.headerBackgroundColor);\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n        // Sync the scroll state after painting.\n        this._syncScrollState();\n    }\n    /**\n     * Scroll immediately to the specified offset position.\n     */\n    _scrollTo(x, y) {\n        // Bail if no data model found.\n        if (!this.dataModel) {\n            return;\n        }\n        // Floor and clamp the position to the allowable range.\n        x = Math.max(0, Math.min(Math.floor(x), this.maxScrollX));\n        y = Math.max(0, Math.min(Math.floor(y), this.maxScrollY));\n        // Synchronize the scroll bar values.\n        this._hScrollBar.value = x;\n        this._vScrollBar.value = y;\n        // Compute the delta scroll amount.\n        let dx = x - this._scrollX;\n        let dy = y - this._scrollY;\n        // Bail early if there is no effective scroll.\n        if (dx === 0 && dy === 0) {\n            return;\n        }\n        // Bail early if the viewport is not visible.\n        if (!this._viewport.isVisible) {\n            this._scrollX = x;\n            this._scrollY = y;\n            return;\n        }\n        // Get the current size of the viewport.\n        let width = this._viewportWidth;\n        let height = this._viewportHeight;\n        // Bail early if the viewport is empty.\n        if (width === 0 || height === 0) {\n            this._scrollX = x;\n            this._scrollY = y;\n            return;\n        }\n        // Get the visible content origin.\n        let contentX = this.headerWidth;\n        let contentY = this.headerHeight;\n        // Get the visible content dimensions.\n        let contentWidth = width - contentX;\n        let contentHeight = height - contentY;\n        // Bail early if there is no content to draw.\n        if (contentWidth <= 0 && contentHeight <= 0) {\n            this._scrollX = x;\n            this._scrollY = y;\n            return;\n        }\n        // Compute the area which needs painting for the `dx` scroll.\n        let dxArea = 0;\n        if (dx !== 0 && contentWidth > 0) {\n            if (Math.abs(dx) >= contentWidth) {\n                dxArea = contentWidth * height;\n            }\n            else {\n                dxArea = Math.abs(dx) * height;\n            }\n        }\n        // Compute the area which needs painting for the `dy` scroll.\n        let dyArea = 0;\n        if (dy !== 0 && contentHeight > 0) {\n            if (Math.abs(dy) >= contentHeight) {\n                dyArea = width * contentHeight;\n            }\n            else {\n                dyArea = width * Math.abs(dy);\n            }\n        }\n        // If the area sum is larger than the total, paint everything.\n        if (dxArea + dyArea >= width * height) {\n            this._scrollX = x;\n            this._scrollY = y;\n            this.paintContent(0, 0, width, height);\n            this._paintOverlay();\n            return;\n        }\n        // Update the internal Y scroll position.\n        this._scrollY = y;\n        // Scroll the Y axis if needed. If the scroll distance exceeds\n        // the visible height, paint everything. Otherwise, blit the\n        // valid content and paint the dirty region.\n        if (dy !== 0 && contentHeight > 0) {\n            if (Math.abs(dy) >= contentHeight) {\n                this.paintContent(0, contentY, width, contentHeight);\n            }\n            else {\n                const x = 0;\n                const y = dy < 0 ? contentY : contentY + dy;\n                const w = width;\n                const h = contentHeight - Math.abs(dy);\n                this._blitContent(this._canvas, x, y, w, h, x, y - dy);\n                this.paintContent(0, dy < 0 ? contentY : height - dy, width, Math.abs(dy));\n                // Repaint merged cells that are intersected by the scroll level\n                // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n                for (const rgn of ['body', 'row-header']) {\n                    const cellgroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn);\n                    let paintRgn = {\n                        region: rgn,\n                        xMin: 0,\n                        xMax: 0,\n                        yMin: 0,\n                        yMax: 0\n                    };\n                    let backgroundColor = undefined;\n                    switch (rgn) {\n                        case 'body':\n                            paintRgn.xMin = this.headerWidth;\n                            paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                            paintRgn.yMin = this.headerHeight;\n                            paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                            backgroundColor = this._style.backgroundColor;\n                            break;\n                        case 'row-header':\n                            paintRgn.xMin = 0;\n                            paintRgn.xMax = this.headerWidth;\n                            paintRgn.yMin = this.headerHeight;\n                            paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                            backgroundColor = this._style.headerBackgroundColor;\n                            break;\n                    }\n                    this._paintMergedCells(cellgroups, paintRgn, backgroundColor);\n                }\n            }\n        }\n        // Update the internal X scroll position.\n        this._scrollX = x;\n        // Scroll the X axis if needed. If the scroll distance exceeds\n        // the visible width, paint everything. Otherwise, blit the\n        // valid content and paint the dirty region.\n        if (dx !== 0 && contentWidth > 0) {\n            if (Math.abs(dx) >= contentWidth) {\n                this.paintContent(contentX, 0, contentWidth, height);\n            }\n            else {\n                const x = dx < 0 ? contentX : contentX + dx;\n                const y = 0;\n                const w = contentWidth - Math.abs(dx);\n                const h = height;\n                this._blitContent(this._canvas, x, y, w, h, x - dx, y);\n                this.paintContent(dx < 0 ? contentX : width - dx, 0, Math.abs(dx), height);\n                // Repaint merged cells that are intersected by the scroll level\n                // Otherwise it will be cut in two by the valid content, and drawn incorrectly\n                for (const rgn of ['body', 'column-header']) {\n                    const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn);\n                    let paintRgn = {\n                        region: rgn,\n                        xMin: 0,\n                        xMax: 0,\n                        yMin: 0,\n                        yMax: 0\n                    };\n                    let backgroundColor = undefined;\n                    switch (rgn) {\n                        case 'body':\n                            paintRgn.xMin = this.headerWidth;\n                            paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                            paintRgn.yMin = this.headerHeight;\n                            paintRgn.yMax = this.headerHeight + this.bodyHeight;\n                            backgroundColor = this._style.backgroundColor;\n                            break;\n                        case 'column-header':\n                            paintRgn.xMin = this.headerWidth;\n                            paintRgn.xMax = this.headerWidth + this.bodyWidth;\n                            paintRgn.yMin = 0;\n                            paintRgn.yMax = this.headerHeight;\n                            backgroundColor = this._style.headerBackgroundColor;\n                            break;\n                    }\n                    this._paintMergedCells(cellGroups, paintRgn, backgroundColor);\n                }\n            }\n        }\n        // Paint the overlay.\n        this._paintOverlay();\n    }\n    /**\n     * Blit content into the on-screen grid canvas.\n     *\n     * The rect should be expressed in viewport coordinates.\n     *\n     * This automatically accounts for the dpi ratio.\n     */\n    _blitContent(source, x, y, w, h, dx, dy) {\n        // Scale the blit coordinates by the dpi ratio.\n        x *= this._dpiRatio;\n        y *= this._dpiRatio;\n        w *= this._dpiRatio;\n        h *= this._dpiRatio;\n        dx *= this._dpiRatio;\n        dy *= this._dpiRatio;\n        // Save the current gc state.\n        this._canvasGC.save();\n        // Set the transform to the identity matrix.\n        this._canvasGC.setTransform(1, 0, 0, 1, 0, 0);\n        // Draw the specified content.\n        this._canvasGC.drawImage(source, x, y, w, h, dx, dy, w, h);\n        // Restore the gc state.\n        this._canvasGC.restore();\n    }\n    /**\n     * Paint the grid content for the given dirty rect.\n     *\n     * The rect should be expressed in valid viewport coordinates.\n     *\n     * This is the primary paint entry point. The individual `_draw*`\n     * methods should not be invoked directly. This method dispatches\n     * to the drawing methods in the correct order.\n     */\n    paintContent(rx, ry, rw, rh) {\n        // Scale the canvas and buffer GC for the dpi ratio.\n        this._canvasGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);\n        this._bufferGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);\n        // Clear the dirty rect of all content.\n        this._canvasGC.clearRect(rx, ry, rw, rh);\n        // Draw the void region.\n        this._drawVoidRegion(rx, ry, rw, rh);\n        // Draw the body region.\n        this._drawBodyRegion(rx, ry, rw, rh);\n        // Draw the row header region.\n        this._drawRowHeaderRegion(rx, ry, rw, rh);\n        // Draw the column header region.\n        this._drawColumnHeaderRegion(rx, ry, rw, rh);\n        // Draw the corner header region.\n        this.drawCornerHeaderRegion(rx, ry, rw, rh);\n    }\n    /**\n     * Resizes body column headers so their text fits\n     * without clipping or wrapping.\n     * @param dataModel\n     */\n    _fitBodyColumnHeaders(dataModel, padding, numCols) {\n        // Get the body column count\n        const bodyColumnCount = numCols === undefined ? dataModel.columnCount('body') : numCols;\n        for (let i = 0; i < bodyColumnCount; i++) {\n            /*\n              if we're working with nested column headers,\n              retrieve the nested levels and iterate on them.\n            */\n            const numRows = dataModel.rowCount('column-header');\n            /*\n              Calculate the maximum text width, across\n              all nested rows under a given column number.\n            */\n            let maxWidth = 0;\n            for (let j = 0; j < numRows; j++) {\n                const config = DataGrid._getConfig(dataModel, j, i, 'column-header');\n                const textWidth = this._getCellTextWidth(config);\n                // Update the maximum width for that column.\n                maxWidth = Math.max(maxWidth, textWidth);\n            }\n            /*\n              Send a resize message with new width for the given column.\n              Using a padding of 15 pixels to leave some room.\n            */\n            this.resizeColumn('body', i, maxWidth + padding);\n        }\n    }\n    /**\n     * Resizes row header columns so their text fits\n     * without clipping or wrapping.\n     * @param dataModel\n     */\n    _fitRowColumnHeaders(dataModel, padding, numCols) {\n        /*\n          if we're working with nested row headers,\n          retrieve the nested levels and iterate on them.\n        */\n        const rowColumnCount = numCols === undefined ? dataModel.columnCount('row-header') : numCols;\n        for (let i = 0; i < rowColumnCount; i++) {\n            const numCols = dataModel.rowCount('column-header');\n            /*\n              Calculate the maximum text width, across\n              all nested columns under a given row index.\n            */\n            let maxWidth = 0;\n            for (let j = 0; j < numCols; j++) {\n                const config = DataGrid._getConfig(dataModel, j, i, 'corner-header');\n                const textWidth = this._getCellTextWidth(config);\n                maxWidth = Math.max(maxWidth, textWidth);\n            }\n            /*\n              Send a resize message with new width for the given column.\n              Using a padding of 15 pixels to leave some room.\n            */\n            this.resizeColumn('row-header', i, maxWidth + padding);\n        }\n    }\n    /**\n     * Paint the overlay content for the entire grid.\n     *\n     * This is the primary overlay paint entry point. The individual\n     * `_draw*` methods should not be invoked directly. This method\n     * dispatches to the drawing methods in the correct order.\n     */\n    _paintOverlay() {\n        // Scale the overlay GC for the dpi ratio.\n        this._overlayGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);\n        // Clear the overlay of all content.\n        this._overlayGC.clearRect(0, 0, this._overlay.width, this._overlay.height);\n        // Draw the body selections.\n        this._drawBodySelections();\n        // Draw the row header selections.\n        this._drawRowHeaderSelections();\n        // Draw the column header selections.\n        this._drawColumnHeaderSelections();\n        // Draw the cursor.\n        this._drawCursor();\n        // Draw the shadows.\n        this._drawShadows();\n    }\n    /**\n     * Draw the void region for the dirty rect.\n     */\n    _drawVoidRegion(rx, ry, rw, rh) {\n        // Look up the void color.\n        let color = this._style.voidColor;\n        // Bail if there is no void color.\n        if (!color) {\n            return;\n        }\n        // Fill the dirty rect with the void color.\n        this._canvasGC.fillStyle = color;\n        this._canvasGC.fillRect(rx, ry, rw, rh);\n    }\n    /**\n     * Draw the body region which intersects the dirty rect.\n     */\n    _drawBodyRegion(rx, ry, rw, rh) {\n        // Get the visible content dimensions.\n        let contentW = this._columnSections.length - this._scrollX;\n        let contentH = this._rowSections.length - this._scrollY;\n        // Bail if there is no content to draw.\n        if (contentW <= 0 || contentH <= 0) {\n            return;\n        }\n        // Get the visible content origin.\n        let contentX = this.headerWidth;\n        let contentY = this.headerHeight;\n        // Bail if the dirty rect does not intersect the content area.\n        if (rx + rw <= contentX) {\n            return;\n        }\n        if (ry + rh <= contentY) {\n            return;\n        }\n        if (rx >= contentX + contentW) {\n            return;\n        }\n        if (ry >= contentY + contentH) {\n            return;\n        }\n        // Fetch the geometry.\n        let bh = this.bodyHeight;\n        let bw = this.bodyWidth;\n        let ph = this.pageHeight;\n        let pw = this.pageWidth;\n        // Get the upper and lower bounds of the dirty content area.\n        let x1 = Math.max(rx, contentX);\n        let y1 = Math.max(ry, contentY);\n        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);\n        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);\n        // Convert the dirty content bounds into cell bounds.\n        let r1 = this._rowSections.indexOf(y1 - contentY + this._scrollY);\n        let c1 = this._columnSections.indexOf(x1 - contentX + this._scrollX);\n        let r2 = this._rowSections.indexOf(y2 - contentY + this._scrollY);\n        let c2 = this._columnSections.indexOf(x2 - contentX + this._scrollX);\n        // Fetch the max row and column.\n        let maxRow = this._rowSections.count - 1;\n        let maxColumn = this._columnSections.count - 1;\n        // Handle a dirty content area larger than the cell count.\n        if (r2 < 0) {\n            r2 = maxRow;\n        }\n        if (c2 < 0) {\n            c2 = maxColumn;\n        }\n        // Convert the cell bounds back to visible coordinates.\n        let x = this._columnSections.offsetOf(c1) + contentX - this._scrollX;\n        let y = this._rowSections.offsetOf(r1) + contentY - this._scrollY;\n        // Set up the paint region size variables.\n        let width = 0;\n        let height = 0;\n        // Allocate the section sizes arrays.\n        let rowSizes = new Array(r2 - r1 + 1);\n        let columnSizes = new Array(c2 - c1 + 1);\n        // Get the row sizes for the region.\n        for (let j = r1; j <= r2; ++j) {\n            let size = this._rowSections.sizeOf(j);\n            rowSizes[j - r1] = size;\n            height += size;\n        }\n        // Get the column sizes for the region.\n        for (let i = c1; i <= c2; ++i) {\n            let size = this._columnSections.sizeOf(i);\n            columnSizes[i - c1] = size;\n            width += size;\n        }\n        // Adjust the geometry if the last row is streched.\n        if (this._stretchLastRow && ph > bh && r2 === maxRow) {\n            let dh = this.pageHeight - this.bodyHeight;\n            rowSizes[rowSizes.length - 1] += dh;\n            height += dh;\n            y2 += dh;\n        }\n        // Adjust the geometry if the last column is streched.\n        if (this._stretchLastColumn && pw > bw && c2 === maxColumn) {\n            let dw = this.pageWidth - this.bodyWidth;\n            columnSizes[columnSizes.length - 1] += dw;\n            width += dw;\n            x2 += dw;\n        }\n        // Create the paint region object.\n        let rgn = {\n            region: 'body',\n            xMin: x1,\n            yMin: y1,\n            xMax: x2,\n            yMax: y2,\n            x,\n            y,\n            width,\n            height,\n            row: r1,\n            column: c1,\n            rowSizes,\n            columnSizes\n        };\n        // Draw the background.\n        this._drawBackground(rgn, this._style.backgroundColor);\n        // Draw the row background.\n        this._drawRowBackground(rgn, this._style.rowBackgroundColor);\n        // Draw the column background.\n        this._drawColumnBackground(rgn, this._style.columnBackgroundColor);\n        // Draw the cell content for the paint region.\n        this._drawCells(rgn);\n        // Draw the horizontal grid lines.\n        this._drawHorizontalGridLines(rgn, this._style.horizontalGridLineColor || this._style.gridLineColor);\n        // Draw the vertical grid lines.\n        this._drawVerticalGridLines(rgn, this._style.verticalGridLineColor || this._style.gridLineColor);\n        // Get the cellgroups from the cell-region that intersects with the paint region\n        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {\n            return this.cellGroupInteresectsRegion(group, rgn);\n        });\n        // Draw merged cells\n        this._paintMergedCells(cellGroups, rgn, this._style.backgroundColor);\n    }\n    /**\n     * Draw the row header region which intersects the dirty rect.\n     */\n    _drawRowHeaderRegion(rx, ry, rw, rh) {\n        // Get the visible content dimensions.\n        let contentW = this.headerWidth;\n        let contentH = this.bodyHeight - this._scrollY;\n        // Bail if there is no content to draw.\n        if (contentW <= 0 || contentH <= 0) {\n            return;\n        }\n        // Get the visible content origin.\n        let contentX = 0;\n        let contentY = this.headerHeight;\n        // Bail if the dirty rect does not intersect the content area.\n        if (rx + rw <= contentX) {\n            return;\n        }\n        if (ry + rh <= contentY) {\n            return;\n        }\n        if (rx >= contentX + contentW) {\n            return;\n        }\n        if (ry >= contentY + contentH) {\n            return;\n        }\n        // Fetch the geometry.\n        let bh = this.bodyHeight;\n        let ph = this.pageHeight;\n        // Get the upper and lower bounds of the dirty content area.\n        let x1 = rx;\n        let y1 = Math.max(ry, contentY);\n        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);\n        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);\n        // Convert the dirty content bounds into cell bounds.\n        let r1 = this._rowSections.indexOf(y1 - contentY + this._scrollY);\n        let c1 = this._rowHeaderSections.indexOf(x1);\n        let r2 = this._rowSections.indexOf(y2 - contentY + this._scrollY);\n        let c2 = this._rowHeaderSections.indexOf(x2);\n        // Fetch max row and column.\n        let maxRow = this._rowSections.count - 1;\n        let maxColumn = this._rowHeaderSections.count - 1;\n        // Handle a dirty content area larger than the cell count.\n        if (r2 < 0) {\n            r2 = maxRow;\n        }\n        if (c2 < 0) {\n            c2 = maxColumn;\n        }\n        // Convert the cell bounds back to visible coordinates.\n        let x = this._rowHeaderSections.offsetOf(c1);\n        let y = this._rowSections.offsetOf(r1) + contentY - this._scrollY;\n        // Set up the paint region size variables.\n        let width = 0;\n        let height = 0;\n        // Allocate the section sizes arrays.\n        let rowSizes = new Array(r2 - r1 + 1);\n        let columnSizes = new Array(c2 - c1 + 1);\n        // Get the row sizes for the region.\n        for (let j = r1; j <= r2; ++j) {\n            let size = this._rowSections.sizeOf(j);\n            rowSizes[j - r1] = size;\n            height += size;\n        }\n        // Get the column sizes for the region.\n        for (let i = c1; i <= c2; ++i) {\n            let size = this._rowHeaderSections.sizeOf(i);\n            columnSizes[i - c1] = size;\n            width += size;\n        }\n        // Adjust the geometry if the last row is stretched.\n        if (this._stretchLastRow && ph > bh && r2 === maxRow) {\n            let dh = this.pageHeight - this.bodyHeight;\n            rowSizes[rowSizes.length - 1] += dh;\n            height += dh;\n            y2 += dh;\n        }\n        // Create the paint region object.\n        let rgn = {\n            region: 'row-header',\n            xMin: x1,\n            yMin: y1,\n            xMax: x2,\n            yMax: y2,\n            x,\n            y,\n            width,\n            height,\n            row: r1,\n            column: c1,\n            rowSizes,\n            columnSizes\n        };\n        // Draw the background.\n        this._drawBackground(rgn, this._style.headerBackgroundColor);\n        // Draw the cell content for the paint region.\n        this._drawCells(rgn);\n        // Draw the horizontal grid lines.\n        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||\n            this._style.headerGridLineColor);\n        // Draw the vertical grid lines.\n        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);\n        // Get the cellgroups from the cell-region that intersects with the paint region\n        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {\n            return this.cellGroupInteresectsRegion(group, rgn);\n        });\n        // Draw merged cells\n        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);\n    }\n    /**\n     * Draw the column header region which intersects the dirty rect.\n     */\n    _drawColumnHeaderRegion(rx, ry, rw, rh) {\n        // Get the visible content dimensions.\n        let contentW = this.bodyWidth - this._scrollX;\n        let contentH = this.headerHeight;\n        // Bail if there is no content to draw.\n        if (contentW <= 0 || contentH <= 0) {\n            return;\n        }\n        // Get the visible content origin.\n        let contentX = this.headerWidth;\n        let contentY = 0;\n        // Bail if the dirty rect does not intersect the content area.\n        if (rx + rw <= contentX) {\n            return;\n        }\n        if (ry + rh <= contentY) {\n            return;\n        }\n        if (rx >= contentX + contentW) {\n            return;\n        }\n        if (ry >= contentY + contentH) {\n            return;\n        }\n        // Fetch the geometry.\n        let bw = this.bodyWidth;\n        let pw = this.pageWidth;\n        // Get the upper and lower bounds of the dirty content area.\n        let x1 = Math.max(rx, contentX);\n        let y1 = ry;\n        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);\n        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);\n        // Convert the dirty content bounds into cell bounds.\n        let r1 = this._columnHeaderSections.indexOf(y1);\n        let c1 = this._columnSections.indexOf(x1 - contentX + this._scrollX);\n        let r2 = this._columnHeaderSections.indexOf(y2);\n        let c2 = this._columnSections.indexOf(x2 - contentX + this._scrollX);\n        // Fetch the max row and column.\n        let maxRow = this._columnHeaderSections.count - 1;\n        let maxColumn = this._columnSections.count - 1;\n        // Handle a dirty content area larger than the cell count.\n        if (r2 < 0) {\n            r2 = maxRow;\n        }\n        if (c2 < 0) {\n            c2 = maxColumn;\n        }\n        // Convert the cell bounds back to visible coordinates.\n        let x = this._columnSections.offsetOf(c1) + contentX - this._scrollX;\n        let y = this._columnHeaderSections.offsetOf(r1);\n        // Set up the paint region size variables.\n        let width = 0;\n        let height = 0;\n        // Allocate the section sizes arrays.\n        let rowSizes = new Array(r2 - r1 + 1);\n        let columnSizes = new Array(c2 - c1 + 1);\n        // Get the row sizes for the region.\n        for (let j = r1; j <= r2; ++j) {\n            let size = this._columnHeaderSections.sizeOf(j);\n            rowSizes[j - r1] = size;\n            height += size;\n        }\n        // Get the column sizes for the region.\n        for (let i = c1; i <= c2; ++i) {\n            let size = this._columnSections.sizeOf(i);\n            columnSizes[i - c1] = size;\n            width += size;\n        }\n        // Adjust the geometry if the last column is stretched.\n        if (this._stretchLastColumn && pw > bw && c2 === maxColumn) {\n            let dw = this.pageWidth - this.bodyWidth;\n            columnSizes[columnSizes.length - 1] += dw;\n            width += dw;\n            x2 += dw;\n        }\n        // Create the paint region object.\n        let rgn = {\n            region: 'column-header',\n            xMin: x1,\n            yMin: y1,\n            xMax: x2,\n            yMax: y2,\n            x,\n            y,\n            width,\n            height,\n            row: r1,\n            column: c1,\n            rowSizes,\n            columnSizes\n        };\n        // Draw the background.\n        this._drawBackground(rgn, this._style.headerBackgroundColor);\n        // Draw the cell content for the paint region.\n        this._drawCells(rgn);\n        // Draw the horizontal grid lines.\n        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||\n            this._style.headerGridLineColor);\n        // Draw the vertical grid lines.\n        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);\n        // Get the cellgroups from the cell-region that intersects with the paint region\n        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {\n            return this.cellGroupInteresectsRegion(group, rgn);\n        });\n        // Draw merged cells\n        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);\n    }\n    /**\n     * Draw the corner header region which intersects the dirty rect.\n     */\n    drawCornerHeaderRegion(rx, ry, rw, rh) {\n        // Get the visible content dimensions.\n        let contentW = this.headerWidth;\n        let contentH = this.headerHeight;\n        // Bail if there is no content to draw.\n        if (contentW <= 0 || contentH <= 0) {\n            return;\n        }\n        // Get the visible content origin.\n        let contentX = 0;\n        let contentY = 0;\n        // Bail if the dirty rect does not intersect the content area.\n        if (rx + rw <= contentX) {\n            return;\n        }\n        if (ry + rh <= contentY) {\n            return;\n        }\n        if (rx >= contentX + contentW) {\n            return;\n        }\n        if (ry >= contentY + contentH) {\n            return;\n        }\n        // Get the upper and lower bounds of the dirty content area.\n        let x1 = rx;\n        let y1 = ry;\n        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);\n        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);\n        // Convert the dirty content bounds into cell bounds.\n        let r1 = this._columnHeaderSections.indexOf(y1);\n        let c1 = this._rowHeaderSections.indexOf(x1);\n        let r2 = this._columnHeaderSections.indexOf(y2);\n        let c2 = this._rowHeaderSections.indexOf(x2);\n        // Handle a dirty content area larger than the cell count.\n        if (r2 < 0) {\n            r2 = this._columnHeaderSections.count - 1;\n        }\n        if (c2 < 0) {\n            c2 = this._rowHeaderSections.count - 1;\n        }\n        // Convert the cell bounds back to visible coordinates.\n        let x = this._rowHeaderSections.offsetOf(c1);\n        let y = this._columnHeaderSections.offsetOf(r1);\n        // Set up the paint region size variables.\n        let width = 0;\n        let height = 0;\n        // Allocate the section sizes arrays.\n        let rowSizes = new Array(r2 - r1 + 1);\n        let columnSizes = new Array(c2 - c1 + 1);\n        // Get the row sizes for the region.\n        for (let j = r1; j <= r2; ++j) {\n            let size = this._columnHeaderSections.sizeOf(j);\n            rowSizes[j - r1] = size;\n            height += size;\n        }\n        // Get the column sizes for the region.\n        for (let i = c1; i <= c2; ++i) {\n            let size = this._rowHeaderSections.sizeOf(i);\n            columnSizes[i - c1] = size;\n            width += size;\n        }\n        // Create the paint region object.\n        let rgn = {\n            region: 'corner-header',\n            xMin: x1,\n            yMin: y1,\n            xMax: x2,\n            yMax: y2,\n            x,\n            y,\n            width,\n            height,\n            row: r1,\n            column: c1,\n            rowSizes,\n            columnSizes\n        };\n        // Draw the background.\n        this._drawBackground(rgn, this._style.headerBackgroundColor);\n        // Draw the cell content for the paint region.\n        this._drawCells(rgn);\n        // Draw the horizontal grid lines.\n        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||\n            this._style.headerGridLineColor);\n        // Draw the vertical grid lines.\n        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);\n        // Get the cellgroups from the cell-region that intersects with the paint region\n        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {\n            return this.cellGroupInteresectsRegion(group, rgn);\n        });\n        // Draw merged cells\n        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);\n    }\n    /**\n     * Draw the background for the given paint region.\n     */\n    _drawBackground(rgn, color) {\n        // Bail if there is no color to draw.\n        if (!color) {\n            return;\n        }\n        // Unpack the region.\n        let { xMin, yMin, xMax, yMax } = rgn;\n        // Fill the region with the specified color.\n        this._canvasGC.fillStyle = color;\n        this._canvasGC.fillRect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);\n    }\n    /**\n     * Draw the row background for the given paint region.\n     */\n    _drawRowBackground(rgn, colorFn) {\n        // Bail if there is no color function.\n        if (!colorFn) {\n            return;\n        }\n        // Compute the X bounds for the row.\n        let x1 = Math.max(rgn.xMin, rgn.x);\n        let x2 = Math.min(rgn.x + rgn.width - 1, rgn.xMax);\n        // Draw the background for the rows in the region.\n        for (let y = rgn.y, j = 0, n = rgn.rowSizes.length; j < n; ++j) {\n            // Fetch the size of the row.\n            let size = rgn.rowSizes[j];\n            // Skip zero sized rows.\n            if (size === 0) {\n                continue;\n            }\n            // Get the background color for the row.\n            let color = colorFn(rgn.row + j);\n            // Fill the row with the background color if needed.\n            if (color) {\n                let y1 = Math.max(rgn.yMin, y);\n                let y2 = Math.min(y + size - 1, rgn.yMax);\n                this._canvasGC.fillStyle = color;\n                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n            }\n            // Increment the running Y coordinate.\n            y += size;\n        }\n    }\n    /**\n     * Draw the column background for the given paint region.\n     */\n    _drawColumnBackground(rgn, colorFn) {\n        // Bail if there is no color function.\n        if (!colorFn) {\n            return;\n        }\n        // Compute the Y bounds for the column.\n        let y1 = Math.max(rgn.yMin, rgn.y);\n        let y2 = Math.min(rgn.y + rgn.height - 1, rgn.yMax);\n        // Draw the background for the columns in the region.\n        for (let x = rgn.x, i = 0, n = rgn.columnSizes.length; i < n; ++i) {\n            // Fetch the size of the column.\n            let size = rgn.columnSizes[i];\n            // Skip zero sized columns.\n            if (size === 0) {\n                continue;\n            }\n            // Get the background color for the column.\n            let color = colorFn(rgn.column + i);\n            // Fill the column with the background color if needed.\n            if (color) {\n                let x1 = Math.max(rgn.xMin, x);\n                let x2 = Math.min(x + size - 1, rgn.xMax);\n                this._canvasGC.fillStyle = color;\n                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n            }\n            // Increment the running X coordinate.\n            x += size;\n        }\n    }\n    /**\n     * Returns column size\n     * @param region\n     * @param index\n     */\n    _getColumnSize(region, index) {\n        if (region === 'corner-header') {\n            return this._rowHeaderSections.sizeOf(index);\n        }\n        return this.columnSize(region, index);\n    }\n    /**\n     * Returns row size\n     * @param region\n     * @param index\n     */\n    _getRowSize(region, index) {\n        if (region === 'corner-header') {\n            return this._columnHeaderSections.sizeOf(index);\n        }\n        return this.rowSize(region, index);\n    }\n    /**\n     * Draw the cells for the given paint region.\n     */\n    _drawCells(rgn) {\n        // Bail if there is no data model.\n        if (!this._dataModel) {\n            return;\n        }\n        // Set up the cell config object for rendering.\n        let config = {\n            x: 0,\n            y: 0,\n            width: 0,\n            height: 0,\n            region: rgn.region,\n            row: 0,\n            column: 0,\n            value: null,\n            metadata: DataModel.emptyMetadata\n        };\n        let groupIndex = -1;\n        // Save the buffer gc before wrapping.\n        this._bufferGC.save();\n        // Wrap the buffer gc for painting the cells.\n        let gc = new GraphicsContext(this._bufferGC);\n        let height = 0;\n        // Loop over the columns in the region.\n        for (let x = rgn.x, i = 0, n = rgn.columnSizes.length; i < n; ++i) {\n            // Fetch the size of the column.\n            let width = rgn.columnSizes[i];\n            // Skip zero sized columns.\n            if (width === 0) {\n                continue;\n            }\n            // Compute the column index.\n            let column = rgn.column + i;\n            // Update the config for the current column.\n            config.x = x;\n            config.width = width;\n            config.column = column;\n            // Loop over the rows in the column.\n            for (let y = rgn.y, j = 0, n = rgn.rowSizes.length; j < n; ++j) {\n                // Fetch the size of the row.\n                height = rgn.rowSizes[j];\n                // Skip zero sized rows.\n                if (height === 0) {\n                    continue;\n                }\n                // Compute the row index.\n                let row = rgn.row + j;\n                groupIndex = CellGroup.getGroupIndex(this.dataModel, config.region, row, column);\n                // For merged cell regions, don't do anything, we draw merged regions later.\n                if (groupIndex !== -1) {\n                    y += height;\n                    continue;\n                }\n                // Clear the buffer rect for the cell.\n                gc.clearRect(x, y, width, height);\n                let value = DataGrid._getCellValue(this.dataModel, rgn.region, row, column);\n                let metadata = DataGrid._getCellMetadata(this.dataModel, rgn.region, row, column);\n                // Update the config for the current cell.\n                config.y = y;\n                config.height = height;\n                config.width = width;\n                config.row = row;\n                config.value = value;\n                config.metadata = metadata;\n                // Get the renderer for the cell.\n                let renderer = this._cellRenderers.get(config);\n                // Save the GC state.\n                gc.save();\n                // Paint the cell into the off-screen buffer.\n                try {\n                    if (renderer instanceof AsyncCellRenderer) {\n                        if (renderer.isReady(config)) {\n                            renderer.paint(gc, config);\n                        }\n                        else {\n                            renderer.paintPlaceholder(gc, config);\n                            renderer.load(config).then(() => {\n                                const r1 = row;\n                                const r2 = row + 1;\n                                const c1 = column;\n                                const c2 = column + 1;\n                                this.repaintRegion(rgn.region, r1, c1, r2, c2);\n                            });\n                        }\n                    }\n                    else {\n                        renderer.paint(gc, config);\n                    }\n                }\n                catch (err) {\n                    console.error(err);\n                }\n                // Restore the GC state.\n                gc.restore();\n                // Compute the actual X bounds for the cell.\n                let x1 = Math.max(rgn.xMin, config.x);\n                let x2 = Math.min(config.x + config.width - 1, rgn.xMax);\n                // Compute the actual Y bounds for the cell.\n                let y1 = Math.max(rgn.yMin, config.y);\n                let y2 = Math.min(config.y + config.height - 1, rgn.yMax);\n                this._blitContent(this._buffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, x1, y1);\n                // Increment the running Y coordinate.\n                y += height;\n            }\n            // Restore the GC state.\n            gc.restore();\n            // Increment the running X coordinate.\n            x += width;\n        }\n        // Dispose of the wrapped gc.\n        gc.dispose();\n        // Restore the final buffer gc state.\n        this._bufferGC.restore();\n    }\n    // TODO Move this in the utils file (but we need the PaintRegion typing)\n    cellGroupInteresectsRegion(group, rgn) {\n        const rgnR1 = rgn.row;\n        const rgnR2 = rgn.row + rgn.rowSizes.length;\n        const rgnC1 = rgn.column;\n        const rgnC2 = rgn.column + rgn.columnSizes.length;\n        const dx = Math.min(group.r2, rgnR2) - Math.max(group.r1, rgnR1);\n        const dy = Math.min(group.c2, rgnC2) - Math.max(group.c1, rgnC1);\n        return dx >= 0 && dy >= 0;\n    }\n    static _getCellValue(dm, region, row, col) {\n        // Get the value for the cell.\n        try {\n            return dm.data(region, row, col);\n        }\n        catch (err) {\n            console.error(err);\n            return null;\n        }\n    }\n    static _getCellMetadata(dm, region, row, col) {\n        // Get the metadata for the cell.\n        try {\n            return dm.metadata(region, row, col);\n        }\n        catch (err) {\n            console.error(err);\n            return DataModel.emptyMetadata;\n        }\n    }\n    /**\n     * Paint group cells.\n     */\n    _paintMergedCells(cellGroups, rgn, backgroundColor) {\n        // Bail if there is no data model.\n        if (!this._dataModel) {\n            return;\n        }\n        // Set up the cell config object for rendering.\n        let config = {\n            x: 0,\n            y: 0,\n            width: 0,\n            height: 0,\n            region: rgn.region,\n            row: 0,\n            column: 0,\n            value: null,\n            metadata: DataModel.emptyMetadata\n        };\n        if (backgroundColor) {\n            this._canvasGC.fillStyle = backgroundColor;\n        }\n        // Set the line width for the grid lines.\n        this._canvasGC.lineWidth = 1;\n        // Save the buffer gc before wrapping.\n        this._bufferGC.save();\n        // Wrap the buffer gc for painting the cells.\n        let gc = new GraphicsContext(this._bufferGC);\n        for (const group of cellGroups) {\n            let width = 0;\n            for (let c = group.c1; c <= group.c2; c++) {\n                width += this._getColumnSize(rgn.region, c);\n            }\n            let height = 0;\n            for (let r = group.r1; r <= group.r2; r++) {\n                height += this._getRowSize(rgn.region, r);\n            }\n            let value = DataGrid._getCellValue(this.dataModel, rgn.region, group.r1, group.c1);\n            let metadata = DataGrid._getCellMetadata(this.dataModel, rgn.region, group.r1, group.c2);\n            let x = 0;\n            let y = 0;\n            switch (rgn.region) {\n                case 'body':\n                    x =\n                        this._columnSections.offsetOf(group.c1) +\n                            this.headerWidth -\n                            this._scrollX;\n                    y =\n                        this._rowSections.offsetOf(group.r1) +\n                            this.headerHeight -\n                            this._scrollY;\n                    break;\n                case 'column-header':\n                    x =\n                        this._columnSections.offsetOf(group.c1) +\n                            this.headerWidth -\n                            this._scrollX;\n                    y = this._rowSections.offsetOf(group.r1);\n                    break;\n                case 'row-header':\n                    x = this._columnSections.offsetOf(group.c1);\n                    y =\n                        this._rowSections.offsetOf(group.r1) +\n                            this.headerHeight -\n                            this._scrollY;\n                    break;\n                case 'corner-header':\n                    x = this._columnSections.offsetOf(group.c1);\n                    y = this._rowSections.offsetOf(group.r1);\n                    break;\n            }\n            config.x = x;\n            config.y = y;\n            config.width = width;\n            config.height = height;\n            config.region = rgn.region;\n            config.row = group.r1;\n            config.column = group.c1;\n            config.value = value;\n            config.metadata = metadata;\n            // Compute the actual X bounds for the cell.\n            const x1 = Math.max(rgn.xMin, x);\n            const x2 = Math.min(x + width - 2, rgn.xMax);\n            // Compute the actual Y bounds for the cell.\n            const y1 = Math.max(rgn.yMin, y);\n            const y2 = Math.min(y + height - 2, rgn.yMax);\n            if (x2 <= x1 || y2 <= y1) {\n                continue;\n            }\n            // Draw the background.\n            if (backgroundColor) {\n                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n            }\n            // Get the renderer for the cell.\n            let renderer = this._cellRenderers.get(config);\n            // Clear the buffer rect for the cell.\n            gc.clearRect(config.x, config.y, width, height);\n            // Save the GC state.\n            gc.save();\n            // Paint the cell into the off-screen buffer.\n            try {\n                if (renderer instanceof AsyncCellRenderer) {\n                    if (renderer.isReady(config)) {\n                        renderer.paint(gc, config);\n                    }\n                    else {\n                        renderer.paintPlaceholder(gc, config);\n                        const r1 = group.r1;\n                        const r2 = group.r2;\n                        const c1 = group.c1;\n                        const c2 = group.c2;\n                        renderer.load(config).then(() => {\n                            this.repaintRegion(rgn.region, r1, c1, r2, c2);\n                        });\n                    }\n                }\n                else {\n                    renderer.paint(gc, config);\n                }\n            }\n            catch (err) {\n                console.error(err);\n            }\n            // Restore the GC state.\n            gc.restore();\n            this._blitContent(this._buffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, x1, y1);\n        }\n        // Dispose of the wrapped gc.\n        gc.dispose();\n        // Restore the final buffer gc state.\n        this._bufferGC.restore();\n    }\n    /**\n     * Draw the horizontal grid lines for the given paint region.\n     */\n    _drawHorizontalGridLines(rgn, color) {\n        // Bail if there is no color to draw.\n        if (!color) {\n            return;\n        }\n        // Compute the X bounds for the horizontal lines.\n        const x1 = Math.max(rgn.xMin, rgn.x);\n        const x2 = Math.min(rgn.x + rgn.width, rgn.xMax + 1);\n        // Begin the path for the grid lines.\n        this._canvasGC.beginPath();\n        // Set the line width for the grid lines.\n        this._canvasGC.lineWidth = 1;\n        // Fetch the geometry.\n        const bh = this.bodyHeight;\n        const ph = this.pageHeight;\n        // Fetch the number of grid lines to be drawn.\n        let n = rgn.rowSizes.length;\n        // Adjust the count down if the last line shouldn't be drawn.\n        if (this._stretchLastRow && ph > bh) {\n            if (rgn.row + n === this._rowSections.count) {\n                n -= 1;\n            }\n        }\n        // Draw the horizontal grid lines.\n        for (let y = rgn.y, j = 0; j < n; ++j) {\n            // Fetch the size of the row.\n            let size = rgn.rowSizes[j];\n            // Skip zero sized rows.\n            if (size === 0) {\n                continue;\n            }\n            // Compute the Y position of the line.\n            let pos = y + size - 1;\n            // Draw the line if it's in range of the dirty rect.\n            if (pos >= rgn.yMin && pos <= rgn.yMax) {\n                this._canvasGC.moveTo(x1, pos + 0.5);\n                this._canvasGC.lineTo(x2, pos + 0.5);\n            }\n            // Increment the running Y coordinate.\n            y += size;\n        }\n        // Stroke the lines with the specified color.\n        this._canvasGC.strokeStyle = color;\n        this._canvasGC.stroke();\n    }\n    /**\n     * Draw the vertical grid lines for the given paint region.\n     */\n    _drawVerticalGridLines(rgn, color) {\n        // Bail if there is no color to draw.\n        if (!color) {\n            return;\n        }\n        // Compute the Y bounds for the vertical lines.\n        const y1 = Math.max(rgn.yMin, rgn.y);\n        const y2 = Math.min(rgn.y + rgn.height, rgn.yMax + 1);\n        // Begin the path for the grid lines\n        this._canvasGC.beginPath();\n        // Set the line width for the grid lines.\n        this._canvasGC.lineWidth = 1;\n        // Fetch the geometry.\n        const bw = this.bodyWidth;\n        const pw = this.pageWidth;\n        // Fetch the number of grid lines to be drawn.\n        let n = rgn.columnSizes.length;\n        // Adjust the count down if the last line shouldn't be drawn.\n        if (this._stretchLastColumn && pw > bw) {\n            if (rgn.column + n === this._columnSections.count) {\n                n -= 1;\n            }\n        }\n        // Draw the vertical grid lines.\n        for (let x = rgn.x, i = 0; i < n; ++i) {\n            // Fetch the size of the column.\n            let size = rgn.columnSizes[i];\n            // Skip zero sized columns.\n            if (size === 0) {\n                continue;\n            }\n            // Compute the X position of the line.\n            let pos = x + size - 1;\n            // Draw the line if it's in range of the dirty rect.\n            if (pos >= rgn.xMin && pos <= rgn.xMax) {\n                this._canvasGC.moveTo(pos + 0.5, y1);\n                this._canvasGC.lineTo(pos + 0.5, y2);\n            }\n            // Increment the running X coordinate.\n            x += size;\n        }\n        // Stroke the lines with the specified color.\n        this._canvasGC.strokeStyle = color;\n        this._canvasGC.stroke();\n    }\n    /**\n     * Draw the body selections for the data grid.\n     */\n    _drawBodySelections() {\n        // Fetch the selection model.\n        let model = this._selectionModel;\n        // Bail early if there are no selections.\n        if (!model || model.isEmpty) {\n            return;\n        }\n        // Fetch the selection colors.\n        let fill = this._style.selectionFillColor;\n        let stroke = this._style.selectionBorderColor;\n        // Bail early if there is nothing to draw.\n        if (!fill && !stroke) {\n            return;\n        }\n        // Fetch the scroll geometry.\n        let sx = this._scrollX;\n        let sy = this._scrollY;\n        // Get the first visible cell of the grid.\n        let r1 = this._rowSections.indexOf(sy);\n        let c1 = this._columnSections.indexOf(sx);\n        // Bail early if there are no visible cells.\n        if (r1 < 0 || c1 < 0) {\n            return;\n        }\n        // Fetch the extra geometry.\n        let bw = this.bodyWidth;\n        let bh = this.bodyHeight;\n        let pw = this.pageWidth;\n        let ph = this.pageHeight;\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        // Get the last visible cell of the grid.\n        let r2 = this._rowSections.indexOf(sy + ph);\n        let c2 = this._columnSections.indexOf(sx + pw);\n        // Fetch the max row and column.\n        let maxRow = this._rowSections.count - 1;\n        let maxColumn = this._columnSections.count - 1;\n        // Clamp the last cell if the void space is visible.\n        r2 = r2 < 0 ? maxRow : r2;\n        c2 = c2 < 0 ? maxColumn : c2;\n        // Fetch the overlay gc.\n        let gc = this._overlayGC;\n        // Save the gc state.\n        gc.save();\n        // Set up the body clipping rect.\n        gc.beginPath();\n        gc.rect(hw, hh, pw, ph);\n        gc.clip();\n        // Set up the gc style.\n        if (fill) {\n            gc.fillStyle = fill;\n        }\n        if (stroke) {\n            gc.strokeStyle = stroke;\n            gc.lineWidth = 1;\n        }\n        // Iterate over the selections.\n        for (let s of model.selections()) {\n            // Skip the section if it's not visible.\n            if (s.r1 < r1 && s.r2 < r1) {\n                continue;\n            }\n            if (s.r1 > r2 && s.r2 > r2) {\n                continue;\n            }\n            if (s.c1 < c1 && s.c2 < c1) {\n                continue;\n            }\n            if (s.c1 > c2 && s.c2 > c2) {\n                continue;\n            }\n            // Clamp the cell to the model bounds.\n            let sr1 = Math.max(0, Math.min(s.r1, maxRow));\n            let sc1 = Math.max(0, Math.min(s.c1, maxColumn));\n            let sr2 = Math.max(0, Math.min(s.r2, maxRow));\n            let sc2 = Math.max(0, Math.min(s.c2, maxColumn));\n            // Swap index order if needed.\n            let tmp;\n            if (sr1 > sr2) {\n                tmp = sr1;\n                sr1 = sr2;\n                sr2 = tmp;\n            }\n            if (sc1 > sc2) {\n                tmp = sc1;\n                sc1 = sc2;\n                sc2 = tmp;\n            }\n            const joinedGroup = CellGroup.joinCellGroupWithMergedCellGroups(this.dataModel, { r1: sr1, r2: sr2, c1: sc1, c2: sc2 }, 'body');\n            sr1 = joinedGroup.r1;\n            sr2 = joinedGroup.r2;\n            sc1 = joinedGroup.c1;\n            sc2 = joinedGroup.c2;\n            // Convert to pixel coordinates.\n            let x1 = this._columnSections.offsetOf(sc1) - sx + hw;\n            let y1 = this._rowSections.offsetOf(sr1) - sy + hh;\n            let x2 = this._columnSections.extentOf(sc2) - sx + hw;\n            let y2 = this._rowSections.extentOf(sr2) - sy + hh;\n            // Adjust the trailing X coordinate for column stretch.\n            if (this._stretchLastColumn && pw > bw && sc2 === maxColumn) {\n                x2 = hw + pw - 1;\n            }\n            // Adjust the trailing Y coordinate for row stretch.\n            if (this._stretchLastRow && ph > bh && sr2 === maxRow) {\n                y2 = hh + ph - 1;\n            }\n            // Clamp the bounds to just outside of the clipping rect.\n            x1 = Math.max(hw - 1, x1);\n            y1 = Math.max(hh - 1, y1);\n            x2 = Math.min(hw + pw + 1, x2);\n            y2 = Math.min(hh + ph + 1, y2);\n            // Skip zero sized ranges.\n            if (x2 < x1 || y2 < y1) {\n                continue;\n            }\n            // Fill the rect if needed.\n            if (fill) {\n                gc.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n            }\n            // Stroke the rect if needed.\n            if (stroke) {\n                gc.strokeRect(x1 - 0.5, y1 - 0.5, x2 - x1 + 1, y2 - y1 + 1);\n            }\n        }\n        // Restore the gc state.\n        gc.restore();\n    }\n    /**\n     * Draw the row header selections for the data grid.\n     */\n    _drawRowHeaderSelections() {\n        // Fetch the selection model.\n        let model = this._selectionModel;\n        // Bail early if there are no selections or if the selectionMode is the entire column.\n        if (!model || model.isEmpty || model.selectionMode == 'column') {\n            return;\n        }\n        // Bail early if the row headers are not visible.\n        if (this.headerWidth === 0 || this.pageHeight === 0) {\n            return;\n        }\n        // Fetch the selection colors.\n        let fill = this._style.headerSelectionFillColor;\n        let stroke = this._style.headerSelectionBorderColor;\n        // Bail early if there is nothing to draw.\n        if (!fill && !stroke) {\n            return;\n        }\n        // Fetch common geometry.\n        let sy = this._scrollY;\n        let bh = this.bodyHeight;\n        let ph = this.pageHeight;\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        let rs = this._rowSections;\n        // Fetch the overlay gc.\n        let gc = this._overlayGC;\n        // Save the gc state.\n        gc.save();\n        // Set up the header clipping rect.\n        gc.beginPath();\n        gc.rect(0, hh, hw, ph);\n        gc.clip();\n        // Set up the gc style.\n        if (fill) {\n            gc.fillStyle = fill;\n        }\n        if (stroke) {\n            gc.strokeStyle = stroke;\n            gc.lineWidth = 1;\n        }\n        // Fetch the max row.\n        let maxRow = rs.count - 1;\n        // Fetch the visible rows.\n        let r1 = rs.indexOf(sy);\n        let r2 = rs.indexOf(sy + ph - 1);\n        r2 = r2 < 0 ? maxRow : r2;\n        // Iterate over the visible rows.\n        for (let j = r1; j <= r2; ++j) {\n            // Skip rows which aren't selected.\n            if (!model.isRowSelected(j)) {\n                continue;\n            }\n            // Get the dimensions of the row.\n            let y = rs.offsetOf(j) - sy + hh;\n            let h = rs.sizeOf(j);\n            // Adjust the height for row stretch.\n            if (this._stretchLastRow && ph > bh && j === maxRow) {\n                h = hh + ph - y;\n            }\n            // Skip zero sized rows.\n            if (h === 0) {\n                continue;\n            }\n            // Fill the rect if needed.\n            if (fill) {\n                gc.fillRect(0, y, hw, h);\n            }\n            // Draw the border if needed.\n            if (stroke) {\n                gc.beginPath();\n                gc.moveTo(hw - 0.5, y - 1);\n                gc.lineTo(hw - 0.5, y + h);\n                gc.stroke();\n            }\n        }\n        // Restore the gc state.\n        gc.restore();\n    }\n    /**\n     * Draw the column header selections for the data grid.\n     */\n    _drawColumnHeaderSelections() {\n        // Fetch the selection model.\n        let model = this._selectionModel;\n        // Bail early if there are no selections or if the selectionMode is the entire row\n        if (!model || model.isEmpty || model.selectionMode == 'row') {\n            return;\n        }\n        // Bail early if the column headers are not visible.\n        if (this.headerHeight === 0 || this.pageWidth === 0) {\n            return;\n        }\n        // Fetch the selection colors.\n        let fill = this._style.headerSelectionFillColor;\n        let stroke = this._style.headerSelectionBorderColor;\n        // Bail early if there is nothing to draw.\n        if (!fill && !stroke) {\n            return;\n        }\n        // Fetch common geometry.\n        let sx = this._scrollX;\n        let bw = this.bodyWidth;\n        let pw = this.pageWidth;\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        let cs = this._columnSections;\n        // Fetch the overlay gc.\n        let gc = this._overlayGC;\n        // Save the gc state.\n        gc.save();\n        // Set up the header clipping rect.\n        gc.beginPath();\n        gc.rect(hw, 0, pw, hh);\n        gc.clip();\n        // Set up the gc style.\n        if (fill) {\n            gc.fillStyle = fill;\n        }\n        if (stroke) {\n            gc.strokeStyle = stroke;\n            gc.lineWidth = 1;\n        }\n        // Fetch the max column.\n        let maxCol = cs.count - 1;\n        // Fetch the visible columns.\n        let c1 = cs.indexOf(sx);\n        let c2 = cs.indexOf(sx + pw - 1);\n        c2 = c2 < 0 ? maxCol : c2;\n        // Iterate over the visible columns.\n        for (let i = c1; i <= c2; ++i) {\n            // Skip columns which aren't selected.\n            if (!model.isColumnSelected(i)) {\n                continue;\n            }\n            // Get the dimensions of the column.\n            let x = cs.offsetOf(i) - sx + hw;\n            let w = cs.sizeOf(i);\n            // Adjust the width for column stretch.\n            if (this._stretchLastColumn && pw > bw && i === maxCol) {\n                w = hw + pw - x;\n            }\n            // Skip zero sized columns.\n            if (w === 0) {\n                continue;\n            }\n            // Fill the rect if needed.\n            if (fill) {\n                gc.fillRect(x, 0, w, hh);\n            }\n            // Draw the border if needed.\n            if (stroke) {\n                gc.beginPath();\n                gc.moveTo(x - 1, hh - 0.5);\n                gc.lineTo(x + w, hh - 0.5);\n                gc.stroke();\n            }\n        }\n        // Restore the gc state.\n        gc.restore();\n    }\n    /**\n     * Draw the overlay cursor for the data grid.\n     */\n    _drawCursor() {\n        // Fetch the selection model.\n        let model = this._selectionModel;\n        // Bail early if there is no cursor.\n        if (!model || model.isEmpty || model.selectionMode !== 'cell') {\n            return;\n        }\n        // Extract the style information.\n        let fill = this._style.cursorFillColor;\n        let stroke = this._style.cursorBorderColor;\n        // Bail early if there is nothing to draw.\n        if (!fill && !stroke) {\n            return;\n        }\n        // Fetch the cursor location.\n        let startRow = model.cursorRow;\n        let startColumn = model.cursorColumn;\n        // Fetch the max row and column.\n        let maxRow = this._rowSections.count - 1;\n        let maxColumn = this._columnSections.count - 1;\n        // Bail early if the cursor is out of bounds.\n        if (startRow < 0 || startRow > maxRow) {\n            return;\n        }\n        if (startColumn < 0 || startColumn > maxColumn) {\n            return;\n        }\n        let endRow = startRow;\n        let endColumn = startColumn;\n        const joinedGroup = CellGroup.joinCellGroupWithMergedCellGroups(this.dataModel, { r1: startRow, r2: endRow, c1: startColumn, c2: endColumn }, 'body');\n        startRow = joinedGroup.r1;\n        endRow = joinedGroup.r2;\n        startColumn = joinedGroup.c1;\n        endColumn = joinedGroup.c2;\n        // Fetch geometry.\n        let sx = this._scrollX;\n        let sy = this._scrollY;\n        let bw = this.bodyWidth;\n        let bh = this.bodyHeight;\n        let pw = this.pageWidth;\n        let ph = this.pageHeight;\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // Get the cursor bounds in viewport coordinates.\n        let x1 = this._columnSections.offsetOf(startColumn) - sx + hw;\n        let x2 = this._columnSections.extentOf(endColumn) - sx + hw;\n        let y1 = this._rowSections.offsetOf(startRow) - sy + hh;\n        let y2 = this._rowSections.extentOf(endRow) - sy + hh;\n        // Adjust the trailing X coordinate for column stretch.\n        if (this._stretchLastColumn && pw > bw && startColumn === maxColumn) {\n            x2 = vw - 1;\n        }\n        // Adjust the trailing Y coordinate for row stretch.\n        if (this._stretchLastRow && ph > bh && startRow === maxRow) {\n            y2 = vh - 1;\n        }\n        // Skip zero sized cursors.\n        if (x2 < x1 || y2 < y1) {\n            return;\n        }\n        // Bail early if the cursor is off the screen.\n        if (x1 - 1 >= vw || y1 - 1 >= vh || x2 + 1 < hw || y2 + 1 < hh) {\n            return;\n        }\n        // Fetch the overlay gc.\n        let gc = this._overlayGC;\n        // Save the gc state.\n        gc.save();\n        // Set up the body clipping rect.\n        gc.beginPath();\n        gc.rect(hw, hh, pw, ph);\n        gc.clip();\n        // Clear any existing overlay content.\n        gc.clearRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n        // Fill the cursor rect if needed.\n        if (fill) {\n            // Set up the fill style.\n            gc.fillStyle = fill;\n            // Fill the cursor rect.\n            gc.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);\n        }\n        // Stroke the cursor border if needed.\n        if (stroke) {\n            // Set up the stroke style.\n            gc.strokeStyle = stroke;\n            gc.lineWidth = 2;\n            // Stroke the cursor rect.\n            gc.strokeRect(x1, y1, x2 - x1, y2 - y1);\n        }\n        // Restore the gc state.\n        gc.restore();\n    }\n    /**\n     * Draw the overlay shadows for the data grid.\n     */\n    _drawShadows() {\n        // Fetch the scroll shadow from the style.\n        let shadow = this._style.scrollShadow;\n        // Bail early if there is no shadow to draw.\n        if (!shadow) {\n            return;\n        }\n        // Fetch the scroll position.\n        let sx = this._scrollX;\n        let sy = this._scrollY;\n        // Fetch maximum scroll position.\n        let sxMax = this.maxScrollX;\n        let syMax = this.maxScrollY;\n        // Fetch the header width and height.\n        let hw = this.headerWidth;\n        let hh = this.headerHeight;\n        // Fetch the page width and height.\n        let pw = this.pageWidth;\n        let ph = this.pageHeight;\n        // Fetch the viewport width and height.\n        let vw = this._viewportWidth;\n        let vh = this._viewportHeight;\n        // Fetch the body width and height.\n        let bw = this.bodyWidth;\n        let bh = this.bodyHeight;\n        // Adjust the body size for row and column stretch.\n        if (this._stretchLastRow && ph > bh) {\n            bh = ph;\n        }\n        if (this._stretchLastColumn && pw > bw) {\n            bw = pw;\n        }\n        // Fetch the gc object.\n        let gc = this._overlayGC;\n        // Save the gc state.\n        gc.save();\n        // Draw the column header shadow if needed.\n        if (sy > 0) {\n            // Set up the gradient coordinates.\n            let x0 = 0;\n            let y0 = hh;\n            let x1 = 0;\n            let y1 = y0 + shadow.size;\n            // Create the gradient object.\n            let grad = gc.createLinearGradient(x0, y0, x1, y1);\n            // Set the gradient stops.\n            grad.addColorStop(0, shadow.color1);\n            grad.addColorStop(0.5, shadow.color2);\n            grad.addColorStop(1, shadow.color3);\n            // Set up the rect coordinates.\n            let x = 0;\n            let y = hh;\n            let w = hw + Math.min(pw, bw - sx);\n            let h = shadow.size;\n            // Fill the shadow rect with the fill style.\n            gc.fillStyle = grad;\n            gc.fillRect(x, y, w, h);\n        }\n        // Draw the row header shadow if needed.\n        if (sx > 0) {\n            // Set up the gradient coordinates.\n            let x0 = hw;\n            let y0 = 0;\n            let x1 = x0 + shadow.size;\n            let y1 = 0;\n            // Create the gradient object.\n            let grad = gc.createLinearGradient(x0, y0, x1, y1);\n            // Set the gradient stops.\n            grad.addColorStop(0, shadow.color1);\n            grad.addColorStop(0.5, shadow.color2);\n            grad.addColorStop(1, shadow.color3);\n            // Set up the rect coordinates.\n            let x = hw;\n            let y = 0;\n            let w = shadow.size;\n            let h = hh + Math.min(ph, bh - sy);\n            // Fill the shadow rect with the fill style.\n            gc.fillStyle = grad;\n            gc.fillRect(x, y, w, h);\n        }\n        // Draw the column footer shadow if needed.\n        if (sy < syMax) {\n            // Set up the gradient coordinates.\n            let x0 = 0;\n            let y0 = vh;\n            let x1 = 0;\n            let y1 = vh - shadow.size;\n            // Create the gradient object.\n            let grad = gc.createLinearGradient(x0, y0, x1, y1);\n            // Set the gradient stops.\n            grad.addColorStop(0, shadow.color1);\n            grad.addColorStop(0.5, shadow.color2);\n            grad.addColorStop(1, shadow.color3);\n            // Set up the rect coordinates.\n            let x = 0;\n            let y = vh - shadow.size;\n            let w = hw + Math.min(pw, bw - sx);\n            let h = shadow.size;\n            // Fill the shadow rect with the fill style.\n            gc.fillStyle = grad;\n            gc.fillRect(x, y, w, h);\n        }\n        // Draw the row footer shadow if needed.\n        if (sx < sxMax) {\n            // Set up the gradient coordinates.\n            let x0 = vw;\n            let y0 = 0;\n            let x1 = vw - shadow.size;\n            let y1 = 0;\n            // Create the gradient object.\n            let grad = gc.createLinearGradient(x0, y0, x1, y1);\n            // Set the gradient stops.\n            grad.addColorStop(0, shadow.color1);\n            grad.addColorStop(0.5, shadow.color2);\n            grad.addColorStop(1, shadow.color3);\n            // Set up the rect coordinates.\n            let x = vw - shadow.size;\n            let y = 0;\n            let w = shadow.size;\n            let h = hh + Math.min(ph, bh - sy);\n            // Fill the shadow rect with the fill style.\n            gc.fillStyle = grad;\n            gc.fillRect(x, y, w, h);\n        }\n        // Restore the gc state.\n        gc.restore();\n    }\n}\n/**\n * The namespace for the `DataGrid` class statics.\n */\n(function (DataGrid) {\n    /**\n     * A generic format function for the copy handler.\n     *\n     * @param args - The format args for the function.\n     *\n     * @returns The string representation of the value.\n     *\n     * #### Notes\n     * This function uses `String()` to coerce a value to a string.\n     */\n    function copyFormatGeneric(args) {\n        if (args.value === null || args.value === undefined) {\n            return '';\n        }\n        return String(args.value);\n    }\n    DataGrid.copyFormatGeneric = copyFormatGeneric;\n    /**\n     * The default theme for a data grid.\n     */\n    DataGrid.defaultStyle = {\n        voidColor: '#F3F3F3',\n        backgroundColor: '#FFFFFF',\n        gridLineColor: 'rgba(20, 20, 20, 0.15)',\n        headerBackgroundColor: '#F3F3F3',\n        headerGridLineColor: 'rgba(20, 20, 20, 0.25)',\n        selectionFillColor: 'rgba(49, 119, 229, 0.2)',\n        selectionBorderColor: 'rgba(0, 107, 247, 1.0)',\n        cursorBorderColor: 'rgba(0, 107, 247, 1.0)',\n        headerSelectionFillColor: 'rgba(20, 20, 20, 0.1)',\n        headerSelectionBorderColor: 'rgba(0, 107, 247, 1.0)',\n        scrollShadow: {\n            size: 10,\n            color1: 'rgba(0, 0, 0, 0.20)',\n            color2: 'rgba(0, 0, 0, 0.05)',\n            color3: 'rgba(0, 0, 0, 0.00)'\n        }\n    };\n    /**\n     * The default sizes for a data grid.\n     */\n    DataGrid.defaultSizes = {\n        rowHeight: 20,\n        columnWidth: 64,\n        rowHeaderWidth: 64,\n        columnHeaderHeight: 20\n    };\n    /**\n     * The default minimum sizes for a data grid.\n     */\n    DataGrid.minimumSizes = {\n        rowHeight: 20,\n        columnWidth: 10,\n        rowHeaderWidth: 10,\n        columnHeaderHeight: 20\n    };\n    /**\n     * The default copy config for a data grid.\n     */\n    DataGrid.defaultCopyConfig = {\n        separator: '\\t',\n        format: copyFormatGeneric,\n        headers: 'none',\n        warningThreshold: 1e6\n    };\n})(DataGrid || (DataGrid = {}));\n/**\n * The namespace for the module implementation details.\n */\nvar Private$1;\n(function (Private) {\n    /**\n     * A singleton `scroll-request` conflatable message.\n     */\n    Private.ScrollRequest = new _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage('scroll-request');\n    /**\n     * A singleton `overlay-paint-request` conflatable message.\n     */\n    Private.OverlayPaintRequest = new _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage('overlay-paint-request');\n    /**\n     * Create a new zero-sized canvas element.\n     */\n    function createCanvas() {\n        let canvas = document.createElement('canvas');\n        canvas.width = 0;\n        canvas.height = 0;\n        return canvas;\n    }\n    Private.createCanvas = createCanvas;\n    /**\n     * Checks whether a given regions has merged cells in it.\n     * @param dataModel grid's data model.\n     * @param region the paint region to be checked.\n     * @returns boolean.\n     */\n    function regionHasMergedCells(dataModel, region) {\n        const regionGroups = CellGroup.getCellGroupsAtRegion(dataModel, region);\n        return regionGroups.length > 0;\n    }\n    Private.regionHasMergedCells = regionHasMergedCells;\n    /**\n     * A conflatable message which merges dirty paint regions.\n     */\n    class PaintRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {\n        /**\n         * Construct a new paint request messages.\n         *\n         * @param region - The cell region for the paint.\n         *\n         * @param r1 - The top-left row of the dirty region.\n         *\n         * @param c1 - The top-left column of the dirty region.\n         *\n         * @param r2 - The bottom-right row of the dirty region.\n         *\n         * @param c2 - The bottom-right column of the dirty region.\n         */\n        constructor(region, r1, c1, r2, c2) {\n            super('paint-request');\n            this._region = region;\n            this._r1 = r1;\n            this._c1 = c1;\n            this._r2 = r2;\n            this._c2 = c2;\n        }\n        /**\n         * The cell region for the paint.\n         */\n        get region() {\n            return this._region;\n        }\n        /**\n         * The top-left row of the dirty region.\n         */\n        get r1() {\n            return this._r1;\n        }\n        /**\n         * The top-left column of the dirty region.\n         */\n        get c1() {\n            return this._c1;\n        }\n        /**\n         * The bottom-right row of the dirty region.\n         */\n        get r2() {\n            return this._r2;\n        }\n        /**\n         * The bottom-right column of the dirty region.\n         */\n        get c2() {\n            return this._c2;\n        }\n        /**\n         * Conflate this message with another paint request.\n         */\n        conflate(other) {\n            // Bail early if the request is already painting everything.\n            if (this._region === 'all') {\n                return true;\n            }\n            // Any region can conflate with the `'all'` region.\n            if (other._region === 'all') {\n                this._region = 'all';\n                return true;\n            }\n            // Otherwise, do not conflate with a different region.\n            if (this._region !== other._region) {\n                return false;\n            }\n            // Conflate the region to the total boundary.\n            this._r1 = Math.min(this._r1, other._r1);\n            this._c1 = Math.min(this._c1, other._c1);\n            this._r2 = Math.max(this._r2, other._r2);\n            this._c2 = Math.max(this._c2, other._c2);\n            return true;\n        }\n    }\n    Private.PaintRequest = PaintRequest;\n    /**\n     * A conflatable message for resizing rows.\n     */\n    class RowResizeRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {\n        /**\n         * Construct a new row resize request.\n         *\n         * @param region - The row region which holds the section.\n         *\n         * @param index - The index of row in the region.\n         *\n         * @param size - The target size of the section.\n         */\n        constructor(region, index, size) {\n            super('row-resize-request');\n            this._region = region;\n            this._index = index;\n            this._size = size;\n        }\n        /**\n         * The row region which holds the section.\n         */\n        get region() {\n            return this._region;\n        }\n        /**\n         * The index of the row in the region.\n         */\n        get index() {\n            return this._index;\n        }\n        /**\n         * The target size of the section.\n         */\n        get size() {\n            return this._size;\n        }\n        /**\n         * Conflate this message with another row resize request.\n         */\n        conflate(other) {\n            if (this._region !== other._region || this._index !== other._index) {\n                return false;\n            }\n            this._size = other._size;\n            return true;\n        }\n    }\n    Private.RowResizeRequest = RowResizeRequest;\n    /**\n     * A conflatable message for resizing columns.\n     */\n    class ColumnResizeRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {\n        /**\n         * Construct a new column resize request.\n         *\n         * @param region - The column region which holds the section.\n         *\n         * @param index - The index of column in the region.\n         *\n         * @param size - The target size of the section.\n         *               If null, then infer the size to fit.\n         */\n        constructor(region, index, size) {\n            super('column-resize-request');\n            this._region = region;\n            this._index = index;\n            this._size = size;\n        }\n        /**\n         * The column region which holds the section.\n         */\n        get region() {\n            return this._region;\n        }\n        /**\n         * The index of the column in the region.\n         */\n        get index() {\n            return this._index;\n        }\n        /**\n         * The target size of the section.\n         */\n        get size() {\n            return this._size;\n        }\n        /**\n         * Conflate this message with another column resize request.\n         */\n        conflate(other) {\n            if (this._region !== other._region || this._index !== other._index) {\n                return false;\n            }\n            this._size = other._size;\n            return true;\n        }\n    }\n    Private.ColumnResizeRequest = ColumnResizeRequest;\n})(Private$1 || (Private$1 = {}));\n\n/**\n * A data model implementation for in-memory JSON data.\n */\nclass JSONModel extends DataModel {\n    /**\n     * Create a data model with static JSON data.\n     *\n     * @param options - The options for initializing the data model.\n     */\n    constructor(options) {\n        super();\n        let split = Private.splitFields(options.schema);\n        this._data = options.data;\n        this._bodyFields = split.bodyFields;\n        this._headerFields = split.headerFields;\n        this._missingValues = Private.createMissingMap(options.schema);\n    }\n    /**\n     * Get the row count for a region in the data model.\n     *\n     * @param region - The row region of interest.\n     *\n     * @returns - The row count for the region.\n     */\n    rowCount(region) {\n        if (region === 'body') {\n            return this._data.length;\n        }\n        return 1; // TODO multiple column-header rows?\n    }\n    /**\n     * Get the column count for a region in the data model.\n     *\n     * @param region - The column region of interest.\n     *\n     * @returns - The column count for the region.\n     */\n    columnCount(region) {\n        if (region === 'body') {\n            return this._bodyFields.length;\n        }\n        return this._headerFields.length;\n    }\n    /**\n     * Get the data value for a cell in the data model.\n     *\n     * @param region - The cell region of interest.\n     *\n     * @param row - The row index of the cell of interest.\n     *\n     * @param column - The column index of the cell of interest.\n     *\n     * @returns - The data value for the specified cell.\n     *\n     * #### Notes\n     * A `missingValue` as defined by the schema is converted to `null`.\n     */\n    data(region, row, column) {\n        // Set up the field and value variables.\n        let field;\n        let value;\n        // Look up the field and value for the region.\n        switch (region) {\n            case 'body':\n                field = this._bodyFields[column];\n                value = this._data[row][field.name];\n                break;\n            case 'column-header':\n                field = this._bodyFields[column];\n                value = field.title || field.name;\n                break;\n            case 'row-header':\n                field = this._headerFields[column];\n                value = this._data[row][field.name];\n                break;\n            case 'corner-header':\n                field = this._headerFields[column];\n                value = field.title || field.name;\n                break;\n            default:\n                throw 'unreachable';\n        }\n        // Test whether the value is a missing value.\n        let missing = this._missingValues !== null &&\n            typeof value === 'string' &&\n            this._missingValues[value] === true;\n        // Return the final value.\n        return missing ? null : value;\n    }\n    /**\n     * Get the metadata for a cell in the data model.\n     *\n     * @param region - The cell region of interest.\n     *\n     * @param row - The row index of the cell of of interest.\n     *\n     * @param column - The column index of the cell of interest.\n     *\n     * @returns The metadata for the cell.\n     */\n    metadata(region, row, column) {\n        if (region === 'body' || region === 'column-header') {\n            return this._bodyFields[column];\n        }\n        return this._headerFields[column];\n    }\n}\n/**\n * The namespace for the module implementation details.\n */\nvar Private;\n(function (Private) {\n    /**\n     * Split the schema fields into header and body fields.\n     */\n    function splitFields(schema) {\n        // Normalize the primary keys.\n        let primaryKeys;\n        if (schema.primaryKey === undefined) {\n            primaryKeys = [];\n        }\n        else if (typeof schema.primaryKey === 'string') {\n            primaryKeys = [schema.primaryKey];\n        }\n        else {\n            primaryKeys = schema.primaryKey;\n        }\n        // Separate the fields for the body and header.\n        let bodyFields = [];\n        let headerFields = [];\n        for (let field of schema.fields) {\n            if (primaryKeys.indexOf(field.name) === -1) {\n                bodyFields.push(field);\n            }\n            else {\n                headerFields.push(field);\n            }\n        }\n        // Return the separated fields.\n        return { bodyFields, headerFields };\n    }\n    Private.splitFields = splitFields;\n    /**\n     * Create a missing values map for a schema.\n     *\n     * This returns `null` if there are no missing values.\n     */\n    function createMissingMap(schema) {\n        // Bail early if there are no missing values.\n        if (!schema.missingValues || schema.missingValues.length === 0) {\n            return null;\n        }\n        // Collect the missing values into a map.\n        let result = Object.create(null);\n        for (let value of schema.missingValues) {\n            result[value] = true;\n        }\n        // Return the populated map.\n        return result;\n    }\n    Private.createMissingMap = createMissingMap;\n})(Private || (Private = {}));\n\n// Copyright (c) Jupyter Development Team.\n// Distributed under the terms of the Modified BSD License.\n/*-----------------------------------------------------------------------------\n| Copyright (c) 2014-2023, Lumino Contributors\n|\n| Distributed under the terms of the BSD 3-Clause License.\n|\n| The full license is in the file LICENSE, distributed with this software.\n|----------------------------------------------------------------------------*/\nconst PERCENTAGE_REGEX = /^(\\d+(\\.\\d+)?)%$/;\nconst PIXEL_REGEX = /^(\\d+(\\.\\d+)?)px$/;\n/**\n * A cell renderer which renders data values as images.\n */\nclass ImageRenderer extends AsyncCellRenderer {\n    /**\n     * Construct a new text renderer.\n     *\n     * @param options - The options for initializing the renderer.\n     */\n    constructor(options = {}) {\n        super();\n        this.backgroundColor = options.backgroundColor || '';\n        this.textColor = options.textColor || '#000000';\n        this.placeholder = options.placeholder || '...';\n        this.width = options.width || '';\n        // Not using the || operator, because the empty string '' is a valid value\n        this.height = options.height === undefined ? '100%' : options.height;\n    }\n    /**\n     * Whether the renderer is ready or not for that specific config.\n     * If it's not ready, the datagrid will paint the placeholder.\n     * If it's ready, the datagrid will paint the image synchronously.\n     *\n     * @param config - The configuration data for the cell.\n     *\n     * @returns Whether the renderer is ready for this config or not.\n     */\n    isReady(config) {\n        return (!config.value || ImageRenderer.dataCache.get(config.value) !== undefined);\n    }\n    /**\n     * Load the image asynchronously for a specific config.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    async load(config) {\n        // Bail early if there is nothing to do\n        if (!config.value) {\n            return;\n        }\n        const value = config.value;\n        const loadedPromise = new _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__.PromiseDelegate();\n        ImageRenderer.dataCache.set(value, undefined);\n        const img = new Image();\n        img.onload = () => {\n            ImageRenderer.dataCache.set(value, img);\n            loadedPromise.resolve();\n        };\n        img.src = value;\n        return loadedPromise.promise;\n    }\n    /**\n     * Paint the placeholder for a cell, waiting for the renderer to be ready.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    paintPlaceholder(gc, config) {\n        this.drawBackground(gc, config);\n        this.drawPlaceholder(gc, config);\n    }\n    /**\n     * Paint the content for a cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    paint(gc, config) {\n        this.drawBackground(gc, config);\n        this.drawImage(gc, config);\n    }\n    /**\n     * Draw the background for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawBackground(gc, config) {\n        // Resolve the background color for the cell.\n        const color = CellRenderer.resolveOption(this.backgroundColor, config);\n        // Bail if there is no background color to draw.\n        if (!color) {\n            return;\n        }\n        // Fill the cell with the background color.\n        gc.fillStyle = color;\n        gc.fillRect(config.x, config.y, config.width, config.height);\n    }\n    /**\n     * Draw the placeholder for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawPlaceholder(gc, config) {\n        const placeholder = CellRenderer.resolveOption(this.placeholder, config);\n        const color = CellRenderer.resolveOption(this.textColor, config);\n        const textX = config.x + config.width / 2;\n        const textY = config.y + config.height / 2;\n        // Draw the placeholder.\n        gc.fillStyle = color;\n        gc.fillText(placeholder, textX, textY);\n    }\n    /**\n     * Draw the image for the cell.\n     *\n     * @param gc - The graphics context to use for drawing.\n     *\n     * @param config - The configuration data for the cell.\n     */\n    drawImage(gc, config) {\n        // Bail early if there is nothing to draw\n        if (!config.value) {\n            return;\n        }\n        const img = ImageRenderer.dataCache.get(config.value);\n        // If it's not loaded yet, show the placeholder\n        if (!img) {\n            return this.drawPlaceholder(gc, config);\n        }\n        const width = CellRenderer.resolveOption(this.width, config);\n        const height = CellRenderer.resolveOption(this.height, config);\n        // width and height are unset, we display the image with its original size\n        if (!width && !height) {\n            gc.drawImage(img, config.x, config.y);\n            return;\n        }\n        let requestedWidth = img.width;\n        let requestedHeight = img.height;\n        let widthPercentageMatch;\n        let widthPixelMatch;\n        let heightPercentageMatch;\n        let heightPixelMatch;\n        if ((widthPercentageMatch = width.match(PERCENTAGE_REGEX))) {\n            requestedWidth =\n                (parseFloat(widthPercentageMatch[1]) / 100) * config.width;\n        }\n        else if ((widthPixelMatch = width.match(PIXEL_REGEX))) {\n            requestedWidth = parseFloat(widthPixelMatch[1]);\n        }\n        if ((heightPercentageMatch = height.match(PERCENTAGE_REGEX))) {\n            requestedHeight =\n                (parseFloat(heightPercentageMatch[1]) / 100) * config.height;\n        }\n        else if ((heightPixelMatch = height.match(PIXEL_REGEX))) {\n            requestedHeight = parseFloat(heightPixelMatch[1]);\n        }\n        // If width is not set, we compute it respecting the image size ratio\n        if (!width) {\n            requestedWidth = (img.width / img.height) * requestedHeight;\n        }\n        // If height is not set, we compute it respecting the image size ratio\n        if (!height) {\n            requestedHeight = (img.height / img.width) * requestedWidth;\n        }\n        gc.drawImage(img, config.x, config.y, requestedWidth, requestedHeight);\n    }\n}\nImageRenderer.dataCache = new Map();\n\n\n//# sourceMappingURL=index.es6.js.map\n\n\n//# sourceURL=webpack://_JUPYTERLAB.CORE_OUTPUT/../node_modules/@lumino/datagrid/dist/index.es6.js?");

/***/ })

}]);