You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			11587 lines
		
	
	
		
			400 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			11587 lines
		
	
	
		
			400 KiB
		
	
	
	
		
			JavaScript
		
	
"use strict";
 | 
						|
(self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] = self["webpackChunk_JUPYTERLAB_CORE_OUTPUT"] || []).push([[8929],{
 | 
						|
 | 
						|
/***/ 98929:
 | 
						|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
 | 
						|
 | 
						|
__webpack_require__.r(__webpack_exports__);
 | 
						|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
 | 
						|
/* harmony export */   AsyncCellRenderer: () => (/* binding */ AsyncCellRenderer),
 | 
						|
/* harmony export */   BasicKeyHandler: () => (/* binding */ BasicKeyHandler),
 | 
						|
/* harmony export */   BasicMouseHandler: () => (/* binding */ BasicMouseHandler),
 | 
						|
/* harmony export */   BasicSelectionModel: () => (/* binding */ BasicSelectionModel),
 | 
						|
/* harmony export */   BooleanCellEditor: () => (/* binding */ BooleanCellEditor),
 | 
						|
/* harmony export */   CellEditor: () => (/* binding */ CellEditor),
 | 
						|
/* harmony export */   CellEditorController: () => (/* binding */ CellEditorController),
 | 
						|
/* harmony export */   CellGroup: () => (/* binding */ CellGroup),
 | 
						|
/* harmony export */   CellRenderer: () => (/* binding */ CellRenderer),
 | 
						|
/* harmony export */   DataGrid: () => (/* binding */ DataGrid),
 | 
						|
/* harmony export */   DataModel: () => (/* binding */ DataModel),
 | 
						|
/* harmony export */   DateCellEditor: () => (/* binding */ DateCellEditor),
 | 
						|
/* harmony export */   DynamicOptionCellEditor: () => (/* binding */ DynamicOptionCellEditor),
 | 
						|
/* harmony export */   GraphicsContext: () => (/* binding */ GraphicsContext),
 | 
						|
/* harmony export */   HyperlinkRenderer: () => (/* binding */ HyperlinkRenderer),
 | 
						|
/* harmony export */   ImageRenderer: () => (/* binding */ ImageRenderer),
 | 
						|
/* harmony export */   InputCellEditor: () => (/* binding */ InputCellEditor),
 | 
						|
/* harmony export */   IntegerCellEditor: () => (/* binding */ IntegerCellEditor),
 | 
						|
/* harmony export */   IntegerInputValidator: () => (/* binding */ IntegerInputValidator),
 | 
						|
/* harmony export */   JSONModel: () => (/* binding */ JSONModel),
 | 
						|
/* harmony export */   MutableDataModel: () => (/* binding */ MutableDataModel),
 | 
						|
/* harmony export */   NumberCellEditor: () => (/* binding */ NumberCellEditor),
 | 
						|
/* harmony export */   NumberInputValidator: () => (/* binding */ NumberInputValidator),
 | 
						|
/* harmony export */   OptionCellEditor: () => (/* binding */ OptionCellEditor),
 | 
						|
/* harmony export */   PassInputValidator: () => (/* binding */ PassInputValidator),
 | 
						|
/* harmony export */   RendererMap: () => (/* binding */ RendererMap),
 | 
						|
/* harmony export */   SectionList: () => (/* binding */ SectionList),
 | 
						|
/* harmony export */   SelectionModel: () => (/* binding */ SelectionModel),
 | 
						|
/* harmony export */   TextCellEditor: () => (/* binding */ TextCellEditor),
 | 
						|
/* harmony export */   TextInputValidator: () => (/* binding */ TextInputValidator),
 | 
						|
/* harmony export */   TextRenderer: () => (/* binding */ TextRenderer),
 | 
						|
/* harmony export */   resolveOption: () => (/* binding */ resolveOption)
 | 
						|
/* harmony export */ });
 | 
						|
/* harmony import */ var _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(23738);
 | 
						|
/* harmony import */ var _lumino_domutils__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__);
 | 
						|
/* harmony import */ var _lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(71864);
 | 
						|
/* harmony import */ var _lumino_keyboard__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__);
 | 
						|
/* harmony import */ var _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(67344);
 | 
						|
/* harmony import */ var _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__);
 | 
						|
/* harmony import */ var _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(56114);
 | 
						|
/* harmony import */ var _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__);
 | 
						|
/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2536);
 | 
						|
/* harmony import */ var _lumino_signaling__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_lumino_signaling__WEBPACK_IMPORTED_MODULE_4__);
 | 
						|
/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(60920);
 | 
						|
/* harmony import */ var _lumino_widgets__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_lumino_widgets__WEBPACK_IMPORTED_MODULE_5__);
 | 
						|
/* harmony import */ var _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(34993);
 | 
						|
/* harmony import */ var _lumino_messaging__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_lumino_messaging__WEBPACK_IMPORTED_MODULE_6__);
 | 
						|
/* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(5406);
 | 
						|
/* harmony import */ var _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(_lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__);
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * A basic implementation of a data grid key handler.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This class may be subclassed and customized as needed.
 | 
						|
 */
 | 
						|
class BasicKeyHandler {
 | 
						|
    constructor() {
 | 
						|
        this._disposed = false;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the key handler is disposed.
 | 
						|
     */
 | 
						|
    get isDisposed() {
 | 
						|
        return this._disposed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by the key handler.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        this._disposed = true;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the key down event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keydown event of interest.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This will not be called if the mouse button is pressed.
 | 
						|
     */
 | 
						|
    onKeyDown(grid, event) {
 | 
						|
        // if grid is editable and cell selection available, start cell editing
 | 
						|
        // on key press (letters, numbers and space only)
 | 
						|
        if (grid.editable &&
 | 
						|
            grid.selectionModel.cursorRow !== -1 &&
 | 
						|
            grid.selectionModel.cursorColumn !== -1) {
 | 
						|
            const input = String.fromCharCode(event.keyCode);
 | 
						|
            if (/[a-zA-Z0-9-_ ]/.test(input)) {
 | 
						|
                const row = grid.selectionModel.cursorRow;
 | 
						|
                const column = grid.selectionModel.cursorColumn;
 | 
						|
                const cell = {
 | 
						|
                    grid: grid,
 | 
						|
                    row: row,
 | 
						|
                    column: column
 | 
						|
                };
 | 
						|
                grid.editorController.edit(cell);
 | 
						|
                if ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event) === 'Space') {
 | 
						|
                    event.stopPropagation();
 | 
						|
                    event.preventDefault();
 | 
						|
                }
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'ArrowLeft':
 | 
						|
                this.onArrowLeft(grid, event);
 | 
						|
                break;
 | 
						|
            case 'ArrowRight':
 | 
						|
                this.onArrowRight(grid, event);
 | 
						|
                break;
 | 
						|
            case 'ArrowUp':
 | 
						|
                this.onArrowUp(grid, event);
 | 
						|
                break;
 | 
						|
            case 'ArrowDown':
 | 
						|
                this.onArrowDown(grid, event);
 | 
						|
                break;
 | 
						|
            case 'PageUp':
 | 
						|
                this.onPageUp(grid, event);
 | 
						|
                break;
 | 
						|
            case 'PageDown':
 | 
						|
                this.onPageDown(grid, event);
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.onEscape(grid, event);
 | 
						|
                break;
 | 
						|
            case 'Delete':
 | 
						|
                this.onDelete(grid, event);
 | 
						|
                break;
 | 
						|
            case 'C':
 | 
						|
                this.onKeyC(grid, event);
 | 
						|
                break;
 | 
						|
            case 'Enter':
 | 
						|
                if (grid.selectionModel) {
 | 
						|
                    grid.moveCursor(event.shiftKey ? 'up' : 'down');
 | 
						|
                    grid.scrollToCursor();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                if (grid.selectionModel) {
 | 
						|
                    grid.moveCursor(event.shiftKey ? 'left' : 'right');
 | 
						|
                    grid.scrollToCursor();
 | 
						|
                    event.stopPropagation();
 | 
						|
                    event.preventDefault();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'ArrowLeft'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onArrowLeft(grid, event) {
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Fetch the modifier flags.
 | 
						|
        let shift = event.shiftKey;
 | 
						|
        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);
 | 
						|
        // Handle no model with the accel modifier.
 | 
						|
        if (!model && accel) {
 | 
						|
            grid.scrollTo(0, grid.scrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle no model and no modifier. (ignore shift)
 | 
						|
        if (!model) {
 | 
						|
            grid.scrollByStep('left');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Handle the row selection mode with accel key.
 | 
						|
        if (mode === 'row' && accel) {
 | 
						|
            grid.scrollTo(0, grid.scrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the row selection mode with no modifier. (ignore shift)
 | 
						|
        if (mode === 'row') {
 | 
						|
            grid.scrollByStep('left');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Dispatch based on the modifier keys.
 | 
						|
        if (accel && shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 - 1 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (accel) {
 | 
						|
            r1 = r;
 | 
						|
            r2 = r;
 | 
						|
            c1 = 0;
 | 
						|
            c2 = 0;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = r;
 | 
						|
            r2 = r;
 | 
						|
            c1 = c - 1;
 | 
						|
            c2 = c - 1;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        if (shift || mode === 'column') {
 | 
						|
            grid.scrollToColumn(cs.c2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            grid.scrollToCursor();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'ArrowRight'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onArrowRight(grid, event) {
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Fetch the modifier flags.
 | 
						|
        let shift = event.shiftKey;
 | 
						|
        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);
 | 
						|
        // Handle no model with the accel modifier.
 | 
						|
        if (!model && accel) {
 | 
						|
            grid.scrollTo(grid.maxScrollX, grid.scrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle no model and no modifier. (ignore shift)
 | 
						|
        if (!model) {
 | 
						|
            grid.scrollByStep('right');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Handle the row selection model with accel key.
 | 
						|
        if (mode === 'row' && accel) {
 | 
						|
            grid.scrollTo(grid.maxScrollX, grid.scrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the row selection mode with no modifier. (ignore shift)
 | 
						|
        if (mode === 'row') {
 | 
						|
            grid.scrollByStep('right');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Dispatch based on the modifier keys.
 | 
						|
        if (accel && shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = Infinity;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 + 1 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (accel) {
 | 
						|
            r1 = r;
 | 
						|
            r2 = r;
 | 
						|
            c1 = Infinity;
 | 
						|
            c2 = Infinity;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = r;
 | 
						|
            r2 = r;
 | 
						|
            c1 = c + 1;
 | 
						|
            c2 = c + 1;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        if (shift || mode === 'column') {
 | 
						|
            grid.scrollToColumn(cs.c2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            grid.scrollToCursor();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'ArrowUp'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onArrowUp(grid, event) {
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Fetch the modifier flags.
 | 
						|
        let shift = event.shiftKey;
 | 
						|
        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);
 | 
						|
        // Handle no model with the accel modifier.
 | 
						|
        if (!model && accel) {
 | 
						|
            grid.scrollTo(grid.scrollX, 0);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle no model and no modifier. (ignore shift)
 | 
						|
        if (!model) {
 | 
						|
            grid.scrollByStep('up');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Handle the column selection mode with accel key.
 | 
						|
        if (mode === 'column' && accel) {
 | 
						|
            grid.scrollTo(grid.scrollX, 0);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the column selection mode with no modifier. (ignore shift)
 | 
						|
        if (mode === 'column') {
 | 
						|
            grid.scrollByStep('up');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Dispatch based on the modifier keys.
 | 
						|
        if (accel && shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 - 1 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (accel) {
 | 
						|
            r1 = 0;
 | 
						|
            r2 = 0;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = r - 1;
 | 
						|
            r2 = r - 1;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        if (shift || mode === 'row') {
 | 
						|
            grid.scrollToRow(cs.r2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            grid.scrollToCursor();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'ArrowDown'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onArrowDown(grid, event) {
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Fetch the modifier flags.
 | 
						|
        let shift = event.shiftKey;
 | 
						|
        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);
 | 
						|
        // Handle no model with the accel modifier.
 | 
						|
        if (!model && accel) {
 | 
						|
            grid.scrollTo(grid.scrollX, grid.maxScrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle no model and no modifier. (ignore shift)
 | 
						|
        if (!model) {
 | 
						|
            grid.scrollByStep('down');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Handle the column selection mode with accel key.
 | 
						|
        if (mode === 'column' && accel) {
 | 
						|
            grid.scrollTo(grid.scrollX, grid.maxScrollY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the column selection mode with no modifier. (ignore shift)
 | 
						|
        if (mode === 'column') {
 | 
						|
            grid.scrollByStep('down');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Dispatch based on the modifier keys.
 | 
						|
        if (accel && shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = Infinity;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (shift) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 + 1 : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else if (accel) {
 | 
						|
            r1 = Infinity;
 | 
						|
            r2 = Infinity;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = r + 1;
 | 
						|
            r2 = r + 1;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c1;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        if (shift || mode === 'row') {
 | 
						|
            grid.scrollToRow(cs.r2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            grid.scrollToCursor();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'PageUp'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onPageUp(grid, event) {
 | 
						|
        // Ignore the event if the accel key is pressed.
 | 
						|
        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Scroll by page if there is no selection model.
 | 
						|
        if (!model || model.selectionMode === 'column') {
 | 
						|
            grid.scrollByPage('up');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the normal number of cells in the page height.
 | 
						|
        let n = Math.floor(grid.pageHeight / grid.defaultSizes.rowHeight);
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Select or resize as needed.
 | 
						|
        if (event.shiftKey) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 - n : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = cs ? cs.r1 - n : 0;
 | 
						|
            r2 = r1;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        grid.scrollToRow(cs.r2);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'PageDown'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onPageDown(grid, event) {
 | 
						|
        // Ignore the event if the accel key is pressed.
 | 
						|
        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Scroll by page if there is no selection model.
 | 
						|
        if (!model || model.selectionMode === 'column') {
 | 
						|
            grid.scrollByPage('down');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the normal number of cells in the page height.
 | 
						|
        let n = Math.floor(grid.pageHeight / grid.defaultSizes.rowHeight);
 | 
						|
        // Fetch the cursor and selection.
 | 
						|
        let r = model.cursorRow;
 | 
						|
        let c = model.cursorColumn;
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let r2;
 | 
						|
        let c1;
 | 
						|
        let c2;
 | 
						|
        let cr;
 | 
						|
        let cc;
 | 
						|
        let clear;
 | 
						|
        // Select or resize as needed.
 | 
						|
        if (event.shiftKey) {
 | 
						|
            r1 = cs ? cs.r1 : 0;
 | 
						|
            r2 = cs ? cs.r2 + n : 0;
 | 
						|
            c1 = cs ? cs.c1 : 0;
 | 
						|
            c2 = cs ? cs.c2 : 0;
 | 
						|
            cr = r;
 | 
						|
            cc = c;
 | 
						|
            clear = 'current';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = cs ? cs.r1 + n : 0;
 | 
						|
            r2 = r1;
 | 
						|
            c1 = c;
 | 
						|
            c2 = c;
 | 
						|
            cr = r1;
 | 
						|
            cc = c;
 | 
						|
            clear = 'all';
 | 
						|
        }
 | 
						|
        // Create the new selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow: cr, cursorColumn: cc, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid appropriately.
 | 
						|
        grid.scrollToRow(cs.r2);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'Escape'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onEscape(grid, event) {
 | 
						|
        if (grid.selectionModel) {
 | 
						|
            grid.selectionModel.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'Delete'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onDelete(grid, event) {
 | 
						|
        if (grid.editable && !grid.selectionModel.isEmpty) {
 | 
						|
            const dataModel = grid.dataModel;
 | 
						|
            // Fetch the max row and column.
 | 
						|
            let maxRow = dataModel.rowCount('body') - 1;
 | 
						|
            let maxColumn = dataModel.columnCount('body') - 1;
 | 
						|
            for (let s of grid.selectionModel.selections()) {
 | 
						|
                // Clamp the cell to the model bounds.
 | 
						|
                let sr1 = Math.max(0, Math.min(s.r1, maxRow));
 | 
						|
                let sc1 = Math.max(0, Math.min(s.c1, maxColumn));
 | 
						|
                let sr2 = Math.max(0, Math.min(s.r2, maxRow));
 | 
						|
                let sc2 = Math.max(0, Math.min(s.c2, maxColumn));
 | 
						|
                for (let r = sr1; r <= sr2; ++r) {
 | 
						|
                    for (let c = sc1; c <= sc2; ++c) {
 | 
						|
                        dataModel.setData('body', r, c, null);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'C'` key press for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The keyboard event of interest.
 | 
						|
     */
 | 
						|
    onKeyC(grid, event) {
 | 
						|
        // Bail early if the modifiers aren't correct for copy.
 | 
						|
        if (event.shiftKey || !_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Copy the current selection to the clipboard.
 | 
						|
        grid.copyToClipboard();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * An object which renders the cells of a data grid.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * If the predefined cell renderers are insufficient for a particular
 | 
						|
 * use case, a custom cell renderer can be defined which derives from
 | 
						|
 * this class.
 | 
						|
 *
 | 
						|
 * The data grid renders cells in column-major order, by region. The
 | 
						|
 * region order is: body, row header, column header, corner header.
 | 
						|
 */
 | 
						|
class CellRenderer {
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the `CellRenderer` class statics.
 | 
						|
 */
 | 
						|
(function (CellRenderer) {
 | 
						|
    /**
 | 
						|
     * Resolve a config option for a cell renderer.
 | 
						|
     *
 | 
						|
     * @param option - The config option to resolve.
 | 
						|
     *
 | 
						|
     * @param config - The cell config object.
 | 
						|
     *
 | 
						|
     * @returns The resolved value for the option.
 | 
						|
     */
 | 
						|
    function resolveOption(option, config) {
 | 
						|
        return typeof option === 'function'
 | 
						|
            ? option(config)
 | 
						|
            : option;
 | 
						|
    }
 | 
						|
    CellRenderer.resolveOption = resolveOption;
 | 
						|
})(CellRenderer || (CellRenderer = {}));
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * A cell renderer which renders data values as text.
 | 
						|
 */
 | 
						|
class TextRenderer extends CellRenderer {
 | 
						|
    /**
 | 
						|
     * Construct a new text renderer.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the renderer.
 | 
						|
     */
 | 
						|
    constructor(options = {}) {
 | 
						|
        super();
 | 
						|
        this.font = options.font || '12px sans-serif';
 | 
						|
        this.textColor = options.textColor || '#000000';
 | 
						|
        this.backgroundColor = options.backgroundColor || '';
 | 
						|
        this.verticalAlignment = options.verticalAlignment || 'center';
 | 
						|
        this.horizontalAlignment = options.horizontalAlignment || 'left';
 | 
						|
        this.horizontalPadding = options.horizontalPadding || 8;
 | 
						|
        this.format = options.format || TextRenderer.formatGeneric();
 | 
						|
        this.elideDirection = options.elideDirection || 'none';
 | 
						|
        this.wrapText = options.wrapText || false;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint the content for a cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    paint(gc, config) {
 | 
						|
        this.drawBackground(gc, config);
 | 
						|
        this.drawText(gc, config);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the background for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawBackground(gc, config) {
 | 
						|
        // Resolve the background color for the cell.
 | 
						|
        let color = CellRenderer.resolveOption(this.backgroundColor, config);
 | 
						|
        // Bail if there is no background color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fill the cell with the background color.
 | 
						|
        gc.fillStyle = color;
 | 
						|
        gc.fillRect(config.x, config.y, config.width, config.height);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the full text to be rendered by the cell.
 | 
						|
     */
 | 
						|
    getText(config) {
 | 
						|
        return this.format(config);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the text for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawText(gc, config) {
 | 
						|
        // Resolve the font for the cell.
 | 
						|
        let font = CellRenderer.resolveOption(this.font, config);
 | 
						|
        // Bail if there is no font to draw.
 | 
						|
        if (!font) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resolve the text color for the cell.
 | 
						|
        let color = CellRenderer.resolveOption(this.textColor, config);
 | 
						|
        // Bail if there is no text color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Format the cell value to text.
 | 
						|
        let text = this.getText(config);
 | 
						|
        // Bail if there is no text to draw.
 | 
						|
        if (!text) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resolve the vertical and horizontal alignment.
 | 
						|
        let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config);
 | 
						|
        let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config);
 | 
						|
        // Resolve the elision direction
 | 
						|
        let elideDirection = CellRenderer.resolveOption(this.elideDirection, config);
 | 
						|
        // Resolve the text wrapping flag
 | 
						|
        let wrapText = CellRenderer.resolveOption(this.wrapText, config);
 | 
						|
        // Compute the padded text box height for the specified alignment.
 | 
						|
        let boxHeight = config.height - (vAlign === 'center' ? 1 : 2);
 | 
						|
        // Bail if the text box has no effective size.
 | 
						|
        if (boxHeight <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the text height for the gc font.
 | 
						|
        let textHeight = TextRenderer.measureFontHeight(font);
 | 
						|
        // Set up the text position variables.
 | 
						|
        let textX;
 | 
						|
        let textY;
 | 
						|
        let boxWidth;
 | 
						|
        // Compute the Y position for the text.
 | 
						|
        switch (vAlign) {
 | 
						|
            case 'top':
 | 
						|
                textY = config.y + 2 + textHeight;
 | 
						|
                break;
 | 
						|
            case 'center':
 | 
						|
                textY = config.y + config.height / 2 + textHeight / 2;
 | 
						|
                break;
 | 
						|
            case 'bottom':
 | 
						|
                textY = config.y + config.height - 2;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Compute the X position for the text.
 | 
						|
        switch (hAlign) {
 | 
						|
            case 'left':
 | 
						|
                textX = config.x + this.horizontalPadding;
 | 
						|
                boxWidth = config.width - 14;
 | 
						|
                break;
 | 
						|
            case 'center':
 | 
						|
                textX = config.x + config.width / 2;
 | 
						|
                boxWidth = config.width;
 | 
						|
                break;
 | 
						|
            case 'right':
 | 
						|
                textX = config.x + config.width - this.horizontalPadding;
 | 
						|
                boxWidth = config.width - 14;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Clip the cell if the text is taller than the text box height.
 | 
						|
        if (textHeight > boxHeight) {
 | 
						|
            gc.beginPath();
 | 
						|
            gc.rect(config.x, config.y, config.width, config.height - 1);
 | 
						|
            gc.clip();
 | 
						|
        }
 | 
						|
        // Set the gc state.
 | 
						|
        gc.font = font;
 | 
						|
        gc.fillStyle = color;
 | 
						|
        gc.textAlign = hAlign;
 | 
						|
        gc.textBaseline = 'bottom';
 | 
						|
        // Terminate call here if we're not eliding or wrapping text
 | 
						|
        if (elideDirection === 'none' && !wrapText) {
 | 
						|
            gc.fillText(text, textX, textY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // The current text width in pixels.
 | 
						|
        let textWidth = gc.measureText(text).width;
 | 
						|
        // Apply text wrapping if enabled.
 | 
						|
        if (wrapText && textWidth > boxWidth) {
 | 
						|
            // Make sure box clipping happens.
 | 
						|
            gc.beginPath();
 | 
						|
            gc.rect(config.x, config.y, config.width, config.height - 1);
 | 
						|
            gc.clip();
 | 
						|
            // Split column name to words based on
 | 
						|
            // whitespace preceding a word boundary.
 | 
						|
            // "Hello  world" --> ["Hello  ", "world"]
 | 
						|
            const wordsInColumn = text.split(/\s(?=\b)/);
 | 
						|
            // Y-coordinate offset for any additional lines
 | 
						|
            let curY = textY;
 | 
						|
            let textInCurrentLine = wordsInColumn.shift();
 | 
						|
            // Single word. Applying text wrap on word by splitting
 | 
						|
            // it into characters and fitting the maximum number of
 | 
						|
            // characters possible per line (box width).
 | 
						|
            if (wordsInColumn.length === 0) {
 | 
						|
                let curLineTextWidth = gc.measureText(textInCurrentLine).width;
 | 
						|
                while (curLineTextWidth > boxWidth && textInCurrentLine !== '') {
 | 
						|
                    // Iterating from the end of the string until we find a
 | 
						|
                    // substring (0,i) which has a width less than the box width.
 | 
						|
                    for (let i = textInCurrentLine.length; i > 0; i--) {
 | 
						|
                        const curSubString = textInCurrentLine.substring(0, i);
 | 
						|
                        const curSubStringWidth = gc.measureText(curSubString).width;
 | 
						|
                        if (curSubStringWidth < boxWidth || curSubString.length === 1) {
 | 
						|
                            // Found a substring which has a width less than the current
 | 
						|
                            // box width. Rendering that substring on the current line
 | 
						|
                            // and setting the remainder of the parent string as the next
 | 
						|
                            // string to iterate on for the next line.
 | 
						|
                            const nextLineText = textInCurrentLine.substring(i, textInCurrentLine.length);
 | 
						|
                            textInCurrentLine = nextLineText;
 | 
						|
                            curLineTextWidth = gc.measureText(textInCurrentLine).width;
 | 
						|
                            gc.fillText(curSubString, textX, curY);
 | 
						|
                            curY += textHeight;
 | 
						|
                            // No need to continue iterating after we identified
 | 
						|
                            // an index to break the string on.
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Multiple words in column header. Fitting maximum
 | 
						|
            // number of words possible per line (box width).
 | 
						|
            else {
 | 
						|
                while (wordsInColumn.length !== 0) {
 | 
						|
                    // Processing the next word in the queue.
 | 
						|
                    const curWord = wordsInColumn.shift();
 | 
						|
                    // Joining that word with the existing text for
 | 
						|
                    // the current line.
 | 
						|
                    const incrementedText = [textInCurrentLine, curWord].join(' ');
 | 
						|
                    const incrementedTextWidth = gc.measureText(incrementedText).width;
 | 
						|
                    if (incrementedTextWidth > boxWidth) {
 | 
						|
                        // If the newly combined text has a width larger than
 | 
						|
                        // the box width, we render the line before the current
 | 
						|
                        // word was added. We set the current word as the next
 | 
						|
                        // line.
 | 
						|
                        gc.fillText(textInCurrentLine, textX, curY);
 | 
						|
                        curY += textHeight;
 | 
						|
                        textInCurrentLine = curWord;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        // The combined text hasd a width less than the box width. We
 | 
						|
                        // set the the current line text to be the new combined text.
 | 
						|
                        textInCurrentLine = incrementedText;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            gc.fillText(textInCurrentLine, textX, curY);
 | 
						|
            // Terminating the call here as we don't want
 | 
						|
            // to apply text eliding when wrapping is active.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Elide text that is too long
 | 
						|
        const elide = '\u2026';
 | 
						|
        // Loop until text width fits box or only one character remains
 | 
						|
        while (textWidth > boxWidth && text.length > 1) {
 | 
						|
            // Convert text string to array for dealing with astral symbols
 | 
						|
            const textArr = [...text];
 | 
						|
            if (elideDirection === 'right') {
 | 
						|
                // If text width is substantially bigger, take half the string
 | 
						|
                if (textArr.length > 4 && textWidth >= 2 * boxWidth) {
 | 
						|
                    text =
 | 
						|
                        textArr.slice(0, Math.floor(textArr.length / 2 + 1)).join('') +
 | 
						|
                            elide;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // Otherwise incrementally remove the last character
 | 
						|
                    text = textArr.slice(0, textArr.length - 2).join('') + elide;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                // If text width is substantially bigger, take half the string
 | 
						|
                if (textArr.length > 4 && textWidth >= 2 * boxWidth) {
 | 
						|
                    text = elide + textArr.slice(Math.floor(textArr.length / 2)).join('');
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // Otherwise incrementally remove the last character
 | 
						|
                    text = elide + textArr.slice(2).join('');
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Measure new text width
 | 
						|
            textWidth = gc.measureText(text).width;
 | 
						|
        }
 | 
						|
        // Draw the text for the cell.
 | 
						|
        gc.fillText(text, textX, textY);
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the `TextRenderer` class statics.
 | 
						|
 */
 | 
						|
(function (TextRenderer) {
 | 
						|
    /**
 | 
						|
     * Create a generic text format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new generic text format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `String()` to coerce any value
 | 
						|
     * to a string.
 | 
						|
     */
 | 
						|
    function formatGeneric(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return String(value);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatGeneric = formatGeneric;
 | 
						|
    /**
 | 
						|
     * Create a fixed decimal format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new fixed decimal format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `Number()` and `toFixed()` to
 | 
						|
     * coerce values.
 | 
						|
     *
 | 
						|
     * The `formatIntlNumber()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatFixed(options = {}) {
 | 
						|
        let digits = options.digits;
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return Number(value).toFixed(digits);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatFixed = formatFixed;
 | 
						|
    /**
 | 
						|
     * Create a significant figure format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new significant figure format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `Number()` and `toPrecision()`
 | 
						|
     * to coerce values.
 | 
						|
     *
 | 
						|
     * The `formatIntlNumber()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatPrecision(options = {}) {
 | 
						|
        let digits = options.digits;
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return Number(value).toPrecision(digits);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatPrecision = formatPrecision;
 | 
						|
    /**
 | 
						|
     * Create a scientific notation format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new scientific notation format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `Number()` and `toExponential()`
 | 
						|
     * to coerce values.
 | 
						|
     *
 | 
						|
     * The `formatIntlNumber()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatExponential(options = {}) {
 | 
						|
        let digits = options.digits;
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return Number(value).toExponential(digits);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatExponential = formatExponential;
 | 
						|
    /**
 | 
						|
     * Create an international number format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new international number format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `Intl.NumberFormat` object to
 | 
						|
     * coerce values.
 | 
						|
     *
 | 
						|
     * This is the most flexible (but slowest) number formatter.
 | 
						|
     */
 | 
						|
    function formatIntlNumber(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        let nft = new Intl.NumberFormat(options.locales, options.options);
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return nft.format(value);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatIntlNumber = formatIntlNumber;
 | 
						|
    /**
 | 
						|
     * Create a date format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new date format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses `Date.toDateString()` to format the values.
 | 
						|
     *
 | 
						|
     * If a value is not a `Date` object, `new Date(value)` is used to
 | 
						|
     * coerce the value to a date.
 | 
						|
     *
 | 
						|
     * The `formatIntlDateTime()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatDate(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            if (value instanceof Date) {
 | 
						|
                return value.toDateString();
 | 
						|
            }
 | 
						|
            return new Date(value).toDateString();
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatDate = formatDate;
 | 
						|
    /**
 | 
						|
     * Create a time format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new time format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses `Date.toTimeString()` to format the values.
 | 
						|
     *
 | 
						|
     * If a value is not a `Date` object, `new Date(value)` is used to
 | 
						|
     * coerce the value to a date.
 | 
						|
     *
 | 
						|
     * The `formatIntlDateTime()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatTime(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            if (value instanceof Date) {
 | 
						|
                return value.toTimeString();
 | 
						|
            }
 | 
						|
            return new Date(value).toTimeString();
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatTime = formatTime;
 | 
						|
    /**
 | 
						|
     * Create an ISO datetime format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new ISO datetime format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses `Date.toISOString()` to format the values.
 | 
						|
     *
 | 
						|
     * If a value is not a `Date` object, `new Date(value)` is used to
 | 
						|
     * coerce the value to a date.
 | 
						|
     *
 | 
						|
     * The `formatIntlDateTime()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatISODateTime(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            if (value instanceof Date) {
 | 
						|
                return value.toISOString();
 | 
						|
            }
 | 
						|
            return new Date(value).toISOString();
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatISODateTime = formatISODateTime;
 | 
						|
    /**
 | 
						|
     * Create a UTC datetime format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new UTC datetime format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses `Date.toUTCString()` to format the values.
 | 
						|
     *
 | 
						|
     * If a value is not a `Date` object, `new Date(value)` is used to
 | 
						|
     * coerce the value to a date.
 | 
						|
     *
 | 
						|
     * The `formatIntlDateTime()` formatter is more flexible, but slower.
 | 
						|
     */
 | 
						|
    function formatUTCDateTime(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            if (value instanceof Date) {
 | 
						|
                return value.toUTCString();
 | 
						|
            }
 | 
						|
            return new Date(value).toUTCString();
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatUTCDateTime = formatUTCDateTime;
 | 
						|
    /**
 | 
						|
     * Create an international datetime format function.
 | 
						|
     *
 | 
						|
     * @param options - The options for creating the format function.
 | 
						|
     *
 | 
						|
     * @returns A new international datetime format function.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This formatter uses the builtin `Intl.DateTimeFormat` object to
 | 
						|
     * coerce values.
 | 
						|
     *
 | 
						|
     * This is the most flexible (but slowest) datetime formatter.
 | 
						|
     */
 | 
						|
    function formatIntlDateTime(options = {}) {
 | 
						|
        let missing = options.missing || '';
 | 
						|
        let dtf = new Intl.DateTimeFormat(options.locales, options.options);
 | 
						|
        return ({ value }) => {
 | 
						|
            if (value === null || value === undefined) {
 | 
						|
                return missing;
 | 
						|
            }
 | 
						|
            return dtf.format(value);
 | 
						|
        };
 | 
						|
    }
 | 
						|
    TextRenderer.formatIntlDateTime = formatIntlDateTime;
 | 
						|
    /**
 | 
						|
     * Measure the height of a font.
 | 
						|
     *
 | 
						|
     * @param font - The CSS font string of interest.
 | 
						|
     *
 | 
						|
     * @returns The height of the font bounding box.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This function uses a temporary DOM node to measure the text box
 | 
						|
     * height for the specified font. The first call for a given font
 | 
						|
     * will incur a DOM reflow, but the return value is cached, so any
 | 
						|
     * subsequent call for the same font will return the cached value.
 | 
						|
     */
 | 
						|
    function measureFontHeight(font) {
 | 
						|
        // Look up the cached font height.
 | 
						|
        let height = Private$6.fontHeightCache[font];
 | 
						|
        // Return the cached font height if it exists.
 | 
						|
        if (height !== undefined) {
 | 
						|
            return height;
 | 
						|
        }
 | 
						|
        // Normalize the font.
 | 
						|
        Private$6.fontMeasurementGC.font = font;
 | 
						|
        let normFont = Private$6.fontMeasurementGC.font;
 | 
						|
        // Set the font on the measurement node.
 | 
						|
        Private$6.fontMeasurementNode.style.font = normFont;
 | 
						|
        // Add the measurement node to the document.
 | 
						|
        document.body.appendChild(Private$6.fontMeasurementNode);
 | 
						|
        // Measure the node height.
 | 
						|
        height = Private$6.fontMeasurementNode.offsetHeight;
 | 
						|
        // Remove the measurement node from the document.
 | 
						|
        document.body.removeChild(Private$6.fontMeasurementNode);
 | 
						|
        // Cache the measured height for the font and norm font.
 | 
						|
        Private$6.fontHeightCache[font] = height;
 | 
						|
        Private$6.fontHeightCache[normFont] = height;
 | 
						|
        // Return the measured height.
 | 
						|
        return height;
 | 
						|
    }
 | 
						|
    TextRenderer.measureFontHeight = measureFontHeight;
 | 
						|
})(TextRenderer || (TextRenderer = {}));
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$6;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * A cache of measured font heights.
 | 
						|
     */
 | 
						|
    Private.fontHeightCache = Object.create(null);
 | 
						|
    /**
 | 
						|
     * The DOM node used for font height measurement.
 | 
						|
     */
 | 
						|
    Private.fontMeasurementNode = (() => {
 | 
						|
        let node = document.createElement('div');
 | 
						|
        node.style.position = 'absolute';
 | 
						|
        node.style.top = '-99999px';
 | 
						|
        node.style.left = '-99999px';
 | 
						|
        node.style.visibility = 'hidden';
 | 
						|
        node.textContent = 'M';
 | 
						|
        return node;
 | 
						|
    })();
 | 
						|
    /**
 | 
						|
     * The GC used for font measurement.
 | 
						|
     */
 | 
						|
    Private.fontMeasurementGC = (() => {
 | 
						|
        let canvas = document.createElement('canvas');
 | 
						|
        canvas.width = 0;
 | 
						|
        canvas.height = 0;
 | 
						|
        return canvas.getContext('2d');
 | 
						|
    })();
 | 
						|
})(Private$6 || (Private$6 = {}));
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * A cell renderer which renders data values as text.
 | 
						|
 */
 | 
						|
class HyperlinkRenderer extends TextRenderer {
 | 
						|
    /**
 | 
						|
     * Construct a new text renderer.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the renderer.
 | 
						|
     */
 | 
						|
    constructor(options = {}) {
 | 
						|
        // Set default parameters before passing over the super.
 | 
						|
        options.textColor = options.textColor || 'navy';
 | 
						|
        options.font = options.font || 'bold 12px sans-serif';
 | 
						|
        super(options);
 | 
						|
        this.url = options.url;
 | 
						|
        this.urlName = options.urlName;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the full text to be rendered by the cell.
 | 
						|
     */
 | 
						|
    getText(config) {
 | 
						|
        let urlName = CellRenderer.resolveOption(this.urlName, config);
 | 
						|
        // If we have a friendly URL name, use that.
 | 
						|
        if (urlName) {
 | 
						|
            return this.format({
 | 
						|
                ...config,
 | 
						|
                value: urlName
 | 
						|
            });
 | 
						|
        }
 | 
						|
        // Otherwise use the raw value attribute.
 | 
						|
        return this.format(config);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the text for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawText(gc, config) {
 | 
						|
        // Resolve the font for the cell.
 | 
						|
        let font = CellRenderer.resolveOption(this.font, config);
 | 
						|
        // Bail if there is no font to draw.
 | 
						|
        if (!font) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resolve the text color for the cell.
 | 
						|
        let color = CellRenderer.resolveOption(this.textColor, config);
 | 
						|
        // Bail if there is no text color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        let text = this.getText(config);
 | 
						|
        // Bail if there is no text to draw.
 | 
						|
        if (!text) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resolve the vertical and horizontal alignment.
 | 
						|
        let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config);
 | 
						|
        let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config);
 | 
						|
        // Resolve the elision direction
 | 
						|
        let elideDirection = CellRenderer.resolveOption(this.elideDirection, config);
 | 
						|
        // Resolve the text wrapping flag
 | 
						|
        let wrapText = CellRenderer.resolveOption(this.wrapText, config);
 | 
						|
        // Compute the padded text box height for the specified alignment.
 | 
						|
        let boxHeight = config.height - (vAlign === 'center' ? 1 : 2);
 | 
						|
        // Bail if the text box has no effective size.
 | 
						|
        if (boxHeight <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the text height for the gc font.
 | 
						|
        let textHeight = HyperlinkRenderer.measureFontHeight(font);
 | 
						|
        // Set up the text position variables.
 | 
						|
        let textX;
 | 
						|
        let textY;
 | 
						|
        let boxWidth;
 | 
						|
        // Compute the Y position for the text.
 | 
						|
        switch (vAlign) {
 | 
						|
            case 'top':
 | 
						|
                textY = config.y + 2 + textHeight;
 | 
						|
                break;
 | 
						|
            case 'center':
 | 
						|
                textY = config.y + config.height / 2 + textHeight / 2;
 | 
						|
                break;
 | 
						|
            case 'bottom':
 | 
						|
                textY = config.y + config.height - 2;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Compute the X position for the text.
 | 
						|
        switch (hAlign) {
 | 
						|
            case 'left':
 | 
						|
                textX = config.x + 8;
 | 
						|
                boxWidth = config.width - 14;
 | 
						|
                break;
 | 
						|
            case 'center':
 | 
						|
                textX = config.x + config.width / 2;
 | 
						|
                boxWidth = config.width;
 | 
						|
                break;
 | 
						|
            case 'right':
 | 
						|
                textX = config.x + config.width - 8;
 | 
						|
                boxWidth = config.width - 14;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Clip the cell if the text is taller than the text box height.
 | 
						|
        if (textHeight > boxHeight) {
 | 
						|
            gc.beginPath();
 | 
						|
            gc.rect(config.x, config.y, config.width, config.height - 1);
 | 
						|
            gc.clip();
 | 
						|
        }
 | 
						|
        // Set the gc state.
 | 
						|
        gc.font = font;
 | 
						|
        gc.fillStyle = color;
 | 
						|
        gc.textAlign = hAlign;
 | 
						|
        gc.textBaseline = 'bottom';
 | 
						|
        // Terminate call here if we're not eliding or wrapping text
 | 
						|
        if (elideDirection === 'none' && !wrapText) {
 | 
						|
            gc.fillText(text, textX, textY);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // The current text width in pixels.
 | 
						|
        let textWidth = gc.measureText(text).width;
 | 
						|
        // Apply text wrapping if enabled.
 | 
						|
        if (wrapText && textWidth > boxWidth) {
 | 
						|
            // Make sure box clipping happens.
 | 
						|
            gc.beginPath();
 | 
						|
            gc.rect(config.x, config.y, config.width, config.height - 1);
 | 
						|
            gc.clip();
 | 
						|
            // Split column name to words based on
 | 
						|
            // whitespace preceding a word boundary.
 | 
						|
            // "Hello  world" --> ["Hello  ", "world"]
 | 
						|
            const wordsInColumn = text.split(/\s(?=\b)/);
 | 
						|
            // Y-coordinate offset for any additional lines
 | 
						|
            let curY = textY;
 | 
						|
            let textInCurrentLine = wordsInColumn.shift();
 | 
						|
            // Single word. Applying text wrap on word by splitting
 | 
						|
            // it into characters and fitting the maximum number of
 | 
						|
            // characters possible per line (box width).
 | 
						|
            if (wordsInColumn.length === 0) {
 | 
						|
                let curLineTextWidth = gc.measureText(textInCurrentLine).width;
 | 
						|
                while (curLineTextWidth > boxWidth && textInCurrentLine !== '') {
 | 
						|
                    // Iterating from the end of the string until we find a
 | 
						|
                    // substring (0,i) which has a width less than the box width.
 | 
						|
                    for (let i = textInCurrentLine.length; i > 0; i--) {
 | 
						|
                        const curSubString = textInCurrentLine.substring(0, i);
 | 
						|
                        const curSubStringWidth = gc.measureText(curSubString).width;
 | 
						|
                        if (curSubStringWidth < boxWidth || curSubString.length === 1) {
 | 
						|
                            // Found a substring which has a width less than the current
 | 
						|
                            // box width. Rendering that substring on the current line
 | 
						|
                            // and setting the remainder of the parent string as the next
 | 
						|
                            // string to iterate on for the next line.
 | 
						|
                            const nextLineText = textInCurrentLine.substring(i, textInCurrentLine.length);
 | 
						|
                            textInCurrentLine = nextLineText;
 | 
						|
                            curLineTextWidth = gc.measureText(textInCurrentLine).width;
 | 
						|
                            gc.fillText(curSubString, textX, curY);
 | 
						|
                            curY += textHeight;
 | 
						|
                            // No need to continue iterating after we identified
 | 
						|
                            // an index to break the string on.
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Multiple words in column header. Fitting maximum
 | 
						|
            // number of words possible per line (box width).
 | 
						|
            else {
 | 
						|
                while (wordsInColumn.length !== 0) {
 | 
						|
                    // Processing the next word in the queue.
 | 
						|
                    const curWord = wordsInColumn.shift();
 | 
						|
                    // Joining that word with the existing text for
 | 
						|
                    // the current line.
 | 
						|
                    const incrementedText = [textInCurrentLine, curWord].join(' ');
 | 
						|
                    const incrementedTextWidth = gc.measureText(incrementedText).width;
 | 
						|
                    if (incrementedTextWidth > boxWidth) {
 | 
						|
                        // If the newly combined text has a width larger than
 | 
						|
                        // the box width, we render the line before the current
 | 
						|
                        // word was added. We set the current word as the next
 | 
						|
                        // line.
 | 
						|
                        gc.fillText(textInCurrentLine, textX, curY);
 | 
						|
                        curY += textHeight;
 | 
						|
                        textInCurrentLine = curWord;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        // The combined text hasd a width less than the box width. We
 | 
						|
                        // set the the current line text to be the new combined text.
 | 
						|
                        textInCurrentLine = incrementedText;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            gc.fillText(textInCurrentLine, textX, curY);
 | 
						|
            // Terminating the call here as we don't want
 | 
						|
            // to apply text eliding when wrapping is active.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Elide text that is too long
 | 
						|
        let elide = '\u2026';
 | 
						|
        // Compute elided text
 | 
						|
        if (elideDirection === 'right') {
 | 
						|
            while (textWidth > boxWidth && text.length > 1) {
 | 
						|
                if (text.length > 4 && textWidth >= 2 * boxWidth) {
 | 
						|
                    // If text width is substantially bigger, take half the string
 | 
						|
                    text = text.substring(0, text.length / 2 + 1) + elide;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // Otherwise incrementally remove the last character
 | 
						|
                    text = text.substring(0, text.length - 2) + elide;
 | 
						|
                }
 | 
						|
                textWidth = gc.measureText(text).width;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            while (textWidth > boxWidth && text.length > 1) {
 | 
						|
                if (text.length > 4 && textWidth >= 2 * boxWidth) {
 | 
						|
                    // If text width is substantially bigger, take half the string
 | 
						|
                    text = elide + text.substring(text.length / 2);
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // Otherwise incrementally remove the last character
 | 
						|
                    text = elide + text.substring(2);
 | 
						|
                }
 | 
						|
                textWidth = gc.measureText(text).width;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Draw the text for the cell.
 | 
						|
        gc.fillText(text, textX, textY);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) Jupyter Development Team.
 | 
						|
 * Distributed under the terms of the Modified BSD License.
 | 
						|
 */
 | 
						|
/**
 | 
						|
 * A collection of helper functions relating to merged cell groups
 | 
						|
 */
 | 
						|
var CellGroup;
 | 
						|
(function (CellGroup) {
 | 
						|
    /**
 | 
						|
     * Checks if two cell-groups are intersecting
 | 
						|
     * in the given axis.
 | 
						|
     * @param group1
 | 
						|
     * @param group2
 | 
						|
     * @param axis
 | 
						|
     */
 | 
						|
    function areCellGroupsIntersectingAtAxis(group1, group2, axis) {
 | 
						|
        if (axis === 'row') {
 | 
						|
            return ((group1.r1 >= group2.r1 && group1.r1 <= group2.r2) ||
 | 
						|
                (group1.r2 >= group2.r1 && group1.r2 <= group2.r2) ||
 | 
						|
                (group2.r1 >= group1.r1 && group2.r1 <= group1.r2) ||
 | 
						|
                (group2.r2 >= group1.r1 && group2.r2 <= group1.r2));
 | 
						|
        }
 | 
						|
        return ((group1.c1 >= group2.c1 && group1.c1 <= group2.c2) ||
 | 
						|
            (group1.c2 >= group2.c1 && group1.c2 <= group2.c2) ||
 | 
						|
            (group2.c1 >= group1.c1 && group2.c1 <= group1.c2) ||
 | 
						|
            (group2.c2 >= group1.c1 && group2.c2 <= group1.c2));
 | 
						|
    }
 | 
						|
    CellGroup.areCellGroupsIntersectingAtAxis = areCellGroupsIntersectingAtAxis;
 | 
						|
    /**
 | 
						|
     * Checks if cell-groups are intersecting.
 | 
						|
     * @param group1
 | 
						|
     * @param group2
 | 
						|
     */
 | 
						|
    function areCellGroupsIntersecting(group1, group2) {
 | 
						|
        return (((group1.r1 >= group2.r1 && group1.r1 <= group2.r2) ||
 | 
						|
            (group1.r2 >= group2.r1 && group1.r2 <= group2.r2) ||
 | 
						|
            (group2.r1 >= group1.r1 && group2.r1 <= group1.r2) ||
 | 
						|
            (group2.r2 >= group1.r1 && group2.r2 <= group1.r2)) &&
 | 
						|
            ((group1.c1 >= group2.c1 && group1.c1 <= group2.c2) ||
 | 
						|
                (group1.c2 >= group2.c1 && group1.c2 <= group2.c2) ||
 | 
						|
                (group2.c1 >= group1.c1 && group2.c1 <= group1.c2) ||
 | 
						|
                (group2.c2 >= group1.c1 && group2.c2 <= group1.c2)));
 | 
						|
    }
 | 
						|
    CellGroup.areCellGroupsIntersecting = areCellGroupsIntersecting;
 | 
						|
    /**
 | 
						|
     * Retrieves the index of the cell-group to which
 | 
						|
     * the cell at the given row, column belongs.
 | 
						|
     * @param dataModel
 | 
						|
     * @param rgn
 | 
						|
     * @param row
 | 
						|
     * @param column
 | 
						|
     */
 | 
						|
    function getGroupIndex(dataModel, rgn, row, column) {
 | 
						|
        const numGroups = dataModel.groupCount(rgn);
 | 
						|
        for (let i = 0; i < numGroups; i++) {
 | 
						|
            const group = dataModel.group(rgn, i);
 | 
						|
            if (row >= group.r1 &&
 | 
						|
                row <= group.r2 &&
 | 
						|
                column >= group.c1 &&
 | 
						|
                column <= group.c2) {
 | 
						|
                return i;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    CellGroup.getGroupIndex = getGroupIndex;
 | 
						|
    /**
 | 
						|
     * Returns a cell-group for the given row/index coordinates.
 | 
						|
     * @param dataModel
 | 
						|
     * @param rgn
 | 
						|
     * @param row
 | 
						|
     * @param column
 | 
						|
     */
 | 
						|
    function getGroup(dataModel, rgn, row, column) {
 | 
						|
        const groupIndex = getGroupIndex(dataModel, rgn, row, column);
 | 
						|
        if (groupIndex === -1) {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        return dataModel.group(rgn, groupIndex);
 | 
						|
    }
 | 
						|
    CellGroup.getGroup = getGroup;
 | 
						|
    /**
 | 
						|
     * Returns all cell groups which belong to
 | 
						|
     * a given cell cell region.
 | 
						|
     * @param dataModel
 | 
						|
     * @param rgn
 | 
						|
     */
 | 
						|
    function getCellGroupsAtRegion(dataModel, rgn) {
 | 
						|
        let groupsAtRegion = [];
 | 
						|
        const numGroups = dataModel.groupCount(rgn);
 | 
						|
        for (let i = 0; i < numGroups; i++) {
 | 
						|
            const group = dataModel.group(rgn, i);
 | 
						|
            groupsAtRegion.push(group);
 | 
						|
        }
 | 
						|
        return groupsAtRegion;
 | 
						|
    }
 | 
						|
    CellGroup.getCellGroupsAtRegion = getCellGroupsAtRegion;
 | 
						|
    /**
 | 
						|
     * Calculates and returns a merged cell-group from
 | 
						|
     * two cell-group objects.
 | 
						|
     * @param groups
 | 
						|
     */
 | 
						|
    function joinCellGroups(groups) {
 | 
						|
        let startRow = Number.MAX_VALUE;
 | 
						|
        let endRow = Number.MIN_VALUE;
 | 
						|
        let startColumn = Number.MAX_VALUE;
 | 
						|
        let endColumn = Number.MIN_VALUE;
 | 
						|
        for (const group of groups) {
 | 
						|
            startRow = Math.min(startRow, group.r1);
 | 
						|
            endRow = Math.max(endRow, group.r2);
 | 
						|
            startColumn = Math.min(startColumn, group.c1);
 | 
						|
            endColumn = Math.max(endColumn, group.c2);
 | 
						|
        }
 | 
						|
        return { r1: startRow, r2: endRow, c1: startColumn, c2: endColumn };
 | 
						|
    }
 | 
						|
    CellGroup.joinCellGroups = joinCellGroups;
 | 
						|
    /**
 | 
						|
     * Merges a cell group with other cells groups in the
 | 
						|
     * same region if they intersect.
 | 
						|
     * @param dataModel the data model of the grid.
 | 
						|
     * @param group the target cell group.
 | 
						|
     * @param region the region of the cell group.
 | 
						|
     * @returns a new cell group after merging has happened.
 | 
						|
     */
 | 
						|
    function joinCellGroupWithMergedCellGroups(dataModel, group, region) {
 | 
						|
        let joinedGroup = { ...group };
 | 
						|
        const mergedCellGroups = getCellGroupsAtRegion(dataModel, region);
 | 
						|
        for (let g = 0; g < mergedCellGroups.length; g++) {
 | 
						|
            const mergedGroup = mergedCellGroups[g];
 | 
						|
            if (areCellGroupsIntersecting(joinedGroup, mergedGroup)) {
 | 
						|
                joinedGroup = joinCellGroups([joinedGroup, mergedGroup]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return joinedGroup;
 | 
						|
    }
 | 
						|
    CellGroup.joinCellGroupWithMergedCellGroups = joinCellGroupWithMergedCellGroups;
 | 
						|
    /**
 | 
						|
     * Retrieves a list of cell groups intersecting at
 | 
						|
     * a given row.
 | 
						|
     * @param dataModel data model of the grid.
 | 
						|
     * @param rgn the cell region.
 | 
						|
     * @param row the target row to look for intersections at.
 | 
						|
     * @returns all cell groups intersecting with the row.
 | 
						|
     */
 | 
						|
    function getCellGroupsAtRow(dataModel, rgn, row) {
 | 
						|
        let groupsAtRow = [];
 | 
						|
        const numGroups = dataModel.groupCount(rgn);
 | 
						|
        for (let i = 0; i < numGroups; i++) {
 | 
						|
            const group = dataModel.group(rgn, i);
 | 
						|
            if (row >= group.r1 && row <= group.r2) {
 | 
						|
                groupsAtRow.push(group);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return groupsAtRow;
 | 
						|
    }
 | 
						|
    CellGroup.getCellGroupsAtRow = getCellGroupsAtRow;
 | 
						|
    /**
 | 
						|
     * Retrieves a list of cell groups intersecting at
 | 
						|
     * a given column.
 | 
						|
     * @param dataModel data model of the grid.
 | 
						|
     * @param rgn the cell region.
 | 
						|
     * @param column the target column to look for intersections at.
 | 
						|
     * @returns all cell groups intersecting with the column.
 | 
						|
     */
 | 
						|
    function getCellGroupsAtColumn(dataModel, rgn, column) {
 | 
						|
        let groupsAtColumn = [];
 | 
						|
        const numGroups = dataModel.groupCount(rgn);
 | 
						|
        for (let i = 0; i < numGroups; i++) {
 | 
						|
            const group = dataModel.group(rgn, i);
 | 
						|
            if (column >= group.c1 && column <= group.c2) {
 | 
						|
                groupsAtColumn.push(group);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return groupsAtColumn;
 | 
						|
    }
 | 
						|
    CellGroup.getCellGroupsAtColumn = getCellGroupsAtColumn;
 | 
						|
    /**
 | 
						|
     * Merges a target cell group with any cell groups
 | 
						|
     * it intersects with at a given row or column.
 | 
						|
     * @param dataModel data model of the grid.
 | 
						|
     * @param regions list of cell regions.
 | 
						|
     * @param axis row or column.
 | 
						|
     * @param group the target cell group.
 | 
						|
     * @returns a new merged cell group.
 | 
						|
     */
 | 
						|
    function joinCellGroupsIntersectingAtAxis(dataModel, regions, axis, group) {
 | 
						|
        let groupsAtAxis = [];
 | 
						|
        if (axis === 'row') {
 | 
						|
            for (const region of regions) {
 | 
						|
                for (let r = group.r1; r <= group.r2; r++) {
 | 
						|
                    groupsAtAxis = groupsAtAxis.concat(CellGroup.getCellGroupsAtRow(dataModel, region, r));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            for (const region of regions) {
 | 
						|
                for (let c = group.c1; c <= group.c2; c++) {
 | 
						|
                    groupsAtAxis = groupsAtAxis.concat(CellGroup.getCellGroupsAtColumn(dataModel, region, c));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        let mergedGroupAtAxis = CellGroup.joinCellGroups(groupsAtAxis);
 | 
						|
        if (groupsAtAxis.length > 0) {
 | 
						|
            let mergedCellGroups = [];
 | 
						|
            for (const region of regions) {
 | 
						|
                mergedCellGroups = mergedCellGroups.concat(CellGroup.getCellGroupsAtRegion(dataModel, region));
 | 
						|
            }
 | 
						|
            for (let g = 0; g < mergedCellGroups.length; g++) {
 | 
						|
                const group = mergedCellGroups[g];
 | 
						|
                if (CellGroup.areCellGroupsIntersectingAtAxis(mergedGroupAtAxis, group, axis)) {
 | 
						|
                    mergedGroupAtAxis = CellGroup.joinCellGroups([
 | 
						|
                        group,
 | 
						|
                        mergedGroupAtAxis
 | 
						|
                    ]);
 | 
						|
                    mergedCellGroups.splice(g, 1);
 | 
						|
                    g = 0;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return mergedGroupAtAxis;
 | 
						|
    }
 | 
						|
    CellGroup.joinCellGroupsIntersectingAtAxis = joinCellGroupsIntersectingAtAxis;
 | 
						|
})(CellGroup || (CellGroup = {}));
 | 
						|
 | 
						|
/**
 | 
						|
 * A basic implementation of a data grid mouse handler.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This class may be subclassed and customized as needed.
 | 
						|
 */
 | 
						|
class BasicMouseHandler {
 | 
						|
    constructor() {
 | 
						|
        this._disposed = false;
 | 
						|
        this._pressData = null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by the mouse handler.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        // Bail early if the handler is already disposed.
 | 
						|
        if (this._disposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Release any held resources.
 | 
						|
        this.release();
 | 
						|
        // Mark the handler as disposed.
 | 
						|
        this._disposed = true;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the mouse handler is disposed.
 | 
						|
     */
 | 
						|
    get isDisposed() {
 | 
						|
        return this._disposed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Release the resources held by the handler.
 | 
						|
     */
 | 
						|
    release() {
 | 
						|
        // Bail early if the is no press data.
 | 
						|
        if (!this._pressData) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clear the autoselect timeout.
 | 
						|
        if (this._pressData.type === 'select') {
 | 
						|
            this._pressData.timeout = -1;
 | 
						|
        }
 | 
						|
        // Clear the press data.
 | 
						|
        this._pressData.override.dispose();
 | 
						|
        this._pressData = null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse hover event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse hover event of interest.
 | 
						|
     */
 | 
						|
    onMouseHover(grid, event) {
 | 
						|
        // Hit test the grid.
 | 
						|
        let hit = grid.hitTest(event.clientX, event.clientY);
 | 
						|
        // Get the resize handle for the hit test.
 | 
						|
        let handle = Private$5.resizeHandleForHitTest(hit);
 | 
						|
        // Fetch the cursor for the handle.
 | 
						|
        let cursor = this.cursorForHandle(handle);
 | 
						|
        // Hyperlink logic.
 | 
						|
        const config = Private$5.createCellConfigObject(grid, hit);
 | 
						|
        if (config) {
 | 
						|
            // Retrieve renderer for hovered cell.
 | 
						|
            const renderer = grid.cellRenderers.get(config);
 | 
						|
            if (renderer instanceof HyperlinkRenderer) {
 | 
						|
                cursor = this.cursorForHandle('hyperlink');
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Update the viewport cursor based on the part.
 | 
						|
        grid.viewport.node.style.cursor = cursor;
 | 
						|
        // TODO support user-defined hover items
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse leave event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse hover event of interest.
 | 
						|
     */
 | 
						|
    onMouseLeave(grid, event) {
 | 
						|
        // TODO support user-defined hover popups.
 | 
						|
        // Clear the viewport cursor.
 | 
						|
        grid.viewport.node.style.cursor = '';
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse down event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse down event of interest.
 | 
						|
     */
 | 
						|
    onMouseDown(grid, event) {
 | 
						|
        // Unpack the event.
 | 
						|
        let { clientX, clientY } = event;
 | 
						|
        // Hit test the grid.
 | 
						|
        let hit = grid.hitTest(clientX, clientY);
 | 
						|
        // Unpack the hit test.
 | 
						|
        const { region, row, column } = hit;
 | 
						|
        // Bail if the hit test is on an uninteresting region.
 | 
						|
        if (region === 'void') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the modifier flags.
 | 
						|
        let shift = event.shiftKey;
 | 
						|
        let accel = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event);
 | 
						|
        // Hyperlink logic.
 | 
						|
        if (grid) {
 | 
						|
            // Create cell config object.
 | 
						|
            const config = Private$5.createCellConfigObject(grid, hit);
 | 
						|
            // Retrieve cell renderer.
 | 
						|
            let renderer = grid.cellRenderers.get(config);
 | 
						|
            // Only process hyperlink renderers.
 | 
						|
            if (renderer instanceof HyperlinkRenderer) {
 | 
						|
                // Use the url param if it exists.
 | 
						|
                let url = CellRenderer.resolveOption(renderer.url, config);
 | 
						|
                // Otherwise assume cell value is the URL.
 | 
						|
                if (!url) {
 | 
						|
                    const format = TextRenderer.formatGeneric();
 | 
						|
                    url = format(config);
 | 
						|
                }
 | 
						|
                // Open the hyperlink only if user hit Ctrl+Click.
 | 
						|
                if (accel) {
 | 
						|
                    window.open(url);
 | 
						|
                    // Reset cursor default after clicking
 | 
						|
                    const cursor = this.cursorForHandle('none');
 | 
						|
                    grid.viewport.node.style.cursor = cursor;
 | 
						|
                    // Not applying selections if navigating away.
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If the hit test is the body region, the only option is select.
 | 
						|
        if (region === 'body') {
 | 
						|
            // Fetch the selection model.
 | 
						|
            let model = grid.selectionModel;
 | 
						|
            // Bail early if there is no selection model.
 | 
						|
            if (!model) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            // Override the document cursor.
 | 
						|
            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor('default');
 | 
						|
            // Set up the press data.
 | 
						|
            this._pressData = {
 | 
						|
                type: 'select',
 | 
						|
                region,
 | 
						|
                row,
 | 
						|
                column,
 | 
						|
                override,
 | 
						|
                localX: -1,
 | 
						|
                localY: -1,
 | 
						|
                timeout: -1
 | 
						|
            };
 | 
						|
            // Set up the selection variables.
 | 
						|
            let r1;
 | 
						|
            let c1;
 | 
						|
            let r2;
 | 
						|
            let c2;
 | 
						|
            let cursorRow;
 | 
						|
            let cursorColumn;
 | 
						|
            let clear;
 | 
						|
            // Accel == new selection, keep old selections.
 | 
						|
            if (accel) {
 | 
						|
                r1 = row;
 | 
						|
                r2 = row;
 | 
						|
                c1 = column;
 | 
						|
                c2 = column;
 | 
						|
                cursorRow = row;
 | 
						|
                cursorColumn = column;
 | 
						|
                clear = 'none';
 | 
						|
            }
 | 
						|
            else if (shift) {
 | 
						|
                r1 = model.cursorRow;
 | 
						|
                r2 = row;
 | 
						|
                c1 = model.cursorColumn;
 | 
						|
                c2 = column;
 | 
						|
                cursorRow = model.cursorRow;
 | 
						|
                cursorColumn = model.cursorColumn;
 | 
						|
                clear = 'current';
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                r1 = row;
 | 
						|
                r2 = row;
 | 
						|
                c1 = column;
 | 
						|
                c2 = column;
 | 
						|
                cursorRow = row;
 | 
						|
                cursorColumn = column;
 | 
						|
                clear = 'all';
 | 
						|
            }
 | 
						|
            // Make the selection.
 | 
						|
            model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });
 | 
						|
            // Done.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Otherwise, the hit test is on a header region.
 | 
						|
        // Convert the hit test into a part.
 | 
						|
        let handle = Private$5.resizeHandleForHitTest(hit);
 | 
						|
        // Fetch the cursor for the handle.
 | 
						|
        let cursor = this.cursorForHandle(handle);
 | 
						|
        // Handle horizontal resize.
 | 
						|
        if (handle === 'left' || handle === 'right') {
 | 
						|
            // Set up the resize data type.
 | 
						|
            const type = 'column-resize';
 | 
						|
            // Determine the column region.
 | 
						|
            let rgn = region === 'column-header' ? 'body' : 'row-header';
 | 
						|
            // Determine the section index.
 | 
						|
            let index = handle === 'left' ? column - 1 : column;
 | 
						|
            // Fetch the section size.
 | 
						|
            let size = grid.columnSize(rgn, index);
 | 
						|
            // Override the document cursor.
 | 
						|
            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor(cursor);
 | 
						|
            // Create the temporary press data.
 | 
						|
            this._pressData = { type, region: rgn, index, size, clientX, override };
 | 
						|
            // Done.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle vertical resize
 | 
						|
        if (handle === 'top' || handle === 'bottom') {
 | 
						|
            // Set up the resize data type.
 | 
						|
            const type = 'row-resize';
 | 
						|
            // Determine the row region.
 | 
						|
            let rgn = region === 'row-header' ? 'body' : 'column-header';
 | 
						|
            // Determine the section index.
 | 
						|
            let index = handle === 'top' ? row - 1 : row;
 | 
						|
            // Fetch the section size.
 | 
						|
            let size = grid.rowSize(rgn, index);
 | 
						|
            // Override the document cursor.
 | 
						|
            let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor(cursor);
 | 
						|
            // Create the temporary press data.
 | 
						|
            this._pressData = { type, region: rgn, index, size, clientY, override };
 | 
						|
            // Done.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Otherwise, the only option is select.
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Bail if there is no selection model.
 | 
						|
        if (!model) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Override the document cursor.
 | 
						|
        let override = _lumino_dragdrop__WEBPACK_IMPORTED_MODULE_2__.Drag.overrideCursor('default');
 | 
						|
        // Set up the press data.
 | 
						|
        this._pressData = {
 | 
						|
            type: 'select',
 | 
						|
            region,
 | 
						|
            row,
 | 
						|
            column,
 | 
						|
            override,
 | 
						|
            localX: -1,
 | 
						|
            localY: -1,
 | 
						|
            timeout: -1
 | 
						|
        };
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let c1;
 | 
						|
        let r2;
 | 
						|
        let c2;
 | 
						|
        let cursorRow;
 | 
						|
        let cursorColumn;
 | 
						|
        let clear;
 | 
						|
        // Compute the selection based on the pressed region.
 | 
						|
        if (region === 'corner-header') {
 | 
						|
            r1 = 0;
 | 
						|
            r2 = Infinity;
 | 
						|
            c1 = 0;
 | 
						|
            c2 = Infinity;
 | 
						|
            cursorRow = accel ? 0 : shift ? model.cursorRow : 0;
 | 
						|
            cursorColumn = accel ? 0 : shift ? model.cursorColumn : 0;
 | 
						|
            clear = accel ? 'none' : shift ? 'current' : 'all';
 | 
						|
        }
 | 
						|
        else if (region === 'row-header') {
 | 
						|
            r1 = accel ? row : shift ? model.cursorRow : row;
 | 
						|
            r2 = row;
 | 
						|
            const selectionGroup = { r1: r1, c1: 0, r2: r2, c2: 0 };
 | 
						|
            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['row-header', 'body'], 'row', selectionGroup);
 | 
						|
            // Check if there are any merges
 | 
						|
            if (joinedGroup.r1 != Number.MAX_VALUE) {
 | 
						|
                r1 = joinedGroup.r1;
 | 
						|
                r2 = joinedGroup.r2;
 | 
						|
            }
 | 
						|
            c1 = 0;
 | 
						|
            c2 = Infinity;
 | 
						|
            cursorRow = accel ? row : shift ? model.cursorRow : row;
 | 
						|
            cursorColumn = accel ? 0 : shift ? model.cursorColumn : 0;
 | 
						|
            clear = accel ? 'none' : shift ? 'current' : 'all';
 | 
						|
        }
 | 
						|
        else if (region === 'column-header') {
 | 
						|
            r1 = 0;
 | 
						|
            r2 = Infinity;
 | 
						|
            c1 = accel ? column : shift ? model.cursorColumn : column;
 | 
						|
            c2 = column;
 | 
						|
            const selectionGroup = { r1: 0, c1: c1, r2: 0, c2: c2 };
 | 
						|
            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['column-header', 'body'], 'column', selectionGroup);
 | 
						|
            // Check if there are any merges
 | 
						|
            if (joinedGroup.c1 != Number.MAX_VALUE) {
 | 
						|
                c1 = joinedGroup.c1;
 | 
						|
                c2 = joinedGroup.c2;
 | 
						|
            }
 | 
						|
            cursorRow = accel ? 0 : shift ? model.cursorRow : 0;
 | 
						|
            cursorColumn = accel ? column : shift ? model.cursorColumn : column;
 | 
						|
            clear = accel ? 'none' : shift ? 'current' : 'all';
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = accel ? row : shift ? model.cursorRow : row;
 | 
						|
            r2 = row;
 | 
						|
            c1 = accel ? column : shift ? model.cursorColumn : column;
 | 
						|
            c2 = column;
 | 
						|
            cursorRow = accel ? row : shift ? model.cursorRow : row;
 | 
						|
            cursorColumn = accel ? column : shift ? model.cursorColumn : column;
 | 
						|
            clear = accel ? 'none' : shift ? 'current' : 'all';
 | 
						|
        }
 | 
						|
        // Make the selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse move event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse move event of interest.
 | 
						|
     */
 | 
						|
    onMouseMove(grid, event) {
 | 
						|
        // Fetch the press data.
 | 
						|
        const data = this._pressData;
 | 
						|
        // Bail early if there is no press data.
 | 
						|
        if (!data) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle a row resize.
 | 
						|
        if (data.type === 'row-resize') {
 | 
						|
            let dy = event.clientY - data.clientY;
 | 
						|
            grid.resizeRow(data.region, data.index, data.size + dy);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle a column resize.
 | 
						|
        if (data.type === 'column-resize') {
 | 
						|
            let dx = event.clientX - data.clientX;
 | 
						|
            grid.resizeColumn(data.region, data.index, data.size + dx);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Otherwise, it's a select.
 | 
						|
        // Mouse moves during a corner header press are a no-op.
 | 
						|
        if (data.region === 'corner-header') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Bail early if the selection model was removed.
 | 
						|
        if (!model) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Map to local coordinates.
 | 
						|
        let { lx, ly } = grid.mapToLocal(event.clientX, event.clientY);
 | 
						|
        // Update the local mouse coordinates in the press data.
 | 
						|
        data.localX = lx;
 | 
						|
        data.localY = ly;
 | 
						|
        // Fetch the grid geometry.
 | 
						|
        let hw = grid.headerWidth;
 | 
						|
        let hh = grid.headerHeight;
 | 
						|
        let vpw = grid.viewportWidth;
 | 
						|
        let vph = grid.viewportHeight;
 | 
						|
        let sx = grid.scrollX;
 | 
						|
        let sy = grid.scrollY;
 | 
						|
        let msx = grid.maxScrollY;
 | 
						|
        let msy = grid.maxScrollY;
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Set up the timeout variable.
 | 
						|
        let timeout = -1;
 | 
						|
        // Compute the timemout based on hit region and mouse position.
 | 
						|
        if (data.region === 'row-header' || mode === 'row') {
 | 
						|
            if (ly < hh && sy > 0) {
 | 
						|
                timeout = Private$5.computeTimeout(hh - ly);
 | 
						|
            }
 | 
						|
            else if (ly >= vph && sy < msy) {
 | 
						|
                timeout = Private$5.computeTimeout(ly - vph);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (data.region === 'column-header' || mode === 'column') {
 | 
						|
            if (lx < hw && sx > 0) {
 | 
						|
                timeout = Private$5.computeTimeout(hw - lx);
 | 
						|
            }
 | 
						|
            else if (lx >= vpw && sx < msx) {
 | 
						|
                timeout = Private$5.computeTimeout(lx - vpw);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            if (lx < hw && sx > 0) {
 | 
						|
                timeout = Private$5.computeTimeout(hw - lx);
 | 
						|
            }
 | 
						|
            else if (lx >= vpw && sx < msx) {
 | 
						|
                timeout = Private$5.computeTimeout(lx - vpw);
 | 
						|
            }
 | 
						|
            else if (ly < hh && sy > 0) {
 | 
						|
                timeout = Private$5.computeTimeout(hh - ly);
 | 
						|
            }
 | 
						|
            else if (ly >= vph && sy < msy) {
 | 
						|
                timeout = Private$5.computeTimeout(ly - vph);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Update or initiate the autoselect if needed.
 | 
						|
        if (timeout >= 0) {
 | 
						|
            if (data.timeout < 0) {
 | 
						|
                data.timeout = timeout;
 | 
						|
                setTimeout(() => {
 | 
						|
                    Private$5.autoselect(grid, data);
 | 
						|
                }, timeout);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                data.timeout = timeout;
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Otherwise, clear the autoselect timeout.
 | 
						|
        data.timeout = -1;
 | 
						|
        // Map the position to virtual coordinates.
 | 
						|
        let { vx, vy } = grid.mapToVirtual(event.clientX, event.clientY);
 | 
						|
        // Clamp the coordinates to the limits.
 | 
						|
        vx = Math.max(0, Math.min(vx, grid.bodyWidth - 1));
 | 
						|
        vy = Math.max(0, Math.min(vy, grid.bodyHeight - 1));
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1;
 | 
						|
        let c1;
 | 
						|
        let r2;
 | 
						|
        let c2;
 | 
						|
        let cursorRow = model.cursorRow;
 | 
						|
        let cursorColumn = model.cursorColumn;
 | 
						|
        let clear = 'current';
 | 
						|
        // Compute the selection based pressed region.
 | 
						|
        if (data.region === 'row-header' || mode === 'row') {
 | 
						|
            r1 = data.row;
 | 
						|
            r2 = grid.rowAt('body', vy);
 | 
						|
            const selectionGroup = { r1: r1, c1: 0, r2: r2, c2: 0 };
 | 
						|
            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['row-header', 'body'], 'row', selectionGroup);
 | 
						|
            // Check if there are any merges
 | 
						|
            if (joinedGroup.r1 != Number.MAX_VALUE) {
 | 
						|
                r1 = Math.min(r1, joinedGroup.r1);
 | 
						|
                r2 = Math.max(r2, joinedGroup.r2);
 | 
						|
            }
 | 
						|
            c1 = 0;
 | 
						|
            c2 = Infinity;
 | 
						|
        }
 | 
						|
        else if (data.region === 'column-header' || mode === 'column') {
 | 
						|
            r1 = 0;
 | 
						|
            r2 = Infinity;
 | 
						|
            c1 = data.column;
 | 
						|
            c2 = grid.columnAt('body', vx);
 | 
						|
            const selectionGroup = { r1: 0, c1: c1, r2: 0, c2: c2 };
 | 
						|
            const joinedGroup = CellGroup.joinCellGroupsIntersectingAtAxis(grid.dataModel, ['column-header', 'body'], 'column', selectionGroup);
 | 
						|
            // Check if there are any merges
 | 
						|
            if (joinedGroup.c1 != Number.MAX_VALUE) {
 | 
						|
                c1 = joinedGroup.c1;
 | 
						|
                c2 = joinedGroup.c2;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r1 = cursorRow;
 | 
						|
            r2 = grid.rowAt('body', vy);
 | 
						|
            c1 = cursorColumn;
 | 
						|
            c2 = grid.columnAt('body', vx);
 | 
						|
        }
 | 
						|
        // Make the selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse up event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse up event of interest.
 | 
						|
     */
 | 
						|
    onMouseUp(grid, event) {
 | 
						|
        this.release();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the mouse double click event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The mouse up event of interest.
 | 
						|
     */
 | 
						|
    onMouseDoubleClick(grid, event) {
 | 
						|
        var _a, _b, _c;
 | 
						|
        if (!grid.dataModel) {
 | 
						|
            this.release();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Unpack the event.
 | 
						|
        let { clientX, clientY } = event;
 | 
						|
        // Hit test the grid.
 | 
						|
        let hit = grid.hitTest(clientX, clientY);
 | 
						|
        // Unpack the hit test.
 | 
						|
        let { region, row, column } = hit;
 | 
						|
        if (region === 'void') {
 | 
						|
            this.release();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (region === 'column-header' || region === 'corner-header') {
 | 
						|
            // Convert the hit test into a part.
 | 
						|
            const handle = Private$5.resizeHandleForHitTest(hit);
 | 
						|
            if (handle === 'left' || handle === 'right') {
 | 
						|
                let colIndex = handle === 'left' ? column - 1 : column;
 | 
						|
                let colRegion = region === 'column-header' ? 'body' : 'row-header';
 | 
						|
                if (colIndex < 0) {
 | 
						|
                    if (region === 'column-header') {
 | 
						|
                        // If the column is -1, it means we are in the corner header
 | 
						|
                        colIndex = grid.dataModel.columnCount('row-header') - 1;
 | 
						|
                        colRegion = 'row-header';
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        // If we are on the left edge of the row header, do nothing
 | 
						|
                        return;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                const cs = (_a = grid.selectionModel) === null || _a === void 0 ? void 0 : _a.currentSelection();
 | 
						|
                const cv = grid.currentViewport;
 | 
						|
                const rowCount = (_c = (_b = grid.selectionModel) === null || _b === void 0 ? void 0 : _b.dataModel.rowCount('body')) !== null && _c !== void 0 ? _c : 0;
 | 
						|
                if (colRegion == 'body' &&
 | 
						|
                    cs != null &&
 | 
						|
                    cv != null &&
 | 
						|
                    cs.r1 == 0 &&
 | 
						|
                    cs.r2 == rowCount - 1) {
 | 
						|
                    // One or more columns are selected
 | 
						|
                    let c1 = Math.max(Math.min(cs.c1, cs.c2), cv.firstColumn);
 | 
						|
                    let c2 = Math.min(Math.max(cs.c1, cs.c2), cv.lastColumn);
 | 
						|
                    if (c1 <= colIndex && colIndex <= c2) {
 | 
						|
                        // When we double-click one of the selected column headers, resize all visible selected columns.
 | 
						|
                        for (let ci = c1; ci <= c2; ci++) {
 | 
						|
                            grid.resizeColumn(colRegion, ci, null);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        // When we double-click the column header outside the selection, resize only the clicked column.
 | 
						|
                        grid.resizeColumn(colRegion, colIndex, null);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // When no columns are selected, resize only the clicked column.
 | 
						|
                    grid.resizeColumn(colRegion, colIndex, null);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (region === 'body') {
 | 
						|
            if (grid.editable) {
 | 
						|
                const cell = {
 | 
						|
                    grid: grid,
 | 
						|
                    row: row,
 | 
						|
                    column: column
 | 
						|
                };
 | 
						|
                grid.editorController.edit(cell);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        this.release();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the context menu event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The context menu event of interest.
 | 
						|
     */
 | 
						|
    onContextMenu(grid, event) {
 | 
						|
        // TODO support user-defined context menus
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the wheel event for the data grid.
 | 
						|
     *
 | 
						|
     * @param grid - The data grid of interest.
 | 
						|
     *
 | 
						|
     * @param event - The wheel event of interest.
 | 
						|
     */
 | 
						|
    onWheel(grid, event) {
 | 
						|
        // Bail if a mouse press is in progress.
 | 
						|
        if (this._pressData) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Extract the delta X and Y movement.
 | 
						|
        let dx = event.deltaX;
 | 
						|
        let dy = event.deltaY;
 | 
						|
        // Convert the delta values to pixel values.
 | 
						|
        switch (event.deltaMode) {
 | 
						|
            case 0: // DOM_DELTA_PIXEL
 | 
						|
                break;
 | 
						|
            case 1: {
 | 
						|
                // DOM_DELTA_LINE
 | 
						|
                let ds = grid.defaultSizes;
 | 
						|
                dx *= ds.columnWidth;
 | 
						|
                dy *= ds.rowHeight;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            case 2: // DOM_DELTA_PAGE
 | 
						|
                dx *= grid.pageWidth;
 | 
						|
                dy *= grid.pageHeight;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Only scroll and stop the event propagation if needed.
 | 
						|
        if (
 | 
						|
        // Scrolling left and not reached min already
 | 
						|
        (dx < 0 && grid.scrollX !== 0) ||
 | 
						|
            // Scrolling right and not reached max already
 | 
						|
            (dx > 0 && grid.scrollX !== grid.maxScrollX) ||
 | 
						|
            // Scrolling top and not reached min already
 | 
						|
            (dy < 0 && grid.scrollY !== 0) ||
 | 
						|
            // Scrolling down and not reached max already
 | 
						|
            (dy > 0 && grid.scrollY !== grid.maxScrollY)) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            // Scroll by the desired amount.
 | 
						|
            grid.scrollBy(dx, dy);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Convert a resize handle into a cursor.
 | 
						|
     */
 | 
						|
    cursorForHandle(handle) {
 | 
						|
        return Private$5.cursorMap[handle];
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the current pressData
 | 
						|
     */
 | 
						|
    get pressData() {
 | 
						|
        return this._pressData;
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$5;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * Creates a CellConfig object from a hit region.
 | 
						|
     */
 | 
						|
    function createCellConfigObject(grid, hit) {
 | 
						|
        const { region, row, column } = hit;
 | 
						|
        // Terminate call if region is void.
 | 
						|
        if (region === 'void') {
 | 
						|
            return undefined;
 | 
						|
        }
 | 
						|
        // Augment hit region params with value and metadata.
 | 
						|
        const value = grid.dataModel.data(region, row, column);
 | 
						|
        const metadata = grid.dataModel.metadata(region, row, column);
 | 
						|
        // Create cell config object to retrieve cell renderer.
 | 
						|
        const config = {
 | 
						|
            ...hit,
 | 
						|
            value: value,
 | 
						|
            metadata: metadata
 | 
						|
        };
 | 
						|
        return config;
 | 
						|
    }
 | 
						|
    Private.createCellConfigObject = createCellConfigObject;
 | 
						|
    /**
 | 
						|
     * Get the resize handle for a grid hit test.
 | 
						|
     */
 | 
						|
    function resizeHandleForHitTest(hit) {
 | 
						|
        // Fetch the row and column.
 | 
						|
        let r = hit.row;
 | 
						|
        let c = hit.column;
 | 
						|
        // Fetch the leading and trailing sizes.
 | 
						|
        let lw = hit.x;
 | 
						|
        let lh = hit.y;
 | 
						|
        let tw = hit.width - hit.x;
 | 
						|
        let th = hit.height - hit.y;
 | 
						|
        // Set up the result variable.
 | 
						|
        let result;
 | 
						|
        // Dispatch based on hit test region.
 | 
						|
        switch (hit.region) {
 | 
						|
            case 'corner-header':
 | 
						|
                if (c > 0 && lw <= 5) {
 | 
						|
                    result = 'left';
 | 
						|
                }
 | 
						|
                else if (tw <= 6) {
 | 
						|
                    result = 'right';
 | 
						|
                }
 | 
						|
                else if (r > 0 && lh <= 5) {
 | 
						|
                    result = 'top';
 | 
						|
                }
 | 
						|
                else if (th <= 6) {
 | 
						|
                    result = 'bottom';
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    result = 'none';
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 'column-header':
 | 
						|
                if (c > 0 && lw <= 5) {
 | 
						|
                    result = 'left';
 | 
						|
                }
 | 
						|
                else if (tw <= 6) {
 | 
						|
                    result = 'right';
 | 
						|
                }
 | 
						|
                else if (r > 0 && lh <= 5) {
 | 
						|
                    result = 'top';
 | 
						|
                }
 | 
						|
                else if (th <= 6) {
 | 
						|
                    result = 'bottom';
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    result = 'none';
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 'row-header':
 | 
						|
                if (c > 0 && lw <= 5) {
 | 
						|
                    result = 'left';
 | 
						|
                }
 | 
						|
                else if (tw <= 6) {
 | 
						|
                    result = 'right';
 | 
						|
                }
 | 
						|
                else if (r > 0 && lh <= 5) {
 | 
						|
                    result = 'top';
 | 
						|
                }
 | 
						|
                else if (th <= 6) {
 | 
						|
                    result = 'bottom';
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    result = 'none';
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            case 'body':
 | 
						|
                result = 'none';
 | 
						|
                break;
 | 
						|
            case 'void':
 | 
						|
                result = 'none';
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Return the result.
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    Private.resizeHandleForHitTest = resizeHandleForHitTest;
 | 
						|
    /**
 | 
						|
     * A timer callback for the autoselect loop.
 | 
						|
     *
 | 
						|
     * @param grid - The datagrid of interest.
 | 
						|
     *
 | 
						|
     * @param data - The select data of interest.
 | 
						|
     */
 | 
						|
    function autoselect(grid, data) {
 | 
						|
        // Bail early if the timeout has been reset.
 | 
						|
        if (data.timeout < 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = grid.selectionModel;
 | 
						|
        // Bail early if the selection model has been removed.
 | 
						|
        if (!model) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the current selection.
 | 
						|
        let cs = model.currentSelection();
 | 
						|
        // Bail early if there is no current selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch local X and Y coordinates of the mouse.
 | 
						|
        let lx = data.localX;
 | 
						|
        let ly = data.localY;
 | 
						|
        // Set up the selection variables.
 | 
						|
        let r1 = cs.r1;
 | 
						|
        let c1 = cs.c1;
 | 
						|
        let r2 = cs.r2;
 | 
						|
        let c2 = cs.c2;
 | 
						|
        let cursorRow = model.cursorRow;
 | 
						|
        let cursorColumn = model.cursorColumn;
 | 
						|
        let clear = 'current';
 | 
						|
        // Fetch the grid geometry.
 | 
						|
        let hw = grid.headerWidth;
 | 
						|
        let hh = grid.headerHeight;
 | 
						|
        let vpw = grid.viewportWidth;
 | 
						|
        let vph = grid.viewportHeight;
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = model.selectionMode;
 | 
						|
        // Update the selection based on the hit region.
 | 
						|
        if (data.region === 'row-header' || mode === 'row') {
 | 
						|
            r2 += ly <= hh ? -1 : ly >= vph ? 1 : 0;
 | 
						|
        }
 | 
						|
        else if (data.region === 'column-header' || mode === 'column') {
 | 
						|
            c2 += lx <= hw ? -1 : lx >= vpw ? 1 : 0;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            r2 += ly <= hh ? -1 : ly >= vph ? 1 : 0;
 | 
						|
            c2 += lx <= hw ? -1 : lx >= vpw ? 1 : 0;
 | 
						|
        }
 | 
						|
        // Update the current selection.
 | 
						|
        model.select({ r1, c1, r2, c2, cursorRow, cursorColumn, clear });
 | 
						|
        // Re-fetch the current selection.
 | 
						|
        cs = model.currentSelection();
 | 
						|
        // Bail if there is no selection.
 | 
						|
        if (!cs) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll the grid based on the hit region.
 | 
						|
        if (data.region === 'row-header' || mode === 'row') {
 | 
						|
            grid.scrollToRow(cs.r2);
 | 
						|
        }
 | 
						|
        else if (data.region === 'column-header' || mode == 'column') {
 | 
						|
            grid.scrollToColumn(cs.c2);
 | 
						|
        }
 | 
						|
        else if (mode === 'cell') {
 | 
						|
            grid.scrollToCell(cs.r2, cs.c2);
 | 
						|
        }
 | 
						|
        // Schedule the next call with the current timeout.
 | 
						|
        setTimeout(() => {
 | 
						|
            autoselect(grid, data);
 | 
						|
        }, data.timeout);
 | 
						|
    }
 | 
						|
    Private.autoselect = autoselect;
 | 
						|
    /**
 | 
						|
     * Compute the scroll timeout for the given delta distance.
 | 
						|
     *
 | 
						|
     * @param delta - The delta pixels from the origin.
 | 
						|
     *
 | 
						|
     * @returns The scaled timeout in milliseconds.
 | 
						|
     */
 | 
						|
    function computeTimeout(delta) {
 | 
						|
        return 5 + 120 * (1 - Math.min(128, Math.abs(delta)) / 128);
 | 
						|
    }
 | 
						|
    Private.computeTimeout = computeTimeout;
 | 
						|
    /**
 | 
						|
     * A mapping of resize handle to cursor.
 | 
						|
     */
 | 
						|
    Private.cursorMap = {
 | 
						|
        top: 'ns-resize',
 | 
						|
        left: 'ew-resize',
 | 
						|
        right: 'ew-resize',
 | 
						|
        bottom: 'ns-resize',
 | 
						|
        hyperlink: 'pointer',
 | 
						|
        none: 'default'
 | 
						|
    };
 | 
						|
})(Private$5 || (Private$5 = {}));
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * A base class for creating data grid selection models.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * If the predefined selection models are insufficient for a particular
 | 
						|
 * use case, a custom model can be defined which derives from this class.
 | 
						|
 */
 | 
						|
class SelectionModel {
 | 
						|
    /**
 | 
						|
     * Construct a new selection model.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the model.
 | 
						|
     */
 | 
						|
    constructor(options) {
 | 
						|
        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);
 | 
						|
        this._selectionMode = 'cell';
 | 
						|
        this.dataModel = options.dataModel;
 | 
						|
        this._selectionMode = options.selectionMode || 'cell';
 | 
						|
        this.dataModel.changed.connect(this.onDataModelChanged, this);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal emitted when the selection model has changed.
 | 
						|
     */
 | 
						|
    get changed() {
 | 
						|
        return this._changed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the selection mode for the model.
 | 
						|
     */
 | 
						|
    get selectionMode() {
 | 
						|
        return this._selectionMode;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the selection mode for the model.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This will clear the selection model.
 | 
						|
     */
 | 
						|
    set selectionMode(value) {
 | 
						|
        // Bail early if the mode does not change.
 | 
						|
        if (this._selectionMode === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal mode.
 | 
						|
        this._selectionMode = value;
 | 
						|
        // Clear the current selections.
 | 
						|
        this.clear();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Test whether any selection intersects a row.
 | 
						|
     *
 | 
						|
     * @param index - The row index of interest.
 | 
						|
     *
 | 
						|
     * @returns Whether any selection intersects the row.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method may be reimplemented in a subclass.
 | 
						|
     */
 | 
						|
    isRowSelected(index) {
 | 
						|
        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsRow(s, index));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Test whether any selection intersects a column.
 | 
						|
     *
 | 
						|
     * @param index - The column index of interest.
 | 
						|
     *
 | 
						|
     * @returns Whether any selection intersects the column.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method may be reimplemented in a subclass.
 | 
						|
     */
 | 
						|
    isColumnSelected(index) {
 | 
						|
        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsColumn(s, index));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Test whether any selection intersects a cell.
 | 
						|
     *
 | 
						|
     * @param row - The row index of interest.
 | 
						|
     *
 | 
						|
     * @param column - The column index of interest.
 | 
						|
     *
 | 
						|
     * @returns Whether any selection intersects the cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method may be reimplemented in a subclass.
 | 
						|
     */
 | 
						|
    isCellSelected(row, column) {
 | 
						|
        return (0,_lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.some)(this.selections(), s => Private$4.containsCell(s, row, column));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal handler for the data model `changed` signal.
 | 
						|
     *
 | 
						|
     * @param args - The arguments for the signal.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * Selection model implementations should update their selections
 | 
						|
     * in a manner that is relevant for the changes to the data model.
 | 
						|
     *
 | 
						|
     * The default implementation of this method is a no-op.
 | 
						|
     */
 | 
						|
    onDataModelChanged(sender, args) {
 | 
						|
        // pass
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Emit the `changed` signal for the selection model.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * Subclasses should call this method whenever the selection model
 | 
						|
     * has changed so that attached data grids can update themselves.
 | 
						|
     */
 | 
						|
    emitChanged() {
 | 
						|
        this._changed.emit(undefined);
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$4;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * Test whether a selection contains a given row.
 | 
						|
     */
 | 
						|
    function containsRow(selection, row) {
 | 
						|
        let { r1, r2 } = selection;
 | 
						|
        return (row >= r1 && row <= r2) || (row >= r2 && row <= r1);
 | 
						|
    }
 | 
						|
    Private.containsRow = containsRow;
 | 
						|
    /**
 | 
						|
     * Test whether a selection contains a given column.
 | 
						|
     */
 | 
						|
    function containsColumn(selection, column) {
 | 
						|
        let { c1, c2 } = selection;
 | 
						|
        return (column >= c1 && column <= c2) || (column >= c2 && column <= c1);
 | 
						|
    }
 | 
						|
    Private.containsColumn = containsColumn;
 | 
						|
    /**
 | 
						|
     * Test whether a selection contains a given cell.
 | 
						|
     */
 | 
						|
    function containsCell(selection, row, column) {
 | 
						|
        return containsRow(selection, row) && containsColumn(selection, column);
 | 
						|
    }
 | 
						|
    Private.containsCell = containsCell;
 | 
						|
})(Private$4 || (Private$4 = {}));
 | 
						|
 | 
						|
/**
 | 
						|
 * A basic selection model implementation.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This selection model is sufficient for most use cases where
 | 
						|
 * structural knowledge of the data source is *not* required.
 | 
						|
 */
 | 
						|
class BasicSelectionModel extends SelectionModel {
 | 
						|
    constructor() {
 | 
						|
        super(...arguments);
 | 
						|
        this._cursorRow = -1;
 | 
						|
        this._cursorColumn = -1;
 | 
						|
        this._cursorRectIndex = -1;
 | 
						|
        this._selections = [];
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the selection model is empty.
 | 
						|
     */
 | 
						|
    get isEmpty() {
 | 
						|
        return this._selections.length === 0;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The row index of the cursor.
 | 
						|
     */
 | 
						|
    get cursorRow() {
 | 
						|
        return this._cursorRow;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The column index of the cursor.
 | 
						|
     */
 | 
						|
    get cursorColumn() {
 | 
						|
        return this._cursorColumn;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Move cursor down/up/left/right while making sure it remains
 | 
						|
     * within the bounds of selected rectangles
 | 
						|
     *
 | 
						|
     * @param direction - The direction of the movement.
 | 
						|
     */
 | 
						|
    moveCursorWithinSelections(direction) {
 | 
						|
        // Bail early if there are no selections or no existing cursor
 | 
						|
        if (this.isEmpty || this.cursorRow === -1 || this._cursorColumn === -1) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if only single cell is selected
 | 
						|
        const firstSelection = this._selections[0];
 | 
						|
        if (this._selections.length === 1 &&
 | 
						|
            firstSelection.r1 === firstSelection.r2 &&
 | 
						|
            firstSelection.c1 === firstSelection.c2) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // start from last selection rectangle
 | 
						|
        if (this._cursorRectIndex === -1) {
 | 
						|
            this._cursorRectIndex = this._selections.length - 1;
 | 
						|
        }
 | 
						|
        let cursorRect = this._selections[this._cursorRectIndex];
 | 
						|
        const dr = direction === 'down' ? 1 : direction === 'up' ? -1 : 0;
 | 
						|
        const dc = direction === 'right' ? 1 : direction === 'left' ? -1 : 0;
 | 
						|
        let newRow = this._cursorRow + dr;
 | 
						|
        let newColumn = this._cursorColumn + dc;
 | 
						|
        const r1 = Math.min(cursorRect.r1, cursorRect.r2);
 | 
						|
        const r2 = Math.max(cursorRect.r1, cursorRect.r2);
 | 
						|
        const c1 = Math.min(cursorRect.c1, cursorRect.c2);
 | 
						|
        const c2 = Math.max(cursorRect.c1, cursorRect.c2);
 | 
						|
        const moveToNextRect = () => {
 | 
						|
            this._cursorRectIndex =
 | 
						|
                (this._cursorRectIndex + 1) % this._selections.length;
 | 
						|
            cursorRect = this._selections[this._cursorRectIndex];
 | 
						|
            newRow = Math.min(cursorRect.r1, cursorRect.r2);
 | 
						|
            newColumn = Math.min(cursorRect.c1, cursorRect.c2);
 | 
						|
        };
 | 
						|
        const moveToPreviousRect = () => {
 | 
						|
            this._cursorRectIndex =
 | 
						|
                this._cursorRectIndex === 0
 | 
						|
                    ? this._selections.length - 1
 | 
						|
                    : this._cursorRectIndex - 1;
 | 
						|
            cursorRect = this._selections[this._cursorRectIndex];
 | 
						|
            newRow = Math.max(cursorRect.r1, cursorRect.r2);
 | 
						|
            newColumn = Math.max(cursorRect.c1, cursorRect.c2);
 | 
						|
        };
 | 
						|
        if (newRow > r2) {
 | 
						|
            newRow = r1;
 | 
						|
            newColumn += 1;
 | 
						|
            if (newColumn > c2) {
 | 
						|
                moveToNextRect();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (newRow < r1) {
 | 
						|
            newRow = r2;
 | 
						|
            newColumn -= 1;
 | 
						|
            if (newColumn < c1) {
 | 
						|
                moveToPreviousRect();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (newColumn > c2) {
 | 
						|
            newColumn = c1;
 | 
						|
            newRow += 1;
 | 
						|
            if (newRow > r2) {
 | 
						|
                moveToNextRect();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (newColumn < c1) {
 | 
						|
            newColumn = c2;
 | 
						|
            newRow -= 1;
 | 
						|
            if (newRow < r1) {
 | 
						|
                moveToPreviousRect();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        this._cursorRow = newRow;
 | 
						|
        this._cursorColumn = newColumn;
 | 
						|
        // Emit the changed signal.
 | 
						|
        this.emitChanged();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the current selection in the selection model.
 | 
						|
     *
 | 
						|
     * @returns The current selection or `null`.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This is the selection which holds the cursor.
 | 
						|
     */
 | 
						|
    currentSelection() {
 | 
						|
        return this._selections[this._selections.length - 1] || null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get an iterator of the selections in the model.
 | 
						|
     *
 | 
						|
     * @returns A new iterator of the current selections.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * The data grid will render the selections in order.
 | 
						|
     */
 | 
						|
    *selections() {
 | 
						|
        yield* this._selections;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Select the specified cells.
 | 
						|
     *
 | 
						|
     * @param args - The arguments for the selection.
 | 
						|
     */
 | 
						|
    select(args) {
 | 
						|
        // Fetch the current row and column counts;
 | 
						|
        let rowCount = this.dataModel.rowCount('body');
 | 
						|
        let columnCount = this.dataModel.columnCount('body');
 | 
						|
        // Bail early if there is no content.
 | 
						|
        if (rowCount <= 0 || columnCount <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Unpack the arguments.
 | 
						|
        let { r1, c1, r2, c2, cursorRow, cursorColumn, clear } = args;
 | 
						|
        // Clear the necessary selections.
 | 
						|
        if (clear === 'all') {
 | 
						|
            this._selections.length = 0;
 | 
						|
        }
 | 
						|
        else if (clear === 'current') {
 | 
						|
            this._selections.pop();
 | 
						|
        }
 | 
						|
        // Clamp to the data model bounds.
 | 
						|
        r1 = Math.max(0, Math.min(r1, rowCount - 1));
 | 
						|
        r2 = Math.max(0, Math.min(r2, rowCount - 1));
 | 
						|
        c1 = Math.max(0, Math.min(c1, columnCount - 1));
 | 
						|
        c2 = Math.max(0, Math.min(c2, columnCount - 1));
 | 
						|
        // Indicate if a row/column has already been selected.
 | 
						|
        let alreadySelected = false;
 | 
						|
        // Handle the selection mode.
 | 
						|
        if (this.selectionMode === 'row') {
 | 
						|
            c1 = 0;
 | 
						|
            c2 = columnCount - 1;
 | 
						|
            alreadySelected =
 | 
						|
                this._selections.filter(selection => selection.r1 === r1).length !== 0;
 | 
						|
            // Remove from selections if already selected.
 | 
						|
            this._selections = alreadySelected
 | 
						|
                ? this._selections.filter(selection => selection.r1 !== r1)
 | 
						|
                : this._selections;
 | 
						|
        }
 | 
						|
        else if (this.selectionMode === 'column') {
 | 
						|
            r1 = 0;
 | 
						|
            r2 = rowCount - 1;
 | 
						|
            alreadySelected =
 | 
						|
                this._selections.filter(selection => selection.c1 === c1).length !== 0;
 | 
						|
            // Remove from selections if already selected.
 | 
						|
            this._selections = alreadySelected
 | 
						|
                ? this._selections.filter(selection => selection.c1 !== c1)
 | 
						|
                : this._selections;
 | 
						|
        }
 | 
						|
        // Alias the cursor row and column.
 | 
						|
        let cr = cursorRow;
 | 
						|
        let cc = cursorColumn;
 | 
						|
        // Compute the new cursor location.
 | 
						|
        if (cr < 0 || (cr < r1 && cr < r2) || (cr > r1 && cr > r2)) {
 | 
						|
            cr = r1;
 | 
						|
        }
 | 
						|
        if (cc < 0 || (cc < c1 && cc < c2) || (cc > c1 && cc > c2)) {
 | 
						|
            cc = c1;
 | 
						|
        }
 | 
						|
        // Update the cursor.
 | 
						|
        this._cursorRow = cr;
 | 
						|
        this._cursorColumn = cc;
 | 
						|
        this._cursorRectIndex = this._selections.length;
 | 
						|
        // Add the new selection if it wasn't already selected.
 | 
						|
        if (!alreadySelected) {
 | 
						|
            this._selections.push({ r1, c1, r2, c2 });
 | 
						|
        }
 | 
						|
        // Emit the changed signal.
 | 
						|
        this.emitChanged();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Clear all selections in the selection model.
 | 
						|
     */
 | 
						|
    clear() {
 | 
						|
        // Bail early if there are no selections.
 | 
						|
        if (this._selections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Reset the internal state.
 | 
						|
        this._cursorRow = -1;
 | 
						|
        this._cursorColumn = -1;
 | 
						|
        this._cursorRectIndex = -1;
 | 
						|
        this._selections.length = 0;
 | 
						|
        // Emit the changed signal.
 | 
						|
        this.emitChanged();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal handler for the data model `changed` signal.
 | 
						|
     *
 | 
						|
     * @param args - The arguments for the signal.
 | 
						|
     */
 | 
						|
    onDataModelChanged(sender, args) {
 | 
						|
        // Bail early if the model has no current selections.
 | 
						|
        if (this._selections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the cells have changed in place.
 | 
						|
        if (args.type === 'cells-changed') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if there is no change to the row or column count.
 | 
						|
        if (args.type === 'rows-moved' || args.type === 'columns-moved') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the last row and column index.
 | 
						|
        let lr = sender.rowCount('body') - 1;
 | 
						|
        let lc = sender.columnCount('body') - 1;
 | 
						|
        // Bail early if the data model is empty.
 | 
						|
        if (lr < 0 || lc < 0) {
 | 
						|
            this._selections.length = 0;
 | 
						|
            this.emitChanged();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection mode.
 | 
						|
        let mode = this.selectionMode;
 | 
						|
        // Set up the assignment index variable.
 | 
						|
        let j = 0;
 | 
						|
        // Iterate over the current selections.
 | 
						|
        for (let i = 0, n = this._selections.length; i < n; ++i) {
 | 
						|
            // Unpack the selection.
 | 
						|
            let { r1, c1, r2, c2 } = this._selections[i];
 | 
						|
            // Skip the selection if it will disappear.
 | 
						|
            if ((lr < r1 && lr < r2) || (lc < c1 && lc < c2)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Modify the bounds based on the selection mode.
 | 
						|
            if (mode === 'row') {
 | 
						|
                r1 = Math.max(0, Math.min(r1, lr));
 | 
						|
                r2 = Math.max(0, Math.min(r2, lr));
 | 
						|
                c1 = 0;
 | 
						|
                c2 = lc;
 | 
						|
            }
 | 
						|
            else if (mode === 'column') {
 | 
						|
                r1 = 0;
 | 
						|
                r2 = lr;
 | 
						|
                c1 = Math.max(0, Math.min(c1, lc));
 | 
						|
                c2 = Math.max(0, Math.min(c2, lc));
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                r1 = Math.max(0, Math.min(r1, lr));
 | 
						|
                r2 = Math.max(0, Math.min(r2, lr));
 | 
						|
                c1 = Math.max(0, Math.min(c1, lc));
 | 
						|
                c2 = Math.max(0, Math.min(c2, lc));
 | 
						|
            }
 | 
						|
            // Assign the modified selection to the array.
 | 
						|
            this._selections[j++] = { r1, c1, r2, c2 };
 | 
						|
        }
 | 
						|
        // Remove the stale selections.
 | 
						|
        this._selections.length = j;
 | 
						|
        // Emit the changed signal.
 | 
						|
        this.emitChanged();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2023, Lumino Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * An object which renders the cells of a data grid asynchronously.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * For performance reason, the datagrid only paints cells synchronously,
 | 
						|
 * though if your cell renderer inherits from AsyncCellRenderer, you will
 | 
						|
 * be able to do some asynchronous work prior to painting the cell.
 | 
						|
 * See `ImageRenderer` for an example of an asynchronous renderer.
 | 
						|
 */
 | 
						|
class AsyncCellRenderer extends CellRenderer {
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) Jupyter Development Team.
 | 
						|
 * Distributed under the terms of the Modified BSD License.
 | 
						|
 */
 | 
						|
// default validation error message
 | 
						|
const DEFAULT_INVALID_INPUT_MESSAGE = 'Invalid input!';
 | 
						|
/**
 | 
						|
 * A cell input validator object which always returns valid.
 | 
						|
 */
 | 
						|
class PassInputValidator {
 | 
						|
    /**
 | 
						|
     * Validate cell input.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param value - The cell value input.
 | 
						|
     *
 | 
						|
     * @returns An object with validation result.
 | 
						|
     */
 | 
						|
    validate(cell, value) {
 | 
						|
        return { valid: true };
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Text cell input validator.
 | 
						|
 */
 | 
						|
class TextInputValidator {
 | 
						|
    constructor() {
 | 
						|
        /**
 | 
						|
         * Minimum text length
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no minimum constraint
 | 
						|
         */
 | 
						|
        this.minLength = Number.NaN;
 | 
						|
        /**
 | 
						|
         * Maximum text length
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no maximum constraint
 | 
						|
         */
 | 
						|
        this.maxLength = Number.NaN;
 | 
						|
        /**
 | 
						|
         * Required text pattern as regular expression
 | 
						|
         *
 | 
						|
         * The default is null, meaning no pattern constraint
 | 
						|
         */
 | 
						|
        this.pattern = null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Validate cell input.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param value - The cell value input.
 | 
						|
     *
 | 
						|
     * @returns An object with validation result.
 | 
						|
     */
 | 
						|
    validate(cell, value) {
 | 
						|
        if (value === null) {
 | 
						|
            return { valid: true };
 | 
						|
        }
 | 
						|
        if (typeof value !== 'string') {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: 'Input must be valid text'
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.minLength) && value.length < this.minLength) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Text length must be greater than ${this.minLength}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.maxLength) && value.length > this.maxLength) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Text length must be less than ${this.maxLength}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (this.pattern && !this.pattern.test(value)) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Text doesn't match the required pattern`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        return { valid: true };
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Integer cell input validator.
 | 
						|
 */
 | 
						|
class IntegerInputValidator {
 | 
						|
    constructor() {
 | 
						|
        /**
 | 
						|
         * Minimum value
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no minimum constraint
 | 
						|
         */
 | 
						|
        this.min = Number.NaN;
 | 
						|
        /**
 | 
						|
         * Maximum value
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no maximum constraint
 | 
						|
         */
 | 
						|
        this.max = Number.NaN;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Validate cell input.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param value - The cell value input.
 | 
						|
     *
 | 
						|
     * @returns An object with validation result.
 | 
						|
     */
 | 
						|
    validate(cell, value) {
 | 
						|
        if (value === null) {
 | 
						|
            return { valid: true };
 | 
						|
        }
 | 
						|
        if (isNaN(value) || value % 1 !== 0) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: 'Input must be valid integer'
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.min) && value < this.min) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Input must be greater than ${this.min}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.max) && value > this.max) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Input must be less than ${this.max}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        return { valid: true };
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Real number cell input validator.
 | 
						|
 */
 | 
						|
class NumberInputValidator {
 | 
						|
    constructor() {
 | 
						|
        /**
 | 
						|
         * Minimum value
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no minimum constraint
 | 
						|
         */
 | 
						|
        this.min = Number.NaN;
 | 
						|
        /**
 | 
						|
         * Maximum value
 | 
						|
         *
 | 
						|
         * The default is Number.NaN, meaning no maximum constraint
 | 
						|
         */
 | 
						|
        this.max = Number.NaN;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Validate cell input.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param value - The cell value input.
 | 
						|
     *
 | 
						|
     * @returns An object with validation result.
 | 
						|
     */
 | 
						|
    validate(cell, value) {
 | 
						|
        if (value === null) {
 | 
						|
            return { valid: true };
 | 
						|
        }
 | 
						|
        if (isNaN(value)) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: 'Input must be valid number'
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.min) && value < this.min) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Input must be greater than ${this.min}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        if (!isNaN(this.max) && value > this.max) {
 | 
						|
            return {
 | 
						|
                valid: false,
 | 
						|
                message: `Input must be less than ${this.max}`
 | 
						|
            };
 | 
						|
        }
 | 
						|
        return { valid: true };
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * An abstract base class that provides the most of the functionality
 | 
						|
 * needed by a cell editor. All of the built-in cell editors
 | 
						|
 * for various cell types are derived from this base class. Custom cell editors
 | 
						|
 * can be easily implemented by extending this class.
 | 
						|
 */
 | 
						|
class CellEditor {
 | 
						|
    /**
 | 
						|
     * Construct a new cell editor.
 | 
						|
     */
 | 
						|
    constructor() {
 | 
						|
        /**
 | 
						|
         * A signal emitted when input changes.
 | 
						|
         */
 | 
						|
        this.inputChanged = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);
 | 
						|
        /**
 | 
						|
         * Notification popup used to show validation error messages.
 | 
						|
         */
 | 
						|
        this.validityNotification = null;
 | 
						|
        /**
 | 
						|
         * Whether the cell editor is disposed.
 | 
						|
         */
 | 
						|
        this._disposed = false;
 | 
						|
        /**
 | 
						|
         * Whether the value input is valid.
 | 
						|
         */
 | 
						|
        this._validInput = true;
 | 
						|
        /**
 | 
						|
         * Grid wheel event handler.
 | 
						|
         */
 | 
						|
        this._gridWheelEventHandler = null;
 | 
						|
        this.inputChanged.connect(() => {
 | 
						|
            this.validate();
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the cell editor is disposed.
 | 
						|
     */
 | 
						|
    get isDisposed() {
 | 
						|
        return this._disposed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this._disposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (this._gridWheelEventHandler) {
 | 
						|
            this.cell.grid.node.removeEventListener('wheel', this._gridWheelEventHandler);
 | 
						|
            this._gridWheelEventHandler = null;
 | 
						|
        }
 | 
						|
        this._closeValidityNotification();
 | 
						|
        this._disposed = true;
 | 
						|
        this.cell.grid.node.removeChild(this.viewportOccluder);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param options - The cell editing options.
 | 
						|
     */
 | 
						|
    edit(cell, options) {
 | 
						|
        this.cell = cell;
 | 
						|
        this.onCommit = options && options.onCommit;
 | 
						|
        this.onCancel = options && options.onCancel;
 | 
						|
        this.validator =
 | 
						|
            options && options.validator
 | 
						|
                ? options.validator
 | 
						|
                : this.createValidatorBasedOnType();
 | 
						|
        this._gridWheelEventHandler = () => {
 | 
						|
            this._closeValidityNotification();
 | 
						|
            this.updatePosition();
 | 
						|
        };
 | 
						|
        cell.grid.node.addEventListener('wheel', this._gridWheelEventHandler);
 | 
						|
        this._addContainer();
 | 
						|
        this.updatePosition();
 | 
						|
        this.startEditing();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Cancel editing the cell.
 | 
						|
     */
 | 
						|
    cancel() {
 | 
						|
        if (this._disposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this.dispose();
 | 
						|
        if (this.onCancel) {
 | 
						|
            this.onCancel();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the value input is valid.
 | 
						|
     */
 | 
						|
    get validInput() {
 | 
						|
        return this._validInput;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Validate the cell input. Shows validation error notification when input is invalid.
 | 
						|
     */
 | 
						|
    validate() {
 | 
						|
        let value;
 | 
						|
        try {
 | 
						|
            value = this.getInput();
 | 
						|
        }
 | 
						|
        catch (error) {
 | 
						|
            console.log(`Input error: ${error.message}`);
 | 
						|
            this.setValidity(false, error.message || DEFAULT_INVALID_INPUT_MESSAGE);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (this.validator) {
 | 
						|
            const result = this.validator.validate(this.cell, value);
 | 
						|
            if (result.valid) {
 | 
						|
                this.setValidity(true);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                this.setValidity(false, result.message || DEFAULT_INVALID_INPUT_MESSAGE);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.setValidity(true);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set validity flag.
 | 
						|
     *
 | 
						|
     * @param valid - Whether the input is valid.
 | 
						|
     *
 | 
						|
     * @param message - Notification message to show.
 | 
						|
     *
 | 
						|
     * If message is set to empty string (which is the default)
 | 
						|
     * existing notification popup is removed if any.
 | 
						|
     */
 | 
						|
    setValidity(valid, message = '') {
 | 
						|
        this._validInput = valid;
 | 
						|
        this._closeValidityNotification();
 | 
						|
        if (valid) {
 | 
						|
            this.editorContainer.classList.remove('lm-mod-invalid');
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.editorContainer.classList.add('lm-mod-invalid');
 | 
						|
            // show a notification popup
 | 
						|
            if (message !== '') {
 | 
						|
                this.validityNotification = new CellEditor.Notification({
 | 
						|
                    target: this.editorContainer,
 | 
						|
                    message: message,
 | 
						|
                    placement: 'bottom',
 | 
						|
                    timeout: 5000
 | 
						|
                });
 | 
						|
                this.validityNotification.show();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Create and return a cell input validator based on configuration of the
 | 
						|
     * cell being edited. If no suitable validator can be found, it returns undefined.
 | 
						|
     */
 | 
						|
    createValidatorBasedOnType() {
 | 
						|
        const cell = this.cell;
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        switch (metadata && metadata.type) {
 | 
						|
            case 'string':
 | 
						|
                {
 | 
						|
                    const validator = new TextInputValidator();
 | 
						|
                    if (typeof metadata.format === 'string') {
 | 
						|
                        const format = metadata.format;
 | 
						|
                        switch (format) {
 | 
						|
                            case 'email':
 | 
						|
                                validator.pattern = new RegExp('^([a-z0-9_.-]+)@([da-z.-]+).([a-z.]{2,6})$');
 | 
						|
                                break;
 | 
						|
                            case 'uuid':
 | 
						|
                                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}');
 | 
						|
                                break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    if (metadata.constraint) {
 | 
						|
                        if (metadata.constraint.minLength !== undefined) {
 | 
						|
                            validator.minLength = metadata.constraint.minLength;
 | 
						|
                        }
 | 
						|
                        if (metadata.constraint.maxLength !== undefined) {
 | 
						|
                            validator.maxLength = metadata.constraint.maxLength;
 | 
						|
                        }
 | 
						|
                        if (typeof metadata.constraint.pattern === 'string') {
 | 
						|
                            validator.pattern = new RegExp(metadata.constraint.pattern);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    return validator;
 | 
						|
                }
 | 
						|
            case 'number':
 | 
						|
                {
 | 
						|
                    const validator = new NumberInputValidator();
 | 
						|
                    if (metadata.constraint) {
 | 
						|
                        if (metadata.constraint.minimum !== undefined) {
 | 
						|
                            validator.min = metadata.constraint.minimum;
 | 
						|
                        }
 | 
						|
                        if (metadata.constraint.maximum !== undefined) {
 | 
						|
                            validator.max = metadata.constraint.maximum;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    return validator;
 | 
						|
                }
 | 
						|
            case 'integer':
 | 
						|
                {
 | 
						|
                    const validator = new IntegerInputValidator();
 | 
						|
                    if (metadata.constraint) {
 | 
						|
                        if (metadata.constraint.minimum !== undefined) {
 | 
						|
                            validator.min = metadata.constraint.minimum;
 | 
						|
                        }
 | 
						|
                        if (metadata.constraint.maximum !== undefined) {
 | 
						|
                            validator.max = metadata.constraint.maximum;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    return validator;
 | 
						|
                }
 | 
						|
        }
 | 
						|
        return undefined;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Compute cell rectangle and return with other cell properties.
 | 
						|
     */
 | 
						|
    getCellInfo(cell) {
 | 
						|
        const { grid, row, column } = cell;
 | 
						|
        let data, columnX, rowY, width, height;
 | 
						|
        const cellGroup = CellGroup.getGroup(grid.dataModel, 'body', row, column);
 | 
						|
        if (cellGroup) {
 | 
						|
            columnX =
 | 
						|
                grid.headerWidth -
 | 
						|
                    grid.scrollX +
 | 
						|
                    grid.columnOffset('body', cellGroup.c1);
 | 
						|
            rowY =
 | 
						|
                grid.headerHeight - grid.scrollY + grid.rowOffset('body', cellGroup.r1);
 | 
						|
            width = 0;
 | 
						|
            height = 0;
 | 
						|
            for (let r = cellGroup.r1; r <= cellGroup.r2; r++) {
 | 
						|
                height += grid.rowSize('body', r);
 | 
						|
            }
 | 
						|
            for (let c = cellGroup.c1; c <= cellGroup.c2; c++) {
 | 
						|
                width += grid.columnSize('body', c);
 | 
						|
            }
 | 
						|
            data = grid.dataModel.data('body', cellGroup.r1, cellGroup.c1);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            columnX =
 | 
						|
                grid.headerWidth - grid.scrollX + grid.columnOffset('body', column);
 | 
						|
            rowY = grid.headerHeight - grid.scrollY + grid.rowOffset('body', row);
 | 
						|
            width = grid.columnSize('body', column);
 | 
						|
            height = grid.rowSize('body', row);
 | 
						|
            data = grid.dataModel.data('body', row, column);
 | 
						|
        }
 | 
						|
        return {
 | 
						|
            grid: grid,
 | 
						|
            row: row,
 | 
						|
            column: column,
 | 
						|
            data: data,
 | 
						|
            x: columnX,
 | 
						|
            y: rowY,
 | 
						|
            width: width,
 | 
						|
            height: height
 | 
						|
        };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Reposition cell editor by moving viewport occluder and cell editor container.
 | 
						|
     */
 | 
						|
    updatePosition() {
 | 
						|
        const grid = this.cell.grid;
 | 
						|
        const cellInfo = this.getCellInfo(this.cell);
 | 
						|
        const headerHeight = grid.headerHeight;
 | 
						|
        const headerWidth = grid.headerWidth;
 | 
						|
        this.viewportOccluder.style.top = headerHeight + 'px';
 | 
						|
        this.viewportOccluder.style.left = headerWidth + 'px';
 | 
						|
        this.viewportOccluder.style.width = grid.viewportWidth - headerWidth + 'px';
 | 
						|
        this.viewportOccluder.style.height =
 | 
						|
            grid.viewportHeight - headerHeight + 'px';
 | 
						|
        this.viewportOccluder.style.position = 'absolute';
 | 
						|
        this.editorContainer.style.left = cellInfo.x - 1 - headerWidth + 'px';
 | 
						|
        this.editorContainer.style.top = cellInfo.y - 1 - headerHeight + 'px';
 | 
						|
        this.editorContainer.style.width = cellInfo.width + 1 + 'px';
 | 
						|
        this.editorContainer.style.height = cellInfo.height + 1 + 'px';
 | 
						|
        this.editorContainer.style.visibility = 'visible';
 | 
						|
        this.editorContainer.style.position = 'absolute';
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Commit the edited value.
 | 
						|
     *
 | 
						|
     * @param cursorMovement - Cursor move direction based on keys pressed to end the edit.
 | 
						|
     *
 | 
						|
     * @returns true on valid input, false otherwise.
 | 
						|
     */
 | 
						|
    commit(cursorMovement = 'none') {
 | 
						|
        this.validate();
 | 
						|
        if (!this._validInput) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        let value;
 | 
						|
        try {
 | 
						|
            value = this.getInput();
 | 
						|
        }
 | 
						|
        catch (error) {
 | 
						|
            console.log(`Input error: ${error.message}`);
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        this.dispose();
 | 
						|
        if (this.onCommit) {
 | 
						|
            this.onCommit({
 | 
						|
                cell: this.cell,
 | 
						|
                value: value,
 | 
						|
                cursorMovement: cursorMovement
 | 
						|
            });
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Create container elements needed to prevent editor widget overflow
 | 
						|
     * beyond viewport and to position cell editor widget.
 | 
						|
     */
 | 
						|
    _addContainer() {
 | 
						|
        this.viewportOccluder = document.createElement('div');
 | 
						|
        this.viewportOccluder.className = 'lm-DataGrid-cellEditorOccluder';
 | 
						|
        this.cell.grid.node.appendChild(this.viewportOccluder);
 | 
						|
        this.editorContainer = document.createElement('div');
 | 
						|
        this.editorContainer.className = 'lm-DataGrid-cellEditorContainer';
 | 
						|
        this.viewportOccluder.appendChild(this.editorContainer);
 | 
						|
        // update mouse event pass-through state based on input validity
 | 
						|
        this.editorContainer.addEventListener('mouseleave', (event) => {
 | 
						|
            this.viewportOccluder.style.pointerEvents = this._validInput
 | 
						|
                ? 'none'
 | 
						|
                : 'auto';
 | 
						|
        });
 | 
						|
        this.editorContainer.addEventListener('mouseenter', (event) => {
 | 
						|
            this.viewportOccluder.style.pointerEvents = 'none';
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Remove validity notification popup.
 | 
						|
     */
 | 
						|
    _closeValidityNotification() {
 | 
						|
        if (this.validityNotification) {
 | 
						|
            this.validityNotification.close();
 | 
						|
            this.validityNotification = null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Abstract base class with shared functionality
 | 
						|
 * for cell editors which use HTML Input widget as editor.
 | 
						|
 */
 | 
						|
class InputCellEditor extends CellEditor {
 | 
						|
    /**
 | 
						|
     * Handle the DOM events for the editor.
 | 
						|
     *
 | 
						|
     * @param event - The DOM event sent to the editor.
 | 
						|
     */
 | 
						|
    handleEvent(event) {
 | 
						|
        switch (event.type) {
 | 
						|
            case 'keydown':
 | 
						|
                this._onKeyDown(event);
 | 
						|
                break;
 | 
						|
            case 'blur':
 | 
						|
                this._onBlur(event);
 | 
						|
                break;
 | 
						|
            case 'input':
 | 
						|
                this._onInput(event);
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this._unbindEvents();
 | 
						|
        super.dispose();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        this.createWidget();
 | 
						|
        const cell = this.cell;
 | 
						|
        const cellInfo = this.getCellInfo(cell);
 | 
						|
        this.input.value = this.deserialize(cellInfo.data);
 | 
						|
        this.editorContainer.appendChild(this.input);
 | 
						|
        this.input.focus();
 | 
						|
        this.input.select();
 | 
						|
        this.bindEvents();
 | 
						|
    }
 | 
						|
    deserialize(value) {
 | 
						|
        if (value === null || value === undefined) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
        return value.toString();
 | 
						|
    }
 | 
						|
    createWidget() {
 | 
						|
        const input = document.createElement('input');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorWidget');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorInput');
 | 
						|
        input.spellcheck = false;
 | 
						|
        input.type = this.inputType;
 | 
						|
        this.input = input;
 | 
						|
    }
 | 
						|
    bindEvents() {
 | 
						|
        this.input.addEventListener('keydown', this);
 | 
						|
        this.input.addEventListener('blur', this);
 | 
						|
        this.input.addEventListener('input', this);
 | 
						|
    }
 | 
						|
    _unbindEvents() {
 | 
						|
        this.input.removeEventListener('keydown', this);
 | 
						|
        this.input.removeEventListener('blur', this);
 | 
						|
        this.input.removeEventListener('input', this);
 | 
						|
    }
 | 
						|
    _onKeyDown(event) {
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'Enter':
 | 
						|
                this.commit(event.shiftKey ? 'up' : 'down');
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                this.commit(event.shiftKey ? 'left' : 'right');
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.cancel();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onBlur(event) {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (!this.commit()) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this.input.focus();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onInput(event) {
 | 
						|
        this.inputChanged.emit(void 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for text cells.
 | 
						|
 */
 | 
						|
class TextCellEditor extends InputCellEditor {
 | 
						|
    constructor() {
 | 
						|
        super(...arguments);
 | 
						|
        this.inputType = 'text';
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current text input entered.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        return this.input.value;
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for real number cells.
 | 
						|
 */
 | 
						|
class NumberCellEditor extends InputCellEditor {
 | 
						|
    constructor() {
 | 
						|
        super(...arguments);
 | 
						|
        this.inputType = 'number';
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        super.startEditing();
 | 
						|
        this.input.step = 'any';
 | 
						|
        const cell = this.cell;
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        const constraint = metadata.constraint;
 | 
						|
        if (constraint) {
 | 
						|
            if (constraint.minimum) {
 | 
						|
                this.input.min = constraint.minimum;
 | 
						|
            }
 | 
						|
            if (constraint.maximum) {
 | 
						|
                this.input.max = constraint.maximum;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current number input entered. This method throws exception
 | 
						|
     * if input is invalid.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        let value = this.input.value;
 | 
						|
        if (value.trim() === '') {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        const floatValue = parseFloat(value);
 | 
						|
        if (isNaN(floatValue)) {
 | 
						|
            throw new Error('Invalid input');
 | 
						|
        }
 | 
						|
        return floatValue;
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for integer cells.
 | 
						|
 */
 | 
						|
class IntegerCellEditor extends InputCellEditor {
 | 
						|
    constructor() {
 | 
						|
        super(...arguments);
 | 
						|
        this.inputType = 'number';
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        super.startEditing();
 | 
						|
        this.input.step = '1';
 | 
						|
        const cell = this.cell;
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        const constraint = metadata.constraint;
 | 
						|
        if (constraint) {
 | 
						|
            if (constraint.minimum) {
 | 
						|
                this.input.min = constraint.minimum;
 | 
						|
            }
 | 
						|
            if (constraint.maximum) {
 | 
						|
                this.input.max = constraint.maximum;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current integer input entered. This method throws exception
 | 
						|
     * if input is invalid.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        let value = this.input.value;
 | 
						|
        if (value.trim() === '') {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        let intValue = parseInt(value);
 | 
						|
        if (isNaN(intValue)) {
 | 
						|
            throw new Error('Invalid input');
 | 
						|
        }
 | 
						|
        return intValue;
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for date cells.
 | 
						|
 */
 | 
						|
class DateCellEditor extends CellEditor {
 | 
						|
    /**
 | 
						|
     * Handle the DOM events for the editor.
 | 
						|
     *
 | 
						|
     * @param event - The DOM event sent to the editor.
 | 
						|
     */
 | 
						|
    handleEvent(event) {
 | 
						|
        switch (event.type) {
 | 
						|
            case 'keydown':
 | 
						|
                this._onKeyDown(event);
 | 
						|
                break;
 | 
						|
            case 'blur':
 | 
						|
                this._onBlur(event);
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this._unbindEvents();
 | 
						|
        super.dispose();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        this._createWidget();
 | 
						|
        const cell = this.cell;
 | 
						|
        const cellInfo = this.getCellInfo(cell);
 | 
						|
        this._input.value = this._deserialize(cellInfo.data);
 | 
						|
        this.editorContainer.appendChild(this._input);
 | 
						|
        this._input.focus();
 | 
						|
        this._bindEvents();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current date input entered.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        return this._input.value;
 | 
						|
    }
 | 
						|
    _deserialize(value) {
 | 
						|
        if (value === null || value === undefined) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
        return value.toString();
 | 
						|
    }
 | 
						|
    _createWidget() {
 | 
						|
        const input = document.createElement('input');
 | 
						|
        input.type = 'date';
 | 
						|
        input.pattern = 'd{4}-d{2}-d{2}';
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorWidget');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorInput');
 | 
						|
        this._input = input;
 | 
						|
    }
 | 
						|
    _bindEvents() {
 | 
						|
        this._input.addEventListener('keydown', this);
 | 
						|
        this._input.addEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _unbindEvents() {
 | 
						|
        this._input.removeEventListener('keydown', this);
 | 
						|
        this._input.removeEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _onKeyDown(event) {
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'Enter':
 | 
						|
                this.commit(event.shiftKey ? 'up' : 'down');
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                this.commit(event.shiftKey ? 'left' : 'right');
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.cancel();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onBlur(event) {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (!this.commit()) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this._input.focus();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for boolean cells.
 | 
						|
 */
 | 
						|
class BooleanCellEditor extends CellEditor {
 | 
						|
    /**
 | 
						|
     * Handle the DOM events for the editor.
 | 
						|
     *
 | 
						|
     * @param event - The DOM event sent to the editor.
 | 
						|
     */
 | 
						|
    handleEvent(event) {
 | 
						|
        switch (event.type) {
 | 
						|
            case 'keydown':
 | 
						|
                this._onKeyDown(event);
 | 
						|
                break;
 | 
						|
            case 'mousedown':
 | 
						|
                // fix focus loss problem in Safari and Firefox
 | 
						|
                this._input.focus();
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'blur':
 | 
						|
                this._onBlur(event);
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this._unbindEvents();
 | 
						|
        super.dispose();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        this._createWidget();
 | 
						|
        const cell = this.cell;
 | 
						|
        const cellInfo = this.getCellInfo(cell);
 | 
						|
        this._input.checked = this._deserialize(cellInfo.data);
 | 
						|
        this.editorContainer.appendChild(this._input);
 | 
						|
        this._input.focus();
 | 
						|
        this._bindEvents();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current boolean input entered.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        return this._input.checked;
 | 
						|
    }
 | 
						|
    _deserialize(value) {
 | 
						|
        if (value === null || value === undefined) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        return value == true;
 | 
						|
    }
 | 
						|
    _createWidget() {
 | 
						|
        const input = document.createElement('input');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorWidget');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorCheckbox');
 | 
						|
        input.type = 'checkbox';
 | 
						|
        input.spellcheck = false;
 | 
						|
        this._input = input;
 | 
						|
    }
 | 
						|
    _bindEvents() {
 | 
						|
        this._input.addEventListener('keydown', this);
 | 
						|
        this._input.addEventListener('mousedown', this);
 | 
						|
        this._input.addEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _unbindEvents() {
 | 
						|
        this._input.removeEventListener('keydown', this);
 | 
						|
        this._input.removeEventListener('mousedown', this);
 | 
						|
        this._input.removeEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _onKeyDown(event) {
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'Enter':
 | 
						|
                this.commit(event.shiftKey ? 'up' : 'down');
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                this.commit(event.shiftKey ? 'left' : 'right');
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.cancel();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onBlur(event) {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (!this.commit()) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this._input.focus();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for option cells.
 | 
						|
 *
 | 
						|
 * It supports multiple option selection. If cell metadata contains
 | 
						|
 * type attribute 'array', then it behaves as a multi select.
 | 
						|
 * In that case cell data is expected to be list of string values.
 | 
						|
 */
 | 
						|
class OptionCellEditor extends CellEditor {
 | 
						|
    constructor() {
 | 
						|
        super(...arguments);
 | 
						|
        this._isMultiSelect = false;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        super.dispose();
 | 
						|
        if (this._isMultiSelect) {
 | 
						|
            document.body.removeChild(this._select);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        const cell = this.cell;
 | 
						|
        const cellInfo = this.getCellInfo(cell);
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        this._isMultiSelect = metadata.type === 'array';
 | 
						|
        this._createWidget();
 | 
						|
        if (this._isMultiSelect) {
 | 
						|
            this._select.multiple = true;
 | 
						|
            const values = this._deserialize(cellInfo.data);
 | 
						|
            for (let i = 0; i < this._select.options.length; ++i) {
 | 
						|
                const option = this._select.options.item(i);
 | 
						|
                option.selected = values.indexOf(option.value) !== -1;
 | 
						|
            }
 | 
						|
            document.body.appendChild(this._select);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._select.value = this._deserialize(cellInfo.data);
 | 
						|
            this.editorContainer.appendChild(this._select);
 | 
						|
        }
 | 
						|
        this._select.focus();
 | 
						|
        this._bindEvents();
 | 
						|
        this.updatePosition();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current option input.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        if (this._isMultiSelect) {
 | 
						|
            const input = [];
 | 
						|
            for (let i = 0; i < this._select.selectedOptions.length; ++i) {
 | 
						|
                input.push(this._select.selectedOptions.item(i).value);
 | 
						|
            }
 | 
						|
            return input;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            return this._select.value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Reposition cell editor.
 | 
						|
     */
 | 
						|
    updatePosition() {
 | 
						|
        super.updatePosition();
 | 
						|
        if (!this._isMultiSelect) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const cellInfo = this.getCellInfo(this.cell);
 | 
						|
        this._select.style.position = 'absolute';
 | 
						|
        const editorContainerRect = this.editorContainer.getBoundingClientRect();
 | 
						|
        this._select.style.left = editorContainerRect.left + 'px';
 | 
						|
        this._select.style.top = editorContainerRect.top + cellInfo.height + 'px';
 | 
						|
        this._select.style.width = editorContainerRect.width + 'px';
 | 
						|
        this._select.style.maxHeight = '60px';
 | 
						|
        this.editorContainer.style.visibility = 'hidden';
 | 
						|
    }
 | 
						|
    _deserialize(value) {
 | 
						|
        if (value === null || value === undefined) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
        if (this._isMultiSelect) {
 | 
						|
            const values = [];
 | 
						|
            if (Array.isArray(value)) {
 | 
						|
                for (let item of value) {
 | 
						|
                    values.push(item.toString());
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return values;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            return value.toString();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _createWidget() {
 | 
						|
        const cell = this.cell;
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        const items = metadata.constraint.enum;
 | 
						|
        const select = document.createElement('select');
 | 
						|
        select.classList.add('lm-DataGrid-cellEditorWidget');
 | 
						|
        for (let item of items) {
 | 
						|
            const option = document.createElement('option');
 | 
						|
            option.value = item;
 | 
						|
            option.text = item;
 | 
						|
            select.appendChild(option);
 | 
						|
        }
 | 
						|
        this._select = select;
 | 
						|
    }
 | 
						|
    _bindEvents() {
 | 
						|
        this._select.addEventListener('keydown', this._onKeyDown.bind(this));
 | 
						|
        this._select.addEventListener('blur', this._onBlur.bind(this));
 | 
						|
    }
 | 
						|
    _onKeyDown(event) {
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'Enter':
 | 
						|
                this.commit(event.shiftKey ? 'up' : 'down');
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                this.commit(event.shiftKey ? 'left' : 'right');
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.cancel();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onBlur(event) {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (!this.commit()) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this._select.focus();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * Cell editor for option cells whose value can be any value
 | 
						|
 * from set of pre-defined options or values that can be input by user.
 | 
						|
 */
 | 
						|
class DynamicOptionCellEditor extends CellEditor {
 | 
						|
    /**
 | 
						|
     * Handle the DOM events for the editor.
 | 
						|
     *
 | 
						|
     * @param event - The DOM event sent to the editor.
 | 
						|
     */
 | 
						|
    handleEvent(event) {
 | 
						|
        switch (event.type) {
 | 
						|
            case 'keydown':
 | 
						|
                this._onKeyDown(event);
 | 
						|
                break;
 | 
						|
            case 'blur':
 | 
						|
                this._onBlur(event);
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by cell editor.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this._unbindEvents();
 | 
						|
        super.dispose();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing the cell.
 | 
						|
     */
 | 
						|
    startEditing() {
 | 
						|
        this._createWidget();
 | 
						|
        const cell = this.cell;
 | 
						|
        const cellInfo = this.getCellInfo(cell);
 | 
						|
        this._input.value = this._deserialize(cellInfo.data);
 | 
						|
        this.editorContainer.appendChild(this._input);
 | 
						|
        this._input.focus();
 | 
						|
        this._input.select();
 | 
						|
        this._bindEvents();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Return the current option input.
 | 
						|
     */
 | 
						|
    getInput() {
 | 
						|
        return this._input.value;
 | 
						|
    }
 | 
						|
    _deserialize(value) {
 | 
						|
        if (value === null || value === undefined) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
        return value.toString();
 | 
						|
    }
 | 
						|
    _createWidget() {
 | 
						|
        const cell = this.cell;
 | 
						|
        const grid = cell.grid;
 | 
						|
        const dataModel = grid.dataModel;
 | 
						|
        const rowCount = dataModel.rowCount('body');
 | 
						|
        const listId = 'cell-editor-list';
 | 
						|
        const list = document.createElement('datalist');
 | 
						|
        list.id = listId;
 | 
						|
        const input = document.createElement('input');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorWidget');
 | 
						|
        input.classList.add('lm-DataGrid-cellEditorInput');
 | 
						|
        const valueSet = new Set();
 | 
						|
        for (let r = 0; r < rowCount; ++r) {
 | 
						|
            const data = dataModel.data('body', r, cell.column);
 | 
						|
            if (data) {
 | 
						|
                valueSet.add(data);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        valueSet.forEach((value) => {
 | 
						|
            const option = document.createElement('option');
 | 
						|
            option.value = value;
 | 
						|
            option.text = value;
 | 
						|
            list.appendChild(option);
 | 
						|
        });
 | 
						|
        this.editorContainer.appendChild(list);
 | 
						|
        input.setAttribute('list', listId);
 | 
						|
        this._input = input;
 | 
						|
    }
 | 
						|
    _bindEvents() {
 | 
						|
        this._input.addEventListener('keydown', this);
 | 
						|
        this._input.addEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _unbindEvents() {
 | 
						|
        this._input.removeEventListener('keydown', this);
 | 
						|
        this._input.removeEventListener('blur', this);
 | 
						|
    }
 | 
						|
    _onKeyDown(event) {
 | 
						|
        switch ((0,_lumino_keyboard__WEBPACK_IMPORTED_MODULE_1__.getKeyboardLayout)().keyForKeydownEvent(event)) {
 | 
						|
            case 'Enter':
 | 
						|
                this.commit(event.shiftKey ? 'up' : 'down');
 | 
						|
                break;
 | 
						|
            case 'Tab':
 | 
						|
                this.commit(event.shiftKey ? 'left' : 'right');
 | 
						|
                event.stopPropagation();
 | 
						|
                event.preventDefault();
 | 
						|
                break;
 | 
						|
            case 'Escape':
 | 
						|
                this.cancel();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onBlur(event) {
 | 
						|
        if (this.isDisposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (!this.commit()) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this._input.focus();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the `CellEditor` class statics.
 | 
						|
 */
 | 
						|
(function (CellEditor) {
 | 
						|
    /**
 | 
						|
     * A widget which implements a notification popup.
 | 
						|
     */
 | 
						|
    class Notification extends _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget {
 | 
						|
        /**
 | 
						|
         * Construct a new notification.
 | 
						|
         *
 | 
						|
         * @param options - The options for initializing the notification.
 | 
						|
         */
 | 
						|
        constructor(options) {
 | 
						|
            super({ node: Notification.createNode() });
 | 
						|
            this._message = '';
 | 
						|
            this.addClass('lm-DataGrid-notification');
 | 
						|
            this.setFlag(_lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.Flag.DisallowLayout);
 | 
						|
            this._target = options.target;
 | 
						|
            this._message = options.message || '';
 | 
						|
            this._placement = options.placement || 'bottom';
 | 
						|
            _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.attach(this, document.body);
 | 
						|
            if (options.timeout && options.timeout > 0) {
 | 
						|
                setTimeout(() => {
 | 
						|
                    this.close();
 | 
						|
                }, options.timeout);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Handle the DOM events for the notification.
 | 
						|
         *
 | 
						|
         * @param event - The DOM event sent to the notification.
 | 
						|
         *
 | 
						|
         * #### Notes
 | 
						|
         * This method implements the DOM `EventListener` interface and is
 | 
						|
         * called in response to events on the notification's DOM node.
 | 
						|
         *
 | 
						|
         * This should not be called directly by user code.
 | 
						|
         */
 | 
						|
        handleEvent(event) {
 | 
						|
            switch (event.type) {
 | 
						|
                case 'mousedown':
 | 
						|
                    this._evtMouseDown(event);
 | 
						|
                    break;
 | 
						|
                case 'contextmenu':
 | 
						|
                    event.preventDefault();
 | 
						|
                    event.stopPropagation();
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Get the placement of the notification.
 | 
						|
         */
 | 
						|
        get placement() {
 | 
						|
            return this._placement;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Set the placement of the notification.
 | 
						|
         */
 | 
						|
        set placement(value) {
 | 
						|
            // Do nothing if the placement does not change.
 | 
						|
            if (this._placement === value) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            // Update the internal placement.
 | 
						|
            this._placement = value;
 | 
						|
            // Schedule an update for notification.
 | 
						|
            this.update();
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Get the current value of the message.
 | 
						|
         */
 | 
						|
        get message() {
 | 
						|
            return this._message;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Set the current value of the message.
 | 
						|
         *
 | 
						|
         */
 | 
						|
        set message(value) {
 | 
						|
            // Do nothing if the value does not change.
 | 
						|
            if (this._message === value) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            // Update the internal value.
 | 
						|
            this._message = value;
 | 
						|
            // Schedule an update for notification.
 | 
						|
            this.update();
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Get the node presenting the message.
 | 
						|
         */
 | 
						|
        get messageNode() {
 | 
						|
            return this.node.getElementsByClassName('lm-DataGrid-notificationMessage')[0];
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * A method invoked on a 'before-attach' message.
 | 
						|
         */
 | 
						|
        onBeforeAttach(msg) {
 | 
						|
            this.node.addEventListener('mousedown', this);
 | 
						|
            this.update();
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * A method invoked on an 'after-detach' message.
 | 
						|
         */
 | 
						|
        onAfterDetach(msg) {
 | 
						|
            this.node.removeEventListener('mousedown', this);
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * A method invoked on an 'update-request' message.
 | 
						|
         */
 | 
						|
        onUpdateRequest(msg) {
 | 
						|
            const targetRect = this._target.getBoundingClientRect();
 | 
						|
            const style = this.node.style;
 | 
						|
            switch (this._placement) {
 | 
						|
                case 'bottom':
 | 
						|
                    style.left = targetRect.left + 'px';
 | 
						|
                    style.top = targetRect.bottom + 'px';
 | 
						|
                    break;
 | 
						|
                case 'top':
 | 
						|
                    style.left = targetRect.left + 'px';
 | 
						|
                    style.height = targetRect.top + 'px';
 | 
						|
                    style.top = '0';
 | 
						|
                    style.alignItems = 'flex-end';
 | 
						|
                    style.justifyContent = 'flex-end';
 | 
						|
                    break;
 | 
						|
                case 'left':
 | 
						|
                    style.left = '0';
 | 
						|
                    style.width = targetRect.left + 'px';
 | 
						|
                    style.top = targetRect.top + 'px';
 | 
						|
                    style.alignItems = 'flex-end';
 | 
						|
                    style.justifyContent = 'flex-end';
 | 
						|
                    break;
 | 
						|
                case 'right':
 | 
						|
                    style.left = targetRect.right + 'px';
 | 
						|
                    style.top = targetRect.top + 'px';
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            this.messageNode.innerHTML = this._message;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Handle the `'mousedown'` event for the notification.
 | 
						|
         */
 | 
						|
        _evtMouseDown(event) {
 | 
						|
            // Do nothing if it's not a left mouse press.
 | 
						|
            if (event.button !== 0) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
            this.close();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    CellEditor.Notification = Notification;
 | 
						|
    /**
 | 
						|
     * The namespace for the `Notification` class statics.
 | 
						|
     */
 | 
						|
    (function (Notification) {
 | 
						|
        /**
 | 
						|
         * Create the DOM node for notification.
 | 
						|
         */
 | 
						|
        function createNode() {
 | 
						|
            const node = document.createElement('div');
 | 
						|
            const container = document.createElement('div');
 | 
						|
            container.className = 'lm-DataGrid-notificationContainer';
 | 
						|
            const message = document.createElement('span');
 | 
						|
            message.className = 'lm-DataGrid-notificationMessage';
 | 
						|
            container.appendChild(message);
 | 
						|
            node.appendChild(container);
 | 
						|
            return node;
 | 
						|
        }
 | 
						|
        Notification.createNode = createNode;
 | 
						|
    })(Notification = CellEditor.Notification || (CellEditor.Notification = {}));
 | 
						|
})(CellEditor || (CellEditor = {}));
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) Jupyter Development Team.
 | 
						|
 * Distributed under the terms of the Modified BSD License.
 | 
						|
 */
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * Resolve a config option for a cell editor.
 | 
						|
 *
 | 
						|
 * @param option - The config option to resolve.
 | 
						|
 *
 | 
						|
 * @param config - The cell config object.
 | 
						|
 *
 | 
						|
 * @returns The resolved value for the option.
 | 
						|
 */
 | 
						|
function resolveOption(option, config) {
 | 
						|
    return typeof option === 'function'
 | 
						|
        ? option(config)
 | 
						|
        : option;
 | 
						|
}
 | 
						|
/**
 | 
						|
 * An object which manages cell editing. It stores editor overrides,
 | 
						|
 * decides which editor to use for a cell, makes sure there is only one editor active.
 | 
						|
 */
 | 
						|
class CellEditorController {
 | 
						|
    constructor() {
 | 
						|
        // active cell editor
 | 
						|
        this._editor = null;
 | 
						|
        // active cell being edited
 | 
						|
        this._cell = null;
 | 
						|
        // cell editor overrides based on cell data type identifier
 | 
						|
        this._typeBasedOverrides = new Map();
 | 
						|
        // cell editor overrides based on partial metadata match
 | 
						|
        this._metadataBasedOverrides = new Map();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Override cell editor for the cells matching the identifier.
 | 
						|
     *
 | 
						|
     * @param identifier - Cell identifier to use when matching cells.
 | 
						|
     * if identifier is a CellDataType, then cell matching is done using data type of the cell,
 | 
						|
     * if identifier is a Metadata, then partial match of the cell metadata with identifier is used for match,
 | 
						|
     * if identifier is 'default' then override is used as default editor when no other editor is found suitable
 | 
						|
     *
 | 
						|
     * @param editor - The cell editor to use or resolver to use to get an editor for matching cells.
 | 
						|
     */
 | 
						|
    setEditor(identifier, editor) {
 | 
						|
        if (typeof identifier === 'string') {
 | 
						|
            this._typeBasedOverrides.set(identifier, editor);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            const key = this._metadataIdentifierToKey(identifier);
 | 
						|
            this._metadataBasedOverrides.set(key, [identifier, editor]);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Start editing a cell.
 | 
						|
     *
 | 
						|
     * @param cell - The object holding cell configuration data.
 | 
						|
     *
 | 
						|
     * @param options - The cell editing options.
 | 
						|
     */
 | 
						|
    edit(cell, options) {
 | 
						|
        const grid = cell.grid;
 | 
						|
        if (!grid.editable) {
 | 
						|
            console.error('Grid cannot be edited!');
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        this.cancel();
 | 
						|
        this._cell = cell;
 | 
						|
        options = options || {};
 | 
						|
        options.onCommit = options.onCommit || this._onCommit.bind(this);
 | 
						|
        options.onCancel = options.onCancel || this._onCancel.bind(this);
 | 
						|
        // if an editor is passed in with options, then use it for editing
 | 
						|
        if (options.editor) {
 | 
						|
            this._editor = options.editor;
 | 
						|
            options.editor.edit(cell, options);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        // choose an editor based on overrides / cell data type
 | 
						|
        const editor = this._getEditor(cell);
 | 
						|
        if (editor) {
 | 
						|
            this._editor = editor;
 | 
						|
            editor.edit(cell, options);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Cancel editing.
 | 
						|
     */
 | 
						|
    cancel() {
 | 
						|
        if (this._editor) {
 | 
						|
            this._editor.cancel();
 | 
						|
            this._editor = null;
 | 
						|
        }
 | 
						|
        this._cell = null;
 | 
						|
    }
 | 
						|
    _onCommit(response) {
 | 
						|
        const cell = this._cell;
 | 
						|
        if (!cell) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const grid = cell.grid;
 | 
						|
        const dataModel = grid.dataModel;
 | 
						|
        let row = cell.row;
 | 
						|
        let column = cell.column;
 | 
						|
        const cellGroup = CellGroup.getGroup(grid.dataModel, 'body', row, column);
 | 
						|
        if (cellGroup) {
 | 
						|
            row = cellGroup.r1;
 | 
						|
            column = cellGroup.c1;
 | 
						|
        }
 | 
						|
        dataModel.setData('body', row, column, response.value);
 | 
						|
        grid.viewport.node.focus();
 | 
						|
        if (response.cursorMovement !== 'none') {
 | 
						|
            grid.moveCursor(response.cursorMovement);
 | 
						|
            grid.scrollToCursor();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    _onCancel() {
 | 
						|
        if (!this._cell) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        this._cell.grid.viewport.node.focus();
 | 
						|
    }
 | 
						|
    _getDataTypeKey(cell) {
 | 
						|
        const metadata = cell.grid.dataModel
 | 
						|
            ? cell.grid.dataModel.metadata('body', cell.row, cell.column)
 | 
						|
            : null;
 | 
						|
        if (!metadata) {
 | 
						|
            return 'default';
 | 
						|
        }
 | 
						|
        let key = '';
 | 
						|
        if (metadata) {
 | 
						|
            key = metadata.type;
 | 
						|
        }
 | 
						|
        if (metadata.constraint && metadata.constraint.enum) {
 | 
						|
            if (metadata.constraint.enum === 'dynamic') {
 | 
						|
                key += ':dynamic-option';
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                key += ':option';
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return key;
 | 
						|
    }
 | 
						|
    _objectToKey(object) {
 | 
						|
        let str = '';
 | 
						|
        for (let key in object) {
 | 
						|
            const value = object[key];
 | 
						|
            if (typeof value === 'object') {
 | 
						|
                str += `${key}:${this._objectToKey(value)}`;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                str += `[${key}:${value}]`;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return str;
 | 
						|
    }
 | 
						|
    _metadataIdentifierToKey(metadata) {
 | 
						|
        return this._objectToKey(metadata);
 | 
						|
    }
 | 
						|
    _metadataMatchesIdentifier(metadata, identifier) {
 | 
						|
        for (let key in identifier) {
 | 
						|
            if (!metadata.hasOwnProperty(key)) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            const identifierValue = identifier[key];
 | 
						|
            const metadataValue = metadata[key];
 | 
						|
            if (typeof identifierValue === 'object') {
 | 
						|
                if (!this._metadataMatchesIdentifier(metadataValue, identifierValue)) {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (metadataValue !== identifierValue) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    _getMetadataBasedEditor(cell) {
 | 
						|
        let editorMatched;
 | 
						|
        const metadata = cell.grid.dataModel.metadata('body', cell.row, cell.column);
 | 
						|
        if (metadata) {
 | 
						|
            this._metadataBasedOverrides.forEach(value => {
 | 
						|
                if (!editorMatched) {
 | 
						|
                    let [identifier, editor] = value;
 | 
						|
                    if (this._metadataMatchesIdentifier(metadata, identifier)) {
 | 
						|
                        editorMatched = resolveOption(editor, cell);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            });
 | 
						|
        }
 | 
						|
        return editorMatched;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Choose the most appropriate cell editor to use based on overrides / cell data type.
 | 
						|
     *
 | 
						|
     * If no match is found in overrides or based on cell data type, and if cell has a primitive
 | 
						|
     * data type then TextCellEditor is used as default cell editor. If 'default' cell editor
 | 
						|
     * is overridden, then it is used instead of TextCellEditor for default.
 | 
						|
     */
 | 
						|
    _getEditor(cell) {
 | 
						|
        const dtKey = this._getDataTypeKey(cell);
 | 
						|
        // find an editor based on data type based override
 | 
						|
        if (this._typeBasedOverrides.has(dtKey)) {
 | 
						|
            const editor = this._typeBasedOverrides.get(dtKey);
 | 
						|
            return resolveOption(editor, cell);
 | 
						|
        } // find an editor based on metadata match based override
 | 
						|
        else if (this._metadataBasedOverrides.size > 0) {
 | 
						|
            const editor = this._getMetadataBasedEditor(cell);
 | 
						|
            if (editor) {
 | 
						|
                return editor;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // choose an editor based on data type
 | 
						|
        switch (dtKey) {
 | 
						|
            case 'string':
 | 
						|
                return new TextCellEditor();
 | 
						|
            case 'number':
 | 
						|
                return new NumberCellEditor();
 | 
						|
            case 'integer':
 | 
						|
                return new IntegerCellEditor();
 | 
						|
            case 'boolean':
 | 
						|
                return new BooleanCellEditor();
 | 
						|
            case 'date':
 | 
						|
                return new DateCellEditor();
 | 
						|
            case 'string:option':
 | 
						|
            case 'number:option':
 | 
						|
            case 'integer:option':
 | 
						|
            case 'date:option':
 | 
						|
            case 'array:option':
 | 
						|
                return new OptionCellEditor();
 | 
						|
            case 'string:dynamic-option':
 | 
						|
            case 'number:dynamic-option':
 | 
						|
            case 'integer:dynamic-option':
 | 
						|
            case 'date:dynamic-option':
 | 
						|
                return new DynamicOptionCellEditor();
 | 
						|
        }
 | 
						|
        // if an override exists for 'default', then use it
 | 
						|
        if (this._typeBasedOverrides.has('default')) {
 | 
						|
            const editor = this._typeBasedOverrides.get('default');
 | 
						|
            return resolveOption(editor, cell);
 | 
						|
        }
 | 
						|
        // if cell has a primitive data type then use TextCellEditor
 | 
						|
        const data = cell.grid.dataModel.data('body', cell.row, cell.column);
 | 
						|
        if (!data || typeof data !== 'object') {
 | 
						|
            return new TextCellEditor();
 | 
						|
        }
 | 
						|
        // no suitable editor found for the cell
 | 
						|
        return undefined;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * An object which provides the data for a data grid.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * If the predefined data models are insufficient for a particular use
 | 
						|
 * case, a custom model can be defined which derives from this class.
 | 
						|
 */
 | 
						|
class DataModel {
 | 
						|
    constructor() {
 | 
						|
        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal emitted when the data model has changed.
 | 
						|
     */
 | 
						|
    get changed() {
 | 
						|
        return this._changed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the count of merged cell groups pertaining to a given
 | 
						|
     * cell region.
 | 
						|
     * @param region the target cell region.
 | 
						|
     */
 | 
						|
    groupCount(region) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the metadata for a cell in the data model.
 | 
						|
     *
 | 
						|
     * @param region - The cell region of interest.
 | 
						|
     *
 | 
						|
     * @param row - The row index of the cell of interest.
 | 
						|
     *
 | 
						|
     * @param column - The column index of the cell of interest.
 | 
						|
     *
 | 
						|
     * @returns The metadata for the specified cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * The returned metadata should be treated as immutable.
 | 
						|
     *
 | 
						|
     * This method is called often, and so should be efficient.
 | 
						|
     *
 | 
						|
     * The default implementation returns `{}`.
 | 
						|
     */
 | 
						|
    metadata(region, row, column) {
 | 
						|
        return DataModel.emptyMetadata;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the merged cell group corresponding to a region and index number.
 | 
						|
     * @param region the cell region of cell group.
 | 
						|
     * @param groupIndex the group index of the cell group.
 | 
						|
     * @returns a cell group.
 | 
						|
     */
 | 
						|
    group(region, groupIndex) {
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Emit the `changed` signal for the data model.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * Subclasses should call this method whenever the data model has
 | 
						|
     * changed so that attached data grids can update themselves.
 | 
						|
     */
 | 
						|
    emitChanged(args) {
 | 
						|
        this._changed.emit(args);
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * An object which provides the mutable data for a data grid.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This object is an extension to `DataModel` and it only adds ability to
 | 
						|
 * change data for cells.
 | 
						|
 */
 | 
						|
class MutableDataModel extends DataModel {
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the `DataModel` class statics.
 | 
						|
 */
 | 
						|
(function (DataModel) {
 | 
						|
    /**
 | 
						|
     * A singleton empty metadata object.
 | 
						|
     */
 | 
						|
    DataModel.emptyMetadata = Object.freeze({});
 | 
						|
})(DataModel || (DataModel = {}));
 | 
						|
 | 
						|
/**
 | 
						|
 * A thin caching wrapper around a 2D canvas rendering context.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This class is mostly a transparent wrapper around a canvas rendering
 | 
						|
 * context which improves performance when writing context state.
 | 
						|
 *
 | 
						|
 * For best performance, avoid reading state from the `gc`. Writes are
 | 
						|
 * cached based on the previously written value.
 | 
						|
 *
 | 
						|
 * Unless otherwise specified, the API and semantics of this class are
 | 
						|
 * identical to the builtin 2D canvas rendering context:
 | 
						|
 * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
 | 
						|
 *
 | 
						|
 * The wrapped canvas context should not be manipulated externally
 | 
						|
 * until the wrapping `GraphicsContext` object is disposed.
 | 
						|
 */
 | 
						|
class GraphicsContext {
 | 
						|
    /**
 | 
						|
     * Create a new graphics context object.
 | 
						|
     *
 | 
						|
     * @param context - The 2D canvas rendering context to wrap.
 | 
						|
     */
 | 
						|
    constructor(context) {
 | 
						|
        this._disposed = false;
 | 
						|
        this._context = context;
 | 
						|
        this._state = Private$3.State.create(context);
 | 
						|
    }
 | 
						|
    dispose() {
 | 
						|
        // Bail if the gc is already disposed.
 | 
						|
        if (this._disposed) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Mark the gc as disposed.
 | 
						|
        this._disposed = true;
 | 
						|
        // Pop any unrestored saves.
 | 
						|
        while (this._state.next) {
 | 
						|
            this._state = this._state.next;
 | 
						|
            this._context.restore();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get isDisposed() {
 | 
						|
        return this._disposed;
 | 
						|
    }
 | 
						|
    get fillStyle() {
 | 
						|
        return this._context.fillStyle;
 | 
						|
    }
 | 
						|
    set fillStyle(value) {
 | 
						|
        if (this._state.fillStyle !== value) {
 | 
						|
            this._state.fillStyle = value;
 | 
						|
            this._context.fillStyle = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get strokeStyle() {
 | 
						|
        return this._context.strokeStyle;
 | 
						|
    }
 | 
						|
    set strokeStyle(value) {
 | 
						|
        if (this._state.strokeStyle !== value) {
 | 
						|
            this._state.strokeStyle = value;
 | 
						|
            this._context.strokeStyle = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get font() {
 | 
						|
        return this._context.font;
 | 
						|
    }
 | 
						|
    set font(value) {
 | 
						|
        if (this._state.font !== value) {
 | 
						|
            this._state.font = value;
 | 
						|
            this._context.font = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get textAlign() {
 | 
						|
        return this._context.textAlign;
 | 
						|
    }
 | 
						|
    set textAlign(value) {
 | 
						|
        if (this._state.textAlign !== value) {
 | 
						|
            this._state.textAlign = value;
 | 
						|
            this._context.textAlign = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get textBaseline() {
 | 
						|
        return this._context.textBaseline;
 | 
						|
    }
 | 
						|
    set textBaseline(value) {
 | 
						|
        if (this._state.textBaseline !== value) {
 | 
						|
            this._state.textBaseline = value;
 | 
						|
            this._context.textBaseline = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get lineCap() {
 | 
						|
        return this._context.lineCap;
 | 
						|
    }
 | 
						|
    set lineCap(value) {
 | 
						|
        if (this._state.lineCap !== value) {
 | 
						|
            this._state.lineCap = value;
 | 
						|
            this._context.lineCap = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get lineDashOffset() {
 | 
						|
        return this._context.lineDashOffset;
 | 
						|
    }
 | 
						|
    set lineDashOffset(value) {
 | 
						|
        if (this._state.lineDashOffset !== value) {
 | 
						|
            this._state.lineDashOffset = value;
 | 
						|
            this._context.lineDashOffset = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get lineJoin() {
 | 
						|
        return this._context.lineJoin;
 | 
						|
    }
 | 
						|
    set lineJoin(value) {
 | 
						|
        if (this._state.lineJoin !== value) {
 | 
						|
            this._state.lineJoin = value;
 | 
						|
            this._context.lineJoin = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get lineWidth() {
 | 
						|
        return this._context.lineWidth;
 | 
						|
    }
 | 
						|
    set lineWidth(value) {
 | 
						|
        if (this._state.lineWidth !== value) {
 | 
						|
            this._state.lineWidth = value;
 | 
						|
            this._context.lineWidth = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get miterLimit() {
 | 
						|
        return this._context.miterLimit;
 | 
						|
    }
 | 
						|
    set miterLimit(value) {
 | 
						|
        if (this._state.miterLimit !== value) {
 | 
						|
            this._state.miterLimit = value;
 | 
						|
            this._context.miterLimit = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get shadowBlur() {
 | 
						|
        return this._context.shadowBlur;
 | 
						|
    }
 | 
						|
    set shadowBlur(value) {
 | 
						|
        if (this._state.shadowBlur !== value) {
 | 
						|
            this._state.shadowBlur = value;
 | 
						|
            this._context.shadowBlur = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get shadowColor() {
 | 
						|
        return this._context.shadowColor;
 | 
						|
    }
 | 
						|
    set shadowColor(value) {
 | 
						|
        if (this._state.shadowColor !== value) {
 | 
						|
            this._state.shadowColor = value;
 | 
						|
            this._context.shadowColor = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get shadowOffsetX() {
 | 
						|
        return this._context.shadowOffsetX;
 | 
						|
    }
 | 
						|
    set shadowOffsetX(value) {
 | 
						|
        if (this._state.shadowOffsetX !== value) {
 | 
						|
            this._state.shadowOffsetX = value;
 | 
						|
            this._context.shadowOffsetX = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get shadowOffsetY() {
 | 
						|
        return this._context.shadowOffsetY;
 | 
						|
    }
 | 
						|
    set shadowOffsetY(value) {
 | 
						|
        if (this._state.shadowOffsetY !== value) {
 | 
						|
            this._state.shadowOffsetY = value;
 | 
						|
            this._context.shadowOffsetY = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get imageSmoothingEnabled() {
 | 
						|
        return this._context.imageSmoothingEnabled;
 | 
						|
    }
 | 
						|
    set imageSmoothingEnabled(value) {
 | 
						|
        if (this._state.imageSmoothingEnabled !== value) {
 | 
						|
            this._state.imageSmoothingEnabled = value;
 | 
						|
            this._context.imageSmoothingEnabled = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get globalAlpha() {
 | 
						|
        return this._context.globalAlpha;
 | 
						|
    }
 | 
						|
    set globalAlpha(value) {
 | 
						|
        if (this._state.globalAlpha !== value) {
 | 
						|
            this._state.globalAlpha = value;
 | 
						|
            this._context.globalAlpha = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    get globalCompositeOperation() {
 | 
						|
        return this._context.globalCompositeOperation;
 | 
						|
    }
 | 
						|
    set globalCompositeOperation(value) {
 | 
						|
        if (this._state.globalCompositeOperation !== value) {
 | 
						|
            this._state.globalCompositeOperation = value;
 | 
						|
            this._context.globalCompositeOperation = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    getLineDash() {
 | 
						|
        return this._context.getLineDash();
 | 
						|
    }
 | 
						|
    setLineDash(segments) {
 | 
						|
        this._context.setLineDash(segments);
 | 
						|
    }
 | 
						|
    rotate(angle) {
 | 
						|
        this._context.rotate(angle);
 | 
						|
    }
 | 
						|
    scale(x, y) {
 | 
						|
        this._context.scale(x, y);
 | 
						|
    }
 | 
						|
    transform(m11, m12, m21, m22, dx, dy) {
 | 
						|
        this._context.transform(m11, m12, m21, m22, dx, dy);
 | 
						|
    }
 | 
						|
    translate(x, y) {
 | 
						|
        this._context.translate(x, y);
 | 
						|
    }
 | 
						|
    setTransform(m11, m12, m21, m22, dx, dy) {
 | 
						|
        this._context.setTransform(m11, m12, m21, m22, dx, dy);
 | 
						|
    }
 | 
						|
    save() {
 | 
						|
        // Clone an push the current state to the stack.
 | 
						|
        this._state = Private$3.State.push(this._state);
 | 
						|
        // Save the wrapped context state.
 | 
						|
        this._context.save();
 | 
						|
    }
 | 
						|
    restore() {
 | 
						|
        // Bail if there is no state to restore.
 | 
						|
        if (!this._state.next) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Pop the saved state from the stack.
 | 
						|
        this._state = Private$3.State.pop(this._state);
 | 
						|
        // Restore the wrapped context state.
 | 
						|
        this._context.restore();
 | 
						|
    }
 | 
						|
    beginPath() {
 | 
						|
        return this._context.beginPath();
 | 
						|
    }
 | 
						|
    closePath() {
 | 
						|
        this._context.closePath();
 | 
						|
    }
 | 
						|
    isPointInPath(x, y, fillRule) {
 | 
						|
        let result;
 | 
						|
        if (arguments.length === 2) {
 | 
						|
            result = this._context.isPointInPath(x, y);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            result = this._context.isPointInPath(x, y, fillRule);
 | 
						|
        }
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    arc(x, y, radius, startAngle, endAngle, anticlockwise) {
 | 
						|
        if (arguments.length === 5) {
 | 
						|
            this._context.arc(x, y, radius, startAngle, endAngle);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    arcTo(x1, y1, x2, y2, radius) {
 | 
						|
        this._context.arcTo(x1, y1, x2, y2, radius);
 | 
						|
    }
 | 
						|
    bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
 | 
						|
        this._context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
 | 
						|
    }
 | 
						|
    ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
 | 
						|
        if (arguments.length === 7) {
 | 
						|
            this._context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    lineTo(x, y) {
 | 
						|
        this._context.lineTo(x, y);
 | 
						|
    }
 | 
						|
    moveTo(x, y) {
 | 
						|
        this._context.moveTo(x, y);
 | 
						|
    }
 | 
						|
    quadraticCurveTo(cpx, cpy, x, y) {
 | 
						|
        this._context.quadraticCurveTo(cpx, cpy, x, y);
 | 
						|
    }
 | 
						|
    rect(x, y, w, h) {
 | 
						|
        this._context.rect(x, y, w, h);
 | 
						|
    }
 | 
						|
    clip(fillRule) {
 | 
						|
        if (arguments.length === 0) {
 | 
						|
            this._context.clip();
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.clip(fillRule);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    fill(fillRule) {
 | 
						|
        if (arguments.length === 0) {
 | 
						|
            this._context.fill();
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.fill(fillRule);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    stroke() {
 | 
						|
        this._context.stroke();
 | 
						|
    }
 | 
						|
    clearRect(x, y, w, h) {
 | 
						|
        return this._context.clearRect(x, y, w, h);
 | 
						|
    }
 | 
						|
    fillRect(x, y, w, h) {
 | 
						|
        this._context.fillRect(x, y, w, h);
 | 
						|
    }
 | 
						|
    fillText(text, x, y, maxWidth) {
 | 
						|
        if (arguments.length === 3) {
 | 
						|
            this._context.fillText(text, x, y);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.fillText(text, x, y, maxWidth);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    strokeRect(x, y, w, h) {
 | 
						|
        this._context.strokeRect(x, y, w, h);
 | 
						|
    }
 | 
						|
    strokeText(text, x, y, maxWidth) {
 | 
						|
        if (arguments.length === 3) {
 | 
						|
            this._context.strokeText(text, x, y);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._context.strokeText(text, x, y, maxWidth);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    measureText(text) {
 | 
						|
        return this._context.measureText(text);
 | 
						|
    }
 | 
						|
    createLinearGradient(x0, y0, x1, y1) {
 | 
						|
        return this._context.createLinearGradient(x0, y0, x1, y1);
 | 
						|
    }
 | 
						|
    createRadialGradient(x0, y0, r0, x1, y1, r1) {
 | 
						|
        return this._context.createRadialGradient(x0, y0, r0, x1, y1, r1);
 | 
						|
    }
 | 
						|
    createPattern(image, repetition) {
 | 
						|
        return this._context.createPattern(image, repetition);
 | 
						|
    }
 | 
						|
    createImageData() {
 | 
						|
        // eslint-disable-next-line prefer-spread, prefer-rest-params
 | 
						|
        return this._context.createImageData.apply(this._context, arguments);
 | 
						|
    }
 | 
						|
    getImageData(sx, sy, sw, sh) {
 | 
						|
        return this._context.getImageData(sx, sy, sw, sh);
 | 
						|
    }
 | 
						|
    putImageData() {
 | 
						|
        // eslint-disable-next-line prefer-spread, prefer-rest-params
 | 
						|
        this._context.putImageData.apply(this._context, arguments);
 | 
						|
    }
 | 
						|
    drawImage() {
 | 
						|
        // eslint-disable-next-line prefer-spread, prefer-rest-params
 | 
						|
        this._context.drawImage.apply(this._context, arguments);
 | 
						|
    }
 | 
						|
    drawFocusIfNeeded(element) {
 | 
						|
        this._context.drawFocusIfNeeded(element);
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$3;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * The index of next valid pool object.
 | 
						|
     */
 | 
						|
    let pi = -1;
 | 
						|
    /**
 | 
						|
     * A state object allocation pool.
 | 
						|
     */
 | 
						|
    const pool = [];
 | 
						|
    /**
 | 
						|
     * An object which holds the state for a gc.
 | 
						|
     */
 | 
						|
    class State {
 | 
						|
        /**
 | 
						|
         * Create a gc state object from a 2D canvas context.
 | 
						|
         */
 | 
						|
        static create(context) {
 | 
						|
            let state = pi < 0 ? new State() : pool[pi--];
 | 
						|
            state.next = null;
 | 
						|
            state.fillStyle = context.fillStyle;
 | 
						|
            state.font = context.font;
 | 
						|
            state.globalAlpha = context.globalAlpha;
 | 
						|
            state.globalCompositeOperation = context.globalCompositeOperation;
 | 
						|
            state.imageSmoothingEnabled = context.imageSmoothingEnabled;
 | 
						|
            state.lineCap = context.lineCap;
 | 
						|
            state.lineDashOffset = context.lineDashOffset;
 | 
						|
            state.lineJoin = context.lineJoin;
 | 
						|
            state.lineWidth = context.lineWidth;
 | 
						|
            state.miterLimit = context.miterLimit;
 | 
						|
            state.shadowBlur = context.shadowBlur;
 | 
						|
            state.shadowColor = context.shadowColor;
 | 
						|
            state.shadowOffsetX = context.shadowOffsetX;
 | 
						|
            state.shadowOffsetY = context.shadowOffsetY;
 | 
						|
            state.strokeStyle = context.strokeStyle;
 | 
						|
            state.textAlign = context.textAlign;
 | 
						|
            state.textBaseline = context.textBaseline;
 | 
						|
            return state;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Clone an existing gc state object and add it to the state stack.
 | 
						|
         */
 | 
						|
        static push(other) {
 | 
						|
            let state = pi < 0 ? new State() : pool[pi--];
 | 
						|
            state.next = other;
 | 
						|
            state.fillStyle = other.fillStyle;
 | 
						|
            state.font = other.font;
 | 
						|
            state.globalAlpha = other.globalAlpha;
 | 
						|
            state.globalCompositeOperation = other.globalCompositeOperation;
 | 
						|
            state.imageSmoothingEnabled = other.imageSmoothingEnabled;
 | 
						|
            state.lineCap = other.lineCap;
 | 
						|
            state.lineDashOffset = other.lineDashOffset;
 | 
						|
            state.lineJoin = other.lineJoin;
 | 
						|
            state.lineWidth = other.lineWidth;
 | 
						|
            state.miterLimit = other.miterLimit;
 | 
						|
            state.shadowBlur = other.shadowBlur;
 | 
						|
            state.shadowColor = other.shadowColor;
 | 
						|
            state.shadowOffsetX = other.shadowOffsetX;
 | 
						|
            state.shadowOffsetY = other.shadowOffsetY;
 | 
						|
            state.strokeStyle = other.strokeStyle;
 | 
						|
            state.textAlign = other.textAlign;
 | 
						|
            state.textBaseline = other.textBaseline;
 | 
						|
            return state;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Pop the next state object and return the current to the pool
 | 
						|
         */
 | 
						|
        static pop(state) {
 | 
						|
            state.fillStyle = '';
 | 
						|
            state.strokeStyle = '';
 | 
						|
            pool[++pi] = state;
 | 
						|
            return state.next;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    Private.State = State;
 | 
						|
})(Private$3 || (Private$3 = {}));
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * A class which manages the mapping of cell renderers.
 | 
						|
 */
 | 
						|
class RendererMap {
 | 
						|
    /**
 | 
						|
     * Construct a new renderer map.
 | 
						|
     *
 | 
						|
     * @param values - The initial values for the map.
 | 
						|
     *
 | 
						|
     * @param fallback - The renderer of last resort.
 | 
						|
     */
 | 
						|
    constructor(values = {}, fallback) {
 | 
						|
        this._changed = new _lumino_signaling__WEBPACK_IMPORTED_MODULE_4__.Signal(this);
 | 
						|
        this._values = { ...values };
 | 
						|
        this._fallback = fallback || new TextRenderer();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal emitted when the renderer map has changed.
 | 
						|
     */
 | 
						|
    get changed() {
 | 
						|
        return this._changed;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the cell renderer to use for the given cell config.
 | 
						|
     *
 | 
						|
     * @param config - The cell config of interest.
 | 
						|
     *
 | 
						|
     * @returns The renderer to use for the cell.
 | 
						|
     */
 | 
						|
    get(config) {
 | 
						|
        // Fetch the renderer from the values map.
 | 
						|
        let renderer = this._values[config.region];
 | 
						|
        // Execute a resolver function if necessary.
 | 
						|
        if (typeof renderer === 'function') {
 | 
						|
            try {
 | 
						|
                renderer = renderer(config);
 | 
						|
            }
 | 
						|
            catch (err) {
 | 
						|
                renderer = undefined;
 | 
						|
                console.error(err);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Return the renderer or the fallback.
 | 
						|
        return renderer || this._fallback;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Update the renderer map with new values
 | 
						|
     *
 | 
						|
     * @param values - The updated values for the map.
 | 
						|
     *
 | 
						|
     * @param fallback - The renderer of last resort.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method always emits the `changed` signal.
 | 
						|
     */
 | 
						|
    update(values = {}, fallback) {
 | 
						|
        this._values = { ...this._values, ...values };
 | 
						|
        this._fallback = fallback || this._fallback;
 | 
						|
        this._changed.emit(undefined);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2019, PhosphorJS Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
/**
 | 
						|
 * An object which manages a collection of variable sized sections.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * This class is an implementation detail. It is designed to manage
 | 
						|
 * the variable row and column sizes for a data grid. User code will
 | 
						|
 * not interact with this class directly.
 | 
						|
 */
 | 
						|
class SectionList {
 | 
						|
    /**
 | 
						|
     * Construct a new section list.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the list.
 | 
						|
     */
 | 
						|
    constructor(options) {
 | 
						|
        this._count = 0;
 | 
						|
        this._length = 0;
 | 
						|
        this._sections = [];
 | 
						|
        this._minimumSize = options.minimumSize || 2;
 | 
						|
        this._defaultSize = Math.max(this._minimumSize, Math.floor(options.defaultSize));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The total size of all sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    get length() {
 | 
						|
        return this._length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The total number of sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    get count() {
 | 
						|
        return this._count;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the minimum size of sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    get minimumSize() {
 | 
						|
        return this._minimumSize;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the minimum size of sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of resized sections.
 | 
						|
     */
 | 
						|
    set minimumSize(value) {
 | 
						|
        // Normalize the value.
 | 
						|
        value = Math.max(2, Math.floor(value));
 | 
						|
        // Bail early if the value does not change.
 | 
						|
        if (this._minimumSize === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal minimum size.
 | 
						|
        this._minimumSize = value;
 | 
						|
        // Update default size if larger than minimum size
 | 
						|
        if (value > this._defaultSize) {
 | 
						|
            this.defaultSize = value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the default size of sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    get defaultSize() {
 | 
						|
        return this._defaultSize;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the default size of sections in the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of resized sections.
 | 
						|
     */
 | 
						|
    set defaultSize(value) {
 | 
						|
        // Normalize the value.
 | 
						|
        value = Math.max(this._minimumSize, Math.floor(value));
 | 
						|
        // Bail early if the value does not change.
 | 
						|
        if (this._defaultSize === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the delta default size.
 | 
						|
        let delta = value - this._defaultSize;
 | 
						|
        // Update the internal default size.
 | 
						|
        this._defaultSize = value;
 | 
						|
        // Update the length.
 | 
						|
        this._length += delta * (this._count - this._sections.length);
 | 
						|
        // Bail early if there are no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Recompute the offsets of the modified sections.
 | 
						|
        for (let i = 0, n = this._sections.length; i < n; ++i) {
 | 
						|
            // Look up the previous and current modified sections.
 | 
						|
            let prev = this._sections[i - 1];
 | 
						|
            let curr = this._sections[i];
 | 
						|
            // Adjust the offset for the current section.
 | 
						|
            if (prev) {
 | 
						|
                let count = curr.index - prev.index - 1;
 | 
						|
                curr.offset = prev.offset + prev.size + count * value;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                curr.offset = curr.index * value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Clamp a size to the minimum section size
 | 
						|
     *
 | 
						|
     * @param size - The size to clamp.
 | 
						|
     *
 | 
						|
     * @returns The size or the section minimum size, whichever is larger
 | 
						|
     */
 | 
						|
    clampSize(size) {
 | 
						|
        return Math.max(this._minimumSize, Math.floor(size));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Find the index of the section which covers the given offset.
 | 
						|
     *
 | 
						|
     * @param offset - The offset of the section of interest.
 | 
						|
     *
 | 
						|
     * @returns The index of the section which covers the given offset,
 | 
						|
     *   or `-1` if the offset is out of range.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Logarithmic on the number of resized sections.
 | 
						|
     */
 | 
						|
    indexOf(offset) {
 | 
						|
        // Bail early if the offset is out of range.
 | 
						|
        if (offset < 0 || offset >= this._length || this._count === 0) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Handle the simple case of no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return Math.floor(offset / this._defaultSize);
 | 
						|
        }
 | 
						|
        // Find the modified section for the given offset.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, offset, Private$2.offsetCmp);
 | 
						|
        // Return the index of an exact match.
 | 
						|
        if (i < this._sections.length && this._sections[i].offset <= offset) {
 | 
						|
            return this._sections[i].index;
 | 
						|
        }
 | 
						|
        // Handle the case of no modified sections before the offset.
 | 
						|
        if (i === 0) {
 | 
						|
            return Math.floor(offset / this._defaultSize);
 | 
						|
        }
 | 
						|
        // Compute the index from the previous modified section.
 | 
						|
        let section = this._sections[i - 1];
 | 
						|
        let span = offset - (section.offset + section.size);
 | 
						|
        return section.index + Math.floor(span / this._defaultSize) + 1;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Find the offset of the section at the given index.
 | 
						|
     *
 | 
						|
     * @param index - The index of the section of interest.
 | 
						|
     *
 | 
						|
     * @returns The offset of the section at the given index, or `-1`
 | 
						|
     *   if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Logarithmic on the number of resized sections.
 | 
						|
     */
 | 
						|
    offsetOf(index) {
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= this._count) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Handle the simple case of no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return index * this._defaultSize;
 | 
						|
        }
 | 
						|
        // Find the modified section for the given index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Return the offset of an exact match.
 | 
						|
        if (i < this._sections.length && this._sections[i].index === index) {
 | 
						|
            return this._sections[i].offset;
 | 
						|
        }
 | 
						|
        // Handle the case of no modified sections before the index.
 | 
						|
        if (i === 0) {
 | 
						|
            return index * this._defaultSize;
 | 
						|
        }
 | 
						|
        // Compute the offset from the previous modified section.
 | 
						|
        let section = this._sections[i - 1];
 | 
						|
        let span = index - section.index - 1;
 | 
						|
        return section.offset + section.size + span * this._defaultSize;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Find the extent of the section at the given index.
 | 
						|
     *
 | 
						|
     * @param index - The index of the section of interest.
 | 
						|
     *
 | 
						|
     * @returns The extent of the section at the given index, or `-1`
 | 
						|
     *   if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Logarithmic on the number of resized sections.
 | 
						|
     */
 | 
						|
    extentOf(index) {
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= this._count) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Handle the simple case of no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return (index + 1) * this._defaultSize - 1;
 | 
						|
        }
 | 
						|
        // Find the modified section for the given index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Return the offset of an exact match.
 | 
						|
        if (i < this._sections.length && this._sections[i].index === index) {
 | 
						|
            return this._sections[i].offset + this._sections[i].size - 1;
 | 
						|
        }
 | 
						|
        // Handle the case of no modified sections before the index.
 | 
						|
        if (i === 0) {
 | 
						|
            return (index + 1) * this._defaultSize - 1;
 | 
						|
        }
 | 
						|
        // Compute the offset from the previous modified section.
 | 
						|
        let section = this._sections[i - 1];
 | 
						|
        let span = index - section.index;
 | 
						|
        return section.offset + section.size + span * this._defaultSize - 1;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Find the size of the section at the given index.
 | 
						|
     *
 | 
						|
     * @param index - The index of the section of interest.
 | 
						|
     *
 | 
						|
     * @returns The size of the section at the given index, or `-1`
 | 
						|
     *   if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Logarithmic on the number of resized sections.
 | 
						|
     */
 | 
						|
    sizeOf(index) {
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= this._count) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Handle the simple case of no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return this._defaultSize;
 | 
						|
        }
 | 
						|
        // Find the modified section for the given index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Return the size of an exact match.
 | 
						|
        if (i < this._sections.length && this._sections[i].index === index) {
 | 
						|
            return this._sections[i].size;
 | 
						|
        }
 | 
						|
        // Return the default size for all other cases.
 | 
						|
        return this._defaultSize;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a section in the list.
 | 
						|
     *
 | 
						|
     * @param index - The index of the section to resize. This method
 | 
						|
     *   is a no-op if this value is out of range.
 | 
						|
     *
 | 
						|
     * @param size - The new size of the section. This value will be
 | 
						|
     *   clamped to an integer `>= 0`.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of resized sections.
 | 
						|
     */
 | 
						|
    resize(index, size) {
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= this._count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the size to an integer >= minimum size.
 | 
						|
        size = Math.max(this._minimumSize, Math.floor(size));
 | 
						|
        // Find the modified section for the given index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Update or create the modified section as needed.
 | 
						|
        let delta;
 | 
						|
        if (i < this._sections.length && this._sections[i].index === index) {
 | 
						|
            let section = this._sections[i];
 | 
						|
            delta = size - section.size;
 | 
						|
            section.size = size;
 | 
						|
        }
 | 
						|
        else if (i === 0) {
 | 
						|
            let offset = index * this._defaultSize;
 | 
						|
            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.insert(this._sections, i, { index, offset, size });
 | 
						|
            delta = size - this._defaultSize;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            let section = this._sections[i - 1];
 | 
						|
            let span = index - section.index - 1;
 | 
						|
            let offset = section.offset + section.size + span * this._defaultSize;
 | 
						|
            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.insert(this._sections, i, { index, offset, size });
 | 
						|
            delta = size - this._defaultSize;
 | 
						|
        }
 | 
						|
        // Adjust the length.
 | 
						|
        this._length += delta;
 | 
						|
        // Update all modified sections after the resized section.
 | 
						|
        for (let j = i + 1, n = this._sections.length; j < n; ++j) {
 | 
						|
            this._sections[j].offset += delta;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Insert sections into the list.
 | 
						|
     *
 | 
						|
     * @param index - The index at which to insert the sections. This
 | 
						|
     *   value will be clamped to the bounds of the list.
 | 
						|
     *
 | 
						|
     * @param count - The number of sections to insert. This method
 | 
						|
     *   is a no-op if this value is `<= 0`.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` or `count` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of resized sections.
 | 
						|
     */
 | 
						|
    insert(index, count) {
 | 
						|
        // Bail early if there are no sections to insert.
 | 
						|
        if (count <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the index to the bounds of the list.
 | 
						|
        index = Math.max(0, Math.min(index, this._count));
 | 
						|
        // Add the new sections to the totals.
 | 
						|
        let span = count * this._defaultSize;
 | 
						|
        this._count += count;
 | 
						|
        this._length += span;
 | 
						|
        // Bail early if there are no modified sections to update.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Find the modified section for the given index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Update all modified sections after the insert location.
 | 
						|
        for (let n = this._sections.length; i < n; ++i) {
 | 
						|
            let section = this._sections[i];
 | 
						|
            section.index += count;
 | 
						|
            section.offset += span;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Remove sections from the list.
 | 
						|
     *
 | 
						|
     * @param index - The index of the first section to remove. This
 | 
						|
     *   method is a no-op if this value is out of range.
 | 
						|
     *
 | 
						|
     * @param count - The number of sections to remove. This method
 | 
						|
     *   is a no-op if this value is `<= 0`.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index` or `count` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of resized sections.
 | 
						|
     */
 | 
						|
    remove(index, count) {
 | 
						|
        // Bail early if there is nothing to remove.
 | 
						|
        if (index < 0 || index >= this._count || count <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the count to the bounds of the list.
 | 
						|
        count = Math.min(this._count - index, count);
 | 
						|
        // Handle the simple case of no modified sections to update.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            this._count -= count;
 | 
						|
            this._length -= count * this._defaultSize;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the simple case of removing all sections.
 | 
						|
        if (count === this._count) {
 | 
						|
            this._length = 0;
 | 
						|
            this._count = 0;
 | 
						|
            this._sections.length = 0;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Find the modified section for the start index.
 | 
						|
        let i = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index, Private$2.indexCmp);
 | 
						|
        // Find the modified section for the end index.
 | 
						|
        let j = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, index + count, Private$2.indexCmp);
 | 
						|
        // Remove the relevant modified sections.
 | 
						|
        let removed = this._sections.splice(i, j - i);
 | 
						|
        // Compute the total removed span.
 | 
						|
        let span = (count - removed.length) * this._defaultSize;
 | 
						|
        for (let k = 0, n = removed.length; k < n; ++k) {
 | 
						|
            span += removed[k].size;
 | 
						|
        }
 | 
						|
        // Adjust the totals.
 | 
						|
        this._count -= count;
 | 
						|
        this._length -= span;
 | 
						|
        // Update all modified sections after the removed span.
 | 
						|
        for (let k = i, n = this._sections.length; k < n; ++k) {
 | 
						|
            let section = this._sections[k];
 | 
						|
            section.index -= count;
 | 
						|
            section.offset -= span;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Move sections within the list.
 | 
						|
     *
 | 
						|
     * @param index - The index of the first section to move. This method
 | 
						|
     *   is a no-op if this value is out of range.
 | 
						|
     *
 | 
						|
     * @param count - The number of sections to move. This method is a
 | 
						|
     *   no-op if this value is `<= 0`.
 | 
						|
     *
 | 
						|
     * @param destination - The destination index for the first section.
 | 
						|
     *   This value will be clamped to the allowable range.
 | 
						|
     *
 | 
						|
     * #### Undefined Behavior
 | 
						|
     * An `index`, `count`, or `destination` which is non-integral.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Linear on the number of moved resized sections.
 | 
						|
     */
 | 
						|
    move(index, count, destination) {
 | 
						|
        // Bail early if there is nothing to move.
 | 
						|
        if (index < 0 || index >= this._count || count <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Handle the simple case of no modified sections.
 | 
						|
        if (this._sections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the move count to the limit.
 | 
						|
        count = Math.min(count, this._count - index);
 | 
						|
        // Clamp the destination index to the limit.
 | 
						|
        destination = Math.min(Math.max(0, destination), this._count - count);
 | 
						|
        // Bail early if there is no effective move.
 | 
						|
        if (index === destination) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the first affected index.
 | 
						|
        let i1 = Math.min(index, destination);
 | 
						|
        // Look up the first affected modified section.
 | 
						|
        let k1 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, i1, Private$2.indexCmp);
 | 
						|
        // Bail early if there are no affected modified sections.
 | 
						|
        if (k1 === this._sections.length) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the last affected index.
 | 
						|
        let i2 = Math.max(index + count - 1, destination + count - 1);
 | 
						|
        // Look up the last affected modified section.
 | 
						|
        let k2 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.upperBound(this._sections, i2, Private$2.indexCmp) - 1;
 | 
						|
        // Bail early if there are no affected modified sections.
 | 
						|
        if (k2 < k1) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the pivot index.
 | 
						|
        let pivot = destination < index ? index : index + count;
 | 
						|
        // Compute the count for each side of the pivot.
 | 
						|
        let count1 = pivot - i1;
 | 
						|
        let count2 = i2 - pivot + 1;
 | 
						|
        // Compute the span for each side of the pivot.
 | 
						|
        let span1 = count1 * this._defaultSize;
 | 
						|
        let span2 = count2 * this._defaultSize;
 | 
						|
        // Adjust the spans for the modified sections.
 | 
						|
        for (let j = k1; j <= k2; ++j) {
 | 
						|
            let section = this._sections[j];
 | 
						|
            if (section.index < pivot) {
 | 
						|
                span1 += section.size - this._defaultSize;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                span2 += section.size - this._defaultSize;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Look up the pivot section.
 | 
						|
        let k3 = _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.lowerBound(this._sections, pivot, Private$2.indexCmp);
 | 
						|
        // Rotate the modified sections if needed.
 | 
						|
        if (k1 <= k3 && k3 <= k2) {
 | 
						|
            _lumino_algorithm__WEBPACK_IMPORTED_MODULE_3__.ArrayExt.rotate(this._sections, k3 - k1, k1, k2);
 | 
						|
        }
 | 
						|
        // Adjust the modified section indices and offsets.
 | 
						|
        for (let j = k1; j <= k2; ++j) {
 | 
						|
            let section = this._sections[j];
 | 
						|
            if (section.index < pivot) {
 | 
						|
                section.index += count2;
 | 
						|
                section.offset += span2;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                section.index -= count1;
 | 
						|
                section.offset -= span1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Reset all modified sections to the default size.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    reset() {
 | 
						|
        this._sections.length = 0;
 | 
						|
        this._length = this._count * this._defaultSize;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Remove all sections from the list.
 | 
						|
     *
 | 
						|
     * #### Complexity
 | 
						|
     * Constant.
 | 
						|
     */
 | 
						|
    clear() {
 | 
						|
        this._count = 0;
 | 
						|
        this._length = 0;
 | 
						|
        this._sections.length = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$2;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * A comparison function for searching by offset.
 | 
						|
     */
 | 
						|
    function offsetCmp(section, offset) {
 | 
						|
        if (offset < section.offset) {
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        if (section.offset + section.size <= offset) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    Private.offsetCmp = offsetCmp;
 | 
						|
    /**
 | 
						|
     * A comparison function for searching by index.
 | 
						|
     */
 | 
						|
    function indexCmp(section, index) {
 | 
						|
        return section.index - index;
 | 
						|
    }
 | 
						|
    Private.indexCmp = indexCmp;
 | 
						|
})(Private$2 || (Private$2 = {}));
 | 
						|
 | 
						|
/**
 | 
						|
 * A widget which implements a high-performance tabular data grid.
 | 
						|
 *
 | 
						|
 * #### Notes
 | 
						|
 * A data grid is implemented as a composition of child widgets. These
 | 
						|
 * child widgets are considered an implementation detail. Manipulating
 | 
						|
 * the child widgets of a data grid directly is undefined behavior.
 | 
						|
 *
 | 
						|
 * This class is not designed to be subclassed.
 | 
						|
 *
 | 
						|
 * See also the related [example](../../examples/datagrid/index.html) and
 | 
						|
 * its [source](https://github.com/jupyterlab/lumino/tree/main/examples/example-datagrid).
 | 
						|
 */
 | 
						|
class DataGrid extends _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget {
 | 
						|
    /**
 | 
						|
     * Construct a new data grid.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the data grid.
 | 
						|
     */
 | 
						|
    constructor(options = {}) {
 | 
						|
        super();
 | 
						|
        this._scrollX = 0;
 | 
						|
        this._scrollY = 0;
 | 
						|
        this._viewportWidth = 0;
 | 
						|
        this._viewportHeight = 0;
 | 
						|
        this._mousedown = false;
 | 
						|
        this._keyHandler = null;
 | 
						|
        this._mouseHandler = null;
 | 
						|
        this._vScrollBarMinWidth = 0;
 | 
						|
        this._hScrollBarMinHeight = 0;
 | 
						|
        this._dpiRatio = Math.ceil(window.devicePixelRatio);
 | 
						|
        this._dataModel = null;
 | 
						|
        this._selectionModel = null;
 | 
						|
        this._editingEnabled = false;
 | 
						|
        this.addClass('lm-DataGrid');
 | 
						|
        // Parse the simple options.
 | 
						|
        this._style = options.style || DataGrid.defaultStyle;
 | 
						|
        this._stretchLastRow = options.stretchLastRow || false;
 | 
						|
        this._stretchLastColumn = options.stretchLastColumn || false;
 | 
						|
        this._headerVisibility = options.headerVisibility || 'all';
 | 
						|
        this._cellRenderers = options.cellRenderers || new RendererMap();
 | 
						|
        this._copyConfig = options.copyConfig || DataGrid.defaultCopyConfig;
 | 
						|
        // Connect to the renderer map changed signal.
 | 
						|
        this._cellRenderers.changed.connect(this._onRenderersChanged, this);
 | 
						|
        // Parse the default sizes.
 | 
						|
        let defaultSizes = options.defaultSizes || DataGrid.defaultSizes;
 | 
						|
        let minimumSizes = options.minimumSizes || DataGrid.minimumSizes;
 | 
						|
        // Set up the sections lists.
 | 
						|
        this._rowSections = new SectionList({
 | 
						|
            defaultSize: defaultSizes.rowHeight,
 | 
						|
            minimumSize: minimumSizes.rowHeight
 | 
						|
        });
 | 
						|
        this._columnSections = new SectionList({
 | 
						|
            defaultSize: defaultSizes.columnWidth,
 | 
						|
            minimumSize: minimumSizes.columnWidth
 | 
						|
        });
 | 
						|
        this._rowHeaderSections = new SectionList({
 | 
						|
            defaultSize: defaultSizes.rowHeaderWidth,
 | 
						|
            minimumSize: minimumSizes.rowHeaderWidth
 | 
						|
        });
 | 
						|
        this._columnHeaderSections = new SectionList({
 | 
						|
            defaultSize: defaultSizes.columnHeaderHeight,
 | 
						|
            minimumSize: minimumSizes.columnHeaderHeight
 | 
						|
        });
 | 
						|
        // Create the canvas, buffer, and overlay objects.
 | 
						|
        this._canvas = Private$1.createCanvas();
 | 
						|
        this._buffer = Private$1.createCanvas();
 | 
						|
        this._overlay = Private$1.createCanvas();
 | 
						|
        // Get the graphics contexts for the canvases.
 | 
						|
        this._canvasGC = this._canvas.getContext('2d');
 | 
						|
        this._bufferGC = this._buffer.getContext('2d');
 | 
						|
        this._overlayGC = this._overlay.getContext('2d');
 | 
						|
        // Set up the on-screen canvas.
 | 
						|
        this._canvas.style.position = 'absolute';
 | 
						|
        this._canvas.style.top = '0px';
 | 
						|
        this._canvas.style.left = '0px';
 | 
						|
        this._canvas.style.width = '0px';
 | 
						|
        this._canvas.style.height = '0px';
 | 
						|
        // Set up the on-screen overlay.
 | 
						|
        this._overlay.style.position = 'absolute';
 | 
						|
        this._overlay.style.top = '0px';
 | 
						|
        this._overlay.style.left = '0px';
 | 
						|
        this._overlay.style.width = '0px';
 | 
						|
        this._overlay.style.height = '0px';
 | 
						|
        // Create the internal widgets for the data grid.
 | 
						|
        this._viewport = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget();
 | 
						|
        this._viewport.node.tabIndex = -1;
 | 
						|
        this._viewport.node.style.outline = 'none';
 | 
						|
        this._vScrollBar = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.ScrollBar({ orientation: 'vertical' });
 | 
						|
        this._hScrollBar = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.ScrollBar({ orientation: 'horizontal' });
 | 
						|
        this._scrollCorner = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget();
 | 
						|
        this._editorController = new CellEditorController();
 | 
						|
        // Add the extra class names to the child widgets.
 | 
						|
        this._viewport.addClass('lm-DataGrid-viewport');
 | 
						|
        this._vScrollBar.addClass('lm-DataGrid-scrollBar');
 | 
						|
        this._hScrollBar.addClass('lm-DataGrid-scrollBar');
 | 
						|
        this._scrollCorner.addClass('lm-DataGrid-scrollCorner');
 | 
						|
        // Add the on-screen canvas to the viewport node.
 | 
						|
        this._viewport.node.appendChild(this._canvas);
 | 
						|
        // Add the on-screen overlay to the viewport node.
 | 
						|
        this._viewport.node.appendChild(this._overlay);
 | 
						|
        // Install the message hooks.
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._viewport, this);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._hScrollBar, this);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.installMessageHook(this._vScrollBar, this);
 | 
						|
        // Hide the scroll bars and corner from the outset.
 | 
						|
        this._vScrollBar.hide();
 | 
						|
        this._hScrollBar.hide();
 | 
						|
        this._scrollCorner.hide();
 | 
						|
        // Connect to the scroll bar signals.
 | 
						|
        this._vScrollBar.thumbMoved.connect(this._onThumbMoved, this);
 | 
						|
        this._hScrollBar.thumbMoved.connect(this._onThumbMoved, this);
 | 
						|
        this._vScrollBar.pageRequested.connect(this._onPageRequested, this);
 | 
						|
        this._hScrollBar.pageRequested.connect(this._onPageRequested, this);
 | 
						|
        this._vScrollBar.stepRequested.connect(this._onStepRequested, this);
 | 
						|
        this._hScrollBar.stepRequested.connect(this._onStepRequested, this);
 | 
						|
        // Set the layout cell config for the child widgets.
 | 
						|
        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._viewport, { row: 0, column: 0 });
 | 
						|
        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._vScrollBar, { row: 0, column: 1 });
 | 
						|
        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._hScrollBar, { row: 1, column: 0 });
 | 
						|
        _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout.setCellConfig(this._scrollCorner, { row: 1, column: 1 });
 | 
						|
        // Create the layout for the data grid.
 | 
						|
        let layout = new _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.GridLayout({
 | 
						|
            rowCount: 2,
 | 
						|
            columnCount: 2,
 | 
						|
            rowSpacing: 0,
 | 
						|
            columnSpacing: 0,
 | 
						|
            fitPolicy: 'set-no-constraint'
 | 
						|
        });
 | 
						|
        // Set the stretch factors for the grid.
 | 
						|
        layout.setRowStretch(0, 1);
 | 
						|
        layout.setRowStretch(1, 0);
 | 
						|
        layout.setColumnStretch(0, 1);
 | 
						|
        layout.setColumnStretch(1, 0);
 | 
						|
        // Add the child widgets to the layout.
 | 
						|
        layout.addWidget(this._viewport);
 | 
						|
        layout.addWidget(this._vScrollBar);
 | 
						|
        layout.addWidget(this._hScrollBar);
 | 
						|
        layout.addWidget(this._scrollCorner);
 | 
						|
        // Install the layout on the data grid.
 | 
						|
        this.layout = layout;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Dispose of the resources held by the widgets.
 | 
						|
     */
 | 
						|
    dispose() {
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
        // Dispose of the handlers.
 | 
						|
        if (this._keyHandler) {
 | 
						|
            this._keyHandler.dispose();
 | 
						|
        }
 | 
						|
        if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.dispose();
 | 
						|
        }
 | 
						|
        this._keyHandler = null;
 | 
						|
        this._mouseHandler = null;
 | 
						|
        // Clear the models.
 | 
						|
        this._dataModel = null;
 | 
						|
        this._selectionModel = null;
 | 
						|
        // Clear the section lists.
 | 
						|
        this._rowSections.clear();
 | 
						|
        this._columnSections.clear();
 | 
						|
        this._rowHeaderSections.clear();
 | 
						|
        this._columnHeaderSections.clear();
 | 
						|
        // Dispose of the base class.
 | 
						|
        super.dispose();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the data model for the data grid.
 | 
						|
     */
 | 
						|
    get dataModel() {
 | 
						|
        return this._dataModel;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the data model for the data grid.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This will automatically remove the current selection model.
 | 
						|
     */
 | 
						|
    set dataModel(value) {
 | 
						|
        // Do nothing if the model does not change.
 | 
						|
        if (this._dataModel === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
        // Clear the selection model.
 | 
						|
        this.selectionModel = null;
 | 
						|
        // Disconnect the change handler from the old model.
 | 
						|
        if (this._dataModel) {
 | 
						|
            this._dataModel.changed.disconnect(this._onDataModelChanged, this);
 | 
						|
        }
 | 
						|
        // Connect the change handler for the new model.
 | 
						|
        if (value) {
 | 
						|
            value.changed.connect(this._onDataModelChanged, this);
 | 
						|
        }
 | 
						|
        // Update the internal model reference.
 | 
						|
        this._dataModel = value;
 | 
						|
        // Clear the section lists.
 | 
						|
        this._rowSections.clear();
 | 
						|
        this._columnSections.clear();
 | 
						|
        this._rowHeaderSections.clear();
 | 
						|
        this._columnHeaderSections.clear();
 | 
						|
        // Populate the section lists.
 | 
						|
        if (value) {
 | 
						|
            this._rowSections.insert(0, value.rowCount('body'));
 | 
						|
            this._columnSections.insert(0, value.columnCount('body'));
 | 
						|
            this._rowHeaderSections.insert(0, value.columnCount('row-header'));
 | 
						|
            this._columnHeaderSections.insert(0, value.rowCount('column-header'));
 | 
						|
        }
 | 
						|
        // Reset the scroll position.
 | 
						|
        this._scrollX = 0;
 | 
						|
        this._scrollY = 0;
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the selection model for the data grid.
 | 
						|
     */
 | 
						|
    get selectionModel() {
 | 
						|
        return this._selectionModel;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the selection model for the data grid.
 | 
						|
     */
 | 
						|
    set selectionModel(value) {
 | 
						|
        // Do nothing if the selection model does not change.
 | 
						|
        if (this._selectionModel === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
        // Ensure the data models are a match.
 | 
						|
        if (value && value.dataModel !== this._dataModel) {
 | 
						|
            throw new Error('SelectionModel.dataModel !== DataGrid.dataModel');
 | 
						|
        }
 | 
						|
        // Disconnect the change handler from the old model.
 | 
						|
        if (this._selectionModel) {
 | 
						|
            this._selectionModel.changed.disconnect(this._onSelectionsChanged, this);
 | 
						|
        }
 | 
						|
        // Connect the change handler for the new model.
 | 
						|
        if (value) {
 | 
						|
            value.changed.connect(this._onSelectionsChanged, this);
 | 
						|
        }
 | 
						|
        // Update the internal selection model reference.
 | 
						|
        this._selectionModel = value;
 | 
						|
        // Schedule a repaint of the overlay.
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the key handler for the data grid.
 | 
						|
     */
 | 
						|
    get keyHandler() {
 | 
						|
        return this._keyHandler;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the key handler for the data grid.
 | 
						|
     */
 | 
						|
    set keyHandler(value) {
 | 
						|
        this._keyHandler = value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the mouse handler for the data grid.
 | 
						|
     */
 | 
						|
    get mouseHandler() {
 | 
						|
        return this._mouseHandler;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the mouse handler for the data grid.
 | 
						|
     */
 | 
						|
    set mouseHandler(value) {
 | 
						|
        // Bail early if the mouse handler does not change.
 | 
						|
        if (this._mouseHandler === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
        // Update the internal mouse handler.
 | 
						|
        this._mouseHandler = value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the style for the data grid.
 | 
						|
     */
 | 
						|
    get style() {
 | 
						|
        return this._style;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the style for the data grid.
 | 
						|
     */
 | 
						|
    set style(value) {
 | 
						|
        // Bail if the style does not change.
 | 
						|
        if (this._style === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal style.
 | 
						|
        this._style = { ...value };
 | 
						|
        // Schedule a repaint of the content.
 | 
						|
        this.repaintContent();
 | 
						|
        // Schedule a repaint of the overlay.
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the cell renderer map for the data grid.
 | 
						|
     */
 | 
						|
    get cellRenderers() {
 | 
						|
        return this._cellRenderers;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the cell renderer map for the data grid.
 | 
						|
     */
 | 
						|
    set cellRenderers(value) {
 | 
						|
        // Bail if the renderer map does not change.
 | 
						|
        if (this._cellRenderers === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Disconnect the old map.
 | 
						|
        this._cellRenderers.changed.disconnect(this._onRenderersChanged, this);
 | 
						|
        // Connect the new map.
 | 
						|
        value.changed.connect(this._onRenderersChanged, this);
 | 
						|
        // Update the internal renderer map.
 | 
						|
        this._cellRenderers = value;
 | 
						|
        // Schedule a repaint of the grid content.
 | 
						|
        this.repaintContent();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the header visibility for the data grid.
 | 
						|
     */
 | 
						|
    get headerVisibility() {
 | 
						|
        return this._headerVisibility;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the header visibility for the data grid.
 | 
						|
     */
 | 
						|
    set headerVisibility(value) {
 | 
						|
        // Bail if the visibility does not change.
 | 
						|
        if (this._headerVisibility === value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal visibility.
 | 
						|
        this._headerVisibility = value;
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the default sizes for the various sections of the data grid.
 | 
						|
     */
 | 
						|
    get defaultSizes() {
 | 
						|
        let rowHeight = this._rowSections.defaultSize;
 | 
						|
        let columnWidth = this._columnSections.defaultSize;
 | 
						|
        let rowHeaderWidth = this._rowHeaderSections.defaultSize;
 | 
						|
        let columnHeaderHeight = this._columnHeaderSections.defaultSize;
 | 
						|
        return { rowHeight, columnWidth, rowHeaderWidth, columnHeaderHeight };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the default sizes for the various sections of the data grid.
 | 
						|
     */
 | 
						|
    set defaultSizes(value) {
 | 
						|
        // Update the section default sizes.
 | 
						|
        this._rowSections.defaultSize = value.rowHeight;
 | 
						|
        this._columnSections.defaultSize = value.columnWidth;
 | 
						|
        this._rowHeaderSections.defaultSize = value.rowHeaderWidth;
 | 
						|
        this._columnHeaderSections.defaultSize = value.columnHeaderHeight;
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the minimum sizes for the various sections of the data grid.
 | 
						|
     */
 | 
						|
    get minimumSizes() {
 | 
						|
        let rowHeight = this._rowSections.minimumSize;
 | 
						|
        let columnWidth = this._columnSections.minimumSize;
 | 
						|
        let rowHeaderWidth = this._rowHeaderSections.minimumSize;
 | 
						|
        let columnHeaderHeight = this._columnHeaderSections.minimumSize;
 | 
						|
        return { rowHeight, columnWidth, rowHeaderWidth, columnHeaderHeight };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the minimum sizes for the various sections of the data grid.
 | 
						|
     */
 | 
						|
    set minimumSizes(value) {
 | 
						|
        // Update the section default sizes.
 | 
						|
        this._rowSections.minimumSize = value.rowHeight;
 | 
						|
        this._columnSections.minimumSize = value.columnWidth;
 | 
						|
        this._rowHeaderSections.minimumSize = value.rowHeaderWidth;
 | 
						|
        this._columnHeaderSections.minimumSize = value.columnHeaderHeight;
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the copy configuration for the data grid.
 | 
						|
     */
 | 
						|
    get copyConfig() {
 | 
						|
        return this._copyConfig;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set the copy configuration for the data grid.
 | 
						|
     */
 | 
						|
    set copyConfig(value) {
 | 
						|
        this._copyConfig = value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get whether the last row is stretched.
 | 
						|
     */
 | 
						|
    get stretchLastRow() {
 | 
						|
        return this._stretchLastRow;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set whether the last row is stretched.
 | 
						|
     */
 | 
						|
    set stretchLastRow(value) {
 | 
						|
        // Bail early if the value does not change.
 | 
						|
        if (value === this._stretchLastRow) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal value.
 | 
						|
        this._stretchLastRow = value;
 | 
						|
        // Sync the viewport
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get whether the last column is stretched.
 | 
						|
     */
 | 
						|
    get stretchLastColumn() {
 | 
						|
        return this._stretchLastColumn;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Set whether the last column is stretched.
 | 
						|
     */
 | 
						|
    set stretchLastColumn(value) {
 | 
						|
        // Bail early if the value does not change.
 | 
						|
        if (value === this._stretchLastColumn) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal value.
 | 
						|
        this._stretchLastColumn = value;
 | 
						|
        // Sync the viewport
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual width of the row headers.
 | 
						|
     */
 | 
						|
    get headerWidth() {
 | 
						|
        if (this._headerVisibility === 'none') {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        if (this._headerVisibility === 'column') {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        return this._rowHeaderSections.length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual height of the column headers.
 | 
						|
     */
 | 
						|
    get headerHeight() {
 | 
						|
        if (this._headerVisibility === 'none') {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        if (this._headerVisibility === 'row') {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        return this._columnHeaderSections.length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual width of the grid body.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This does *not* account for a stretched last column.
 | 
						|
     */
 | 
						|
    get bodyWidth() {
 | 
						|
        return this._columnSections.length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual height of the grid body.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This does *not* account for a stretched last row.
 | 
						|
     */
 | 
						|
    get bodyHeight() {
 | 
						|
        return this._rowSections.length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual width of the entire grid.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This does *not* account for a stretched last column.
 | 
						|
     */
 | 
						|
    get totalWidth() {
 | 
						|
        return this.headerWidth + this.bodyWidth;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The virtual height of the entire grid.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This does *not* account for a stretched last row.
 | 
						|
     */
 | 
						|
    get totalHeight() {
 | 
						|
        return this.headerHeight + this.bodyHeight;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The actual width of the viewport.
 | 
						|
     */
 | 
						|
    get viewportWidth() {
 | 
						|
        return this._viewportWidth;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The actual height of the viewport.
 | 
						|
     */
 | 
						|
    get viewportHeight() {
 | 
						|
        return this._viewportHeight;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The width of the visible portion of the grid body.
 | 
						|
     */
 | 
						|
    get pageWidth() {
 | 
						|
        return Math.max(0, this.viewportWidth - this.headerWidth);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The height of the visible portion of the grid body.
 | 
						|
     */
 | 
						|
    get pageHeight() {
 | 
						|
        return Math.max(0, this.viewportHeight - this.headerHeight);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The current scroll X position of the viewport.
 | 
						|
     */
 | 
						|
    get scrollX() {
 | 
						|
        return this._hScrollBar.value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The current scroll Y position of the viewport.
 | 
						|
     */
 | 
						|
    get scrollY() {
 | 
						|
        return this._vScrollBar.value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The maximum scroll X position for the grid.
 | 
						|
     */
 | 
						|
    get maxScrollX() {
 | 
						|
        return Math.max(0, this.bodyWidth - this.pageWidth - 1);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The maximum scroll Y position for the grid.
 | 
						|
     */
 | 
						|
    get maxScrollY() {
 | 
						|
        return Math.max(0, this.bodyHeight - this.pageHeight - 1);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The viewport widget for the data grid.
 | 
						|
     */
 | 
						|
    get viewport() {
 | 
						|
        return this._viewport;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The cell editor controller object for the data grid.
 | 
						|
     */
 | 
						|
    get editorController() {
 | 
						|
        return this._editorController;
 | 
						|
    }
 | 
						|
    set editorController(controller) {
 | 
						|
        this._editorController = controller;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the cell editing is enabled for the data grid.
 | 
						|
     */
 | 
						|
    get editingEnabled() {
 | 
						|
        return this._editingEnabled;
 | 
						|
    }
 | 
						|
    set editingEnabled(enabled) {
 | 
						|
        this._editingEnabled = enabled;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the grid cells are editable.
 | 
						|
     *
 | 
						|
     * `editingEnabled` flag must be on and grid must have required
 | 
						|
     * selection model, editor controller and data model properties.
 | 
						|
     */
 | 
						|
    get editable() {
 | 
						|
        return (this._editingEnabled &&
 | 
						|
            this._selectionModel !== null &&
 | 
						|
            this._editorController !== null &&
 | 
						|
            this.dataModel instanceof MutableDataModel);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The rendering context for painting the data grid.
 | 
						|
     */
 | 
						|
    get canvasGC() {
 | 
						|
        return this._canvasGC;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The row sections of the data grid.
 | 
						|
     */
 | 
						|
    get rowSections() {
 | 
						|
        return this._rowSections;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The column sections of the data grid.
 | 
						|
     */
 | 
						|
    get columnSections() {
 | 
						|
        return this._columnSections;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The row header sections of the data grid.
 | 
						|
     */
 | 
						|
    get rowHeaderSections() {
 | 
						|
        return this._rowHeaderSections;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * The column header sections of the data grid.
 | 
						|
     */
 | 
						|
    get columnHeaderSections() {
 | 
						|
        return this._columnHeaderSections;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the grid to the specified row.
 | 
						|
     *
 | 
						|
     * @param row - The row index of the cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This is a no-op if the row is already visible.
 | 
						|
     */
 | 
						|
    scrollToRow(row) {
 | 
						|
        // Fetch the row count.
 | 
						|
        let nr = this._rowSections.count;
 | 
						|
        // Bail early if there is no content.
 | 
						|
        if (nr === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Floor the row index.
 | 
						|
        row = Math.floor(row);
 | 
						|
        // Clamp the row index.
 | 
						|
        row = Math.max(0, Math.min(row, nr - 1));
 | 
						|
        // Get the virtual bounds of the row.
 | 
						|
        let y1 = this._rowSections.offsetOf(row);
 | 
						|
        let y2 = this._rowSections.extentOf(row);
 | 
						|
        // Get the virtual bounds of the viewport.
 | 
						|
        let vy1 = this._scrollY;
 | 
						|
        let vy2 = this._scrollY + this.pageHeight - 1;
 | 
						|
        // Set up the delta variables.
 | 
						|
        let dy = 0;
 | 
						|
        // Compute the delta Y scroll.
 | 
						|
        if (y1 < vy1) {
 | 
						|
            dy = y1 - vy1 - 10;
 | 
						|
        }
 | 
						|
        else if (y2 > vy2) {
 | 
						|
            dy = y2 - vy2 + 10;
 | 
						|
        }
 | 
						|
        // Bail early if no scroll is needed.
 | 
						|
        if (dy === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll by the computed delta.
 | 
						|
        this.scrollBy(0, dy);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the grid to the specified column.
 | 
						|
     *
 | 
						|
     * @param column - The column index of the cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This is a no-op if the column is already visible.
 | 
						|
     */
 | 
						|
    scrollToColumn(column) {
 | 
						|
        // Fetch the column count.
 | 
						|
        let nc = this._columnSections.count;
 | 
						|
        // Bail early if there is no content.
 | 
						|
        if (nc === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Floor the column index.
 | 
						|
        column = Math.floor(column);
 | 
						|
        // Clamp the column index.
 | 
						|
        column = Math.max(0, Math.min(column, nc - 1));
 | 
						|
        // Get the virtual bounds of the column.
 | 
						|
        let x1 = this._columnSections.offsetOf(column);
 | 
						|
        let x2 = this._columnSections.extentOf(column);
 | 
						|
        // Get the virtual bounds of the viewport.
 | 
						|
        let vx1 = this._scrollX;
 | 
						|
        let vx2 = this._scrollX + this.pageWidth - 1;
 | 
						|
        // Set up the delta variables.
 | 
						|
        let dx = 0;
 | 
						|
        // Compute the delta X scroll.
 | 
						|
        if (x1 < vx1) {
 | 
						|
            dx = x1 - vx1 - 10;
 | 
						|
        }
 | 
						|
        else if (x2 > vx2) {
 | 
						|
            dx = x2 - vx2 + 10;
 | 
						|
        }
 | 
						|
        // Bail early if no scroll is needed.
 | 
						|
        if (dx === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll by the computed delta.
 | 
						|
        this.scrollBy(dx, 0);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the grid to the specified cell.
 | 
						|
     *
 | 
						|
     * @param row - The row index of the cell.
 | 
						|
     *
 | 
						|
     * @param column - The column index of the cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This is a no-op if the cell is already visible.
 | 
						|
     */
 | 
						|
    scrollToCell(row, column) {
 | 
						|
        // Fetch the row and column count.
 | 
						|
        let nr = this._rowSections.count;
 | 
						|
        let nc = this._columnSections.count;
 | 
						|
        // Bail early if there is no content.
 | 
						|
        if (nr === 0 || nc === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Floor the cell index.
 | 
						|
        row = Math.floor(row);
 | 
						|
        column = Math.floor(column);
 | 
						|
        // Clamp the cell index.
 | 
						|
        row = Math.max(0, Math.min(row, nr - 1));
 | 
						|
        column = Math.max(0, Math.min(column, nc - 1));
 | 
						|
        // Get the virtual bounds of the cell.
 | 
						|
        let x1 = this._columnSections.offsetOf(column);
 | 
						|
        let x2 = this._columnSections.extentOf(column);
 | 
						|
        let y1 = this._rowSections.offsetOf(row);
 | 
						|
        let y2 = this._rowSections.extentOf(row);
 | 
						|
        // Get the virtual bounds of the viewport.
 | 
						|
        let vx1 = this._scrollX;
 | 
						|
        let vx2 = this._scrollX + this.pageWidth - 1;
 | 
						|
        let vy1 = this._scrollY;
 | 
						|
        let vy2 = this._scrollY + this.pageHeight - 1;
 | 
						|
        // Set up the delta variables.
 | 
						|
        let dx = 0;
 | 
						|
        let dy = 0;
 | 
						|
        // Compute the delta X scroll.
 | 
						|
        if (x1 < vx1) {
 | 
						|
            dx = x1 - vx1 - 10;
 | 
						|
        }
 | 
						|
        else if (x2 > vx2) {
 | 
						|
            dx = x2 - vx2 + 10;
 | 
						|
        }
 | 
						|
        // Compute the delta Y scroll.
 | 
						|
        if (y1 < vy1) {
 | 
						|
            dy = y1 - vy1 - 10;
 | 
						|
        }
 | 
						|
        else if (y2 > vy2) {
 | 
						|
            dy = y2 - vy2 + 10;
 | 
						|
        }
 | 
						|
        // Bail early if no scroll is needed.
 | 
						|
        if (dx === 0 && dy === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Scroll by the computed delta.
 | 
						|
        this.scrollBy(dx, dy);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Move cursor down/up/left/right while making sure it remains
 | 
						|
     * within the bounds of selected rectangles
 | 
						|
     *
 | 
						|
     * @param direction - The direction of the movement.
 | 
						|
     */
 | 
						|
    moveCursor(direction) {
 | 
						|
        // Bail early if there is no selection
 | 
						|
        if (!this.dataModel ||
 | 
						|
            !this._selectionModel ||
 | 
						|
            this._selectionModel.isEmpty) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const iter = this._selectionModel.selections();
 | 
						|
        const onlyOne = iter.next() && !iter.next();
 | 
						|
        // if there is a single selection that is a single cell selection
 | 
						|
        // then move the selection and cursor within grid bounds
 | 
						|
        if (onlyOne) {
 | 
						|
            const currentSel = this._selectionModel.currentSelection();
 | 
						|
            if (currentSel.r1 === currentSel.r2 && currentSel.c1 === currentSel.c2) {
 | 
						|
                const dr = direction === 'down' ? 1 : direction === 'up' ? -1 : 0;
 | 
						|
                const dc = direction === 'right' ? 1 : direction === 'left' ? -1 : 0;
 | 
						|
                let newRow = currentSel.r1 + dr;
 | 
						|
                let newColumn = currentSel.c1 + dc;
 | 
						|
                const rowCount = this.dataModel.rowCount('body');
 | 
						|
                const columnCount = this.dataModel.columnCount('body');
 | 
						|
                if (newRow >= rowCount) {
 | 
						|
                    newRow = 0;
 | 
						|
                    newColumn += 1;
 | 
						|
                }
 | 
						|
                else if (newRow === -1) {
 | 
						|
                    newRow = rowCount - 1;
 | 
						|
                    newColumn -= 1;
 | 
						|
                }
 | 
						|
                if (newColumn >= columnCount) {
 | 
						|
                    newColumn = 0;
 | 
						|
                    newRow += 1;
 | 
						|
                    if (newRow >= rowCount) {
 | 
						|
                        newRow = 0;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else if (newColumn === -1) {
 | 
						|
                    newColumn = columnCount - 1;
 | 
						|
                    newRow -= 1;
 | 
						|
                    if (newRow === -1) {
 | 
						|
                        newRow = rowCount - 1;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                this._selectionModel.select({
 | 
						|
                    r1: newRow,
 | 
						|
                    c1: newColumn,
 | 
						|
                    r2: newRow,
 | 
						|
                    c2: newColumn,
 | 
						|
                    cursorRow: newRow,
 | 
						|
                    cursorColumn: newColumn,
 | 
						|
                    clear: 'all'
 | 
						|
                });
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // if there are multiple selections, move cursor
 | 
						|
        // within selection rectangles
 | 
						|
        this._selectionModel.moveCursorWithinSelections(direction);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the grid to the current cursor position.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This is a no-op if the cursor is already visible or
 | 
						|
     * if there is no selection model installed on the grid.
 | 
						|
     */
 | 
						|
    scrollToCursor() {
 | 
						|
        // Bail early if there is no selection model.
 | 
						|
        if (!this._selectionModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor row and column.
 | 
						|
        let row = this._selectionModel.cursorRow;
 | 
						|
        let column = this._selectionModel.cursorColumn;
 | 
						|
        // Scroll to the cursor cell.
 | 
						|
        this.scrollToCell(row, column);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the viewport by the specified amount.
 | 
						|
     *
 | 
						|
     * @param dx - The X scroll amount.
 | 
						|
     *
 | 
						|
     * @param dy - The Y scroll amount.
 | 
						|
     */
 | 
						|
    scrollBy(dx, dy) {
 | 
						|
        this.scrollTo(this.scrollX + dx, this.scrollY + dy);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the viewport by one page.
 | 
						|
     *
 | 
						|
     * @param dir - The desired direction of the scroll.
 | 
						|
     */
 | 
						|
    scrollByPage(dir) {
 | 
						|
        let dx = 0;
 | 
						|
        let dy = 0;
 | 
						|
        switch (dir) {
 | 
						|
            case 'up':
 | 
						|
                dy = -this.pageHeight;
 | 
						|
                break;
 | 
						|
            case 'down':
 | 
						|
                dy = this.pageHeight;
 | 
						|
                break;
 | 
						|
            case 'left':
 | 
						|
                dx = -this.pageWidth;
 | 
						|
                break;
 | 
						|
            case 'right':
 | 
						|
                dx = this.pageWidth;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        this.scrollTo(this.scrollX + dx, this.scrollY + dy);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll the viewport by one cell-aligned step.
 | 
						|
     *
 | 
						|
     * @param dir - The desired direction of the scroll.
 | 
						|
     */
 | 
						|
    scrollByStep(dir) {
 | 
						|
        let r;
 | 
						|
        let c;
 | 
						|
        let x = this.scrollX;
 | 
						|
        let y = this.scrollY;
 | 
						|
        let rows = this._rowSections;
 | 
						|
        let columns = this._columnSections;
 | 
						|
        switch (dir) {
 | 
						|
            case 'up':
 | 
						|
                r = rows.indexOf(y - 1);
 | 
						|
                y = r < 0 ? y : rows.offsetOf(r);
 | 
						|
                break;
 | 
						|
            case 'down':
 | 
						|
                r = rows.indexOf(y);
 | 
						|
                y = r < 0 ? y : rows.offsetOf(r) + rows.sizeOf(r);
 | 
						|
                break;
 | 
						|
            case 'left':
 | 
						|
                c = columns.indexOf(x - 1);
 | 
						|
                x = c < 0 ? x : columns.offsetOf(c);
 | 
						|
                break;
 | 
						|
            case 'right':
 | 
						|
                c = columns.indexOf(x);
 | 
						|
                x = c < 0 ? x : columns.offsetOf(c) + columns.sizeOf(c);
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        this.scrollTo(x, y);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll to the specified offset position.
 | 
						|
     *
 | 
						|
     * @param x - The desired X position.
 | 
						|
     *
 | 
						|
     * @param y - The desired Y position.
 | 
						|
     */
 | 
						|
    scrollTo(x, y) {
 | 
						|
        // Floor and clamp the position to the allowable range.
 | 
						|
        x = Math.max(0, Math.min(Math.floor(x), this.maxScrollX));
 | 
						|
        y = Math.max(0, Math.min(Math.floor(y), this.maxScrollY));
 | 
						|
        // Update the scroll bar values with the desired position.
 | 
						|
        this._hScrollBar.value = x;
 | 
						|
        this._vScrollBar.value = y;
 | 
						|
        // Post a scroll request message to the viewport.
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.ScrollRequest);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the row count for a particular region in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The row region of interest.
 | 
						|
     *
 | 
						|
     * @returns The row count for the specified region.
 | 
						|
     */
 | 
						|
    rowCount(region) {
 | 
						|
        let count;
 | 
						|
        if (region === 'body') {
 | 
						|
            count = this._rowSections.count;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            count = this._columnHeaderSections.count;
 | 
						|
        }
 | 
						|
        return count;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the column count for a particular region in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The column region of interest.
 | 
						|
     *
 | 
						|
     * @returns The column count for the specified region.
 | 
						|
     */
 | 
						|
    columnCount(region) {
 | 
						|
        let count;
 | 
						|
        if (region === 'body') {
 | 
						|
            count = this._columnSections.count;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            count = this._rowHeaderSections.count;
 | 
						|
        }
 | 
						|
        return count;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the row at a virtual offset in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the row of interest.
 | 
						|
     *
 | 
						|
     * @param offset - The virtual offset of the row of interest.
 | 
						|
     *
 | 
						|
     * @returns The index of the row, or `-1` if the offset is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method accounts for a stretched last row.
 | 
						|
     */
 | 
						|
    rowAt(region, offset) {
 | 
						|
        // Bail early if the offset is negative.
 | 
						|
        if (offset < 0) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Return early for the column header region.
 | 
						|
        if (region === 'column-header') {
 | 
						|
            return this._columnHeaderSections.indexOf(offset);
 | 
						|
        }
 | 
						|
        // Fetch the index.
 | 
						|
        let index = this._rowSections.indexOf(offset);
 | 
						|
        // Return early if the section is found.
 | 
						|
        if (index >= 0) {
 | 
						|
            return index;
 | 
						|
        }
 | 
						|
        // Bail early if the last row is not stretched.
 | 
						|
        if (!this._stretchLastRow) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        // Bail early if no row stretching is required.
 | 
						|
        if (ph <= bh) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Bail early if the offset is out of bounds.
 | 
						|
        if (offset >= ph) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Otherwise, return the last row.
 | 
						|
        return this._rowSections.count - 1;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the column at a virtual offset in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the column of interest.
 | 
						|
     *
 | 
						|
     * @param offset - The virtual offset of the column of interest.
 | 
						|
     *
 | 
						|
     * @returns The index of the column, or `-1` if the offset is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method accounts for a stretched last column.
 | 
						|
     */
 | 
						|
    columnAt(region, offset) {
 | 
						|
        if (offset < 0) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Return early for the row header region.
 | 
						|
        if (region === 'row-header') {
 | 
						|
            return this._rowHeaderSections.indexOf(offset);
 | 
						|
        }
 | 
						|
        // Fetch the index.
 | 
						|
        let index = this._columnSections.indexOf(offset);
 | 
						|
        // Return early if the section is found.
 | 
						|
        if (index >= 0) {
 | 
						|
            return index;
 | 
						|
        }
 | 
						|
        // Bail early if the last column is not stretched.
 | 
						|
        if (!this._stretchLastColumn) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        // Bail early if no column stretching is required.
 | 
						|
        if (pw <= bw) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Bail early if the offset is out of bounds.
 | 
						|
        if (offset >= pw) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        // Otherwise, return the last column.
 | 
						|
        return this._columnSections.count - 1;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the offset of a row in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the row of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the row of interest.
 | 
						|
     *
 | 
						|
     * @returns The offset of the row, or `-1` if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * A stretched last row has no effect on the return value.
 | 
						|
     */
 | 
						|
    rowOffset(region, index) {
 | 
						|
        let offset;
 | 
						|
        if (region === 'body') {
 | 
						|
            offset = this._rowSections.offsetOf(index);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            offset = this._columnHeaderSections.offsetOf(index);
 | 
						|
        }
 | 
						|
        return offset;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the offset of a column in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the column of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the column of interest.
 | 
						|
     *
 | 
						|
     * @returns The offset of the column, or `-1` if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * A stretched last column has no effect on the return value.
 | 
						|
     */
 | 
						|
    columnOffset(region, index) {
 | 
						|
        let offset;
 | 
						|
        if (region === 'body') {
 | 
						|
            offset = this._columnSections.offsetOf(index);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            offset = this._rowHeaderSections.offsetOf(index);
 | 
						|
        }
 | 
						|
        return offset;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the size of a row in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the row of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the row of interest.
 | 
						|
     *
 | 
						|
     * @returns The size of the row, or `-1` if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method accounts for a stretched last row.
 | 
						|
     */
 | 
						|
    rowSize(region, index) {
 | 
						|
        // Return early for the column header region.
 | 
						|
        if (region === 'column-header') {
 | 
						|
            return this._columnHeaderSections.sizeOf(index);
 | 
						|
        }
 | 
						|
        // Fetch the row size.
 | 
						|
        let size = this._rowSections.sizeOf(index);
 | 
						|
        // Bail early if the index is out of bounds.
 | 
						|
        if (size < 0) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return early if the last row is not stretched.
 | 
						|
        if (!this._stretchLastRow) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return early if its not the last row.
 | 
						|
        if (index < this._rowSections.count - 1) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        // Return early if no stretching is needed.
 | 
						|
        if (ph <= bh) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return the adjusted size.
 | 
						|
        return size + (ph - bh);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the size of a column in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the column of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the column of interest.
 | 
						|
     *
 | 
						|
     * @returns The size of the column, or `-1` if the index is out of range.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method accounts for a stretched last column.
 | 
						|
     */
 | 
						|
    columnSize(region, index) {
 | 
						|
        // Return early for the row header region.
 | 
						|
        if (region === 'row-header') {
 | 
						|
            return this._rowHeaderSections.sizeOf(index);
 | 
						|
        }
 | 
						|
        // Fetch the column size.
 | 
						|
        let size = this._columnSections.sizeOf(index);
 | 
						|
        // Bail early if the index is out of bounds.
 | 
						|
        if (size < 0) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return early if the last column is not stretched.
 | 
						|
        if (!this._stretchLastColumn) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return early if its not the last column.
 | 
						|
        if (index < this._columnSections.count - 1) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        // Return early if no stretching is needed.
 | 
						|
        if (pw <= bw) {
 | 
						|
            return size;
 | 
						|
        }
 | 
						|
        // Return the adjusted size.
 | 
						|
        return size + (pw - bw);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a row in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the row of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the row of interest.
 | 
						|
     *
 | 
						|
     * @param size - The desired size of the row.
 | 
						|
     */
 | 
						|
    resizeRow(region, index, size) {
 | 
						|
        let msg = new Private$1.RowResizeRequest(region, index, size);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a column in the data grid.
 | 
						|
     *
 | 
						|
     * @param region - The region which holds the column of interest.
 | 
						|
     *
 | 
						|
     * @param index - The index of the column of interest.
 | 
						|
     *
 | 
						|
     * @param size - The desired size of the column.
 | 
						|
     */
 | 
						|
    resizeColumn(region, index, size) {
 | 
						|
        let msg = new Private$1.ColumnResizeRequest(region, index, size);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Reset modified rows to their default size.
 | 
						|
     *
 | 
						|
     * @param region - The row region of interest.
 | 
						|
     */
 | 
						|
    resetRows(region) {
 | 
						|
        switch (region) {
 | 
						|
            case 'all':
 | 
						|
                this._rowSections.reset();
 | 
						|
                this._columnHeaderSections.reset();
 | 
						|
                break;
 | 
						|
            case 'body':
 | 
						|
                this._rowSections.reset();
 | 
						|
                break;
 | 
						|
            case 'column-header':
 | 
						|
                this._columnHeaderSections.reset();
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        this.repaintContent();
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Reset modified columns to their default size.
 | 
						|
     *
 | 
						|
     * @param region - The column region of interest.
 | 
						|
     */
 | 
						|
    resetColumns(region) {
 | 
						|
        switch (region) {
 | 
						|
            case 'all':
 | 
						|
                this._columnSections.reset();
 | 
						|
                this._rowHeaderSections.reset();
 | 
						|
                break;
 | 
						|
            case 'body':
 | 
						|
                this._columnSections.reset();
 | 
						|
                break;
 | 
						|
            case 'row-header':
 | 
						|
                this._rowHeaderSections.reset();
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        this.repaintContent();
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Auto sizes column-header widths based on their text content.
 | 
						|
     * @param area which area to resize: 'body', 'row-header' or 'all'.
 | 
						|
     * @param padding padding added to resized columns (pixels).
 | 
						|
     * @param numCols specify cap on the number of column resizes (optional).
 | 
						|
     */
 | 
						|
    fitColumnNames(area = 'all', padding = 15, numCols) {
 | 
						|
        // Attempt resizing only if a data model is present.
 | 
						|
        if (this.dataModel) {
 | 
						|
            // Tracking remaining columns to be resized if numCols arg passed.
 | 
						|
            let colsRemaining = numCols === undefined || numCols < 0 ? undefined : numCols;
 | 
						|
            if (area === 'row-header' || area === 'all') {
 | 
						|
                // Respecting any column resize cap, if one has been passed.
 | 
						|
                if (colsRemaining !== undefined) {
 | 
						|
                    const rowColumnCount = this.dataModel.columnCount('row-header');
 | 
						|
                    /*
 | 
						|
                      If we have more row-header columns than columns available
 | 
						|
                      for resize, resize only remaining columns as per allowance
 | 
						|
                      and set remaining resize allowance number to 0.
 | 
						|
                    */
 | 
						|
                    if (colsRemaining - rowColumnCount < 0) {
 | 
						|
                        this._fitRowColumnHeaders(this.dataModel, padding, colsRemaining);
 | 
						|
                        colsRemaining = 0;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        /*
 | 
						|
                          Otherwise the entire row-header column count can be resized.
 | 
						|
                          Resize all row-header columns and subtract from remaining
 | 
						|
                          column resize allowance.
 | 
						|
                        */
 | 
						|
                        this._fitRowColumnHeaders(this.dataModel, padding, rowColumnCount);
 | 
						|
                        colsRemaining = colsRemaining - rowColumnCount;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // No column resize cap passed - resizing all columns.
 | 
						|
                    this._fitRowColumnHeaders(this.dataModel, padding);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (area === 'body' || area === 'all') {
 | 
						|
                // Respecting any column resize cap, if one has been passed.
 | 
						|
                if (colsRemaining !== undefined) {
 | 
						|
                    const bodyColumnCount = this.dataModel.columnCount('body');
 | 
						|
                    /*
 | 
						|
                      If we have more body columns than columns available
 | 
						|
                      for resize, resize only remaining columns as per allowance
 | 
						|
                      and set remaining resize allowance number to 0.
 | 
						|
                    */
 | 
						|
                    if (colsRemaining - bodyColumnCount < 0) {
 | 
						|
                        this._fitBodyColumnHeaders(this.dataModel, padding, colsRemaining);
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        /*
 | 
						|
                          Otherwise the entire body column count can be resized.
 | 
						|
                          Resize based on the smallest number between remaining
 | 
						|
                          resize allowance and body column count.
 | 
						|
                        */
 | 
						|
                        this._fitBodyColumnHeaders(this.dataModel, padding, Math.min(colsRemaining, bodyColumnCount));
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    // No column resize cap passed - resizing all columns.
 | 
						|
                    this._fitBodyColumnHeaders(this.dataModel, padding);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Map a client position to local viewport coordinates.
 | 
						|
     *
 | 
						|
     * @param clientX - The client X position of the mouse.
 | 
						|
     *
 | 
						|
     * @param clientY - The client Y position of the mouse.
 | 
						|
     *
 | 
						|
     * @returns The local viewport coordinates for the position.
 | 
						|
     */
 | 
						|
    mapToLocal(clientX, clientY) {
 | 
						|
        // Fetch the viewport rect.
 | 
						|
        let rect = this._viewport.node.getBoundingClientRect();
 | 
						|
        // Extract the rect coordinates.
 | 
						|
        let { left, top } = rect;
 | 
						|
        // Round the rect coordinates for sub-pixel positioning.
 | 
						|
        left = Math.floor(left);
 | 
						|
        top = Math.floor(top);
 | 
						|
        // Convert to local coordinates.
 | 
						|
        let lx = clientX - left;
 | 
						|
        let ly = clientY - top;
 | 
						|
        // Return the local coordinates.
 | 
						|
        return { lx, ly };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Map a client position to virtual grid coordinates.
 | 
						|
     *
 | 
						|
     * @param clientX - The client X position of the mouse.
 | 
						|
     *
 | 
						|
     * @param clientY - The client Y position of the mouse.
 | 
						|
     *
 | 
						|
     * @returns The virtual grid coordinates for the position.
 | 
						|
     */
 | 
						|
    mapToVirtual(clientX, clientY) {
 | 
						|
        // Convert to local coordiates.
 | 
						|
        let { lx, ly } = this.mapToLocal(clientX, clientY);
 | 
						|
        // Convert to virtual coordinates.
 | 
						|
        let vx = lx + this.scrollX - this.headerWidth;
 | 
						|
        let vy = ly + this.scrollY - this.headerHeight;
 | 
						|
        // Return the local coordinates.
 | 
						|
        return { vx, vy };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Hit test the viewport for the given client position.
 | 
						|
     *
 | 
						|
     * @param clientX - The client X position of the mouse.
 | 
						|
     *
 | 
						|
     * @param clientY - The client Y position of the mouse.
 | 
						|
     *
 | 
						|
     * @returns The hit test result, or `null` if the client
 | 
						|
     *   position is out of bounds.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method accounts for a stretched last row and/or column.
 | 
						|
     */
 | 
						|
    hitTest(clientX, clientY) {
 | 
						|
        // Convert the mouse position into local coordinates.
 | 
						|
        let { lx, ly } = this.mapToLocal(clientX, clientY);
 | 
						|
        // Fetch the header and body dimensions.
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        // Adjust the body width for a stretched last column.
 | 
						|
        if (this._stretchLastColumn && pw > bw) {
 | 
						|
            bw = pw;
 | 
						|
        }
 | 
						|
        // Adjust the body height for a stretched last row.
 | 
						|
        if (this._stretchLastRow && ph > bh) {
 | 
						|
            bh = ph;
 | 
						|
        }
 | 
						|
        // Check for a corner header hit.
 | 
						|
        if (lx >= 0 && lx < hw && ly >= 0 && ly < hh) {
 | 
						|
            // Convert to unscrolled virtual coordinates.
 | 
						|
            let vx = lx;
 | 
						|
            let vy = ly;
 | 
						|
            // Fetch the row and column index.
 | 
						|
            let row = this.rowAt('column-header', vy);
 | 
						|
            let column = this.columnAt('row-header', vx);
 | 
						|
            // Fetch the cell offset position.
 | 
						|
            let ox = this.columnOffset('row-header', column);
 | 
						|
            let oy = this.rowOffset('column-header', row);
 | 
						|
            // Fetch cell width and height.
 | 
						|
            let width = this.columnSize('row-header', column);
 | 
						|
            let height = this.rowSize('column-header', row);
 | 
						|
            // Compute the leading and trailing positions.
 | 
						|
            let x = vx - ox;
 | 
						|
            let y = vy - oy;
 | 
						|
            // Return the hit test result.
 | 
						|
            return { region: 'corner-header', row, column, x, y, width, height };
 | 
						|
        }
 | 
						|
        // Check for a column header hit.
 | 
						|
        if (ly >= 0 && ly < hh && lx >= 0 && lx < hw + bw) {
 | 
						|
            // Convert to unscrolled virtual coordinates.
 | 
						|
            let vx = lx + this._scrollX - hw;
 | 
						|
            let vy = ly;
 | 
						|
            // Fetch the row and column index.
 | 
						|
            let row = this.rowAt('column-header', vy);
 | 
						|
            let column = this.columnAt('body', vx);
 | 
						|
            // Fetch the cell offset position.
 | 
						|
            let ox = this.columnOffset('body', column);
 | 
						|
            let oy = this.rowOffset('column-header', row);
 | 
						|
            // Fetch the cell width and height.
 | 
						|
            let width = this.columnSize('body', column);
 | 
						|
            let height = this.rowSize('column-header', row);
 | 
						|
            // Compute the leading and trailing positions.
 | 
						|
            let x = vx - ox;
 | 
						|
            let y = vy - oy;
 | 
						|
            // Return the hit test result.
 | 
						|
            return { region: 'column-header', row, column, x, y, width, height };
 | 
						|
        }
 | 
						|
        // Check for a row header hit.
 | 
						|
        if (lx >= 0 && lx < hw && ly >= 0 && ly < hh + bh) {
 | 
						|
            // Convert to unscrolled virtual coordinates.
 | 
						|
            let vx = lx;
 | 
						|
            let vy = ly + this._scrollY - hh;
 | 
						|
            // Fetch the row and column index.
 | 
						|
            let row = this.rowAt('body', vy);
 | 
						|
            let column = this.columnAt('row-header', vx);
 | 
						|
            // Fetch the cell offset position.
 | 
						|
            let ox = this.columnOffset('row-header', column);
 | 
						|
            let oy = this.rowOffset('body', row);
 | 
						|
            // Fetch the cell width and height.
 | 
						|
            let width = this.columnSize('row-header', column);
 | 
						|
            let height = this.rowSize('body', row);
 | 
						|
            // Compute the leading and trailing positions.
 | 
						|
            let x = vx - ox;
 | 
						|
            let y = vy - oy;
 | 
						|
            // Return the hit test result.
 | 
						|
            return { region: 'row-header', row, column, x, y, width, height };
 | 
						|
        }
 | 
						|
        // Check for a body hit.
 | 
						|
        if (lx >= hw && lx < hw + bw && ly >= hh && ly < hh + bh) {
 | 
						|
            // Convert to unscrolled virtual coordinates.
 | 
						|
            let vx = lx + this._scrollX - hw;
 | 
						|
            let vy = ly + this._scrollY - hh;
 | 
						|
            // Fetch the row and column index.
 | 
						|
            let row = this.rowAt('body', vy);
 | 
						|
            let column = this.columnAt('body', vx);
 | 
						|
            // Fetch the cell offset position.
 | 
						|
            let ox = this.columnOffset('body', column);
 | 
						|
            let oy = this.rowOffset('body', row);
 | 
						|
            // Fetch the cell width and height.
 | 
						|
            let width = this.columnSize('body', column);
 | 
						|
            let height = this.rowSize('body', row);
 | 
						|
            // Compute the part coordinates.
 | 
						|
            let x = vx - ox;
 | 
						|
            let y = vy - oy;
 | 
						|
            // Return the result.
 | 
						|
            return { region: 'body', row, column, x, y, width, height };
 | 
						|
        }
 | 
						|
        // Otherwise, it's a void space hit.
 | 
						|
        let row = -1;
 | 
						|
        let column = -1;
 | 
						|
        let x = -1;
 | 
						|
        let y = -1;
 | 
						|
        let width = -1;
 | 
						|
        let height = -1;
 | 
						|
        // Return the hit test result.
 | 
						|
        return { region: 'void', row, column, x, y, width, height };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Copy the current selection to the system clipboard.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * The grid must have a data model and a selection model.
 | 
						|
     *
 | 
						|
     * The behavior can be configured via `DataGrid.copyConfig`.
 | 
						|
     */
 | 
						|
    copyToClipboard() {
 | 
						|
        // Fetch the data model.
 | 
						|
        let dataModel = this._dataModel;
 | 
						|
        // Bail early if there is no data model.
 | 
						|
        if (!dataModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection model.
 | 
						|
        let selectionModel = this._selectionModel;
 | 
						|
        // Bail early if there is no selection model.
 | 
						|
        if (!selectionModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Coerce the selections to an array.
 | 
						|
        let selections = Array.from(selectionModel.selections());
 | 
						|
        // Bail early if there are no selections.
 | 
						|
        if (selections.length === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Alert that multiple selections cannot be copied.
 | 
						|
        if (selections.length > 1) {
 | 
						|
            alert('Cannot copy multiple grid selections.');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the model counts.
 | 
						|
        let br = dataModel.rowCount('body');
 | 
						|
        let bc = dataModel.columnCount('body');
 | 
						|
        // Bail early if there is nothing to copy.
 | 
						|
        if (br === 0 || bc === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Unpack the selection.
 | 
						|
        let { r1, c1, r2, c2 } = selections[0];
 | 
						|
        // Clamp the selection to the model bounds.
 | 
						|
        r1 = Math.max(0, Math.min(r1, br - 1));
 | 
						|
        c1 = Math.max(0, Math.min(c1, bc - 1));
 | 
						|
        r2 = Math.max(0, Math.min(r2, br - 1));
 | 
						|
        c2 = Math.max(0, Math.min(c2, bc - 1));
 | 
						|
        // Ensure the limits are well-orderd.
 | 
						|
        if (r2 < r1)
 | 
						|
            [r1, r2] = [r2, r1];
 | 
						|
        if (c2 < c1)
 | 
						|
            [c1, c2] = [c2, c1];
 | 
						|
        // Fetch the header counts.
 | 
						|
        let rhc = dataModel.columnCount('row-header');
 | 
						|
        let chr = dataModel.rowCount('column-header');
 | 
						|
        // Unpack the copy config.
 | 
						|
        let separator = this._copyConfig.separator;
 | 
						|
        let format = this._copyConfig.format;
 | 
						|
        let headers = this._copyConfig.headers;
 | 
						|
        let warningThreshold = this._copyConfig.warningThreshold;
 | 
						|
        // Compute the number of cells to be copied.
 | 
						|
        let rowCount = r2 - r1 + 1;
 | 
						|
        let colCount = c2 - c1 + 1;
 | 
						|
        switch (headers) {
 | 
						|
            case 'none':
 | 
						|
                rhc = 0;
 | 
						|
                chr = 0;
 | 
						|
                break;
 | 
						|
            case 'row':
 | 
						|
                chr = 0;
 | 
						|
                colCount += rhc;
 | 
						|
                break;
 | 
						|
            case 'column':
 | 
						|
                rhc = 0;
 | 
						|
                rowCount += chr;
 | 
						|
                break;
 | 
						|
            case 'all':
 | 
						|
                rowCount += chr;
 | 
						|
                colCount += rhc;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Compute the total cell count.
 | 
						|
        let cellCount = rowCount * colCount;
 | 
						|
        // Allow the user to cancel a large copy request.
 | 
						|
        if (cellCount > warningThreshold) {
 | 
						|
            let msg = `Copying ${cellCount} cells may take a while. Continue?`;
 | 
						|
            if (!window.confirm(msg)) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Set up the format args.
 | 
						|
        let args = {
 | 
						|
            region: 'body',
 | 
						|
            row: 0,
 | 
						|
            column: 0,
 | 
						|
            value: null,
 | 
						|
            metadata: {}
 | 
						|
        };
 | 
						|
        // Allocate the array of rows.
 | 
						|
        let rows = new Array(rowCount);
 | 
						|
        // Iterate over the rows.
 | 
						|
        for (let j = 0; j < rowCount; ++j) {
 | 
						|
            // Allocate the array of cells.
 | 
						|
            let cells = new Array(colCount);
 | 
						|
            // Iterate over the columns.
 | 
						|
            for (let i = 0; i < colCount; ++i) {
 | 
						|
                // Set up the format variables.
 | 
						|
                let region;
 | 
						|
                let row;
 | 
						|
                let column;
 | 
						|
                // Populate the format variables.
 | 
						|
                if (j < chr && i < rhc) {
 | 
						|
                    region = 'corner-header';
 | 
						|
                    row = j;
 | 
						|
                    column = i;
 | 
						|
                }
 | 
						|
                else if (j < chr) {
 | 
						|
                    region = 'column-header';
 | 
						|
                    row = j;
 | 
						|
                    column = i - rhc + c1;
 | 
						|
                }
 | 
						|
                else if (i < rhc) {
 | 
						|
                    region = 'row-header';
 | 
						|
                    row = j - chr + r1;
 | 
						|
                    column = i;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    region = 'body';
 | 
						|
                    row = j - chr + r1;
 | 
						|
                    column = i - rhc + c1;
 | 
						|
                }
 | 
						|
                // Populate the format args.
 | 
						|
                args.region = region;
 | 
						|
                args.row = row;
 | 
						|
                args.column = column;
 | 
						|
                args.value = dataModel.data(region, row, column);
 | 
						|
                args.metadata = dataModel.metadata(region, row, column);
 | 
						|
                // Format the cell.
 | 
						|
                cells[i] = format(args);
 | 
						|
            }
 | 
						|
            // Save the row of cells.
 | 
						|
            rows[j] = cells;
 | 
						|
        }
 | 
						|
        // Convert the cells into lines.
 | 
						|
        let lines = rows.map(cells => cells.join(separator));
 | 
						|
        // Convert the lines into text.
 | 
						|
        let text = lines.join('\n');
 | 
						|
        // Copy the text to the clipboard.
 | 
						|
        _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ClipboardExt.copyText(text);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Process a message sent to the widget.
 | 
						|
     *
 | 
						|
     * @param msg - The message sent to the widget.
 | 
						|
     */
 | 
						|
    processMessage(msg) {
 | 
						|
        // Ignore child show/hide messages. The data grid controls the
 | 
						|
        // visibility of its children, and will manually dispatch the
 | 
						|
        // fit-request messages as a result of visibility change.
 | 
						|
        if (msg.type === 'child-shown' || msg.type === 'child-hidden') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Recompute the scroll bar minimums before the layout refits.
 | 
						|
        if (msg.type === 'fit-request') {
 | 
						|
            let vsbLimits = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ElementExt.sizeLimits(this._vScrollBar.node);
 | 
						|
            let hsbLimits = _lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.ElementExt.sizeLimits(this._hScrollBar.node);
 | 
						|
            this._vScrollBarMinWidth = vsbLimits.minWidth;
 | 
						|
            this._hScrollBarMinHeight = hsbLimits.minHeight;
 | 
						|
        }
 | 
						|
        // Process all other messages as normal.
 | 
						|
        super.processMessage(msg);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Intercept a message sent to a message handler.
 | 
						|
     *
 | 
						|
     * @param handler - The target handler of the message.
 | 
						|
     *
 | 
						|
     * @param msg - The message to be sent to the handler.
 | 
						|
     *
 | 
						|
     * @returns `true` if the message should continue to be processed
 | 
						|
     *   as normal, or `false` if processing should cease immediately.
 | 
						|
     */
 | 
						|
    messageHook(handler, msg) {
 | 
						|
        // Process viewport messages.
 | 
						|
        if (handler === this._viewport) {
 | 
						|
            this._processViewportMessage(msg);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
        // Process horizontal scroll bar messages.
 | 
						|
        if (handler === this._hScrollBar && msg.type === 'activate-request') {
 | 
						|
            this.activate();
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        // Process vertical scroll bar messages.
 | 
						|
        if (handler === this._vScrollBar && msg.type === 'activate-request') {
 | 
						|
            this.activate();
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        // Ignore all other messages.
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the DOM events for the data grid.
 | 
						|
     *
 | 
						|
     * @param event - The DOM event sent to the data grid.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This method implements the DOM `EventListener` interface and is
 | 
						|
     * called in response to events on the data grid's DOM node. It
 | 
						|
     * should not be called directly by user code.
 | 
						|
     */
 | 
						|
    handleEvent(event) {
 | 
						|
        switch (event.type) {
 | 
						|
            case 'keydown':
 | 
						|
                this._evtKeyDown(event);
 | 
						|
                break;
 | 
						|
            case 'mousedown':
 | 
						|
                this._evtMouseDown(event);
 | 
						|
                break;
 | 
						|
            case 'mousemove':
 | 
						|
                this._evtMouseMove(event);
 | 
						|
                break;
 | 
						|
            case 'mouseup':
 | 
						|
                this._evtMouseUp(event);
 | 
						|
                break;
 | 
						|
            case 'dblclick':
 | 
						|
                this._evtMouseDoubleClick(event);
 | 
						|
                break;
 | 
						|
            case 'mouseleave':
 | 
						|
                this._evtMouseLeave(event);
 | 
						|
                break;
 | 
						|
            case 'contextmenu':
 | 
						|
                this._evtContextMenu(event);
 | 
						|
                break;
 | 
						|
            case 'wheel':
 | 
						|
                this._evtWheel(event);
 | 
						|
                break;
 | 
						|
            case 'resize':
 | 
						|
                this._refreshDPI();
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the current viewport.
 | 
						|
     *
 | 
						|
     * @returns The current viewport as row/column coordinates.
 | 
						|
     * Returns undefined if the grid is not visible.
 | 
						|
     */
 | 
						|
    get currentViewport() {
 | 
						|
        let width = this.viewport.node.offsetWidth;
 | 
						|
        let height = this.viewport.node.offsetHeight;
 | 
						|
        width = Math.round(width);
 | 
						|
        height = Math.round(height);
 | 
						|
        if (width <= 0 || height <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const contentW = this._columnSections.length - this.scrollX;
 | 
						|
        const contentH = this._rowSections.length - this.scrollY;
 | 
						|
        const contentX = this.headerWidth;
 | 
						|
        const contentY = this.headerHeight;
 | 
						|
        const x1 = contentX;
 | 
						|
        const y1 = contentY;
 | 
						|
        const x2 = Math.min(width - 1, contentX + contentW - 1);
 | 
						|
        const y2 = Math.min(height - 1, contentY + contentH - 1);
 | 
						|
        const firstRow = this._rowSections.indexOf(y1 - contentY + this.scrollY);
 | 
						|
        const firstColumn = this._columnSections.indexOf(x1 - contentX + this.scrollX);
 | 
						|
        const lastRow = this._rowSections.indexOf(y2 - contentY + this.scrollY);
 | 
						|
        const lastColumn = this._columnSections.indexOf(x2 - contentX + this.scrollX);
 | 
						|
        return {
 | 
						|
            firstRow,
 | 
						|
            firstColumn,
 | 
						|
            lastRow,
 | 
						|
            lastColumn
 | 
						|
        };
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message handler invoked on an `'activate-request'` message.
 | 
						|
     */
 | 
						|
    onActivateRequest(msg) {
 | 
						|
        this.viewport.node.focus({ preventScroll: true });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message handler invoked on a `'before-attach'` message.
 | 
						|
     */
 | 
						|
    onBeforeAttach(msg) {
 | 
						|
        window.addEventListener('resize', this);
 | 
						|
        this.node.addEventListener('wheel', this);
 | 
						|
        this._viewport.node.addEventListener('keydown', this);
 | 
						|
        this._viewport.node.addEventListener('mousedown', this);
 | 
						|
        this._viewport.node.addEventListener('mousemove', this);
 | 
						|
        this._viewport.node.addEventListener('dblclick', this);
 | 
						|
        this._viewport.node.addEventListener('mouseleave', this);
 | 
						|
        this._viewport.node.addEventListener('contextmenu', this);
 | 
						|
        this.repaintContent();
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message handler invoked on an `'after-detach'` message.
 | 
						|
     */
 | 
						|
    onAfterDetach(msg) {
 | 
						|
        window.removeEventListener('resize', this);
 | 
						|
        this.node.removeEventListener('wheel', this);
 | 
						|
        this._viewport.node.removeEventListener('keydown', this);
 | 
						|
        this._viewport.node.removeEventListener('mousedown', this);
 | 
						|
        this._viewport.node.removeEventListener('mousemove', this);
 | 
						|
        this._viewport.node.removeEventListener('mouseleave', this);
 | 
						|
        this._viewport.node.removeEventListener('dblclick', this);
 | 
						|
        this._viewport.node.removeEventListener('contextmenu', this);
 | 
						|
        this._releaseMouse();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message handler invoked on a `'before-show'` message.
 | 
						|
     */
 | 
						|
    onBeforeShow(msg) {
 | 
						|
        this.repaintContent();
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message handler invoked on a `'resize'` message.
 | 
						|
     */
 | 
						|
    onResize(msg) {
 | 
						|
        if (this._editorController) {
 | 
						|
            this._editorController.cancel();
 | 
						|
        }
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Schedule a repaint of all of the grid content.
 | 
						|
     */
 | 
						|
    repaintContent() {
 | 
						|
        let msg = new Private$1.PaintRequest('all', 0, 0, 0, 0);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Schedule a repaint of specific grid content.
 | 
						|
     */
 | 
						|
    repaintRegion(region, r1, c1, r2, c2) {
 | 
						|
        let msg = new Private$1.PaintRequest(region, r1, c1, r2, c2);
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, msg);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Schedule a repaint of the overlay.
 | 
						|
     */
 | 
						|
    repaintOverlay() {
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.OverlayPaintRequest);
 | 
						|
    }
 | 
						|
    _getMaxWidthInColumn(index, columnRegion) {
 | 
						|
        const dataModel = this.dataModel;
 | 
						|
        if (!dataModel) {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        const columnHeaderRegion = columnRegion == 'row-header' ? 'corner-header' : 'column-header';
 | 
						|
        return Math.max(this._getMaxWidthInArea(dataModel, index, columnHeaderRegion, 'column-header'), this._getMaxWidthInArea(dataModel, index, columnRegion, 'body'));
 | 
						|
    }
 | 
						|
    _getMaxWidthInArea(dataModel, index, region, rowRegion) {
 | 
						|
        const numRows = dataModel.rowCount(rowRegion);
 | 
						|
        // Will only allocate up to 1_000_000 elements otherwise performance can tank.
 | 
						|
        const configs = Array.from({ length: Math.min(numRows, 1000000) }, (_val, idx) => DataGrid._getConfig(dataModel, idx, index, region));
 | 
						|
        // Heuristic: Sort by the length of the text to render and only fully calculate the text width
 | 
						|
        // for the top 100_000 rows by text length
 | 
						|
        if (numRows > 100000) {
 | 
						|
            // Sort by descending length
 | 
						|
            configs.sort(x => -this._getTextToRender(x).length);
 | 
						|
        }
 | 
						|
        let maxWidth = 0;
 | 
						|
        for (let i = 0; i < numRows && i < 100000; ++i) {
 | 
						|
            const textWidth = this._getCellTextWidth(configs[i]);
 | 
						|
            maxWidth = Math.max(maxWidth, textWidth);
 | 
						|
        }
 | 
						|
        return maxWidth;
 | 
						|
    }
 | 
						|
    static _getConfig(dataModel, row, col, location) {
 | 
						|
        return {
 | 
						|
            x: 0,
 | 
						|
            y: 0,
 | 
						|
            width: 0,
 | 
						|
            height: 0,
 | 
						|
            region: location,
 | 
						|
            row: row,
 | 
						|
            column: col,
 | 
						|
            value: DataGrid._getCellValue(dataModel, location, row, col),
 | 
						|
            metadata: DataGrid._getCellMetadata(dataModel, location, row, col)
 | 
						|
        };
 | 
						|
    }
 | 
						|
    _getTextToRender(config) {
 | 
						|
        const renderer = this.cellRenderers.get(config);
 | 
						|
        return renderer.getText(config);
 | 
						|
    }
 | 
						|
    _getCellTextWidth(config) {
 | 
						|
        // Get the renderer for the given cell.
 | 
						|
        const renderer = this.cellRenderers.get(config);
 | 
						|
        // Use the canvas context to measure the cell's text width
 | 
						|
        const gc = this.canvasGC;
 | 
						|
        gc.font = CellRenderer.resolveOption(renderer.font, config);
 | 
						|
        gc.fillStyle = CellRenderer.resolveOption(renderer.textColor, config);
 | 
						|
        gc.textAlign = CellRenderer.resolveOption(renderer.horizontalAlignment, config);
 | 
						|
        gc.textBaseline = 'bottom';
 | 
						|
        const text = this._getTextToRender(config);
 | 
						|
        return gc.measureText(text).width + 2 * renderer.horizontalPadding;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Ensure the canvas is at least the specified size.
 | 
						|
     *
 | 
						|
     * This method will retain the valid canvas content.
 | 
						|
     */
 | 
						|
    _resizeCanvasIfNeeded(width, height) {
 | 
						|
        // Scale the size by the dpi ratio.
 | 
						|
        width = width * this._dpiRatio;
 | 
						|
        height = height * this._dpiRatio;
 | 
						|
        // Compute the maximum canvas size for the given width and height.
 | 
						|
        let maxW = (Math.ceil((width + 1) / 512) + 1) * 512;
 | 
						|
        let maxH = (Math.ceil((height + 1) / 512) + 1) * 512;
 | 
						|
        // Get the current size of the canvas.
 | 
						|
        let curW = this._canvas.width;
 | 
						|
        let curH = this._canvas.height;
 | 
						|
        // Bail early if the canvas size is within bounds.
 | 
						|
        if (curW >= width && curH >= height && curW <= maxW && curH <= maxH) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the expanded canvas size.
 | 
						|
        let expW = maxW - 512;
 | 
						|
        let expH = maxH - 512;
 | 
						|
        // Set the transforms to the identity matrix.
 | 
						|
        this._canvasGC.setTransform(1, 0, 0, 1, 0, 0);
 | 
						|
        this._bufferGC.setTransform(1, 0, 0, 1, 0, 0);
 | 
						|
        this._overlayGC.setTransform(1, 0, 0, 1, 0, 0);
 | 
						|
        // Resize the buffer if needed.
 | 
						|
        if (curW < width) {
 | 
						|
            this._buffer.width = expW;
 | 
						|
        }
 | 
						|
        else if (curW > maxW) {
 | 
						|
            this._buffer.width = maxW;
 | 
						|
        }
 | 
						|
        // Resize the buffer height if needed.
 | 
						|
        if (curH < height) {
 | 
						|
            this._buffer.height = expH;
 | 
						|
        }
 | 
						|
        else if (curH > maxH) {
 | 
						|
            this._buffer.height = maxH;
 | 
						|
        }
 | 
						|
        // Test whether there is content to blit.
 | 
						|
        let needBlit = curW > 0 && curH > 0 && width > 0 && height > 0;
 | 
						|
        // Copy the valid canvas content into the buffer if needed.
 | 
						|
        if (needBlit) {
 | 
						|
            this._bufferGC.drawImage(this._canvas, 0, 0);
 | 
						|
        }
 | 
						|
        // Resize the canvas width if needed.
 | 
						|
        if (curW < width) {
 | 
						|
            this._canvas.width = expW;
 | 
						|
            this._canvas.style.width = `${expW / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        else if (curW > maxW) {
 | 
						|
            this._canvas.width = maxW;
 | 
						|
            this._canvas.style.width = `${maxW / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        // Resize the canvas height if needed.
 | 
						|
        if (curH < height) {
 | 
						|
            this._canvas.height = expH;
 | 
						|
            this._canvas.style.height = `${expH / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        else if (curH > maxH) {
 | 
						|
            this._canvas.height = maxH;
 | 
						|
            this._canvas.style.height = `${maxH / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        // Copy the valid canvas content from the buffer if needed.
 | 
						|
        if (needBlit) {
 | 
						|
            this._canvasGC.drawImage(this._buffer, 0, 0);
 | 
						|
        }
 | 
						|
        // Copy the valid overlay content into the buffer if needed.
 | 
						|
        if (needBlit) {
 | 
						|
            this._bufferGC.drawImage(this._overlay, 0, 0);
 | 
						|
        }
 | 
						|
        // Resize the overlay width if needed.
 | 
						|
        if (curW < width) {
 | 
						|
            this._overlay.width = expW;
 | 
						|
            this._overlay.style.width = `${expW / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        else if (curW > maxW) {
 | 
						|
            this._overlay.width = maxW;
 | 
						|
            this._overlay.style.width = `${maxW / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        // Resize the overlay height if needed.
 | 
						|
        if (curH < height) {
 | 
						|
            this._overlay.height = expH;
 | 
						|
            this._overlay.style.height = `${expH / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        else if (curH > maxH) {
 | 
						|
            this._overlay.height = maxH;
 | 
						|
            this._overlay.style.height = `${maxH / this._dpiRatio}px`;
 | 
						|
        }
 | 
						|
        // Copy the valid overlay content from the buffer if needed.
 | 
						|
        if (needBlit) {
 | 
						|
            this._overlayGC.drawImage(this._buffer, 0, 0);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Sync the scroll bars and scroll state with the viewport.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * If the visibility of either scroll bar changes, a synchronous
 | 
						|
     * fit-request will be dispatched to the data grid to immediately
 | 
						|
     * resize the viewport.
 | 
						|
     */
 | 
						|
    _syncScrollState() {
 | 
						|
        // Fetch the viewport dimensions.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        // Get the current scroll bar visibility.
 | 
						|
        let hasVScroll = !this._vScrollBar.isHidden;
 | 
						|
        let hasHScroll = !this._hScrollBar.isHidden;
 | 
						|
        // Get the minimum sizes of the scroll bars.
 | 
						|
        let vsw = this._vScrollBarMinWidth;
 | 
						|
        let hsh = this._hScrollBarMinHeight;
 | 
						|
        // Get the page size as if no scroll bars are visible.
 | 
						|
        let apw = pw + (hasVScroll ? vsw : 0);
 | 
						|
        let aph = ph + (hasHScroll ? hsh : 0);
 | 
						|
        // Test whether scroll bars are needed for the adjusted size.
 | 
						|
        let needVScroll = aph < bh - 1;
 | 
						|
        let needHScroll = apw < bw - 1;
 | 
						|
        // Re-test the horizontal scroll if a vertical scroll is needed.
 | 
						|
        if (needVScroll && !needHScroll) {
 | 
						|
            needHScroll = apw - vsw < bw - 1;
 | 
						|
        }
 | 
						|
        // Re-test the vertical scroll if a horizontal scroll is needed.
 | 
						|
        if (needHScroll && !needVScroll) {
 | 
						|
            needVScroll = aph - hsh < bh - 1;
 | 
						|
        }
 | 
						|
        // If the visibility changes, immediately refit the grid.
 | 
						|
        if (needVScroll !== hasVScroll || needHScroll !== hasHScroll) {
 | 
						|
            this._vScrollBar.setHidden(!needVScroll);
 | 
						|
            this._hScrollBar.setHidden(!needHScroll);
 | 
						|
            this._scrollCorner.setHidden(!needVScroll || !needHScroll);
 | 
						|
            _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.sendMessage(this, _lumino_widgets__WEBPACK_IMPORTED_MODULE_5__.Widget.Msg.FitRequest);
 | 
						|
        }
 | 
						|
        // Update the scroll bar limits.
 | 
						|
        this._vScrollBar.maximum = this.maxScrollY;
 | 
						|
        this._vScrollBar.page = this.pageHeight;
 | 
						|
        this._hScrollBar.maximum = this.maxScrollX;
 | 
						|
        this._hScrollBar.page = this.pageWidth;
 | 
						|
        // Re-clamp the scroll position.
 | 
						|
        this._scrollTo(this._scrollX, this._scrollY);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Sync the viewport to the given scroll position.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This schedules a full repaint and syncs the scroll state.
 | 
						|
     */
 | 
						|
    _syncViewport() {
 | 
						|
        this.repaintContent();
 | 
						|
        this.repaintOverlay();
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Process a message sent to the viewport
 | 
						|
     */
 | 
						|
    _processViewportMessage(msg) {
 | 
						|
        switch (msg.type) {
 | 
						|
            case 'resize':
 | 
						|
                this._onViewportResize(msg);
 | 
						|
                break;
 | 
						|
            case 'scroll-request':
 | 
						|
                this._onViewportScrollRequest(msg);
 | 
						|
                break;
 | 
						|
            case 'paint-request':
 | 
						|
                this._onViewportPaintRequest(msg);
 | 
						|
                break;
 | 
						|
            case 'overlay-paint-request':
 | 
						|
                this._onViewportOverlayPaintRequest(msg);
 | 
						|
                break;
 | 
						|
            case 'row-resize-request':
 | 
						|
                this._onViewportRowResizeRequest(msg);
 | 
						|
                break;
 | 
						|
            case 'column-resize-request':
 | 
						|
                this._onViewportColumnResizeRequest(msg);
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'resize'` message.
 | 
						|
     */
 | 
						|
    _onViewportResize(msg) {
 | 
						|
        // Bail early if the viewport is not visible.
 | 
						|
        if (!this._viewport.isVisible) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Unpack the message data.
 | 
						|
        let { width, height } = msg;
 | 
						|
        // Measure the viewport node if the dimensions are unknown.
 | 
						|
        if (width === -1) {
 | 
						|
            width = this._viewport.node.offsetWidth;
 | 
						|
        }
 | 
						|
        if (height === -1) {
 | 
						|
            height = this._viewport.node.offsetHeight;
 | 
						|
        }
 | 
						|
        // Round the dimensions to the nearest pixel.
 | 
						|
        width = Math.round(width);
 | 
						|
        height = Math.round(height);
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let oldWidth = this._viewportWidth;
 | 
						|
        let oldHeight = this._viewportHeight;
 | 
						|
        // Updated internal viewport size.
 | 
						|
        this._viewportWidth = width;
 | 
						|
        this._viewportHeight = height;
 | 
						|
        // Resize the canvas if needed.
 | 
						|
        this._resizeCanvasIfNeeded(width, height);
 | 
						|
        // Bail early if there is nothing to paint.
 | 
						|
        if (width === 0 || height === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the whole grid if the old size was zero.
 | 
						|
        if (oldWidth === 0 || oldHeight === 0) {
 | 
						|
            this.paintContent(0, 0, width, height);
 | 
						|
            this._paintOverlay();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the right edge as needed.
 | 
						|
        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {
 | 
						|
            let bx = this._columnSections.offsetOf(this._columnSections.count - 1);
 | 
						|
            let x = Math.min(this.headerWidth + bx, oldWidth);
 | 
						|
            this.paintContent(x, 0, width - x, height);
 | 
						|
        }
 | 
						|
        else if (width > oldWidth) {
 | 
						|
            this.paintContent(oldWidth, 0, width - oldWidth + 1, height);
 | 
						|
        }
 | 
						|
        // Paint the bottom edge as needed.
 | 
						|
        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {
 | 
						|
            let by = this._rowSections.offsetOf(this._rowSections.count - 1);
 | 
						|
            let y = Math.min(this.headerHeight + by, oldHeight);
 | 
						|
            this.paintContent(0, y, width, height - y);
 | 
						|
        }
 | 
						|
        else if (height > oldHeight) {
 | 
						|
            this.paintContent(0, oldHeight, width, height - oldHeight + 1);
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'scroll-request'` message.
 | 
						|
     */
 | 
						|
    _onViewportScrollRequest(msg) {
 | 
						|
        this._scrollTo(this._hScrollBar.value, this._vScrollBar.value);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'paint-request'` message.
 | 
						|
     */
 | 
						|
    _onViewportPaintRequest(msg) {
 | 
						|
        // Bail early if the viewport is not visible.
 | 
						|
        if (!this._viewport.isVisible) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the viewport has zero area.
 | 
						|
        if (this._viewportWidth === 0 || this._viewportHeight === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Set up the paint limits.
 | 
						|
        let xMin = 0;
 | 
						|
        let yMin = 0;
 | 
						|
        let xMax = this._viewportWidth - 1;
 | 
						|
        let yMax = this._viewportHeight - 1;
 | 
						|
        // Fetch the scroll position.
 | 
						|
        let sx = this._scrollX;
 | 
						|
        let sy = this._scrollY;
 | 
						|
        // Fetch the header dimensions.
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        // Fetch the section lists.
 | 
						|
        let rs = this._rowSections;
 | 
						|
        let cs = this._columnSections;
 | 
						|
        let rhs = this._rowHeaderSections;
 | 
						|
        let chs = this._columnHeaderSections;
 | 
						|
        // Unpack the message data.
 | 
						|
        let { region, r1, c1, r2, c2 } = msg;
 | 
						|
        // Set up the paint variables.
 | 
						|
        let x1;
 | 
						|
        let y1;
 | 
						|
        let x2;
 | 
						|
        let y2;
 | 
						|
        // Fill the paint variables based on the paint region.
 | 
						|
        switch (region) {
 | 
						|
            case 'all':
 | 
						|
                x1 = xMin;
 | 
						|
                y1 = yMin;
 | 
						|
                x2 = xMax;
 | 
						|
                y2 = yMax;
 | 
						|
                break;
 | 
						|
            case 'body':
 | 
						|
                r1 = Math.max(0, Math.min(r1, rs.count));
 | 
						|
                c1 = Math.max(0, Math.min(c1, cs.count));
 | 
						|
                r2 = Math.max(0, Math.min(r2, rs.count));
 | 
						|
                c2 = Math.max(0, Math.min(c2, cs.count));
 | 
						|
                x1 = cs.offsetOf(c1) - sx + hw;
 | 
						|
                y1 = rs.offsetOf(r1) - sy + hh;
 | 
						|
                x2 = cs.extentOf(c2) - sx + hw;
 | 
						|
                y2 = rs.extentOf(r2) - sy + hh;
 | 
						|
                break;
 | 
						|
            case 'row-header':
 | 
						|
                r1 = Math.max(0, Math.min(r1, rs.count));
 | 
						|
                c1 = Math.max(0, Math.min(c1, rhs.count));
 | 
						|
                r2 = Math.max(0, Math.min(r2, rs.count));
 | 
						|
                c2 = Math.max(0, Math.min(c2, rhs.count));
 | 
						|
                x1 = rhs.offsetOf(c1);
 | 
						|
                y1 = rs.offsetOf(r1) - sy + hh;
 | 
						|
                x2 = rhs.extentOf(c2);
 | 
						|
                y2 = rs.extentOf(r2) - sy + hh;
 | 
						|
                break;
 | 
						|
            case 'column-header':
 | 
						|
                r1 = Math.max(0, Math.min(r1, chs.count));
 | 
						|
                c1 = Math.max(0, Math.min(c1, cs.count));
 | 
						|
                r2 = Math.max(0, Math.min(r2, chs.count));
 | 
						|
                c2 = Math.max(0, Math.min(c2, cs.count));
 | 
						|
                x1 = cs.offsetOf(c1) - sx + hw;
 | 
						|
                y1 = chs.offsetOf(r1);
 | 
						|
                x2 = cs.extentOf(c2) - sx + hw;
 | 
						|
                y2 = chs.extentOf(r2);
 | 
						|
                break;
 | 
						|
            case 'corner-header':
 | 
						|
                r1 = Math.max(0, Math.min(r1, chs.count));
 | 
						|
                c1 = Math.max(0, Math.min(c1, rhs.count));
 | 
						|
                r2 = Math.max(0, Math.min(r2, chs.count));
 | 
						|
                c2 = Math.max(0, Math.min(c2, rhs.count));
 | 
						|
                x1 = rhs.offsetOf(c1);
 | 
						|
                y1 = chs.offsetOf(r1);
 | 
						|
                x2 = rhs.extentOf(c2);
 | 
						|
                y2 = chs.extentOf(r2);
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Bail early if the dirty rect is outside the bounds.
 | 
						|
        if (x2 < xMin || y2 < yMin || x1 > xMax || y1 > yMax) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the dirty rect to the paint bounds.
 | 
						|
        x1 = Math.max(xMin, Math.min(x1, xMax));
 | 
						|
        y1 = Math.max(yMin, Math.min(y1, yMax));
 | 
						|
        x2 = Math.max(xMin, Math.min(x2, xMax));
 | 
						|
        y2 = Math.max(yMin, Math.min(y2, yMax));
 | 
						|
        // Paint the content of the dirty rect.
 | 
						|
        this.paintContent(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'overlay-paint-request'` message.
 | 
						|
     */
 | 
						|
    _onViewportOverlayPaintRequest(msg) {
 | 
						|
        // Bail early if the viewport is not visible.
 | 
						|
        if (!this._viewport.isVisible) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the viewport has zero area.
 | 
						|
        if (this._viewportWidth === 0 || this._viewportHeight === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the content of the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'row-resize-request'` message.
 | 
						|
     */
 | 
						|
    _onViewportRowResizeRequest(msg) {
 | 
						|
        if (msg.region === 'body') {
 | 
						|
            this._resizeRow(msg.index, msg.size);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._resizeColumnHeader(msg.index, msg.size);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A message hook invoked on a viewport `'column-resize-request'` message.
 | 
						|
     */
 | 
						|
    _onViewportColumnResizeRequest(msg) {
 | 
						|
        if (msg.region === 'body') {
 | 
						|
            this._resizeColumn(msg.index, msg.size);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._resizeRowHeader(msg.index, msg.size);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `thumbMoved` signal from a scroll bar.
 | 
						|
     */
 | 
						|
    _onThumbMoved(sender) {
 | 
						|
        _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.MessageLoop.postMessage(this._viewport, Private$1.ScrollRequest);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `pageRequested` signal from a scroll bar.
 | 
						|
     */
 | 
						|
    _onPageRequested(sender, dir) {
 | 
						|
        if (sender === this._vScrollBar) {
 | 
						|
            this.scrollByPage(dir === 'decrement' ? 'up' : 'down');
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.scrollByPage(dir === 'decrement' ? 'left' : 'right');
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `stepRequested` signal from a scroll bar.
 | 
						|
     */
 | 
						|
    _onStepRequested(sender, dir) {
 | 
						|
        if (sender === this._vScrollBar) {
 | 
						|
            this.scrollByStep(dir === 'decrement' ? 'up' : 'down');
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.scrollByStep(dir === 'decrement' ? 'left' : 'right');
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal handler for the data model `changed` signal.
 | 
						|
     */
 | 
						|
    _onDataModelChanged(sender, args) {
 | 
						|
        switch (args.type) {
 | 
						|
            case 'rows-inserted':
 | 
						|
                this._onRowsInserted(args);
 | 
						|
                break;
 | 
						|
            case 'columns-inserted':
 | 
						|
                this._onColumnsInserted(args);
 | 
						|
                break;
 | 
						|
            case 'rows-removed':
 | 
						|
                this._onRowsRemoved(args);
 | 
						|
                break;
 | 
						|
            case 'columns-removed':
 | 
						|
                this._onColumnsRemoved(args);
 | 
						|
                break;
 | 
						|
            case 'rows-moved':
 | 
						|
                this._onRowsMoved(args);
 | 
						|
                break;
 | 
						|
            case 'columns-moved':
 | 
						|
                this._onColumnsMoved(args);
 | 
						|
                break;
 | 
						|
            case 'cells-changed':
 | 
						|
                this._onCellsChanged(args);
 | 
						|
                break;
 | 
						|
            case 'model-reset':
 | 
						|
                this._onModelReset(args);
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal handler for the selection model `changed` signal.
 | 
						|
     */
 | 
						|
    _onSelectionsChanged(sender) {
 | 
						|
        this.repaintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle rows being inserted in the data model.
 | 
						|
     */
 | 
						|
    _onRowsInserted(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span } = args;
 | 
						|
        // Bail early if there are no sections to insert.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._rowSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._columnHeaderSections;
 | 
						|
        }
 | 
						|
        // Insert the span, maintaining the scroll position as needed.
 | 
						|
        if (this._scrollY === this.maxScrollY && this.maxScrollY > 0) {
 | 
						|
            list.insert(index, span);
 | 
						|
            this._scrollY = this.maxScrollY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list.insert(index, span);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle columns being inserted into the data model.
 | 
						|
     */
 | 
						|
    _onColumnsInserted(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span } = args;
 | 
						|
        // Bail early if there are no sections to insert.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._columnSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._rowHeaderSections;
 | 
						|
        }
 | 
						|
        // Insert the span, maintaining the scroll position as needed.
 | 
						|
        if (this._scrollX === this.maxScrollX && this.maxScrollX > 0) {
 | 
						|
            list.insert(index, span);
 | 
						|
            this._scrollX = this.maxScrollX;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list.insert(index, span);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle rows being removed from the data model.
 | 
						|
     */
 | 
						|
    _onRowsRemoved(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span } = args;
 | 
						|
        // Bail early if there are no sections to remove.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._rowSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._columnHeaderSections;
 | 
						|
        }
 | 
						|
        // Bail if the index or is invalid
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Remove the span, maintaining the scroll position as needed.
 | 
						|
        if (this._scrollY === this.maxScrollY && this.maxScrollY > 0) {
 | 
						|
            list.remove(index, span);
 | 
						|
            this._scrollY = this.maxScrollY;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list.remove(index, span);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle columns being removed from the data model.
 | 
						|
     */
 | 
						|
    _onColumnsRemoved(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span } = args;
 | 
						|
        // Bail early if there are no sections to remove.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._columnSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._rowHeaderSections;
 | 
						|
        }
 | 
						|
        // Bail if the index or is invalid
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Remove the span, maintaining the scroll position as needed.
 | 
						|
        if (this._scrollX === this.maxScrollX && this.maxScrollX > 0) {
 | 
						|
            list.remove(index, span);
 | 
						|
            this._scrollX = this.maxScrollX;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list.remove(index, span);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle rows moving in the data model.
 | 
						|
     */
 | 
						|
    _onRowsMoved(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span, destination } = args;
 | 
						|
        // Bail early if there are no sections to move.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._rowSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._columnHeaderSections;
 | 
						|
        }
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the move span to the limit.
 | 
						|
        span = Math.min(span, list.count - index);
 | 
						|
        // Clamp the destination index to the limit.
 | 
						|
        destination = Math.min(Math.max(0, destination), list.count - span);
 | 
						|
        // Bail early if there is no effective move.
 | 
						|
        if (index === destination) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the first affected index.
 | 
						|
        let r1 = Math.min(index, destination);
 | 
						|
        // Compute the last affected index.
 | 
						|
        let r2 = Math.max(index + span - 1, destination + span - 1);
 | 
						|
        // Move the sections in the list.
 | 
						|
        list.move(index, span, destination);
 | 
						|
        // Schedule a repaint of the dirty cells.
 | 
						|
        if (region === 'body') {
 | 
						|
            this.repaintRegion('body', r1, 0, r2, Infinity);
 | 
						|
            this.repaintRegion('row-header', r1, 0, r2, Infinity);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.repaintRegion('column-header', r1, 0, r2, Infinity);
 | 
						|
            this.repaintRegion('corner-header', r1, 0, r2, Infinity);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle columns moving in the data model.
 | 
						|
     */
 | 
						|
    _onColumnsMoved(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, index, span, destination } = args;
 | 
						|
        // Bail early if there are no sections to move.
 | 
						|
        if (span <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the relevant section list.
 | 
						|
        let list;
 | 
						|
        if (region === 'body') {
 | 
						|
            list = this._columnSections;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            list = this._rowHeaderSections;
 | 
						|
        }
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Clamp the move span to the limit.
 | 
						|
        span = Math.min(span, list.count - index);
 | 
						|
        // Clamp the destination index to the limit.
 | 
						|
        destination = Math.min(Math.max(0, destination), list.count - span);
 | 
						|
        // Bail early if there is no effective move.
 | 
						|
        if (index === destination) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Move the sections in the list.
 | 
						|
        list.move(index, span, destination);
 | 
						|
        // Compute the first affected index.
 | 
						|
        let c1 = Math.min(index, destination);
 | 
						|
        // Compute the last affected index.
 | 
						|
        let c2 = Math.max(index + span - 1, destination + span - 1);
 | 
						|
        // Schedule a repaint of the dirty cells.
 | 
						|
        if (region === 'body') {
 | 
						|
            this.repaintRegion('body', 0, c1, Infinity, c2);
 | 
						|
            this.repaintRegion('column-header', 0, c1, Infinity, c2);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this.repaintRegion('row-header', 0, c1, Infinity, c2);
 | 
						|
            this.repaintRegion('corner-header', 0, c1, Infinity, c2);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle cells changing in the data model.
 | 
						|
     */
 | 
						|
    _onCellsChanged(args) {
 | 
						|
        // Unpack the arg data.
 | 
						|
        let { region, row, column, rowSpan, columnSpan } = args;
 | 
						|
        // Bail early if there are no cells to modify.
 | 
						|
        if (rowSpan <= 0 && columnSpan <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the changed cell bounds.
 | 
						|
        let r1 = row;
 | 
						|
        let c1 = column;
 | 
						|
        let r2 = r1 + rowSpan - 1;
 | 
						|
        let c2 = c1 + columnSpan - 1;
 | 
						|
        // Schedule a repaint of the cell content.
 | 
						|
        this.repaintRegion(region, r1, c1, r2, c2);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle a full data model reset.
 | 
						|
     */
 | 
						|
    _onModelReset(args) {
 | 
						|
        // Look up the various current section counts.
 | 
						|
        let nr = this._rowSections.count;
 | 
						|
        let nc = this._columnSections.count;
 | 
						|
        let nrh = this._rowHeaderSections.count;
 | 
						|
        let nch = this._columnHeaderSections.count;
 | 
						|
        // Compute the delta count for each region.
 | 
						|
        let dr = this._dataModel.rowCount('body') - nr;
 | 
						|
        let dc = this._dataModel.columnCount('body') - nc;
 | 
						|
        let drh = this._dataModel.columnCount('row-header') - nrh;
 | 
						|
        let dch = this._dataModel.rowCount('column-header') - nch;
 | 
						|
        // Update the row sections, if needed.
 | 
						|
        if (dr > 0) {
 | 
						|
            this._rowSections.insert(nr, dr);
 | 
						|
        }
 | 
						|
        else if (dr < 0) {
 | 
						|
            this._rowSections.remove(nr + dr, -dr);
 | 
						|
        }
 | 
						|
        // Update the column sections, if needed.
 | 
						|
        if (dc > 0) {
 | 
						|
            this._columnSections.insert(nc, dc);
 | 
						|
        }
 | 
						|
        else if (dc < 0) {
 | 
						|
            this._columnSections.remove(nc + dc, -dc);
 | 
						|
        }
 | 
						|
        // Update the row header sections, if needed.
 | 
						|
        if (drh > 0) {
 | 
						|
            this._rowHeaderSections.insert(nrh, drh);
 | 
						|
        }
 | 
						|
        else if (drh < 0) {
 | 
						|
            this._rowHeaderSections.remove(nrh + drh, -drh);
 | 
						|
        }
 | 
						|
        // Update the column header sections, if needed.
 | 
						|
        if (dch > 0) {
 | 
						|
            this._columnHeaderSections.insert(nch, dch);
 | 
						|
        }
 | 
						|
        else if (dch < 0) {
 | 
						|
            this._columnHeaderSections.remove(nch + dch, -dch);
 | 
						|
        }
 | 
						|
        // Sync the viewport.
 | 
						|
        this._syncViewport();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * A signal handler for the renderer map `changed` signal.
 | 
						|
     */
 | 
						|
    _onRenderersChanged() {
 | 
						|
        this.repaintContent();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'keydown'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtKeyDown(event) {
 | 
						|
        if (this._mousedown) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
        }
 | 
						|
        else if (this._keyHandler) {
 | 
						|
            this._keyHandler.onKeyDown(this, event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'mousedown'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtMouseDown(event) {
 | 
						|
        // Ignore everything except the left mouse button.
 | 
						|
        if (event.button !== 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Activate the grid.
 | 
						|
        this.activate();
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Add the extra document listeners.
 | 
						|
        document.addEventListener('keydown', this, true);
 | 
						|
        document.addEventListener('mouseup', this, true);
 | 
						|
        document.addEventListener('mousedown', this, true);
 | 
						|
        document.addEventListener('mousemove', this, true);
 | 
						|
        document.addEventListener('contextmenu', this, true);
 | 
						|
        // Flip the mousedown flag.
 | 
						|
        this._mousedown = true;
 | 
						|
        // Dispatch to the mouse handler.
 | 
						|
        if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.onMouseDown(this, event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'mousemove'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtMouseMove(event) {
 | 
						|
        // Stop the event propagation if the mouse is down.
 | 
						|
        if (this._mousedown) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
        }
 | 
						|
        // Bail if there is no mouse handler.
 | 
						|
        if (!this._mouseHandler) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Dispatch to the mouse handler.
 | 
						|
        if (this._mousedown) {
 | 
						|
            this._mouseHandler.onMouseMove(this, event);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            this._mouseHandler.onMouseHover(this, event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'mouseup'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtMouseUp(event) {
 | 
						|
        // Ignore everything except the left mouse button.
 | 
						|
        if (event.button !== 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Dispatch to the mouse handler.
 | 
						|
        if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.onMouseUp(this, event);
 | 
						|
        }
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'dblclick'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtMouseDoubleClick(event) {
 | 
						|
        // Ignore everything except the left mouse button.
 | 
						|
        if (event.button !== 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Stop the event propagation.
 | 
						|
        event.preventDefault();
 | 
						|
        event.stopPropagation();
 | 
						|
        // Dispatch to the mouse handler.
 | 
						|
        if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.onMouseDoubleClick(this, event);
 | 
						|
        }
 | 
						|
        // Release the mouse.
 | 
						|
        this._releaseMouse();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'mouseleave'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtMouseLeave(event) {
 | 
						|
        if (this._mousedown) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
        }
 | 
						|
        else if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.onMouseLeave(this, event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'contextmenu'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtContextMenu(event) {
 | 
						|
        if (this._mousedown) {
 | 
						|
            event.preventDefault();
 | 
						|
            event.stopPropagation();
 | 
						|
        }
 | 
						|
        else if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.onContextMenu(this, event);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Handle the `'wheel'` event for the data grid.
 | 
						|
     */
 | 
						|
    _evtWheel(event) {
 | 
						|
        // Ignore the event if `accel` is held.
 | 
						|
        if (_lumino_domutils__WEBPACK_IMPORTED_MODULE_0__.Platform.accelKey(event)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if there is no mouse handler.
 | 
						|
        if (!this._mouseHandler) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Dispatch to the mouse handler.
 | 
						|
        this._mouseHandler.onWheel(this, event);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Release the mouse grab.
 | 
						|
     */
 | 
						|
    _releaseMouse() {
 | 
						|
        // Clear the mousedown flag.
 | 
						|
        this._mousedown = false;
 | 
						|
        // Relase the mouse handler.
 | 
						|
        if (this._mouseHandler) {
 | 
						|
            this._mouseHandler.release();
 | 
						|
        }
 | 
						|
        // Remove the document listeners.
 | 
						|
        document.removeEventListener('keydown', this, true);
 | 
						|
        document.removeEventListener('mouseup', this, true);
 | 
						|
        document.removeEventListener('mousedown', this, true);
 | 
						|
        document.removeEventListener('mousemove', this, true);
 | 
						|
        document.removeEventListener('contextmenu', this, true);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Refresh the dpi ratio.
 | 
						|
     */
 | 
						|
    _refreshDPI() {
 | 
						|
        // Get the best integral value for the dpi ratio.
 | 
						|
        let dpiRatio = Math.ceil(window.devicePixelRatio);
 | 
						|
        // Bail early if the computed dpi ratio has not changed.
 | 
						|
        if (this._dpiRatio === dpiRatio) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal dpi ratio.
 | 
						|
        this._dpiRatio = dpiRatio;
 | 
						|
        // Schedule a repaint of the content.
 | 
						|
        this.repaintContent();
 | 
						|
        // Schedule a repaint of the overlay.
 | 
						|
        this.repaintOverlay();
 | 
						|
        // Update the canvas size for the new dpi ratio.
 | 
						|
        this._resizeCanvasIfNeeded(this._viewportWidth, this._viewportHeight);
 | 
						|
        // Ensure the canvas style is scaled for the new ratio.
 | 
						|
        this._canvas.style.width = `${this._canvas.width / this._dpiRatio}px`;
 | 
						|
        this._canvas.style.height = `${this._canvas.height / this._dpiRatio}px`;
 | 
						|
        // Ensure the overlay style is scaled for the new ratio.
 | 
						|
        this._overlay.style.width = `${this._overlay.width / this._dpiRatio}px`;
 | 
						|
        this._overlay.style.height = `${this._overlay.height / this._dpiRatio}px`;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a row section immediately.
 | 
						|
     */
 | 
						|
    _resizeRow(index, size) {
 | 
						|
        // Look up the target section list.
 | 
						|
        let list = this._rowSections;
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the old size of the section.
 | 
						|
        let oldSize = list.sizeOf(index);
 | 
						|
        // Normalize the new size of the section.
 | 
						|
        let newSize = list.clampSize(size);
 | 
						|
        // Bail early if the size does not change.
 | 
						|
        if (oldSize === newSize) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resize the section in the list.
 | 
						|
        list.resize(index, newSize);
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // If there is nothing to paint, sync the scroll state.
 | 
						|
        if (!this._viewport.isVisible || vw === 0 || vh === 0) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the size delta.
 | 
						|
        let delta = newSize - oldSize;
 | 
						|
        // Look up the column header height.
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        // Compute the viewport offset of the section.
 | 
						|
        let offset = list.offsetOf(index) + hh - this._scrollY;
 | 
						|
        // Bail early if there is nothing to paint.
 | 
						|
        if (hh >= vh || offset >= vh) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the scroll position if the section is not visible.
 | 
						|
        if (offset + oldSize <= hh) {
 | 
						|
            this._scrollY += delta;
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the paint origin of the section.
 | 
						|
        let pos = Math.max(hh, offset);
 | 
						|
        // Paint from the section onward if it spans the viewport.
 | 
						|
        if (offset + oldSize >= vh || offset + newSize >= vh) {
 | 
						|
            this.paintContent(0, pos, vw, vh - pos);
 | 
						|
            this._paintOverlay();
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the X blit dimensions.
 | 
						|
        let sx = 0;
 | 
						|
        let sw = vw;
 | 
						|
        let dx = 0;
 | 
						|
        // Compute the Y blit dimensions.
 | 
						|
        let sy;
 | 
						|
        let sh;
 | 
						|
        let dy;
 | 
						|
        if (offset + newSize <= hh) {
 | 
						|
            sy = hh - delta;
 | 
						|
            sh = vh - sy;
 | 
						|
            dy = hh;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            sy = offset + oldSize;
 | 
						|
            sh = vh - sy;
 | 
						|
            dy = sy + delta;
 | 
						|
        }
 | 
						|
        // Blit the valid content to the destination.
 | 
						|
        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);
 | 
						|
        // Repaint the section if needed.
 | 
						|
        if (newSize > 0 && offset + newSize > hh) {
 | 
						|
            this.paintContent(0, pos, vw, offset + newSize - pos);
 | 
						|
        }
 | 
						|
        // Paint the trailing space as needed.
 | 
						|
        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {
 | 
						|
            let r = this._rowSections.count - 1;
 | 
						|
            let y = hh + this._rowSections.offsetOf(r);
 | 
						|
            this.paintContent(0, y, vw, vh - y);
 | 
						|
        }
 | 
						|
        else if (delta < 0) {
 | 
						|
            this.paintContent(0, vh + delta, vw, -delta);
 | 
						|
        }
 | 
						|
        // Repaint merged cells that are intersected by the resized row
 | 
						|
        // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
        for (const rgn of ['body', 'row-header']) {
 | 
						|
            const cellGroups = CellGroup.getCellGroupsAtRow(this.dataModel, rgn, index);
 | 
						|
            let paintRgn = {
 | 
						|
                region: rgn,
 | 
						|
                xMin: 0,
 | 
						|
                xMax: 0,
 | 
						|
                yMin: 0,
 | 
						|
                yMax: 0
 | 
						|
            };
 | 
						|
            let backgroundColor = undefined;
 | 
						|
            switch (rgn) {
 | 
						|
                case 'body':
 | 
						|
                    paintRgn.xMin = this.headerWidth;
 | 
						|
                    paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                    paintRgn.yMin = this.headerHeight;
 | 
						|
                    paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                    backgroundColor = this._style.backgroundColor;
 | 
						|
                    break;
 | 
						|
                case 'row-header':
 | 
						|
                    paintRgn.xMin = 0;
 | 
						|
                    paintRgn.xMax = this.headerWidth;
 | 
						|
                    paintRgn.yMin = this.headerHeight;
 | 
						|
                    paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                    backgroundColor = this._style.headerBackgroundColor;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            this._paintMergedCells(cellGroups, paintRgn, backgroundColor);
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
        // Sync the scroll state.
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a column section immediately.
 | 
						|
     */
 | 
						|
    _resizeColumn(index, size) {
 | 
						|
        // Look up the target section list.
 | 
						|
        let list = this._columnSections;
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const adjustedSize = size !== null && size !== void 0 ? size : this._getMaxWidthInColumn(index, 'body');
 | 
						|
        if (!adjustedSize || adjustedSize == 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the old size of the section.
 | 
						|
        let oldSize = list.sizeOf(index);
 | 
						|
        // Normalize the new size of the section.
 | 
						|
        let newSize = list.clampSize(adjustedSize);
 | 
						|
        // Bail early if the size does not change.
 | 
						|
        if (oldSize === newSize) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resize the section in the list.
 | 
						|
        list.resize(index, newSize);
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // If there is nothing to paint, sync the scroll state.
 | 
						|
        if (!this._viewport.isVisible || vw === 0 || vh === 0) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the size delta.
 | 
						|
        let delta = newSize - oldSize;
 | 
						|
        // Look up the row header width.
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        // Compute the viewport offset of the section.
 | 
						|
        let offset = list.offsetOf(index) + hw - this._scrollX;
 | 
						|
        // Bail early if there is nothing to paint.
 | 
						|
        if (hw >= vw || offset >= vw) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the scroll position if the section is not visible.
 | 
						|
        if (offset + oldSize <= hw) {
 | 
						|
            this._scrollX += delta;
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the paint origin of the section.
 | 
						|
        let pos = Math.max(hw, offset);
 | 
						|
        // Paint from the section onward if it spans the viewport.
 | 
						|
        if (offset + oldSize >= vw || offset + newSize >= vw) {
 | 
						|
            this.paintContent(pos, 0, vw - pos, vh);
 | 
						|
            this._paintOverlay();
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the Y blit dimensions.
 | 
						|
        let sy = 0;
 | 
						|
        let sh = vh;
 | 
						|
        let dy = 0;
 | 
						|
        // Compute the X blit dimensions.
 | 
						|
        let sx;
 | 
						|
        let sw;
 | 
						|
        let dx;
 | 
						|
        if (offset + newSize <= hw) {
 | 
						|
            sx = hw - delta;
 | 
						|
            sw = vw - sx;
 | 
						|
            dx = hw;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            sx = offset + oldSize;
 | 
						|
            sw = vw - sx;
 | 
						|
            dx = sx + delta;
 | 
						|
        }
 | 
						|
        // Blit the valid content to the destination.
 | 
						|
        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);
 | 
						|
        // Repaint the section if needed.
 | 
						|
        if (newSize > 0 && offset + newSize > hw) {
 | 
						|
            this.paintContent(pos, 0, offset + newSize - pos, vh);
 | 
						|
        }
 | 
						|
        // Paint the trailing space as needed.
 | 
						|
        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {
 | 
						|
            let c = this._columnSections.count - 1;
 | 
						|
            let x = hw + this._columnSections.offsetOf(c);
 | 
						|
            this.paintContent(x, 0, vw - x, vh);
 | 
						|
        }
 | 
						|
        else if (delta < 0) {
 | 
						|
            this.paintContent(vw + delta, 0, -delta, vh);
 | 
						|
        }
 | 
						|
        // Repaint merged cells that are intersected by the resized column
 | 
						|
        // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
        for (const rgn of ['body', 'column-header']) {
 | 
						|
            const cellGroups = CellGroup.getCellGroupsAtColumn(this.dataModel, rgn, index);
 | 
						|
            let paintRgn = {
 | 
						|
                region: rgn,
 | 
						|
                xMin: 0,
 | 
						|
                xMax: 0,
 | 
						|
                yMin: 0,
 | 
						|
                yMax: 0
 | 
						|
            };
 | 
						|
            let backgroundColor = undefined;
 | 
						|
            switch (rgn) {
 | 
						|
                case 'body':
 | 
						|
                    paintRgn.xMin = this.headerWidth;
 | 
						|
                    paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                    paintRgn.yMin = this.headerHeight;
 | 
						|
                    paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                    backgroundColor = this._style.backgroundColor;
 | 
						|
                    break;
 | 
						|
                case 'column-header':
 | 
						|
                    paintRgn.xMin = this.headerWidth;
 | 
						|
                    paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                    paintRgn.yMin = 0;
 | 
						|
                    paintRgn.yMax = this.headerHeight;
 | 
						|
                    backgroundColor = this._style.headerBackgroundColor;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            this._paintMergedCells(cellGroups, paintRgn, backgroundColor);
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
        // Sync the scroll state after painting.
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a row header section immediately.
 | 
						|
     */
 | 
						|
    _resizeRowHeader(index, size) {
 | 
						|
        // Look up the target section list.
 | 
						|
        let list = this._rowHeaderSections;
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const adjustedSize = size !== null && size !== void 0 ? size : this._getMaxWidthInColumn(index, 'row-header');
 | 
						|
        if (!adjustedSize || adjustedSize == 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the old size of the section.
 | 
						|
        let oldSize = list.sizeOf(index);
 | 
						|
        // Normalize the new size of the section.
 | 
						|
        let newSize = list.clampSize(adjustedSize);
 | 
						|
        // Bail early if the size does not change.
 | 
						|
        if (oldSize === newSize) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resize the section in the list.
 | 
						|
        list.resize(index, newSize);
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // If there is nothing to paint, sync the scroll state.
 | 
						|
        if (!this._viewport.isVisible || vw === 0 || vh === 0) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the size delta.
 | 
						|
        let delta = newSize - oldSize;
 | 
						|
        // Look up the offset of the section.
 | 
						|
        let offset = list.offsetOf(index);
 | 
						|
        // Bail early if the section is fully outside the viewport.
 | 
						|
        if (offset >= vw) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the entire tail if the section spans the viewport.
 | 
						|
        if (offset + oldSize >= vw || offset + newSize >= vw) {
 | 
						|
            this.paintContent(offset, 0, vw - offset, vh);
 | 
						|
            this._paintOverlay();
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the blit content dimensions.
 | 
						|
        let sx = offset + oldSize;
 | 
						|
        let sy = 0;
 | 
						|
        let sw = vw - sx;
 | 
						|
        let sh = vh;
 | 
						|
        let dx = sx + delta;
 | 
						|
        let dy = 0;
 | 
						|
        // Blit the valid content to the destination.
 | 
						|
        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);
 | 
						|
        // Repaint the header section if needed.
 | 
						|
        if (newSize > 0) {
 | 
						|
            this.paintContent(offset, 0, newSize, vh);
 | 
						|
        }
 | 
						|
        // Paint the trailing space as needed.
 | 
						|
        if (this._stretchLastColumn && this.pageWidth > this.bodyWidth) {
 | 
						|
            let c = this._columnSections.count - 1;
 | 
						|
            let x = this.headerWidth + this._columnSections.offsetOf(c);
 | 
						|
            this.paintContent(x, 0, vw - x, vh);
 | 
						|
        }
 | 
						|
        else if (delta < 0) {
 | 
						|
            this.paintContent(vw + delta, 0, -delta, vh);
 | 
						|
        }
 | 
						|
        // Repaint merged cells that are intersected by the resized row
 | 
						|
        // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
        for (const rgn of [
 | 
						|
            'corner-header',
 | 
						|
            'row-header'
 | 
						|
        ]) {
 | 
						|
            const cellGroups = CellGroup.getCellGroupsAtColumn(this.dataModel, rgn, index);
 | 
						|
            let paintRgn = {
 | 
						|
                region: rgn,
 | 
						|
                xMin: 0,
 | 
						|
                xMax: 0,
 | 
						|
                yMin: 0,
 | 
						|
                yMax: 0
 | 
						|
            };
 | 
						|
            switch (rgn) {
 | 
						|
                case 'corner-header':
 | 
						|
                    paintRgn.xMin = 0;
 | 
						|
                    paintRgn.xMax = this.headerWidth;
 | 
						|
                    paintRgn.yMin = 0;
 | 
						|
                    paintRgn.yMax = this.headerHeight;
 | 
						|
                    break;
 | 
						|
                case 'row-header':
 | 
						|
                    paintRgn.xMin = 0;
 | 
						|
                    paintRgn.xMax = this.headerWidth;
 | 
						|
                    paintRgn.yMin = this.headerHeight;
 | 
						|
                    paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            this._paintMergedCells(cellGroups, paintRgn, this._style.headerBackgroundColor);
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
        // Sync the scroll state after painting.
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resize a column header section immediately.
 | 
						|
     */
 | 
						|
    _resizeColumnHeader(index, size) {
 | 
						|
        // Look up the target section list.
 | 
						|
        let list = this._columnHeaderSections;
 | 
						|
        // Bail early if the index is out of range.
 | 
						|
        if (index < 0 || index >= list.count) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Look up the old size of the section.
 | 
						|
        let oldSize = list.sizeOf(index);
 | 
						|
        // Normalize the new size of the section.
 | 
						|
        let newSize = list.clampSize(size);
 | 
						|
        // Bail early if the size does not change.
 | 
						|
        if (oldSize === newSize) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Resize the section in the list.
 | 
						|
        list.resize(index, newSize);
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // If there is nothing to paint, sync the scroll state.
 | 
						|
        if (!this._viewport.isVisible || vw === 0 || vh === 0) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
        // Compute the size delta.
 | 
						|
        let delta = newSize - oldSize;
 | 
						|
        // Look up the offset of the section.
 | 
						|
        let offset = list.offsetOf(index);
 | 
						|
        // Bail early if the section is fully outside the viewport.
 | 
						|
        if (offset >= vh) {
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Paint the entire tail if the section spans the viewport.
 | 
						|
        if (offset + oldSize >= vh || offset + newSize >= vh) {
 | 
						|
            this.paintContent(0, offset, vw, vh - offset);
 | 
						|
            this._paintOverlay();
 | 
						|
            this._syncScrollState();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the blit content dimensions.
 | 
						|
        let sx = 0;
 | 
						|
        let sy = offset + oldSize;
 | 
						|
        let sw = vw;
 | 
						|
        let sh = vh - sy;
 | 
						|
        let dx = 0;
 | 
						|
        let dy = sy + delta;
 | 
						|
        // Blit the valid contents to the destination.
 | 
						|
        this._blitContent(this._canvas, sx, sy, sw, sh, dx, dy);
 | 
						|
        // Repaint the header section if needed.
 | 
						|
        if (newSize > 0) {
 | 
						|
            this.paintContent(0, offset, vw, newSize);
 | 
						|
        }
 | 
						|
        // Paint the trailing space as needed.
 | 
						|
        if (this._stretchLastRow && this.pageHeight > this.bodyHeight) {
 | 
						|
            let r = this._rowSections.count - 1;
 | 
						|
            let y = this.headerHeight + this._rowSections.offsetOf(r);
 | 
						|
            this.paintContent(0, y, vw, vh - y);
 | 
						|
        }
 | 
						|
        else if (delta < 0) {
 | 
						|
            this.paintContent(0, vh + delta, vw, -delta);
 | 
						|
        }
 | 
						|
        // Repaint merged cells that are intersected by the resized row
 | 
						|
        // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
        for (const rgn of [
 | 
						|
            'corner-header',
 | 
						|
            'column-header'
 | 
						|
        ]) {
 | 
						|
            const cellGroups = CellGroup.getCellGroupsAtRow(this.dataModel, rgn, index);
 | 
						|
            let paintRgn = {
 | 
						|
                region: rgn,
 | 
						|
                xMin: 0,
 | 
						|
                xMax: 0,
 | 
						|
                yMin: 0,
 | 
						|
                yMax: 0
 | 
						|
            };
 | 
						|
            switch (rgn) {
 | 
						|
                case 'corner-header':
 | 
						|
                    paintRgn.xMin = 0;
 | 
						|
                    paintRgn.xMax = this.headerWidth;
 | 
						|
                    paintRgn.yMin = 0;
 | 
						|
                    paintRgn.yMax = this.headerHeight;
 | 
						|
                    break;
 | 
						|
                case 'column-header':
 | 
						|
                    paintRgn.xMin = this.headerWidth;
 | 
						|
                    paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                    paintRgn.yMin = 0;
 | 
						|
                    paintRgn.yMax = this.headerHeight;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            this._paintMergedCells(cellGroups, paintRgn, this._style.headerBackgroundColor);
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
        // Sync the scroll state after painting.
 | 
						|
        this._syncScrollState();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Scroll immediately to the specified offset position.
 | 
						|
     */
 | 
						|
    _scrollTo(x, y) {
 | 
						|
        // Bail if no data model found.
 | 
						|
        if (!this.dataModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Floor and clamp the position to the allowable range.
 | 
						|
        x = Math.max(0, Math.min(Math.floor(x), this.maxScrollX));
 | 
						|
        y = Math.max(0, Math.min(Math.floor(y), this.maxScrollY));
 | 
						|
        // Synchronize the scroll bar values.
 | 
						|
        this._hScrollBar.value = x;
 | 
						|
        this._vScrollBar.value = y;
 | 
						|
        // Compute the delta scroll amount.
 | 
						|
        let dx = x - this._scrollX;
 | 
						|
        let dy = y - this._scrollY;
 | 
						|
        // Bail early if there is no effective scroll.
 | 
						|
        if (dx === 0 && dy === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the viewport is not visible.
 | 
						|
        if (!this._viewport.isVisible) {
 | 
						|
            this._scrollX = x;
 | 
						|
            this._scrollY = y;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the current size of the viewport.
 | 
						|
        let width = this._viewportWidth;
 | 
						|
        let height = this._viewportHeight;
 | 
						|
        // Bail early if the viewport is empty.
 | 
						|
        if (width === 0 || height === 0) {
 | 
						|
            this._scrollX = x;
 | 
						|
            this._scrollY = y;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the visible content origin.
 | 
						|
        let contentX = this.headerWidth;
 | 
						|
        let contentY = this.headerHeight;
 | 
						|
        // Get the visible content dimensions.
 | 
						|
        let contentWidth = width - contentX;
 | 
						|
        let contentHeight = height - contentY;
 | 
						|
        // Bail early if there is no content to draw.
 | 
						|
        if (contentWidth <= 0 && contentHeight <= 0) {
 | 
						|
            this._scrollX = x;
 | 
						|
            this._scrollY = y;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the area which needs painting for the `dx` scroll.
 | 
						|
        let dxArea = 0;
 | 
						|
        if (dx !== 0 && contentWidth > 0) {
 | 
						|
            if (Math.abs(dx) >= contentWidth) {
 | 
						|
                dxArea = contentWidth * height;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                dxArea = Math.abs(dx) * height;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Compute the area which needs painting for the `dy` scroll.
 | 
						|
        let dyArea = 0;
 | 
						|
        if (dy !== 0 && contentHeight > 0) {
 | 
						|
            if (Math.abs(dy) >= contentHeight) {
 | 
						|
                dyArea = width * contentHeight;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                dyArea = width * Math.abs(dy);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // If the area sum is larger than the total, paint everything.
 | 
						|
        if (dxArea + dyArea >= width * height) {
 | 
						|
            this._scrollX = x;
 | 
						|
            this._scrollY = y;
 | 
						|
            this.paintContent(0, 0, width, height);
 | 
						|
            this._paintOverlay();
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Update the internal Y scroll position.
 | 
						|
        this._scrollY = y;
 | 
						|
        // Scroll the Y axis if needed. If the scroll distance exceeds
 | 
						|
        // the visible height, paint everything. Otherwise, blit the
 | 
						|
        // valid content and paint the dirty region.
 | 
						|
        if (dy !== 0 && contentHeight > 0) {
 | 
						|
            if (Math.abs(dy) >= contentHeight) {
 | 
						|
                this.paintContent(0, contentY, width, contentHeight);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                const x = 0;
 | 
						|
                const y = dy < 0 ? contentY : contentY + dy;
 | 
						|
                const w = width;
 | 
						|
                const h = contentHeight - Math.abs(dy);
 | 
						|
                this._blitContent(this._canvas, x, y, w, h, x, y - dy);
 | 
						|
                this.paintContent(0, dy < 0 ? contentY : height - dy, width, Math.abs(dy));
 | 
						|
                // Repaint merged cells that are intersected by the scroll level
 | 
						|
                // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
                for (const rgn of ['body', 'row-header']) {
 | 
						|
                    const cellgroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn);
 | 
						|
                    let paintRgn = {
 | 
						|
                        region: rgn,
 | 
						|
                        xMin: 0,
 | 
						|
                        xMax: 0,
 | 
						|
                        yMin: 0,
 | 
						|
                        yMax: 0
 | 
						|
                    };
 | 
						|
                    let backgroundColor = undefined;
 | 
						|
                    switch (rgn) {
 | 
						|
                        case 'body':
 | 
						|
                            paintRgn.xMin = this.headerWidth;
 | 
						|
                            paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                            paintRgn.yMin = this.headerHeight;
 | 
						|
                            paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                            backgroundColor = this._style.backgroundColor;
 | 
						|
                            break;
 | 
						|
                        case 'row-header':
 | 
						|
                            paintRgn.xMin = 0;
 | 
						|
                            paintRgn.xMax = this.headerWidth;
 | 
						|
                            paintRgn.yMin = this.headerHeight;
 | 
						|
                            paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                            backgroundColor = this._style.headerBackgroundColor;
 | 
						|
                            break;
 | 
						|
                    }
 | 
						|
                    this._paintMergedCells(cellgroups, paintRgn, backgroundColor);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Update the internal X scroll position.
 | 
						|
        this._scrollX = x;
 | 
						|
        // Scroll the X axis if needed. If the scroll distance exceeds
 | 
						|
        // the visible width, paint everything. Otherwise, blit the
 | 
						|
        // valid content and paint the dirty region.
 | 
						|
        if (dx !== 0 && contentWidth > 0) {
 | 
						|
            if (Math.abs(dx) >= contentWidth) {
 | 
						|
                this.paintContent(contentX, 0, contentWidth, height);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                const x = dx < 0 ? contentX : contentX + dx;
 | 
						|
                const y = 0;
 | 
						|
                const w = contentWidth - Math.abs(dx);
 | 
						|
                const h = height;
 | 
						|
                this._blitContent(this._canvas, x, y, w, h, x - dx, y);
 | 
						|
                this.paintContent(dx < 0 ? contentX : width - dx, 0, Math.abs(dx), height);
 | 
						|
                // Repaint merged cells that are intersected by the scroll level
 | 
						|
                // Otherwise it will be cut in two by the valid content, and drawn incorrectly
 | 
						|
                for (const rgn of ['body', 'column-header']) {
 | 
						|
                    const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn);
 | 
						|
                    let paintRgn = {
 | 
						|
                        region: rgn,
 | 
						|
                        xMin: 0,
 | 
						|
                        xMax: 0,
 | 
						|
                        yMin: 0,
 | 
						|
                        yMax: 0
 | 
						|
                    };
 | 
						|
                    let backgroundColor = undefined;
 | 
						|
                    switch (rgn) {
 | 
						|
                        case 'body':
 | 
						|
                            paintRgn.xMin = this.headerWidth;
 | 
						|
                            paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                            paintRgn.yMin = this.headerHeight;
 | 
						|
                            paintRgn.yMax = this.headerHeight + this.bodyHeight;
 | 
						|
                            backgroundColor = this._style.backgroundColor;
 | 
						|
                            break;
 | 
						|
                        case 'column-header':
 | 
						|
                            paintRgn.xMin = this.headerWidth;
 | 
						|
                            paintRgn.xMax = this.headerWidth + this.bodyWidth;
 | 
						|
                            paintRgn.yMin = 0;
 | 
						|
                            paintRgn.yMax = this.headerHeight;
 | 
						|
                            backgroundColor = this._style.headerBackgroundColor;
 | 
						|
                            break;
 | 
						|
                    }
 | 
						|
                    this._paintMergedCells(cellGroups, paintRgn, backgroundColor);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Paint the overlay.
 | 
						|
        this._paintOverlay();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Blit content into the on-screen grid canvas.
 | 
						|
     *
 | 
						|
     * The rect should be expressed in viewport coordinates.
 | 
						|
     *
 | 
						|
     * This automatically accounts for the dpi ratio.
 | 
						|
     */
 | 
						|
    _blitContent(source, x, y, w, h, dx, dy) {
 | 
						|
        // Scale the blit coordinates by the dpi ratio.
 | 
						|
        x *= this._dpiRatio;
 | 
						|
        y *= this._dpiRatio;
 | 
						|
        w *= this._dpiRatio;
 | 
						|
        h *= this._dpiRatio;
 | 
						|
        dx *= this._dpiRatio;
 | 
						|
        dy *= this._dpiRatio;
 | 
						|
        // Save the current gc state.
 | 
						|
        this._canvasGC.save();
 | 
						|
        // Set the transform to the identity matrix.
 | 
						|
        this._canvasGC.setTransform(1, 0, 0, 1, 0, 0);
 | 
						|
        // Draw the specified content.
 | 
						|
        this._canvasGC.drawImage(source, x, y, w, h, dx, dy, w, h);
 | 
						|
        // Restore the gc state.
 | 
						|
        this._canvasGC.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint the grid content for the given dirty rect.
 | 
						|
     *
 | 
						|
     * The rect should be expressed in valid viewport coordinates.
 | 
						|
     *
 | 
						|
     * This is the primary paint entry point. The individual `_draw*`
 | 
						|
     * methods should not be invoked directly. This method dispatches
 | 
						|
     * to the drawing methods in the correct order.
 | 
						|
     */
 | 
						|
    paintContent(rx, ry, rw, rh) {
 | 
						|
        // Scale the canvas and buffer GC for the dpi ratio.
 | 
						|
        this._canvasGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);
 | 
						|
        this._bufferGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);
 | 
						|
        // Clear the dirty rect of all content.
 | 
						|
        this._canvasGC.clearRect(rx, ry, rw, rh);
 | 
						|
        // Draw the void region.
 | 
						|
        this._drawVoidRegion(rx, ry, rw, rh);
 | 
						|
        // Draw the body region.
 | 
						|
        this._drawBodyRegion(rx, ry, rw, rh);
 | 
						|
        // Draw the row header region.
 | 
						|
        this._drawRowHeaderRegion(rx, ry, rw, rh);
 | 
						|
        // Draw the column header region.
 | 
						|
        this._drawColumnHeaderRegion(rx, ry, rw, rh);
 | 
						|
        // Draw the corner header region.
 | 
						|
        this.drawCornerHeaderRegion(rx, ry, rw, rh);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resizes body column headers so their text fits
 | 
						|
     * without clipping or wrapping.
 | 
						|
     * @param dataModel
 | 
						|
     */
 | 
						|
    _fitBodyColumnHeaders(dataModel, padding, numCols) {
 | 
						|
        // Get the body column count
 | 
						|
        const bodyColumnCount = numCols === undefined ? dataModel.columnCount('body') : numCols;
 | 
						|
        for (let i = 0; i < bodyColumnCount; i++) {
 | 
						|
            /*
 | 
						|
              if we're working with nested column headers,
 | 
						|
              retrieve the nested levels and iterate on them.
 | 
						|
            */
 | 
						|
            const numRows = dataModel.rowCount('column-header');
 | 
						|
            /*
 | 
						|
              Calculate the maximum text width, across
 | 
						|
              all nested rows under a given column number.
 | 
						|
            */
 | 
						|
            let maxWidth = 0;
 | 
						|
            for (let j = 0; j < numRows; j++) {
 | 
						|
                const config = DataGrid._getConfig(dataModel, j, i, 'column-header');
 | 
						|
                const textWidth = this._getCellTextWidth(config);
 | 
						|
                // Update the maximum width for that column.
 | 
						|
                maxWidth = Math.max(maxWidth, textWidth);
 | 
						|
            }
 | 
						|
            /*
 | 
						|
              Send a resize message with new width for the given column.
 | 
						|
              Using a padding of 15 pixels to leave some room.
 | 
						|
            */
 | 
						|
            this.resizeColumn('body', i, maxWidth + padding);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Resizes row header columns so their text fits
 | 
						|
     * without clipping or wrapping.
 | 
						|
     * @param dataModel
 | 
						|
     */
 | 
						|
    _fitRowColumnHeaders(dataModel, padding, numCols) {
 | 
						|
        /*
 | 
						|
          if we're working with nested row headers,
 | 
						|
          retrieve the nested levels and iterate on them.
 | 
						|
        */
 | 
						|
        const rowColumnCount = numCols === undefined ? dataModel.columnCount('row-header') : numCols;
 | 
						|
        for (let i = 0; i < rowColumnCount; i++) {
 | 
						|
            const numCols = dataModel.rowCount('column-header');
 | 
						|
            /*
 | 
						|
              Calculate the maximum text width, across
 | 
						|
              all nested columns under a given row index.
 | 
						|
            */
 | 
						|
            let maxWidth = 0;
 | 
						|
            for (let j = 0; j < numCols; j++) {
 | 
						|
                const config = DataGrid._getConfig(dataModel, j, i, 'corner-header');
 | 
						|
                const textWidth = this._getCellTextWidth(config);
 | 
						|
                maxWidth = Math.max(maxWidth, textWidth);
 | 
						|
            }
 | 
						|
            /*
 | 
						|
              Send a resize message with new width for the given column.
 | 
						|
              Using a padding of 15 pixels to leave some room.
 | 
						|
            */
 | 
						|
            this.resizeColumn('row-header', i, maxWidth + padding);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint the overlay content for the entire grid.
 | 
						|
     *
 | 
						|
     * This is the primary overlay paint entry point. The individual
 | 
						|
     * `_draw*` methods should not be invoked directly. This method
 | 
						|
     * dispatches to the drawing methods in the correct order.
 | 
						|
     */
 | 
						|
    _paintOverlay() {
 | 
						|
        // Scale the overlay GC for the dpi ratio.
 | 
						|
        this._overlayGC.setTransform(this._dpiRatio, 0, 0, this._dpiRatio, 0, 0);
 | 
						|
        // Clear the overlay of all content.
 | 
						|
        this._overlayGC.clearRect(0, 0, this._overlay.width, this._overlay.height);
 | 
						|
        // Draw the body selections.
 | 
						|
        this._drawBodySelections();
 | 
						|
        // Draw the row header selections.
 | 
						|
        this._drawRowHeaderSelections();
 | 
						|
        // Draw the column header selections.
 | 
						|
        this._drawColumnHeaderSelections();
 | 
						|
        // Draw the cursor.
 | 
						|
        this._drawCursor();
 | 
						|
        // Draw the shadows.
 | 
						|
        this._drawShadows();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the void region for the dirty rect.
 | 
						|
     */
 | 
						|
    _drawVoidRegion(rx, ry, rw, rh) {
 | 
						|
        // Look up the void color.
 | 
						|
        let color = this._style.voidColor;
 | 
						|
        // Bail if there is no void color.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fill the dirty rect with the void color.
 | 
						|
        this._canvasGC.fillStyle = color;
 | 
						|
        this._canvasGC.fillRect(rx, ry, rw, rh);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the body region which intersects the dirty rect.
 | 
						|
     */
 | 
						|
    _drawBodyRegion(rx, ry, rw, rh) {
 | 
						|
        // Get the visible content dimensions.
 | 
						|
        let contentW = this._columnSections.length - this._scrollX;
 | 
						|
        let contentH = this._rowSections.length - this._scrollY;
 | 
						|
        // Bail if there is no content to draw.
 | 
						|
        if (contentW <= 0 || contentH <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the visible content origin.
 | 
						|
        let contentX = this.headerWidth;
 | 
						|
        let contentY = this.headerHeight;
 | 
						|
        // Bail if the dirty rect does not intersect the content area.
 | 
						|
        if (rx + rw <= contentX) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry + rh <= contentY) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (rx >= contentX + contentW) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry >= contentY + contentH) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        // Get the upper and lower bounds of the dirty content area.
 | 
						|
        let x1 = Math.max(rx, contentX);
 | 
						|
        let y1 = Math.max(ry, contentY);
 | 
						|
        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);
 | 
						|
        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);
 | 
						|
        // Convert the dirty content bounds into cell bounds.
 | 
						|
        let r1 = this._rowSections.indexOf(y1 - contentY + this._scrollY);
 | 
						|
        let c1 = this._columnSections.indexOf(x1 - contentX + this._scrollX);
 | 
						|
        let r2 = this._rowSections.indexOf(y2 - contentY + this._scrollY);
 | 
						|
        let c2 = this._columnSections.indexOf(x2 - contentX + this._scrollX);
 | 
						|
        // Fetch the max row and column.
 | 
						|
        let maxRow = this._rowSections.count - 1;
 | 
						|
        let maxColumn = this._columnSections.count - 1;
 | 
						|
        // Handle a dirty content area larger than the cell count.
 | 
						|
        if (r2 < 0) {
 | 
						|
            r2 = maxRow;
 | 
						|
        }
 | 
						|
        if (c2 < 0) {
 | 
						|
            c2 = maxColumn;
 | 
						|
        }
 | 
						|
        // Convert the cell bounds back to visible coordinates.
 | 
						|
        let x = this._columnSections.offsetOf(c1) + contentX - this._scrollX;
 | 
						|
        let y = this._rowSections.offsetOf(r1) + contentY - this._scrollY;
 | 
						|
        // Set up the paint region size variables.
 | 
						|
        let width = 0;
 | 
						|
        let height = 0;
 | 
						|
        // Allocate the section sizes arrays.
 | 
						|
        let rowSizes = new Array(r2 - r1 + 1);
 | 
						|
        let columnSizes = new Array(c2 - c1 + 1);
 | 
						|
        // Get the row sizes for the region.
 | 
						|
        for (let j = r1; j <= r2; ++j) {
 | 
						|
            let size = this._rowSections.sizeOf(j);
 | 
						|
            rowSizes[j - r1] = size;
 | 
						|
            height += size;
 | 
						|
        }
 | 
						|
        // Get the column sizes for the region.
 | 
						|
        for (let i = c1; i <= c2; ++i) {
 | 
						|
            let size = this._columnSections.sizeOf(i);
 | 
						|
            columnSizes[i - c1] = size;
 | 
						|
            width += size;
 | 
						|
        }
 | 
						|
        // Adjust the geometry if the last row is streched.
 | 
						|
        if (this._stretchLastRow && ph > bh && r2 === maxRow) {
 | 
						|
            let dh = this.pageHeight - this.bodyHeight;
 | 
						|
            rowSizes[rowSizes.length - 1] += dh;
 | 
						|
            height += dh;
 | 
						|
            y2 += dh;
 | 
						|
        }
 | 
						|
        // Adjust the geometry if the last column is streched.
 | 
						|
        if (this._stretchLastColumn && pw > bw && c2 === maxColumn) {
 | 
						|
            let dw = this.pageWidth - this.bodyWidth;
 | 
						|
            columnSizes[columnSizes.length - 1] += dw;
 | 
						|
            width += dw;
 | 
						|
            x2 += dw;
 | 
						|
        }
 | 
						|
        // Create the paint region object.
 | 
						|
        let rgn = {
 | 
						|
            region: 'body',
 | 
						|
            xMin: x1,
 | 
						|
            yMin: y1,
 | 
						|
            xMax: x2,
 | 
						|
            yMax: y2,
 | 
						|
            x,
 | 
						|
            y,
 | 
						|
            width,
 | 
						|
            height,
 | 
						|
            row: r1,
 | 
						|
            column: c1,
 | 
						|
            rowSizes,
 | 
						|
            columnSizes
 | 
						|
        };
 | 
						|
        // Draw the background.
 | 
						|
        this._drawBackground(rgn, this._style.backgroundColor);
 | 
						|
        // Draw the row background.
 | 
						|
        this._drawRowBackground(rgn, this._style.rowBackgroundColor);
 | 
						|
        // Draw the column background.
 | 
						|
        this._drawColumnBackground(rgn, this._style.columnBackgroundColor);
 | 
						|
        // Draw the cell content for the paint region.
 | 
						|
        this._drawCells(rgn);
 | 
						|
        // Draw the horizontal grid lines.
 | 
						|
        this._drawHorizontalGridLines(rgn, this._style.horizontalGridLineColor || this._style.gridLineColor);
 | 
						|
        // Draw the vertical grid lines.
 | 
						|
        this._drawVerticalGridLines(rgn, this._style.verticalGridLineColor || this._style.gridLineColor);
 | 
						|
        // Get the cellgroups from the cell-region that intersects with the paint region
 | 
						|
        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {
 | 
						|
            return this.cellGroupInteresectsRegion(group, rgn);
 | 
						|
        });
 | 
						|
        // Draw merged cells
 | 
						|
        this._paintMergedCells(cellGroups, rgn, this._style.backgroundColor);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the row header region which intersects the dirty rect.
 | 
						|
     */
 | 
						|
    _drawRowHeaderRegion(rx, ry, rw, rh) {
 | 
						|
        // Get the visible content dimensions.
 | 
						|
        let contentW = this.headerWidth;
 | 
						|
        let contentH = this.bodyHeight - this._scrollY;
 | 
						|
        // Bail if there is no content to draw.
 | 
						|
        if (contentW <= 0 || contentH <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the visible content origin.
 | 
						|
        let contentX = 0;
 | 
						|
        let contentY = this.headerHeight;
 | 
						|
        // Bail if the dirty rect does not intersect the content area.
 | 
						|
        if (rx + rw <= contentX) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry + rh <= contentY) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (rx >= contentX + contentW) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry >= contentY + contentH) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        // Get the upper and lower bounds of the dirty content area.
 | 
						|
        let x1 = rx;
 | 
						|
        let y1 = Math.max(ry, contentY);
 | 
						|
        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);
 | 
						|
        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);
 | 
						|
        // Convert the dirty content bounds into cell bounds.
 | 
						|
        let r1 = this._rowSections.indexOf(y1 - contentY + this._scrollY);
 | 
						|
        let c1 = this._rowHeaderSections.indexOf(x1);
 | 
						|
        let r2 = this._rowSections.indexOf(y2 - contentY + this._scrollY);
 | 
						|
        let c2 = this._rowHeaderSections.indexOf(x2);
 | 
						|
        // Fetch max row and column.
 | 
						|
        let maxRow = this._rowSections.count - 1;
 | 
						|
        let maxColumn = this._rowHeaderSections.count - 1;
 | 
						|
        // Handle a dirty content area larger than the cell count.
 | 
						|
        if (r2 < 0) {
 | 
						|
            r2 = maxRow;
 | 
						|
        }
 | 
						|
        if (c2 < 0) {
 | 
						|
            c2 = maxColumn;
 | 
						|
        }
 | 
						|
        // Convert the cell bounds back to visible coordinates.
 | 
						|
        let x = this._rowHeaderSections.offsetOf(c1);
 | 
						|
        let y = this._rowSections.offsetOf(r1) + contentY - this._scrollY;
 | 
						|
        // Set up the paint region size variables.
 | 
						|
        let width = 0;
 | 
						|
        let height = 0;
 | 
						|
        // Allocate the section sizes arrays.
 | 
						|
        let rowSizes = new Array(r2 - r1 + 1);
 | 
						|
        let columnSizes = new Array(c2 - c1 + 1);
 | 
						|
        // Get the row sizes for the region.
 | 
						|
        for (let j = r1; j <= r2; ++j) {
 | 
						|
            let size = this._rowSections.sizeOf(j);
 | 
						|
            rowSizes[j - r1] = size;
 | 
						|
            height += size;
 | 
						|
        }
 | 
						|
        // Get the column sizes for the region.
 | 
						|
        for (let i = c1; i <= c2; ++i) {
 | 
						|
            let size = this._rowHeaderSections.sizeOf(i);
 | 
						|
            columnSizes[i - c1] = size;
 | 
						|
            width += size;
 | 
						|
        }
 | 
						|
        // Adjust the geometry if the last row is stretched.
 | 
						|
        if (this._stretchLastRow && ph > bh && r2 === maxRow) {
 | 
						|
            let dh = this.pageHeight - this.bodyHeight;
 | 
						|
            rowSizes[rowSizes.length - 1] += dh;
 | 
						|
            height += dh;
 | 
						|
            y2 += dh;
 | 
						|
        }
 | 
						|
        // Create the paint region object.
 | 
						|
        let rgn = {
 | 
						|
            region: 'row-header',
 | 
						|
            xMin: x1,
 | 
						|
            yMin: y1,
 | 
						|
            xMax: x2,
 | 
						|
            yMax: y2,
 | 
						|
            x,
 | 
						|
            y,
 | 
						|
            width,
 | 
						|
            height,
 | 
						|
            row: r1,
 | 
						|
            column: c1,
 | 
						|
            rowSizes,
 | 
						|
            columnSizes
 | 
						|
        };
 | 
						|
        // Draw the background.
 | 
						|
        this._drawBackground(rgn, this._style.headerBackgroundColor);
 | 
						|
        // Draw the cell content for the paint region.
 | 
						|
        this._drawCells(rgn);
 | 
						|
        // Draw the horizontal grid lines.
 | 
						|
        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||
 | 
						|
            this._style.headerGridLineColor);
 | 
						|
        // Draw the vertical grid lines.
 | 
						|
        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);
 | 
						|
        // Get the cellgroups from the cell-region that intersects with the paint region
 | 
						|
        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {
 | 
						|
            return this.cellGroupInteresectsRegion(group, rgn);
 | 
						|
        });
 | 
						|
        // Draw merged cells
 | 
						|
        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the column header region which intersects the dirty rect.
 | 
						|
     */
 | 
						|
    _drawColumnHeaderRegion(rx, ry, rw, rh) {
 | 
						|
        // Get the visible content dimensions.
 | 
						|
        let contentW = this.bodyWidth - this._scrollX;
 | 
						|
        let contentH = this.headerHeight;
 | 
						|
        // Bail if there is no content to draw.
 | 
						|
        if (contentW <= 0 || contentH <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the visible content origin.
 | 
						|
        let contentX = this.headerWidth;
 | 
						|
        let contentY = 0;
 | 
						|
        // Bail if the dirty rect does not intersect the content area.
 | 
						|
        if (rx + rw <= contentX) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry + rh <= contentY) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (rx >= contentX + contentW) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry >= contentY + contentH) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the geometry.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        // Get the upper and lower bounds of the dirty content area.
 | 
						|
        let x1 = Math.max(rx, contentX);
 | 
						|
        let y1 = ry;
 | 
						|
        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);
 | 
						|
        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);
 | 
						|
        // Convert the dirty content bounds into cell bounds.
 | 
						|
        let r1 = this._columnHeaderSections.indexOf(y1);
 | 
						|
        let c1 = this._columnSections.indexOf(x1 - contentX + this._scrollX);
 | 
						|
        let r2 = this._columnHeaderSections.indexOf(y2);
 | 
						|
        let c2 = this._columnSections.indexOf(x2 - contentX + this._scrollX);
 | 
						|
        // Fetch the max row and column.
 | 
						|
        let maxRow = this._columnHeaderSections.count - 1;
 | 
						|
        let maxColumn = this._columnSections.count - 1;
 | 
						|
        // Handle a dirty content area larger than the cell count.
 | 
						|
        if (r2 < 0) {
 | 
						|
            r2 = maxRow;
 | 
						|
        }
 | 
						|
        if (c2 < 0) {
 | 
						|
            c2 = maxColumn;
 | 
						|
        }
 | 
						|
        // Convert the cell bounds back to visible coordinates.
 | 
						|
        let x = this._columnSections.offsetOf(c1) + contentX - this._scrollX;
 | 
						|
        let y = this._columnHeaderSections.offsetOf(r1);
 | 
						|
        // Set up the paint region size variables.
 | 
						|
        let width = 0;
 | 
						|
        let height = 0;
 | 
						|
        // Allocate the section sizes arrays.
 | 
						|
        let rowSizes = new Array(r2 - r1 + 1);
 | 
						|
        let columnSizes = new Array(c2 - c1 + 1);
 | 
						|
        // Get the row sizes for the region.
 | 
						|
        for (let j = r1; j <= r2; ++j) {
 | 
						|
            let size = this._columnHeaderSections.sizeOf(j);
 | 
						|
            rowSizes[j - r1] = size;
 | 
						|
            height += size;
 | 
						|
        }
 | 
						|
        // Get the column sizes for the region.
 | 
						|
        for (let i = c1; i <= c2; ++i) {
 | 
						|
            let size = this._columnSections.sizeOf(i);
 | 
						|
            columnSizes[i - c1] = size;
 | 
						|
            width += size;
 | 
						|
        }
 | 
						|
        // Adjust the geometry if the last column is stretched.
 | 
						|
        if (this._stretchLastColumn && pw > bw && c2 === maxColumn) {
 | 
						|
            let dw = this.pageWidth - this.bodyWidth;
 | 
						|
            columnSizes[columnSizes.length - 1] += dw;
 | 
						|
            width += dw;
 | 
						|
            x2 += dw;
 | 
						|
        }
 | 
						|
        // Create the paint region object.
 | 
						|
        let rgn = {
 | 
						|
            region: 'column-header',
 | 
						|
            xMin: x1,
 | 
						|
            yMin: y1,
 | 
						|
            xMax: x2,
 | 
						|
            yMax: y2,
 | 
						|
            x,
 | 
						|
            y,
 | 
						|
            width,
 | 
						|
            height,
 | 
						|
            row: r1,
 | 
						|
            column: c1,
 | 
						|
            rowSizes,
 | 
						|
            columnSizes
 | 
						|
        };
 | 
						|
        // Draw the background.
 | 
						|
        this._drawBackground(rgn, this._style.headerBackgroundColor);
 | 
						|
        // Draw the cell content for the paint region.
 | 
						|
        this._drawCells(rgn);
 | 
						|
        // Draw the horizontal grid lines.
 | 
						|
        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||
 | 
						|
            this._style.headerGridLineColor);
 | 
						|
        // Draw the vertical grid lines.
 | 
						|
        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);
 | 
						|
        // Get the cellgroups from the cell-region that intersects with the paint region
 | 
						|
        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {
 | 
						|
            return this.cellGroupInteresectsRegion(group, rgn);
 | 
						|
        });
 | 
						|
        // Draw merged cells
 | 
						|
        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the corner header region which intersects the dirty rect.
 | 
						|
     */
 | 
						|
    drawCornerHeaderRegion(rx, ry, rw, rh) {
 | 
						|
        // Get the visible content dimensions.
 | 
						|
        let contentW = this.headerWidth;
 | 
						|
        let contentH = this.headerHeight;
 | 
						|
        // Bail if there is no content to draw.
 | 
						|
        if (contentW <= 0 || contentH <= 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the visible content origin.
 | 
						|
        let contentX = 0;
 | 
						|
        let contentY = 0;
 | 
						|
        // Bail if the dirty rect does not intersect the content area.
 | 
						|
        if (rx + rw <= contentX) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry + rh <= contentY) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (rx >= contentX + contentW) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (ry >= contentY + contentH) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Get the upper and lower bounds of the dirty content area.
 | 
						|
        let x1 = rx;
 | 
						|
        let y1 = ry;
 | 
						|
        let x2 = Math.min(rx + rw - 1, contentX + contentW - 1);
 | 
						|
        let y2 = Math.min(ry + rh - 1, contentY + contentH - 1);
 | 
						|
        // Convert the dirty content bounds into cell bounds.
 | 
						|
        let r1 = this._columnHeaderSections.indexOf(y1);
 | 
						|
        let c1 = this._rowHeaderSections.indexOf(x1);
 | 
						|
        let r2 = this._columnHeaderSections.indexOf(y2);
 | 
						|
        let c2 = this._rowHeaderSections.indexOf(x2);
 | 
						|
        // Handle a dirty content area larger than the cell count.
 | 
						|
        if (r2 < 0) {
 | 
						|
            r2 = this._columnHeaderSections.count - 1;
 | 
						|
        }
 | 
						|
        if (c2 < 0) {
 | 
						|
            c2 = this._rowHeaderSections.count - 1;
 | 
						|
        }
 | 
						|
        // Convert the cell bounds back to visible coordinates.
 | 
						|
        let x = this._rowHeaderSections.offsetOf(c1);
 | 
						|
        let y = this._columnHeaderSections.offsetOf(r1);
 | 
						|
        // Set up the paint region size variables.
 | 
						|
        let width = 0;
 | 
						|
        let height = 0;
 | 
						|
        // Allocate the section sizes arrays.
 | 
						|
        let rowSizes = new Array(r2 - r1 + 1);
 | 
						|
        let columnSizes = new Array(c2 - c1 + 1);
 | 
						|
        // Get the row sizes for the region.
 | 
						|
        for (let j = r1; j <= r2; ++j) {
 | 
						|
            let size = this._columnHeaderSections.sizeOf(j);
 | 
						|
            rowSizes[j - r1] = size;
 | 
						|
            height += size;
 | 
						|
        }
 | 
						|
        // Get the column sizes for the region.
 | 
						|
        for (let i = c1; i <= c2; ++i) {
 | 
						|
            let size = this._rowHeaderSections.sizeOf(i);
 | 
						|
            columnSizes[i - c1] = size;
 | 
						|
            width += size;
 | 
						|
        }
 | 
						|
        // Create the paint region object.
 | 
						|
        let rgn = {
 | 
						|
            region: 'corner-header',
 | 
						|
            xMin: x1,
 | 
						|
            yMin: y1,
 | 
						|
            xMax: x2,
 | 
						|
            yMax: y2,
 | 
						|
            x,
 | 
						|
            y,
 | 
						|
            width,
 | 
						|
            height,
 | 
						|
            row: r1,
 | 
						|
            column: c1,
 | 
						|
            rowSizes,
 | 
						|
            columnSizes
 | 
						|
        };
 | 
						|
        // Draw the background.
 | 
						|
        this._drawBackground(rgn, this._style.headerBackgroundColor);
 | 
						|
        // Draw the cell content for the paint region.
 | 
						|
        this._drawCells(rgn);
 | 
						|
        // Draw the horizontal grid lines.
 | 
						|
        this._drawHorizontalGridLines(rgn, this._style.headerHorizontalGridLineColor ||
 | 
						|
            this._style.headerGridLineColor);
 | 
						|
        // Draw the vertical grid lines.
 | 
						|
        this._drawVerticalGridLines(rgn, this._style.headerVerticalGridLineColor || this._style.headerGridLineColor);
 | 
						|
        // Get the cellgroups from the cell-region that intersects with the paint region
 | 
						|
        const cellGroups = CellGroup.getCellGroupsAtRegion(this.dataModel, rgn.region).filter(group => {
 | 
						|
            return this.cellGroupInteresectsRegion(group, rgn);
 | 
						|
        });
 | 
						|
        // Draw merged cells
 | 
						|
        this._paintMergedCells(cellGroups, rgn, this._style.headerBackgroundColor);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the background for the given paint region.
 | 
						|
     */
 | 
						|
    _drawBackground(rgn, color) {
 | 
						|
        // Bail if there is no color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Unpack the region.
 | 
						|
        let { xMin, yMin, xMax, yMax } = rgn;
 | 
						|
        // Fill the region with the specified color.
 | 
						|
        this._canvasGC.fillStyle = color;
 | 
						|
        this._canvasGC.fillRect(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the row background for the given paint region.
 | 
						|
     */
 | 
						|
    _drawRowBackground(rgn, colorFn) {
 | 
						|
        // Bail if there is no color function.
 | 
						|
        if (!colorFn) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the X bounds for the row.
 | 
						|
        let x1 = Math.max(rgn.xMin, rgn.x);
 | 
						|
        let x2 = Math.min(rgn.x + rgn.width - 1, rgn.xMax);
 | 
						|
        // Draw the background for the rows in the region.
 | 
						|
        for (let y = rgn.y, j = 0, n = rgn.rowSizes.length; j < n; ++j) {
 | 
						|
            // Fetch the size of the row.
 | 
						|
            let size = rgn.rowSizes[j];
 | 
						|
            // Skip zero sized rows.
 | 
						|
            if (size === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Get the background color for the row.
 | 
						|
            let color = colorFn(rgn.row + j);
 | 
						|
            // Fill the row with the background color if needed.
 | 
						|
            if (color) {
 | 
						|
                let y1 = Math.max(rgn.yMin, y);
 | 
						|
                let y2 = Math.min(y + size - 1, rgn.yMax);
 | 
						|
                this._canvasGC.fillStyle = color;
 | 
						|
                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
            }
 | 
						|
            // Increment the running Y coordinate.
 | 
						|
            y += size;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the column background for the given paint region.
 | 
						|
     */
 | 
						|
    _drawColumnBackground(rgn, colorFn) {
 | 
						|
        // Bail if there is no color function.
 | 
						|
        if (!colorFn) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the Y bounds for the column.
 | 
						|
        let y1 = Math.max(rgn.yMin, rgn.y);
 | 
						|
        let y2 = Math.min(rgn.y + rgn.height - 1, rgn.yMax);
 | 
						|
        // Draw the background for the columns in the region.
 | 
						|
        for (let x = rgn.x, i = 0, n = rgn.columnSizes.length; i < n; ++i) {
 | 
						|
            // Fetch the size of the column.
 | 
						|
            let size = rgn.columnSizes[i];
 | 
						|
            // Skip zero sized columns.
 | 
						|
            if (size === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Get the background color for the column.
 | 
						|
            let color = colorFn(rgn.column + i);
 | 
						|
            // Fill the column with the background color if needed.
 | 
						|
            if (color) {
 | 
						|
                let x1 = Math.max(rgn.xMin, x);
 | 
						|
                let x2 = Math.min(x + size - 1, rgn.xMax);
 | 
						|
                this._canvasGC.fillStyle = color;
 | 
						|
                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
            }
 | 
						|
            // Increment the running X coordinate.
 | 
						|
            x += size;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns column size
 | 
						|
     * @param region
 | 
						|
     * @param index
 | 
						|
     */
 | 
						|
    _getColumnSize(region, index) {
 | 
						|
        if (region === 'corner-header') {
 | 
						|
            return this._rowHeaderSections.sizeOf(index);
 | 
						|
        }
 | 
						|
        return this.columnSize(region, index);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Returns row size
 | 
						|
     * @param region
 | 
						|
     * @param index
 | 
						|
     */
 | 
						|
    _getRowSize(region, index) {
 | 
						|
        if (region === 'corner-header') {
 | 
						|
            return this._columnHeaderSections.sizeOf(index);
 | 
						|
        }
 | 
						|
        return this.rowSize(region, index);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the cells for the given paint region.
 | 
						|
     */
 | 
						|
    _drawCells(rgn) {
 | 
						|
        // Bail if there is no data model.
 | 
						|
        if (!this._dataModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Set up the cell config object for rendering.
 | 
						|
        let config = {
 | 
						|
            x: 0,
 | 
						|
            y: 0,
 | 
						|
            width: 0,
 | 
						|
            height: 0,
 | 
						|
            region: rgn.region,
 | 
						|
            row: 0,
 | 
						|
            column: 0,
 | 
						|
            value: null,
 | 
						|
            metadata: DataModel.emptyMetadata
 | 
						|
        };
 | 
						|
        let groupIndex = -1;
 | 
						|
        // Save the buffer gc before wrapping.
 | 
						|
        this._bufferGC.save();
 | 
						|
        // Wrap the buffer gc for painting the cells.
 | 
						|
        let gc = new GraphicsContext(this._bufferGC);
 | 
						|
        let height = 0;
 | 
						|
        // Loop over the columns in the region.
 | 
						|
        for (let x = rgn.x, i = 0, n = rgn.columnSizes.length; i < n; ++i) {
 | 
						|
            // Fetch the size of the column.
 | 
						|
            let width = rgn.columnSizes[i];
 | 
						|
            // Skip zero sized columns.
 | 
						|
            if (width === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Compute the column index.
 | 
						|
            let column = rgn.column + i;
 | 
						|
            // Update the config for the current column.
 | 
						|
            config.x = x;
 | 
						|
            config.width = width;
 | 
						|
            config.column = column;
 | 
						|
            // Loop over the rows in the column.
 | 
						|
            for (let y = rgn.y, j = 0, n = rgn.rowSizes.length; j < n; ++j) {
 | 
						|
                // Fetch the size of the row.
 | 
						|
                height = rgn.rowSizes[j];
 | 
						|
                // Skip zero sized rows.
 | 
						|
                if (height === 0) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                // Compute the row index.
 | 
						|
                let row = rgn.row + j;
 | 
						|
                groupIndex = CellGroup.getGroupIndex(this.dataModel, config.region, row, column);
 | 
						|
                // For merged cell regions, don't do anything, we draw merged regions later.
 | 
						|
                if (groupIndex !== -1) {
 | 
						|
                    y += height;
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                // Clear the buffer rect for the cell.
 | 
						|
                gc.clearRect(x, y, width, height);
 | 
						|
                let value = DataGrid._getCellValue(this.dataModel, rgn.region, row, column);
 | 
						|
                let metadata = DataGrid._getCellMetadata(this.dataModel, rgn.region, row, column);
 | 
						|
                // Update the config for the current cell.
 | 
						|
                config.y = y;
 | 
						|
                config.height = height;
 | 
						|
                config.width = width;
 | 
						|
                config.row = row;
 | 
						|
                config.value = value;
 | 
						|
                config.metadata = metadata;
 | 
						|
                // Get the renderer for the cell.
 | 
						|
                let renderer = this._cellRenderers.get(config);
 | 
						|
                // Save the GC state.
 | 
						|
                gc.save();
 | 
						|
                // Paint the cell into the off-screen buffer.
 | 
						|
                try {
 | 
						|
                    if (renderer instanceof AsyncCellRenderer) {
 | 
						|
                        if (renderer.isReady(config)) {
 | 
						|
                            renderer.paint(gc, config);
 | 
						|
                        }
 | 
						|
                        else {
 | 
						|
                            renderer.paintPlaceholder(gc, config);
 | 
						|
                            renderer.load(config).then(() => {
 | 
						|
                                const r1 = row;
 | 
						|
                                const r2 = row + 1;
 | 
						|
                                const c1 = column;
 | 
						|
                                const c2 = column + 1;
 | 
						|
                                this.repaintRegion(rgn.region, r1, c1, r2, c2);
 | 
						|
                            });
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        renderer.paint(gc, config);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                catch (err) {
 | 
						|
                    console.error(err);
 | 
						|
                }
 | 
						|
                // Restore the GC state.
 | 
						|
                gc.restore();
 | 
						|
                // Compute the actual X bounds for the cell.
 | 
						|
                let x1 = Math.max(rgn.xMin, config.x);
 | 
						|
                let x2 = Math.min(config.x + config.width - 1, rgn.xMax);
 | 
						|
                // Compute the actual Y bounds for the cell.
 | 
						|
                let y1 = Math.max(rgn.yMin, config.y);
 | 
						|
                let y2 = Math.min(config.y + config.height - 1, rgn.yMax);
 | 
						|
                this._blitContent(this._buffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, x1, y1);
 | 
						|
                // Increment the running Y coordinate.
 | 
						|
                y += height;
 | 
						|
            }
 | 
						|
            // Restore the GC state.
 | 
						|
            gc.restore();
 | 
						|
            // Increment the running X coordinate.
 | 
						|
            x += width;
 | 
						|
        }
 | 
						|
        // Dispose of the wrapped gc.
 | 
						|
        gc.dispose();
 | 
						|
        // Restore the final buffer gc state.
 | 
						|
        this._bufferGC.restore();
 | 
						|
    }
 | 
						|
    // TODO Move this in the utils file (but we need the PaintRegion typing)
 | 
						|
    cellGroupInteresectsRegion(group, rgn) {
 | 
						|
        const rgnR1 = rgn.row;
 | 
						|
        const rgnR2 = rgn.row + rgn.rowSizes.length;
 | 
						|
        const rgnC1 = rgn.column;
 | 
						|
        const rgnC2 = rgn.column + rgn.columnSizes.length;
 | 
						|
        const dx = Math.min(group.r2, rgnR2) - Math.max(group.r1, rgnR1);
 | 
						|
        const dy = Math.min(group.c2, rgnC2) - Math.max(group.c1, rgnC1);
 | 
						|
        return dx >= 0 && dy >= 0;
 | 
						|
    }
 | 
						|
    static _getCellValue(dm, region, row, col) {
 | 
						|
        // Get the value for the cell.
 | 
						|
        try {
 | 
						|
            return dm.data(region, row, col);
 | 
						|
        }
 | 
						|
        catch (err) {
 | 
						|
            console.error(err);
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    static _getCellMetadata(dm, region, row, col) {
 | 
						|
        // Get the metadata for the cell.
 | 
						|
        try {
 | 
						|
            return dm.metadata(region, row, col);
 | 
						|
        }
 | 
						|
        catch (err) {
 | 
						|
            console.error(err);
 | 
						|
            return DataModel.emptyMetadata;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint group cells.
 | 
						|
     */
 | 
						|
    _paintMergedCells(cellGroups, rgn, backgroundColor) {
 | 
						|
        // Bail if there is no data model.
 | 
						|
        if (!this._dataModel) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Set up the cell config object for rendering.
 | 
						|
        let config = {
 | 
						|
            x: 0,
 | 
						|
            y: 0,
 | 
						|
            width: 0,
 | 
						|
            height: 0,
 | 
						|
            region: rgn.region,
 | 
						|
            row: 0,
 | 
						|
            column: 0,
 | 
						|
            value: null,
 | 
						|
            metadata: DataModel.emptyMetadata
 | 
						|
        };
 | 
						|
        if (backgroundColor) {
 | 
						|
            this._canvasGC.fillStyle = backgroundColor;
 | 
						|
        }
 | 
						|
        // Set the line width for the grid lines.
 | 
						|
        this._canvasGC.lineWidth = 1;
 | 
						|
        // Save the buffer gc before wrapping.
 | 
						|
        this._bufferGC.save();
 | 
						|
        // Wrap the buffer gc for painting the cells.
 | 
						|
        let gc = new GraphicsContext(this._bufferGC);
 | 
						|
        for (const group of cellGroups) {
 | 
						|
            let width = 0;
 | 
						|
            for (let c = group.c1; c <= group.c2; c++) {
 | 
						|
                width += this._getColumnSize(rgn.region, c);
 | 
						|
            }
 | 
						|
            let height = 0;
 | 
						|
            for (let r = group.r1; r <= group.r2; r++) {
 | 
						|
                height += this._getRowSize(rgn.region, r);
 | 
						|
            }
 | 
						|
            let value = DataGrid._getCellValue(this.dataModel, rgn.region, group.r1, group.c1);
 | 
						|
            let metadata = DataGrid._getCellMetadata(this.dataModel, rgn.region, group.r1, group.c2);
 | 
						|
            let x = 0;
 | 
						|
            let y = 0;
 | 
						|
            switch (rgn.region) {
 | 
						|
                case 'body':
 | 
						|
                    x =
 | 
						|
                        this._columnSections.offsetOf(group.c1) +
 | 
						|
                            this.headerWidth -
 | 
						|
                            this._scrollX;
 | 
						|
                    y =
 | 
						|
                        this._rowSections.offsetOf(group.r1) +
 | 
						|
                            this.headerHeight -
 | 
						|
                            this._scrollY;
 | 
						|
                    break;
 | 
						|
                case 'column-header':
 | 
						|
                    x =
 | 
						|
                        this._columnSections.offsetOf(group.c1) +
 | 
						|
                            this.headerWidth -
 | 
						|
                            this._scrollX;
 | 
						|
                    y = this._rowSections.offsetOf(group.r1);
 | 
						|
                    break;
 | 
						|
                case 'row-header':
 | 
						|
                    x = this._columnSections.offsetOf(group.c1);
 | 
						|
                    y =
 | 
						|
                        this._rowSections.offsetOf(group.r1) +
 | 
						|
                            this.headerHeight -
 | 
						|
                            this._scrollY;
 | 
						|
                    break;
 | 
						|
                case 'corner-header':
 | 
						|
                    x = this._columnSections.offsetOf(group.c1);
 | 
						|
                    y = this._rowSections.offsetOf(group.r1);
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
            config.x = x;
 | 
						|
            config.y = y;
 | 
						|
            config.width = width;
 | 
						|
            config.height = height;
 | 
						|
            config.region = rgn.region;
 | 
						|
            config.row = group.r1;
 | 
						|
            config.column = group.c1;
 | 
						|
            config.value = value;
 | 
						|
            config.metadata = metadata;
 | 
						|
            // Compute the actual X bounds for the cell.
 | 
						|
            const x1 = Math.max(rgn.xMin, x);
 | 
						|
            const x2 = Math.min(x + width - 2, rgn.xMax);
 | 
						|
            // Compute the actual Y bounds for the cell.
 | 
						|
            const y1 = Math.max(rgn.yMin, y);
 | 
						|
            const y2 = Math.min(y + height - 2, rgn.yMax);
 | 
						|
            if (x2 <= x1 || y2 <= y1) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Draw the background.
 | 
						|
            if (backgroundColor) {
 | 
						|
                this._canvasGC.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
            }
 | 
						|
            // Get the renderer for the cell.
 | 
						|
            let renderer = this._cellRenderers.get(config);
 | 
						|
            // Clear the buffer rect for the cell.
 | 
						|
            gc.clearRect(config.x, config.y, width, height);
 | 
						|
            // Save the GC state.
 | 
						|
            gc.save();
 | 
						|
            // Paint the cell into the off-screen buffer.
 | 
						|
            try {
 | 
						|
                if (renderer instanceof AsyncCellRenderer) {
 | 
						|
                    if (renderer.isReady(config)) {
 | 
						|
                        renderer.paint(gc, config);
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        renderer.paintPlaceholder(gc, config);
 | 
						|
                        const r1 = group.r1;
 | 
						|
                        const r2 = group.r2;
 | 
						|
                        const c1 = group.c1;
 | 
						|
                        const c2 = group.c2;
 | 
						|
                        renderer.load(config).then(() => {
 | 
						|
                            this.repaintRegion(rgn.region, r1, c1, r2, c2);
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    renderer.paint(gc, config);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            catch (err) {
 | 
						|
                console.error(err);
 | 
						|
            }
 | 
						|
            // Restore the GC state.
 | 
						|
            gc.restore();
 | 
						|
            this._blitContent(this._buffer, x1, y1, x2 - x1 + 1, y2 - y1 + 1, x1, y1);
 | 
						|
        }
 | 
						|
        // Dispose of the wrapped gc.
 | 
						|
        gc.dispose();
 | 
						|
        // Restore the final buffer gc state.
 | 
						|
        this._bufferGC.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the horizontal grid lines for the given paint region.
 | 
						|
     */
 | 
						|
    _drawHorizontalGridLines(rgn, color) {
 | 
						|
        // Bail if there is no color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the X bounds for the horizontal lines.
 | 
						|
        const x1 = Math.max(rgn.xMin, rgn.x);
 | 
						|
        const x2 = Math.min(rgn.x + rgn.width, rgn.xMax + 1);
 | 
						|
        // Begin the path for the grid lines.
 | 
						|
        this._canvasGC.beginPath();
 | 
						|
        // Set the line width for the grid lines.
 | 
						|
        this._canvasGC.lineWidth = 1;
 | 
						|
        // Fetch the geometry.
 | 
						|
        const bh = this.bodyHeight;
 | 
						|
        const ph = this.pageHeight;
 | 
						|
        // Fetch the number of grid lines to be drawn.
 | 
						|
        let n = rgn.rowSizes.length;
 | 
						|
        // Adjust the count down if the last line shouldn't be drawn.
 | 
						|
        if (this._stretchLastRow && ph > bh) {
 | 
						|
            if (rgn.row + n === this._rowSections.count) {
 | 
						|
                n -= 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Draw the horizontal grid lines.
 | 
						|
        for (let y = rgn.y, j = 0; j < n; ++j) {
 | 
						|
            // Fetch the size of the row.
 | 
						|
            let size = rgn.rowSizes[j];
 | 
						|
            // Skip zero sized rows.
 | 
						|
            if (size === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Compute the Y position of the line.
 | 
						|
            let pos = y + size - 1;
 | 
						|
            // Draw the line if it's in range of the dirty rect.
 | 
						|
            if (pos >= rgn.yMin && pos <= rgn.yMax) {
 | 
						|
                this._canvasGC.moveTo(x1, pos + 0.5);
 | 
						|
                this._canvasGC.lineTo(x2, pos + 0.5);
 | 
						|
            }
 | 
						|
            // Increment the running Y coordinate.
 | 
						|
            y += size;
 | 
						|
        }
 | 
						|
        // Stroke the lines with the specified color.
 | 
						|
        this._canvasGC.strokeStyle = color;
 | 
						|
        this._canvasGC.stroke();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the vertical grid lines for the given paint region.
 | 
						|
     */
 | 
						|
    _drawVerticalGridLines(rgn, color) {
 | 
						|
        // Bail if there is no color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Compute the Y bounds for the vertical lines.
 | 
						|
        const y1 = Math.max(rgn.yMin, rgn.y);
 | 
						|
        const y2 = Math.min(rgn.y + rgn.height, rgn.yMax + 1);
 | 
						|
        // Begin the path for the grid lines
 | 
						|
        this._canvasGC.beginPath();
 | 
						|
        // Set the line width for the grid lines.
 | 
						|
        this._canvasGC.lineWidth = 1;
 | 
						|
        // Fetch the geometry.
 | 
						|
        const bw = this.bodyWidth;
 | 
						|
        const pw = this.pageWidth;
 | 
						|
        // Fetch the number of grid lines to be drawn.
 | 
						|
        let n = rgn.columnSizes.length;
 | 
						|
        // Adjust the count down if the last line shouldn't be drawn.
 | 
						|
        if (this._stretchLastColumn && pw > bw) {
 | 
						|
            if (rgn.column + n === this._columnSections.count) {
 | 
						|
                n -= 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Draw the vertical grid lines.
 | 
						|
        for (let x = rgn.x, i = 0; i < n; ++i) {
 | 
						|
            // Fetch the size of the column.
 | 
						|
            let size = rgn.columnSizes[i];
 | 
						|
            // Skip zero sized columns.
 | 
						|
            if (size === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Compute the X position of the line.
 | 
						|
            let pos = x + size - 1;
 | 
						|
            // Draw the line if it's in range of the dirty rect.
 | 
						|
            if (pos >= rgn.xMin && pos <= rgn.xMax) {
 | 
						|
                this._canvasGC.moveTo(pos + 0.5, y1);
 | 
						|
                this._canvasGC.lineTo(pos + 0.5, y2);
 | 
						|
            }
 | 
						|
            // Increment the running X coordinate.
 | 
						|
            x += size;
 | 
						|
        }
 | 
						|
        // Stroke the lines with the specified color.
 | 
						|
        this._canvasGC.strokeStyle = color;
 | 
						|
        this._canvasGC.stroke();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the body selections for the data grid.
 | 
						|
     */
 | 
						|
    _drawBodySelections() {
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = this._selectionModel;
 | 
						|
        // Bail early if there are no selections.
 | 
						|
        if (!model || model.isEmpty) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection colors.
 | 
						|
        let fill = this._style.selectionFillColor;
 | 
						|
        let stroke = this._style.selectionBorderColor;
 | 
						|
        // Bail early if there is nothing to draw.
 | 
						|
        if (!fill && !stroke) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the scroll geometry.
 | 
						|
        let sx = this._scrollX;
 | 
						|
        let sy = this._scrollY;
 | 
						|
        // Get the first visible cell of the grid.
 | 
						|
        let r1 = this._rowSections.indexOf(sy);
 | 
						|
        let c1 = this._columnSections.indexOf(sx);
 | 
						|
        // Bail early if there are no visible cells.
 | 
						|
        if (r1 < 0 || c1 < 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the extra geometry.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        // Get the last visible cell of the grid.
 | 
						|
        let r2 = this._rowSections.indexOf(sy + ph);
 | 
						|
        let c2 = this._columnSections.indexOf(sx + pw);
 | 
						|
        // Fetch the max row and column.
 | 
						|
        let maxRow = this._rowSections.count - 1;
 | 
						|
        let maxColumn = this._columnSections.count - 1;
 | 
						|
        // Clamp the last cell if the void space is visible.
 | 
						|
        r2 = r2 < 0 ? maxRow : r2;
 | 
						|
        c2 = c2 < 0 ? maxColumn : c2;
 | 
						|
        // Fetch the overlay gc.
 | 
						|
        let gc = this._overlayGC;
 | 
						|
        // Save the gc state.
 | 
						|
        gc.save();
 | 
						|
        // Set up the body clipping rect.
 | 
						|
        gc.beginPath();
 | 
						|
        gc.rect(hw, hh, pw, ph);
 | 
						|
        gc.clip();
 | 
						|
        // Set up the gc style.
 | 
						|
        if (fill) {
 | 
						|
            gc.fillStyle = fill;
 | 
						|
        }
 | 
						|
        if (stroke) {
 | 
						|
            gc.strokeStyle = stroke;
 | 
						|
            gc.lineWidth = 1;
 | 
						|
        }
 | 
						|
        // Iterate over the selections.
 | 
						|
        for (let s of model.selections()) {
 | 
						|
            // Skip the section if it's not visible.
 | 
						|
            if (s.r1 < r1 && s.r2 < r1) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if (s.r1 > r2 && s.r2 > r2) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if (s.c1 < c1 && s.c2 < c1) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if (s.c1 > c2 && s.c2 > c2) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Clamp the cell to the model bounds.
 | 
						|
            let sr1 = Math.max(0, Math.min(s.r1, maxRow));
 | 
						|
            let sc1 = Math.max(0, Math.min(s.c1, maxColumn));
 | 
						|
            let sr2 = Math.max(0, Math.min(s.r2, maxRow));
 | 
						|
            let sc2 = Math.max(0, Math.min(s.c2, maxColumn));
 | 
						|
            // Swap index order if needed.
 | 
						|
            let tmp;
 | 
						|
            if (sr1 > sr2) {
 | 
						|
                tmp = sr1;
 | 
						|
                sr1 = sr2;
 | 
						|
                sr2 = tmp;
 | 
						|
            }
 | 
						|
            if (sc1 > sc2) {
 | 
						|
                tmp = sc1;
 | 
						|
                sc1 = sc2;
 | 
						|
                sc2 = tmp;
 | 
						|
            }
 | 
						|
            const joinedGroup = CellGroup.joinCellGroupWithMergedCellGroups(this.dataModel, { r1: sr1, r2: sr2, c1: sc1, c2: sc2 }, 'body');
 | 
						|
            sr1 = joinedGroup.r1;
 | 
						|
            sr2 = joinedGroup.r2;
 | 
						|
            sc1 = joinedGroup.c1;
 | 
						|
            sc2 = joinedGroup.c2;
 | 
						|
            // Convert to pixel coordinates.
 | 
						|
            let x1 = this._columnSections.offsetOf(sc1) - sx + hw;
 | 
						|
            let y1 = this._rowSections.offsetOf(sr1) - sy + hh;
 | 
						|
            let x2 = this._columnSections.extentOf(sc2) - sx + hw;
 | 
						|
            let y2 = this._rowSections.extentOf(sr2) - sy + hh;
 | 
						|
            // Adjust the trailing X coordinate for column stretch.
 | 
						|
            if (this._stretchLastColumn && pw > bw && sc2 === maxColumn) {
 | 
						|
                x2 = hw + pw - 1;
 | 
						|
            }
 | 
						|
            // Adjust the trailing Y coordinate for row stretch.
 | 
						|
            if (this._stretchLastRow && ph > bh && sr2 === maxRow) {
 | 
						|
                y2 = hh + ph - 1;
 | 
						|
            }
 | 
						|
            // Clamp the bounds to just outside of the clipping rect.
 | 
						|
            x1 = Math.max(hw - 1, x1);
 | 
						|
            y1 = Math.max(hh - 1, y1);
 | 
						|
            x2 = Math.min(hw + pw + 1, x2);
 | 
						|
            y2 = Math.min(hh + ph + 1, y2);
 | 
						|
            // Skip zero sized ranges.
 | 
						|
            if (x2 < x1 || y2 < y1) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Fill the rect if needed.
 | 
						|
            if (fill) {
 | 
						|
                gc.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
            }
 | 
						|
            // Stroke the rect if needed.
 | 
						|
            if (stroke) {
 | 
						|
                gc.strokeRect(x1 - 0.5, y1 - 0.5, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Restore the gc state.
 | 
						|
        gc.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the row header selections for the data grid.
 | 
						|
     */
 | 
						|
    _drawRowHeaderSelections() {
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = this._selectionModel;
 | 
						|
        // Bail early if there are no selections or if the selectionMode is the entire column.
 | 
						|
        if (!model || model.isEmpty || model.selectionMode == 'column') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the row headers are not visible.
 | 
						|
        if (this.headerWidth === 0 || this.pageHeight === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection colors.
 | 
						|
        let fill = this._style.headerSelectionFillColor;
 | 
						|
        let stroke = this._style.headerSelectionBorderColor;
 | 
						|
        // Bail early if there is nothing to draw.
 | 
						|
        if (!fill && !stroke) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch common geometry.
 | 
						|
        let sy = this._scrollY;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        let rs = this._rowSections;
 | 
						|
        // Fetch the overlay gc.
 | 
						|
        let gc = this._overlayGC;
 | 
						|
        // Save the gc state.
 | 
						|
        gc.save();
 | 
						|
        // Set up the header clipping rect.
 | 
						|
        gc.beginPath();
 | 
						|
        gc.rect(0, hh, hw, ph);
 | 
						|
        gc.clip();
 | 
						|
        // Set up the gc style.
 | 
						|
        if (fill) {
 | 
						|
            gc.fillStyle = fill;
 | 
						|
        }
 | 
						|
        if (stroke) {
 | 
						|
            gc.strokeStyle = stroke;
 | 
						|
            gc.lineWidth = 1;
 | 
						|
        }
 | 
						|
        // Fetch the max row.
 | 
						|
        let maxRow = rs.count - 1;
 | 
						|
        // Fetch the visible rows.
 | 
						|
        let r1 = rs.indexOf(sy);
 | 
						|
        let r2 = rs.indexOf(sy + ph - 1);
 | 
						|
        r2 = r2 < 0 ? maxRow : r2;
 | 
						|
        // Iterate over the visible rows.
 | 
						|
        for (let j = r1; j <= r2; ++j) {
 | 
						|
            // Skip rows which aren't selected.
 | 
						|
            if (!model.isRowSelected(j)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Get the dimensions of the row.
 | 
						|
            let y = rs.offsetOf(j) - sy + hh;
 | 
						|
            let h = rs.sizeOf(j);
 | 
						|
            // Adjust the height for row stretch.
 | 
						|
            if (this._stretchLastRow && ph > bh && j === maxRow) {
 | 
						|
                h = hh + ph - y;
 | 
						|
            }
 | 
						|
            // Skip zero sized rows.
 | 
						|
            if (h === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Fill the rect if needed.
 | 
						|
            if (fill) {
 | 
						|
                gc.fillRect(0, y, hw, h);
 | 
						|
            }
 | 
						|
            // Draw the border if needed.
 | 
						|
            if (stroke) {
 | 
						|
                gc.beginPath();
 | 
						|
                gc.moveTo(hw - 0.5, y - 1);
 | 
						|
                gc.lineTo(hw - 0.5, y + h);
 | 
						|
                gc.stroke();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Restore the gc state.
 | 
						|
        gc.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the column header selections for the data grid.
 | 
						|
     */
 | 
						|
    _drawColumnHeaderSelections() {
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = this._selectionModel;
 | 
						|
        // Bail early if there are no selections or if the selectionMode is the entire row
 | 
						|
        if (!model || model.isEmpty || model.selectionMode == 'row') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the column headers are not visible.
 | 
						|
        if (this.headerHeight === 0 || this.pageWidth === 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the selection colors.
 | 
						|
        let fill = this._style.headerSelectionFillColor;
 | 
						|
        let stroke = this._style.headerSelectionBorderColor;
 | 
						|
        // Bail early if there is nothing to draw.
 | 
						|
        if (!fill && !stroke) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch common geometry.
 | 
						|
        let sx = this._scrollX;
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        let cs = this._columnSections;
 | 
						|
        // Fetch the overlay gc.
 | 
						|
        let gc = this._overlayGC;
 | 
						|
        // Save the gc state.
 | 
						|
        gc.save();
 | 
						|
        // Set up the header clipping rect.
 | 
						|
        gc.beginPath();
 | 
						|
        gc.rect(hw, 0, pw, hh);
 | 
						|
        gc.clip();
 | 
						|
        // Set up the gc style.
 | 
						|
        if (fill) {
 | 
						|
            gc.fillStyle = fill;
 | 
						|
        }
 | 
						|
        if (stroke) {
 | 
						|
            gc.strokeStyle = stroke;
 | 
						|
            gc.lineWidth = 1;
 | 
						|
        }
 | 
						|
        // Fetch the max column.
 | 
						|
        let maxCol = cs.count - 1;
 | 
						|
        // Fetch the visible columns.
 | 
						|
        let c1 = cs.indexOf(sx);
 | 
						|
        let c2 = cs.indexOf(sx + pw - 1);
 | 
						|
        c2 = c2 < 0 ? maxCol : c2;
 | 
						|
        // Iterate over the visible columns.
 | 
						|
        for (let i = c1; i <= c2; ++i) {
 | 
						|
            // Skip columns which aren't selected.
 | 
						|
            if (!model.isColumnSelected(i)) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Get the dimensions of the column.
 | 
						|
            let x = cs.offsetOf(i) - sx + hw;
 | 
						|
            let w = cs.sizeOf(i);
 | 
						|
            // Adjust the width for column stretch.
 | 
						|
            if (this._stretchLastColumn && pw > bw && i === maxCol) {
 | 
						|
                w = hw + pw - x;
 | 
						|
            }
 | 
						|
            // Skip zero sized columns.
 | 
						|
            if (w === 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // Fill the rect if needed.
 | 
						|
            if (fill) {
 | 
						|
                gc.fillRect(x, 0, w, hh);
 | 
						|
            }
 | 
						|
            // Draw the border if needed.
 | 
						|
            if (stroke) {
 | 
						|
                gc.beginPath();
 | 
						|
                gc.moveTo(x - 1, hh - 0.5);
 | 
						|
                gc.lineTo(x + w, hh - 0.5);
 | 
						|
                gc.stroke();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Restore the gc state.
 | 
						|
        gc.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the overlay cursor for the data grid.
 | 
						|
     */
 | 
						|
    _drawCursor() {
 | 
						|
        // Fetch the selection model.
 | 
						|
        let model = this._selectionModel;
 | 
						|
        // Bail early if there is no cursor.
 | 
						|
        if (!model || model.isEmpty || model.selectionMode !== 'cell') {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Extract the style information.
 | 
						|
        let fill = this._style.cursorFillColor;
 | 
						|
        let stroke = this._style.cursorBorderColor;
 | 
						|
        // Bail early if there is nothing to draw.
 | 
						|
        if (!fill && !stroke) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the cursor location.
 | 
						|
        let startRow = model.cursorRow;
 | 
						|
        let startColumn = model.cursorColumn;
 | 
						|
        // Fetch the max row and column.
 | 
						|
        let maxRow = this._rowSections.count - 1;
 | 
						|
        let maxColumn = this._columnSections.count - 1;
 | 
						|
        // Bail early if the cursor is out of bounds.
 | 
						|
        if (startRow < 0 || startRow > maxRow) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        if (startColumn < 0 || startColumn > maxColumn) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        let endRow = startRow;
 | 
						|
        let endColumn = startColumn;
 | 
						|
        const joinedGroup = CellGroup.joinCellGroupWithMergedCellGroups(this.dataModel, { r1: startRow, r2: endRow, c1: startColumn, c2: endColumn }, 'body');
 | 
						|
        startRow = joinedGroup.r1;
 | 
						|
        endRow = joinedGroup.r2;
 | 
						|
        startColumn = joinedGroup.c1;
 | 
						|
        endColumn = joinedGroup.c2;
 | 
						|
        // Fetch geometry.
 | 
						|
        let sx = this._scrollX;
 | 
						|
        let sy = this._scrollY;
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // Get the cursor bounds in viewport coordinates.
 | 
						|
        let x1 = this._columnSections.offsetOf(startColumn) - sx + hw;
 | 
						|
        let x2 = this._columnSections.extentOf(endColumn) - sx + hw;
 | 
						|
        let y1 = this._rowSections.offsetOf(startRow) - sy + hh;
 | 
						|
        let y2 = this._rowSections.extentOf(endRow) - sy + hh;
 | 
						|
        // Adjust the trailing X coordinate for column stretch.
 | 
						|
        if (this._stretchLastColumn && pw > bw && startColumn === maxColumn) {
 | 
						|
            x2 = vw - 1;
 | 
						|
        }
 | 
						|
        // Adjust the trailing Y coordinate for row stretch.
 | 
						|
        if (this._stretchLastRow && ph > bh && startRow === maxRow) {
 | 
						|
            y2 = vh - 1;
 | 
						|
        }
 | 
						|
        // Skip zero sized cursors.
 | 
						|
        if (x2 < x1 || y2 < y1) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Bail early if the cursor is off the screen.
 | 
						|
        if (x1 - 1 >= vw || y1 - 1 >= vh || x2 + 1 < hw || y2 + 1 < hh) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the overlay gc.
 | 
						|
        let gc = this._overlayGC;
 | 
						|
        // Save the gc state.
 | 
						|
        gc.save();
 | 
						|
        // Set up the body clipping rect.
 | 
						|
        gc.beginPath();
 | 
						|
        gc.rect(hw, hh, pw, ph);
 | 
						|
        gc.clip();
 | 
						|
        // Clear any existing overlay content.
 | 
						|
        gc.clearRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
        // Fill the cursor rect if needed.
 | 
						|
        if (fill) {
 | 
						|
            // Set up the fill style.
 | 
						|
            gc.fillStyle = fill;
 | 
						|
            // Fill the cursor rect.
 | 
						|
            gc.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
 | 
						|
        }
 | 
						|
        // Stroke the cursor border if needed.
 | 
						|
        if (stroke) {
 | 
						|
            // Set up the stroke style.
 | 
						|
            gc.strokeStyle = stroke;
 | 
						|
            gc.lineWidth = 2;
 | 
						|
            // Stroke the cursor rect.
 | 
						|
            gc.strokeRect(x1, y1, x2 - x1, y2 - y1);
 | 
						|
        }
 | 
						|
        // Restore the gc state.
 | 
						|
        gc.restore();
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the overlay shadows for the data grid.
 | 
						|
     */
 | 
						|
    _drawShadows() {
 | 
						|
        // Fetch the scroll shadow from the style.
 | 
						|
        let shadow = this._style.scrollShadow;
 | 
						|
        // Bail early if there is no shadow to draw.
 | 
						|
        if (!shadow) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fetch the scroll position.
 | 
						|
        let sx = this._scrollX;
 | 
						|
        let sy = this._scrollY;
 | 
						|
        // Fetch maximum scroll position.
 | 
						|
        let sxMax = this.maxScrollX;
 | 
						|
        let syMax = this.maxScrollY;
 | 
						|
        // Fetch the header width and height.
 | 
						|
        let hw = this.headerWidth;
 | 
						|
        let hh = this.headerHeight;
 | 
						|
        // Fetch the page width and height.
 | 
						|
        let pw = this.pageWidth;
 | 
						|
        let ph = this.pageHeight;
 | 
						|
        // Fetch the viewport width and height.
 | 
						|
        let vw = this._viewportWidth;
 | 
						|
        let vh = this._viewportHeight;
 | 
						|
        // Fetch the body width and height.
 | 
						|
        let bw = this.bodyWidth;
 | 
						|
        let bh = this.bodyHeight;
 | 
						|
        // Adjust the body size for row and column stretch.
 | 
						|
        if (this._stretchLastRow && ph > bh) {
 | 
						|
            bh = ph;
 | 
						|
        }
 | 
						|
        if (this._stretchLastColumn && pw > bw) {
 | 
						|
            bw = pw;
 | 
						|
        }
 | 
						|
        // Fetch the gc object.
 | 
						|
        let gc = this._overlayGC;
 | 
						|
        // Save the gc state.
 | 
						|
        gc.save();
 | 
						|
        // Draw the column header shadow if needed.
 | 
						|
        if (sy > 0) {
 | 
						|
            // Set up the gradient coordinates.
 | 
						|
            let x0 = 0;
 | 
						|
            let y0 = hh;
 | 
						|
            let x1 = 0;
 | 
						|
            let y1 = y0 + shadow.size;
 | 
						|
            // Create the gradient object.
 | 
						|
            let grad = gc.createLinearGradient(x0, y0, x1, y1);
 | 
						|
            // Set the gradient stops.
 | 
						|
            grad.addColorStop(0, shadow.color1);
 | 
						|
            grad.addColorStop(0.5, shadow.color2);
 | 
						|
            grad.addColorStop(1, shadow.color3);
 | 
						|
            // Set up the rect coordinates.
 | 
						|
            let x = 0;
 | 
						|
            let y = hh;
 | 
						|
            let w = hw + Math.min(pw, bw - sx);
 | 
						|
            let h = shadow.size;
 | 
						|
            // Fill the shadow rect with the fill style.
 | 
						|
            gc.fillStyle = grad;
 | 
						|
            gc.fillRect(x, y, w, h);
 | 
						|
        }
 | 
						|
        // Draw the row header shadow if needed.
 | 
						|
        if (sx > 0) {
 | 
						|
            // Set up the gradient coordinates.
 | 
						|
            let x0 = hw;
 | 
						|
            let y0 = 0;
 | 
						|
            let x1 = x0 + shadow.size;
 | 
						|
            let y1 = 0;
 | 
						|
            // Create the gradient object.
 | 
						|
            let grad = gc.createLinearGradient(x0, y0, x1, y1);
 | 
						|
            // Set the gradient stops.
 | 
						|
            grad.addColorStop(0, shadow.color1);
 | 
						|
            grad.addColorStop(0.5, shadow.color2);
 | 
						|
            grad.addColorStop(1, shadow.color3);
 | 
						|
            // Set up the rect coordinates.
 | 
						|
            let x = hw;
 | 
						|
            let y = 0;
 | 
						|
            let w = shadow.size;
 | 
						|
            let h = hh + Math.min(ph, bh - sy);
 | 
						|
            // Fill the shadow rect with the fill style.
 | 
						|
            gc.fillStyle = grad;
 | 
						|
            gc.fillRect(x, y, w, h);
 | 
						|
        }
 | 
						|
        // Draw the column footer shadow if needed.
 | 
						|
        if (sy < syMax) {
 | 
						|
            // Set up the gradient coordinates.
 | 
						|
            let x0 = 0;
 | 
						|
            let y0 = vh;
 | 
						|
            let x1 = 0;
 | 
						|
            let y1 = vh - shadow.size;
 | 
						|
            // Create the gradient object.
 | 
						|
            let grad = gc.createLinearGradient(x0, y0, x1, y1);
 | 
						|
            // Set the gradient stops.
 | 
						|
            grad.addColorStop(0, shadow.color1);
 | 
						|
            grad.addColorStop(0.5, shadow.color2);
 | 
						|
            grad.addColorStop(1, shadow.color3);
 | 
						|
            // Set up the rect coordinates.
 | 
						|
            let x = 0;
 | 
						|
            let y = vh - shadow.size;
 | 
						|
            let w = hw + Math.min(pw, bw - sx);
 | 
						|
            let h = shadow.size;
 | 
						|
            // Fill the shadow rect with the fill style.
 | 
						|
            gc.fillStyle = grad;
 | 
						|
            gc.fillRect(x, y, w, h);
 | 
						|
        }
 | 
						|
        // Draw the row footer shadow if needed.
 | 
						|
        if (sx < sxMax) {
 | 
						|
            // Set up the gradient coordinates.
 | 
						|
            let x0 = vw;
 | 
						|
            let y0 = 0;
 | 
						|
            let x1 = vw - shadow.size;
 | 
						|
            let y1 = 0;
 | 
						|
            // Create the gradient object.
 | 
						|
            let grad = gc.createLinearGradient(x0, y0, x1, y1);
 | 
						|
            // Set the gradient stops.
 | 
						|
            grad.addColorStop(0, shadow.color1);
 | 
						|
            grad.addColorStop(0.5, shadow.color2);
 | 
						|
            grad.addColorStop(1, shadow.color3);
 | 
						|
            // Set up the rect coordinates.
 | 
						|
            let x = vw - shadow.size;
 | 
						|
            let y = 0;
 | 
						|
            let w = shadow.size;
 | 
						|
            let h = hh + Math.min(ph, bh - sy);
 | 
						|
            // Fill the shadow rect with the fill style.
 | 
						|
            gc.fillStyle = grad;
 | 
						|
            gc.fillRect(x, y, w, h);
 | 
						|
        }
 | 
						|
        // Restore the gc state.
 | 
						|
        gc.restore();
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the `DataGrid` class statics.
 | 
						|
 */
 | 
						|
(function (DataGrid) {
 | 
						|
    /**
 | 
						|
     * A generic format function for the copy handler.
 | 
						|
     *
 | 
						|
     * @param args - The format args for the function.
 | 
						|
     *
 | 
						|
     * @returns The string representation of the value.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * This function uses `String()` to coerce a value to a string.
 | 
						|
     */
 | 
						|
    function copyFormatGeneric(args) {
 | 
						|
        if (args.value === null || args.value === undefined) {
 | 
						|
            return '';
 | 
						|
        }
 | 
						|
        return String(args.value);
 | 
						|
    }
 | 
						|
    DataGrid.copyFormatGeneric = copyFormatGeneric;
 | 
						|
    /**
 | 
						|
     * The default theme for a data grid.
 | 
						|
     */
 | 
						|
    DataGrid.defaultStyle = {
 | 
						|
        voidColor: '#F3F3F3',
 | 
						|
        backgroundColor: '#FFFFFF',
 | 
						|
        gridLineColor: 'rgba(20, 20, 20, 0.15)',
 | 
						|
        headerBackgroundColor: '#F3F3F3',
 | 
						|
        headerGridLineColor: 'rgba(20, 20, 20, 0.25)',
 | 
						|
        selectionFillColor: 'rgba(49, 119, 229, 0.2)',
 | 
						|
        selectionBorderColor: 'rgba(0, 107, 247, 1.0)',
 | 
						|
        cursorBorderColor: 'rgba(0, 107, 247, 1.0)',
 | 
						|
        headerSelectionFillColor: 'rgba(20, 20, 20, 0.1)',
 | 
						|
        headerSelectionBorderColor: 'rgba(0, 107, 247, 1.0)',
 | 
						|
        scrollShadow: {
 | 
						|
            size: 10,
 | 
						|
            color1: 'rgba(0, 0, 0, 0.20)',
 | 
						|
            color2: 'rgba(0, 0, 0, 0.05)',
 | 
						|
            color3: 'rgba(0, 0, 0, 0.00)'
 | 
						|
        }
 | 
						|
    };
 | 
						|
    /**
 | 
						|
     * The default sizes for a data grid.
 | 
						|
     */
 | 
						|
    DataGrid.defaultSizes = {
 | 
						|
        rowHeight: 20,
 | 
						|
        columnWidth: 64,
 | 
						|
        rowHeaderWidth: 64,
 | 
						|
        columnHeaderHeight: 20
 | 
						|
    };
 | 
						|
    /**
 | 
						|
     * The default minimum sizes for a data grid.
 | 
						|
     */
 | 
						|
    DataGrid.minimumSizes = {
 | 
						|
        rowHeight: 20,
 | 
						|
        columnWidth: 10,
 | 
						|
        rowHeaderWidth: 10,
 | 
						|
        columnHeaderHeight: 20
 | 
						|
    };
 | 
						|
    /**
 | 
						|
     * The default copy config for a data grid.
 | 
						|
     */
 | 
						|
    DataGrid.defaultCopyConfig = {
 | 
						|
        separator: '\t',
 | 
						|
        format: copyFormatGeneric,
 | 
						|
        headers: 'none',
 | 
						|
        warningThreshold: 1e6
 | 
						|
    };
 | 
						|
})(DataGrid || (DataGrid = {}));
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private$1;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * A singleton `scroll-request` conflatable message.
 | 
						|
     */
 | 
						|
    Private.ScrollRequest = new _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage('scroll-request');
 | 
						|
    /**
 | 
						|
     * A singleton `overlay-paint-request` conflatable message.
 | 
						|
     */
 | 
						|
    Private.OverlayPaintRequest = new _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage('overlay-paint-request');
 | 
						|
    /**
 | 
						|
     * Create a new zero-sized canvas element.
 | 
						|
     */
 | 
						|
    function createCanvas() {
 | 
						|
        let canvas = document.createElement('canvas');
 | 
						|
        canvas.width = 0;
 | 
						|
        canvas.height = 0;
 | 
						|
        return canvas;
 | 
						|
    }
 | 
						|
    Private.createCanvas = createCanvas;
 | 
						|
    /**
 | 
						|
     * Checks whether a given regions has merged cells in it.
 | 
						|
     * @param dataModel grid's data model.
 | 
						|
     * @param region the paint region to be checked.
 | 
						|
     * @returns boolean.
 | 
						|
     */
 | 
						|
    function regionHasMergedCells(dataModel, region) {
 | 
						|
        const regionGroups = CellGroup.getCellGroupsAtRegion(dataModel, region);
 | 
						|
        return regionGroups.length > 0;
 | 
						|
    }
 | 
						|
    Private.regionHasMergedCells = regionHasMergedCells;
 | 
						|
    /**
 | 
						|
     * A conflatable message which merges dirty paint regions.
 | 
						|
     */
 | 
						|
    class PaintRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {
 | 
						|
        /**
 | 
						|
         * Construct a new paint request messages.
 | 
						|
         *
 | 
						|
         * @param region - The cell region for the paint.
 | 
						|
         *
 | 
						|
         * @param r1 - The top-left row of the dirty region.
 | 
						|
         *
 | 
						|
         * @param c1 - The top-left column of the dirty region.
 | 
						|
         *
 | 
						|
         * @param r2 - The bottom-right row of the dirty region.
 | 
						|
         *
 | 
						|
         * @param c2 - The bottom-right column of the dirty region.
 | 
						|
         */
 | 
						|
        constructor(region, r1, c1, r2, c2) {
 | 
						|
            super('paint-request');
 | 
						|
            this._region = region;
 | 
						|
            this._r1 = r1;
 | 
						|
            this._c1 = c1;
 | 
						|
            this._r2 = r2;
 | 
						|
            this._c2 = c2;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The cell region for the paint.
 | 
						|
         */
 | 
						|
        get region() {
 | 
						|
            return this._region;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The top-left row of the dirty region.
 | 
						|
         */
 | 
						|
        get r1() {
 | 
						|
            return this._r1;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The top-left column of the dirty region.
 | 
						|
         */
 | 
						|
        get c1() {
 | 
						|
            return this._c1;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The bottom-right row of the dirty region.
 | 
						|
         */
 | 
						|
        get r2() {
 | 
						|
            return this._r2;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The bottom-right column of the dirty region.
 | 
						|
         */
 | 
						|
        get c2() {
 | 
						|
            return this._c2;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Conflate this message with another paint request.
 | 
						|
         */
 | 
						|
        conflate(other) {
 | 
						|
            // Bail early if the request is already painting everything.
 | 
						|
            if (this._region === 'all') {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            // Any region can conflate with the `'all'` region.
 | 
						|
            if (other._region === 'all') {
 | 
						|
                this._region = 'all';
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
            // Otherwise, do not conflate with a different region.
 | 
						|
            if (this._region !== other._region) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            // Conflate the region to the total boundary.
 | 
						|
            this._r1 = Math.min(this._r1, other._r1);
 | 
						|
            this._c1 = Math.min(this._c1, other._c1);
 | 
						|
            this._r2 = Math.max(this._r2, other._r2);
 | 
						|
            this._c2 = Math.max(this._c2, other._c2);
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    Private.PaintRequest = PaintRequest;
 | 
						|
    /**
 | 
						|
     * A conflatable message for resizing rows.
 | 
						|
     */
 | 
						|
    class RowResizeRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {
 | 
						|
        /**
 | 
						|
         * Construct a new row resize request.
 | 
						|
         *
 | 
						|
         * @param region - The row region which holds the section.
 | 
						|
         *
 | 
						|
         * @param index - The index of row in the region.
 | 
						|
         *
 | 
						|
         * @param size - The target size of the section.
 | 
						|
         */
 | 
						|
        constructor(region, index, size) {
 | 
						|
            super('row-resize-request');
 | 
						|
            this._region = region;
 | 
						|
            this._index = index;
 | 
						|
            this._size = size;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The row region which holds the section.
 | 
						|
         */
 | 
						|
        get region() {
 | 
						|
            return this._region;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The index of the row in the region.
 | 
						|
         */
 | 
						|
        get index() {
 | 
						|
            return this._index;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The target size of the section.
 | 
						|
         */
 | 
						|
        get size() {
 | 
						|
            return this._size;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Conflate this message with another row resize request.
 | 
						|
         */
 | 
						|
        conflate(other) {
 | 
						|
            if (this._region !== other._region || this._index !== other._index) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            this._size = other._size;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    Private.RowResizeRequest = RowResizeRequest;
 | 
						|
    /**
 | 
						|
     * A conflatable message for resizing columns.
 | 
						|
     */
 | 
						|
    class ColumnResizeRequest extends _lumino_messaging__WEBPACK_IMPORTED_MODULE_6__.ConflatableMessage {
 | 
						|
        /**
 | 
						|
         * Construct a new column resize request.
 | 
						|
         *
 | 
						|
         * @param region - The column region which holds the section.
 | 
						|
         *
 | 
						|
         * @param index - The index of column in the region.
 | 
						|
         *
 | 
						|
         * @param size - The target size of the section.
 | 
						|
         *               If null, then infer the size to fit.
 | 
						|
         */
 | 
						|
        constructor(region, index, size) {
 | 
						|
            super('column-resize-request');
 | 
						|
            this._region = region;
 | 
						|
            this._index = index;
 | 
						|
            this._size = size;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The column region which holds the section.
 | 
						|
         */
 | 
						|
        get region() {
 | 
						|
            return this._region;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The index of the column in the region.
 | 
						|
         */
 | 
						|
        get index() {
 | 
						|
            return this._index;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * The target size of the section.
 | 
						|
         */
 | 
						|
        get size() {
 | 
						|
            return this._size;
 | 
						|
        }
 | 
						|
        /**
 | 
						|
         * Conflate this message with another column resize request.
 | 
						|
         */
 | 
						|
        conflate(other) {
 | 
						|
            if (this._region !== other._region || this._index !== other._index) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            this._size = other._size;
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    Private.ColumnResizeRequest = ColumnResizeRequest;
 | 
						|
})(Private$1 || (Private$1 = {}));
 | 
						|
 | 
						|
/**
 | 
						|
 * A data model implementation for in-memory JSON data.
 | 
						|
 */
 | 
						|
class JSONModel extends DataModel {
 | 
						|
    /**
 | 
						|
     * Create a data model with static JSON data.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the data model.
 | 
						|
     */
 | 
						|
    constructor(options) {
 | 
						|
        super();
 | 
						|
        let split = Private.splitFields(options.schema);
 | 
						|
        this._data = options.data;
 | 
						|
        this._bodyFields = split.bodyFields;
 | 
						|
        this._headerFields = split.headerFields;
 | 
						|
        this._missingValues = Private.createMissingMap(options.schema);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the row count for a region in the data model.
 | 
						|
     *
 | 
						|
     * @param region - The row region of interest.
 | 
						|
     *
 | 
						|
     * @returns - The row count for the region.
 | 
						|
     */
 | 
						|
    rowCount(region) {
 | 
						|
        if (region === 'body') {
 | 
						|
            return this._data.length;
 | 
						|
        }
 | 
						|
        return 1; // TODO multiple column-header rows?
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the column count for a region in the data model.
 | 
						|
     *
 | 
						|
     * @param region - The column region of interest.
 | 
						|
     *
 | 
						|
     * @returns - The column count for the region.
 | 
						|
     */
 | 
						|
    columnCount(region) {
 | 
						|
        if (region === 'body') {
 | 
						|
            return this._bodyFields.length;
 | 
						|
        }
 | 
						|
        return this._headerFields.length;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the data value for a cell in the data model.
 | 
						|
     *
 | 
						|
     * @param region - The cell region of interest.
 | 
						|
     *
 | 
						|
     * @param row - The row index of the cell of interest.
 | 
						|
     *
 | 
						|
     * @param column - The column index of the cell of interest.
 | 
						|
     *
 | 
						|
     * @returns - The data value for the specified cell.
 | 
						|
     *
 | 
						|
     * #### Notes
 | 
						|
     * A `missingValue` as defined by the schema is converted to `null`.
 | 
						|
     */
 | 
						|
    data(region, row, column) {
 | 
						|
        // Set up the field and value variables.
 | 
						|
        let field;
 | 
						|
        let value;
 | 
						|
        // Look up the field and value for the region.
 | 
						|
        switch (region) {
 | 
						|
            case 'body':
 | 
						|
                field = this._bodyFields[column];
 | 
						|
                value = this._data[row][field.name];
 | 
						|
                break;
 | 
						|
            case 'column-header':
 | 
						|
                field = this._bodyFields[column];
 | 
						|
                value = field.title || field.name;
 | 
						|
                break;
 | 
						|
            case 'row-header':
 | 
						|
                field = this._headerFields[column];
 | 
						|
                value = this._data[row][field.name];
 | 
						|
                break;
 | 
						|
            case 'corner-header':
 | 
						|
                field = this._headerFields[column];
 | 
						|
                value = field.title || field.name;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                throw 'unreachable';
 | 
						|
        }
 | 
						|
        // Test whether the value is a missing value.
 | 
						|
        let missing = this._missingValues !== null &&
 | 
						|
            typeof value === 'string' &&
 | 
						|
            this._missingValues[value] === true;
 | 
						|
        // Return the final value.
 | 
						|
        return missing ? null : value;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get the metadata for a cell in the data model.
 | 
						|
     *
 | 
						|
     * @param region - The cell region of interest.
 | 
						|
     *
 | 
						|
     * @param row - The row index of the cell of of interest.
 | 
						|
     *
 | 
						|
     * @param column - The column index of the cell of interest.
 | 
						|
     *
 | 
						|
     * @returns The metadata for the cell.
 | 
						|
     */
 | 
						|
    metadata(region, row, column) {
 | 
						|
        if (region === 'body' || region === 'column-header') {
 | 
						|
            return this._bodyFields[column];
 | 
						|
        }
 | 
						|
        return this._headerFields[column];
 | 
						|
    }
 | 
						|
}
 | 
						|
/**
 | 
						|
 * The namespace for the module implementation details.
 | 
						|
 */
 | 
						|
var Private;
 | 
						|
(function (Private) {
 | 
						|
    /**
 | 
						|
     * Split the schema fields into header and body fields.
 | 
						|
     */
 | 
						|
    function splitFields(schema) {
 | 
						|
        // Normalize the primary keys.
 | 
						|
        let primaryKeys;
 | 
						|
        if (schema.primaryKey === undefined) {
 | 
						|
            primaryKeys = [];
 | 
						|
        }
 | 
						|
        else if (typeof schema.primaryKey === 'string') {
 | 
						|
            primaryKeys = [schema.primaryKey];
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            primaryKeys = schema.primaryKey;
 | 
						|
        }
 | 
						|
        // Separate the fields for the body and header.
 | 
						|
        let bodyFields = [];
 | 
						|
        let headerFields = [];
 | 
						|
        for (let field of schema.fields) {
 | 
						|
            if (primaryKeys.indexOf(field.name) === -1) {
 | 
						|
                bodyFields.push(field);
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                headerFields.push(field);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // Return the separated fields.
 | 
						|
        return { bodyFields, headerFields };
 | 
						|
    }
 | 
						|
    Private.splitFields = splitFields;
 | 
						|
    /**
 | 
						|
     * Create a missing values map for a schema.
 | 
						|
     *
 | 
						|
     * This returns `null` if there are no missing values.
 | 
						|
     */
 | 
						|
    function createMissingMap(schema) {
 | 
						|
        // Bail early if there are no missing values.
 | 
						|
        if (!schema.missingValues || schema.missingValues.length === 0) {
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
        // Collect the missing values into a map.
 | 
						|
        let result = Object.create(null);
 | 
						|
        for (let value of schema.missingValues) {
 | 
						|
            result[value] = true;
 | 
						|
        }
 | 
						|
        // Return the populated map.
 | 
						|
        return result;
 | 
						|
    }
 | 
						|
    Private.createMissingMap = createMissingMap;
 | 
						|
})(Private || (Private = {}));
 | 
						|
 | 
						|
// Copyright (c) Jupyter Development Team.
 | 
						|
// Distributed under the terms of the Modified BSD License.
 | 
						|
/*-----------------------------------------------------------------------------
 | 
						|
| Copyright (c) 2014-2023, Lumino Contributors
 | 
						|
|
 | 
						|
| Distributed under the terms of the BSD 3-Clause License.
 | 
						|
|
 | 
						|
| The full license is in the file LICENSE, distributed with this software.
 | 
						|
|----------------------------------------------------------------------------*/
 | 
						|
const PERCENTAGE_REGEX = /^(\d+(\.\d+)?)%$/;
 | 
						|
const PIXEL_REGEX = /^(\d+(\.\d+)?)px$/;
 | 
						|
/**
 | 
						|
 * A cell renderer which renders data values as images.
 | 
						|
 */
 | 
						|
class ImageRenderer extends AsyncCellRenderer {
 | 
						|
    /**
 | 
						|
     * Construct a new text renderer.
 | 
						|
     *
 | 
						|
     * @param options - The options for initializing the renderer.
 | 
						|
     */
 | 
						|
    constructor(options = {}) {
 | 
						|
        super();
 | 
						|
        this.backgroundColor = options.backgroundColor || '';
 | 
						|
        this.textColor = options.textColor || '#000000';
 | 
						|
        this.placeholder = options.placeholder || '...';
 | 
						|
        this.width = options.width || '';
 | 
						|
        // Not using the || operator, because the empty string '' is a valid value
 | 
						|
        this.height = options.height === undefined ? '100%' : options.height;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Whether the renderer is ready or not for that specific config.
 | 
						|
     * If it's not ready, the datagrid will paint the placeholder.
 | 
						|
     * If it's ready, the datagrid will paint the image synchronously.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     *
 | 
						|
     * @returns Whether the renderer is ready for this config or not.
 | 
						|
     */
 | 
						|
    isReady(config) {
 | 
						|
        return (!config.value || ImageRenderer.dataCache.get(config.value) !== undefined);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Load the image asynchronously for a specific config.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    async load(config) {
 | 
						|
        // Bail early if there is nothing to do
 | 
						|
        if (!config.value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const value = config.value;
 | 
						|
        const loadedPromise = new _lumino_coreutils__WEBPACK_IMPORTED_MODULE_7__.PromiseDelegate();
 | 
						|
        ImageRenderer.dataCache.set(value, undefined);
 | 
						|
        const img = new Image();
 | 
						|
        img.onload = () => {
 | 
						|
            ImageRenderer.dataCache.set(value, img);
 | 
						|
            loadedPromise.resolve();
 | 
						|
        };
 | 
						|
        img.src = value;
 | 
						|
        return loadedPromise.promise;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint the placeholder for a cell, waiting for the renderer to be ready.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    paintPlaceholder(gc, config) {
 | 
						|
        this.drawBackground(gc, config);
 | 
						|
        this.drawPlaceholder(gc, config);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Paint the content for a cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    paint(gc, config) {
 | 
						|
        this.drawBackground(gc, config);
 | 
						|
        this.drawImage(gc, config);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the background for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawBackground(gc, config) {
 | 
						|
        // Resolve the background color for the cell.
 | 
						|
        const color = CellRenderer.resolveOption(this.backgroundColor, config);
 | 
						|
        // Bail if there is no background color to draw.
 | 
						|
        if (!color) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        // Fill the cell with the background color.
 | 
						|
        gc.fillStyle = color;
 | 
						|
        gc.fillRect(config.x, config.y, config.width, config.height);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the placeholder for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawPlaceholder(gc, config) {
 | 
						|
        const placeholder = CellRenderer.resolveOption(this.placeholder, config);
 | 
						|
        const color = CellRenderer.resolveOption(this.textColor, config);
 | 
						|
        const textX = config.x + config.width / 2;
 | 
						|
        const textY = config.y + config.height / 2;
 | 
						|
        // Draw the placeholder.
 | 
						|
        gc.fillStyle = color;
 | 
						|
        gc.fillText(placeholder, textX, textY);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Draw the image for the cell.
 | 
						|
     *
 | 
						|
     * @param gc - The graphics context to use for drawing.
 | 
						|
     *
 | 
						|
     * @param config - The configuration data for the cell.
 | 
						|
     */
 | 
						|
    drawImage(gc, config) {
 | 
						|
        // Bail early if there is nothing to draw
 | 
						|
        if (!config.value) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const img = ImageRenderer.dataCache.get(config.value);
 | 
						|
        // If it's not loaded yet, show the placeholder
 | 
						|
        if (!img) {
 | 
						|
            return this.drawPlaceholder(gc, config);
 | 
						|
        }
 | 
						|
        const width = CellRenderer.resolveOption(this.width, config);
 | 
						|
        const height = CellRenderer.resolveOption(this.height, config);
 | 
						|
        // width and height are unset, we display the image with its original size
 | 
						|
        if (!width && !height) {
 | 
						|
            gc.drawImage(img, config.x, config.y);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        let requestedWidth = img.width;
 | 
						|
        let requestedHeight = img.height;
 | 
						|
        let widthPercentageMatch;
 | 
						|
        let widthPixelMatch;
 | 
						|
        let heightPercentageMatch;
 | 
						|
        let heightPixelMatch;
 | 
						|
        if ((widthPercentageMatch = width.match(PERCENTAGE_REGEX))) {
 | 
						|
            requestedWidth =
 | 
						|
                (parseFloat(widthPercentageMatch[1]) / 100) * config.width;
 | 
						|
        }
 | 
						|
        else if ((widthPixelMatch = width.match(PIXEL_REGEX))) {
 | 
						|
            requestedWidth = parseFloat(widthPixelMatch[1]);
 | 
						|
        }
 | 
						|
        if ((heightPercentageMatch = height.match(PERCENTAGE_REGEX))) {
 | 
						|
            requestedHeight =
 | 
						|
                (parseFloat(heightPercentageMatch[1]) / 100) * config.height;
 | 
						|
        }
 | 
						|
        else if ((heightPixelMatch = height.match(PIXEL_REGEX))) {
 | 
						|
            requestedHeight = parseFloat(heightPixelMatch[1]);
 | 
						|
        }
 | 
						|
        // If width is not set, we compute it respecting the image size ratio
 | 
						|
        if (!width) {
 | 
						|
            requestedWidth = (img.width / img.height) * requestedHeight;
 | 
						|
        }
 | 
						|
        // If height is not set, we compute it respecting the image size ratio
 | 
						|
        if (!height) {
 | 
						|
            requestedHeight = (img.height / img.width) * requestedWidth;
 | 
						|
        }
 | 
						|
        gc.drawImage(img, config.x, config.y, requestedWidth, requestedHeight);
 | 
						|
    }
 | 
						|
}
 | 
						|
ImageRenderer.dataCache = new Map();
 | 
						|
 | 
						|
 | 
						|
//# sourceMappingURL=index.es6.js.map
 | 
						|
 | 
						|
 | 
						|
/***/ })
 | 
						|
 | 
						|
}]);
 | 
						|
//# sourceMappingURL=8929.f522b600b8907f9241c6.js.map?v=f522b600b8907f9241c6
 |