[
  {
    "path": ".eslintrc.json",
    "content": "{\n    \"extends\": \"eslint:recommended\",\n    \"ignorePatterns\": [\"dist/\", \"node_modules/\"],\n    \"rules\": {\n        \"quotes\": [\"error\", \"single\"],\n        \"semi\": [\"error\", \"always\"]\n    }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n/package-lock.json\nnode_modules\n/vue/package-lock.json\n"
  },
  {
    "path": ".npmignore",
    "content": ".idea\n.gitignore\n.gitattributes\ndocs\npublic\n/react/\nsrc\ntest\n/vue/\n/wrappers/\nbuild.cjs\nmocha.config.cjs\nwebpack.config.cjs"
  },
  {
    "path": ".prettierignore",
    "content": "dist/\ndocs/"
  },
  {
    "path": ".prettierrc",
    "content": "{\n    \"tabWidth\": 4,\n    \"singleQuote\": true,\n    \"semi\": true,\n    \"trailingComma\": \"es5\",\n    \"printWidth\": 160\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Jspreadsheet Ltd\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Jspreadsheet CE v5: The JavaScript spreadsheet\n\n<b>Jexcel CE</b> has been renamed to <b>Jspreadsheet CE</b><br><br>\n\n## Jspreadsheet CE Use Cases\n\nJspreadsheet CE is an extensible framework for building sophisticated data-oriented interfaces with Excel-like controls. By bringing familiar spreadsheet features to your application, you can drastically reduce development time while delivering an interface that users already know how to use, leading to faster adoption and increased productivity. You can use Jspreadsheet in many different applications, such as:\n\n-   An editable data grid-based interface to simplify inventory management and production planning in a manufacturing company's ERP system.\n-   At an educational institution, Jspreadsheet powers grade management systems where teachers can efficiently import and modify student data.\n-   A logistics company uses Jspreadsheet to create dynamic delivery route planning tables with real-time updates.\n-   In a research laboratory, scientists use Jspreadsheet to collect and analyze experimental data with custom validation rules.\n-   At a retail chain, managers use Spreadsheet-based tools to coordinate staff schedules across multiple locations.\n\n### Jspreadsheet Pro - Enterprise Solution\n\n-   [Jspreadsheet Pro](https://jspreadsheet.com/)\n-   [Real-time React Spreadsheets](https://github.com/jspreadsheet/spreadsheet-react-server)\n\n## Overview\n\n### Why Choose Jspreadsheet CE?\n\n-   Create rich, interactive data grid interfaces\n-   Handle complex data inputs with Excel-like functionality\n-   Direct Excel compatibility: Copy and paste using standard shortcuts\n-   Proven success across thousands of implementations\n-   Lightweight, fast, and intuitive\n-   Easy integration with third-party plugins\n-   Built for collaboration and sharing\n\n## Screenshot\n\n![The JavaScript spreadsheet](https://bossanova.uk/templates/default/img/jexcel.gif)\n\n## Installation\n\n### NPM\n\n`npm install jspreadsheet-ce`\n\n### CDN\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css\" type=\"text/css\" />\n```\n\n### Basic Demo\n\nA basic example to integrate the Jspreadsheet in your website to create your first data grid with spreadsheet controls. <https://codepen.io/hchiam/pen/qBRzXKK>\n\n#### Usage\n\nAdd jexcel/jspreadsheet and Jsuites to your html file\n\n```html\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n```\n\nYou should initialize your data grid on a div container, such as:\n\n```html\n<div id=\"spreadsheet\"></div>\n```\n\nTo initialize a Jspreadsheet CE table you should run JavaScript, such as:\n\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        {\n            data: [\n                ['Jazz', 'Honda', '2019-02-12', '', true, '$ 2.000,00', '#777700'],\n                ['Civic', 'Honda', '2018-07-11', '', true, '$ 4.000,01', '#007777'],\n            ],\n            columns: [\n                { type: 'text', title: 'Car', width: 120 },\n                {\n                    type: 'dropdown',\n                    title: 'Make',\n                    width: 200,\n                    source: ['Alfa Romeo', 'Audi', 'Bmw', 'Honda'],\n                },\n                { type: 'calendar', title: 'Available', width: 200 },\n                { type: 'image', title: 'Photo', width: 120 },\n                { type: 'checkbox', title: 'Stock', width: 80 },\n                {\n                    type: 'numeric',\n                    title: 'Price',\n                    width: 100,\n                    mask: '$ #.##,00',\n                    decimal: ',',\n                },\n                { type: 'color', width: 100, render: 'square' },\n            ],\n        },\n    ],\n});\n```\n\nServe your html file and then you will get the rendered table in your browser\n\n![sampleTable](./docs/sampleTable.png)\n\n<br>\n\n## Development\n\n### Build your package\n\n% npm run build\n\n### Start a web service\n\n% npm run start\n\n## Data Grid Examples\n\n-   https://bossanova.uk/jspreadsheet/docs/examples/create-from-table\n-   https://bossanova.uk/jspreadsheet/docs/examples/translations\n-   https://bossanova.uk/jspreadsheet/docs/examples/table-overflow\n-   https://bossanova.uk/jspreadsheet/docs/examples/richtext-html-editor\n-   https://bossanova.uk/jspreadsheet/docs/examples/column-dragging\n-   https://bossanova.uk/jspreadsheet/docs/examples/web-component\n-   https://bossanova.uk/jspreadsheet/docs/examples/jquery\n\n## Jspreadsheet Changelog\n\n### Custom\n\n-   Add onupdateresult event after search/filter;\n\n### Jspreadsheet 5.0.0\n\n-   Separation of spreadsheet and worksheets;\n-   New worksheet methods and events;\n-   Dedicated wrappers for React and Vue for better framework integration;\n-   Modern development environment powered by Webpack;\n-   Updated architecture aligned with other distributions;\n\n[More information](https://bossanova.uk/jspreadsheet/docs/upgrade-from-v4-to-v5)\n\n### Jspreadsheet 4.6.0\n\n-   Jexcel renamed to Jspreadsheet.\n-   Integration with Jsuites v4.\n\n### Jspreadsheet 4.2.3\n\n-   The spreadsheet plugin is now compatible with Jsuites v3.\n-   New flags and security implementations.\n-   New DOM element references in the toolbar.\n-   Worksheet events are now tabbed.\n\n### Jspreadsheet 4.0.0\n\nSpecial thanks to [FDL - Fonds de Dotation du Libre](https://www.fdl-lef.org/) for their support and sponsorship, which made the new version possible with many exciting features.\n\n-   Workbook/tab support for spreadsheets.\n-   Create dynamic spreadsheets from static HTML elements.\n-   Highlight selected cells in the spreadsheet after CTRL+C.\n-   Footer with formula support.\n-   Multiple column resizing.\n-   JSON update support (helpers to update a remote server).\n-   Centralized event dispatch method for all spreadsheet events.\n-   Custom helpers: `=PROGRESS` (progress bar), `=RATING` (5-star rating).\n-   Custom formula helpers: `=COLUMN`, `=ROW`, `=CELL`, `=TABLE`, `=VALUE`.\n-   Dynamic nested header updates.\n-   New HTML editing column type.\n-   New flags: `includeHeadersOnCopy`, `persistence`, `filters`, `autoCasting`, `freezeColumns`.\n-   New events: `onevent`, `onchangepage`, `onbeforesave`, `onsave`.\n-   More examples and documentation.\n\n### Jspreadsheet 3.9.0\n\n-   New methods.\n-   General fixes.\n\n### Jspreadsheet 3.6.0\n\n-   Improved spreadsheet formula parsing.\n-   New spreadsheet events.\n-   New initialization options.\n-   General fixes.\n\n### Jspreadsheet 3.2.3\n\n-   `getMeta`, `setMeta` methods.\n-   NPM package with jSuites.\n-   General fixes.\n\n### Jspreadsheet 3.0.1\n\nJspreadsheet v3 is a complete rebuild of the JavaScript spreadsheet (previously a jQuery plugin). Due to the changes, full compatibility could not be ensured. If upgrading, your code may require some updates. For more information, refer to the article on upgrading from Jspreadsheet v2 or Handsontable.\n\n**New features in Jspreadsheet v3:**\n\n-   Drag and drop columns.\n-   Resizable rows.\n-   Merge columns.\n-   Search functionality.\n-   Pagination.\n-   Lazy loading.\n-   Full-screen mode.\n-   Image upload.\n-   Native color picker.\n-   Better mobile compatibility.\n-   Enhanced nested headers support.\n-   Advanced keyboard navigation.\n-   Better hidden column management.\n-   Data picker enhancements: dropdown, autocomplete, multiple selection, group options, and icons.\n-   Import from XLSX (experimental).\n\n**Major improvements:**\n\n-   A new formula engine with faster results and no external dependencies.\n-   No use of selectors, leading to faster performance.\n-   New native column types.\n-   No jQuery required.\n-   Examples for React, Vue, and Angular.\n-   XLSX support via a custom SheetJS integration (experimental).\n\n### Jspreadsheet 2.1.0\n\n-   Mobile touch improvements.\n-   Paste fixes and a new CSV parser.\n\n### Jspreadsheet 2.0.0\n\n-   New radio column type.\n-   New dropdown with autocomplete and multiple selection options.\n-   Header/body separation for better scroll and column resize behavior.\n-   Text-wrap improvements, including Excel-compatible `alt+enter`.\n-   New `set/get` meta information.\n-   New `set/get` configuration parameters.\n-   Programmatic `set/get` cell styles.\n-   `set/get` cell comments.\n-   Custom toolbar for tables.\n-   Responsive calendar picker.\n\n### Jspreadsheet 1.5.7\n\n-   Improvements to checkbox column type.\n-   Updates to table destruction in jQuery.\n\n### Jspreadsheet 1.5.1\n\n-   Spreadsheet data overflow and fixed headers. See an [example](https://jspreadsheet.com/examples/table-with-fixed-headers).\n-   Navigation improvements.\n\n### Jspreadsheet 1.5.0\n\n-   Relative `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`. See an [example](https://jspreadsheet.com/examples/working-with-the-data).\n-   Redo and undo support for `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`, `moveRow`.\n-   New formula column recursive chain.\n-   New alternative design option (Bootstrap-like). See an [example](https://jspreadsheet.com/examples/a-custom-table-design).\n-   `updateSettings` improvements.\n\n## Official websites\n\n-   [Jspreadsheet CE v4 - Javascript Spreadsheet](https://bossanova.uk/jspreadsheet/v4)\n-   [Jspreadsheet CE v3 - Vanilla JavaScript](https://bossanova.uk/jspreadsheet/v3)\n-   [Jspreadsheet CE v2 - jQuery Plugin](https://bossanova.uk/jspreadsheet/v2)\n-   [Jspreadsheet Pro v11 - Javascript Spreadsheet](https://jspreadsheet.com/docs)\n-   [Jspreadsheet Pro v10 - Javascript Spreadsheet](https://jspreadsheet.com/docs/v10)\n-   [Jspreadsheet Pro v9 - Javascript Spreadsheet](https://jspreadsheet.com/docs/v9)\n-   [Jspreadsheet Pro v8 - Javascript Spreadsheet](https://jspreadsheet.com/docs/v8)\n-   [Jspreadsheet Pro v7 - Javascript Spreadsheet](https://jspreadsheet.com/docs/v7)\n\n## Community\n\n-   [GitHub](https://github.com/jspreadsheet/ce/issues)\n\n## Contributing\n\nSee [contributing](contributing.md)\n\n## Copyright and license\n\nJspreadsheet CE is released under the [MIT license]. Contact contact@jspreadsheet.com\n\n## Other tools\n\n-   [LemonadeJS Reactive Library](https://lemonadejs.com)\n-   [jSuites Components](https://jsuites.net)\n-   [CalendarJS](https://calendarjs.com)\n"
  },
  {
    "path": "build.cjs",
    "content": "module.exports = function (source) {\n    let result = source.replace(/import\\s+jSuites\\s+from\\s+(?:'|\")jsuites(?:'|\");?/gm, '');\n\n    result = result.replace(/import\\s+formula\\s+from\\s+(?:'|\")@jspreadsheet\\/formula(?:'|\");?/gm, '');\n\n    return result;\n};\n"
  },
  {
    "path": "contributing.md",
    "content": "# Contributing\n\nWhen contributing to this repository, please follow the following process\n\n## Pull Request Process\n\n1. Create a fork of this repo\n2. Create a branch named according to what you are developing (new-feature, fix-issue)\n3. Make your changes\n4. Create a Pull Request (PR) against origin:dev branch (origin:master)\n5. Comment your PR explaining your changes, if it resolves an issue or adds a feature, also modify documentation accordingly if necessary.\n6. Wait for yout PR to be reviewed and approved\n\n## Steps to run the project locally after forking the repository.\n\nOpen [http://localhost:8000](http://localhost:8000/) in your browser, or check the devServer settings in webpack.config.js if the port is different.\nAlternatively, you can change the port in the webpack.config.js file.\n\n# Code of Conduct\n\n### Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of experience,\nnationality, personal appearance, race, religion, or sexual identity and\norientation.\n\n### Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n-   Using welcoming and inclusive language\n-   Being respectful of differing viewpoints and experiences\n-   Gracefully accepting constructive criticism\n-   Focusing on what is best for the community\n-   Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n-   The use of sexualized language or imagery and unwelcome sexual attention or\n    advances\n-   Trolling, insulting/derogatory comments, and personal or political attacks\n-   Public or private harassment\n-   Publishing others' private information, such as a physical or electronic\n    address, without explicit permission\n-   Other conduct which could reasonably be considered inappropriate in a\n    professional setting\n\n### Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n### Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n### Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n### Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "dist/index.d.ts",
    "content": "export = jspreadsheet;\nexport as namespace jspreadsheet;\n\ndeclare const jspreadsheet: jspreadsheet.JSpreadsheet;\n\ndeclare namespace jspreadsheet {\n    type CellValue = string | number | boolean;\n\n    type DropdownSourceItem =\n        | string\n        | number\n        | {\n              id: string | number;\n              name: string;\n              title?: string;\n              image?: string;\n              group?: string;\n          };\n\n    interface CalendarOptions {\n        /**\n         * @default \"YYYY-MM-DD\"\n         */\n        format?: string;\n\n        /**\n         * Open calendar in full screen mode (this is automatic set for screensize < 800).\n         */\n        fullscreen?: boolean;\n\n        /** Placeholder. */\n        placeholder?: string;\n\n        /**\n         * Allow keyboard date entry.\n         * @default true\n         */\n        readonly?: boolean;\n\n        /**\n         * Show the reset button.\n         * @default true\n         */\n        resetButton?: boolean;\n\n        /**\n         * Show timepicker.\n         * @default false\n         */\n        time?: boolean;\n\n        /** Today is default. */\n        today?: boolean;\n    }\n\n    interface CustomEditor {\n        /**\n         * Event responsible for closing the editor of a cell with a custom editor.\n         * @param cell - Td tag whose editor should close.\n         * @param save - If true, the value returned by this event will be the cell's new value. Otherwise, the value returned by this event is ignored.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param instance - Worksheet instance.\n         * @param options - Column configuration object.\n         * @returns New cell value.\n         */\n        closeEditor?: (cell: HTMLTableCellElement, save: boolean, x: number, y: number, instance: WorksheetInstance, options: Column) => CellValue | undefined;\n\n        /**\n         * Event called when creating new cells.\n         * @param cell - HTML element prepared to be the new cell.\n         * @param value - Cell value.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param instance - Worksheet instance.\n         * @param options - Column configuration object.\n         * @returns HTML element that will be the new cell.\n         */\n        createCell?: (cell: HTMLTableCellElement, value: CellValue, x: number, y: number, instance: WorksheetInstance, options: Column) => HTMLTableCellElement;\n\n        /**\n         * Event responsible for opening the editor of a cell with a custom editor.\n         * @param cell - Td tag whose editor should open.\n         * @param value - Cell value.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param instance - Worksheet instance.\n         * @param options - Column configuration object.\n         * @param e - Event that called this method.\n         */\n        openEditor?: (\n            cell: HTMLTableCellElement,\n            value: CellValue,\n            x: number,\n            y: number,\n            instance: WorksheetInstance,\n            options: Column,\n            e: KeyboardEvent | MouseEvent | TouchEvent | undefined\n        ) => void;\n\n        /**\n         * Event called before changing the value of a cell.\n         *\n         * The returned value will be the cell's new value.\n         * @param cell - Cell whose value has changed.\n         * @param value - New value.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param instance - Worksheet instance.\n         * @param options - Column configuration object.\n         */\n        updateCell?: (\n            cell: HTMLTableCellElement,\n            value: CellValue | undefined,\n            x: number,\n            y: number,\n            instance: WorksheetInstance,\n            options: Column\n        ) => CellValue | undefined;\n    }\n\n    type HorizontalAlign = 'center' | 'left' | 'right' | 'justify';\n\n    interface BaseColumn {\n        /** Cell alignment. */\n        align?: HorizontalAlign;\n\n        decimal?: string;\n\n        /** Cell mask. */\n        mask?: string;\n\n        /** Name used to refer to this column if the data is arranged as an array of objects. */\n        name?: string;\n\n        /**\n         * Prevent user from changing cell values.\n         * @default false\n         */\n        readOnly?: boolean;\n\n        /**\n         * Defines a modification that must be made to a cell value before it is displayed in the spreadsheet.\n         * @param cell - Cell whose value has changed.\n         * @param value - New value.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param instance - Worksheet instance.\n         * @param options - Column configuration object.\n         */\n        render?: (cell: HTMLTableCellElement, value: CellValue | undefined, x: number, y: number, instance: WorksheetInstance, options: Column) => void;\n\n        /** Custom column title. */\n        title?: string;\n\n        /**\n         * The type of cells in this column.\n         * @default \"text\"\n         */\n        type?: 'text' | 'numeric' | 'hidden' | 'dropdown' | 'autocomplete' | 'checkbox' | 'radio' | 'calendar' | 'image' | 'color' | 'html' | CustomEditor;\n\n        /** Column width. */\n        width?: string | number;\n\n        /**\n         * Enable automatic word wrapping in cells in this column.\n         * @default false\n         */\n        wordWrap?: boolean;\n    }\n\n    interface DropdownColumn extends BaseColumn {\n        autocomplete?: boolean;\n\n        /**\n         * Allow selection of more than one item.\n         * @default false\n         */\n        multiple?: boolean;\n\n        /** Options available in the dropdown. */\n        source?: DropdownSourceItem[];\n\n        /** Url to fetch options from an external source. */\n        url?: string;\n    }\n\n    interface CalendarColumn extends BaseColumn {\n        /** Calendar options. */\n        options?: CalendarOptions;\n    }\n\n    interface ColorColumn extends Omit<BaseColumn, 'render'> {\n        /** If undefined, the cell shows the hex code of the color, if \"square\", it shows a square filled with the color. */\n        render?: 'square';\n    }\n\n    type Column = DropdownColumn | CalendarColumn | ColorColumn | BaseColumn;\n\n    interface Row {\n        /** Row height. */\n        height?: string | number;\n\n        /** Text that replaces the row number. */\n        title?: string;\n    }\n\n    interface ToolbarItemBase {\n        /** Tooltip shown when hovering over this option. */\n        tooltip?: string;\n\n        /**\n         * Method called when the toolbar state should be updated.\n         * @param toolbarElement - Toolbar HTML element.\n         * @param toolbarInstance - Toolbar instance. For more information, read the jSuites toolbar documentation.\n         * @param itemElement - Toolbar item HTML element.\n         * @param worksheetInstance - Worksheet instance.\n         */\n        updateState?: (\n            toolbarElement: HTMLDivElement,\n            toolbarInstance: Record<string, any>,\n            itemElement: HTMLDivElement,\n            worksheetInstance: WorksheetInstance\n        ) => void;\n\n        [property: string]: any;\n    }\n\n    interface ToolbarIconItem extends ToolbarItemBase {\n        /** Defines the icon (from material icons). */\n        content: string;\n\n        /**\n         * Event fired when clicking on the html item referring to that item.\n         * @param toolbarElement - Toolbar HTML element.\n         * @param toolbarInstance - Toolbar instance. For more information, read the jSuites toolbar documentation.\n         * @param itemElement - Toolbar item HTML element.\n         * @param event - Pointer event that triggered the onclick.\n         */\n        onclick?: (toolbarElement: HTMLDivElement, toolbarInstance: Record<string, any>, itemElement: HTMLDivElement, event: PointerEvent) => void;\n    }\n\n    interface ToolbarSelectItem extends ToolbarItemBase {\n        type: 'select';\n\n        /** Defines the icon (from material icons). */\n        content: string;\n\n        /**\n         * Event called when the item picker value is changed.\n         * @param itemElement - Toolbar item HTML element.\n         * @param pickerInstance - Picker instance. For more information, read the jSuites picker documentation.\n         * @param value - New picker value.\n         * @param value2 - New picker value.\n         * @param valueIndex - Index of the new picker value.\n         * @param event - Pointer event that triggered this event.\n         */\n        onchange?: (\n            itemElement: HTMLDivElement,\n            pickerInstance: Record<string, any>,\n            value: string,\n            value2: string,\n            valueIndex: string,\n            event: PointerEvent\n        ) => void;\n\n        /**\n         * Options available in the picker.\n         */\n        options?: string[];\n\n        /**\n         * Creates picker items based on the picker options.\n         * @param option - One of the items in the {@link ToolbarSelectItem.options} array.\n         * @param pickerInstance - Picker instance. For more information, read the jSuites picker documentation.\n         * @returns string representing the HTML of the picker item.\n         */\n        render?: (option: string, pickerInstance: Record<string, any>) => string;\n\n        /** Initial value of the selectbox. */\n        value?: string;\n\n        /** Item width. */\n        width?: string;\n    }\n\n    interface ToolbarColorItem extends ToolbarItemBase {\n        type: 'color';\n\n        /** Defines the icon (from material icons). */\n        content: string;\n\n        /**\n         * Style that should be changed when input value changes. If this property is set, the onclick event is overridden.\n         */\n        k: string;\n    }\n\n    interface ToolbarDivisorItem {\n        type: 'divisor';\n    }\n\n    /**\n     * Item that makes up the toolbar configuration array. This item may have properties not described here. For more information, read the jSuites toolbar documentation.\n     */\n    type ToolbarItem = ToolbarIconItem | ToolbarSelectItem | ToolbarColorItem | ToolbarDivisorItem;\n\n    interface NestedHeaderCell {\n        id?: string;\n        colspan?: number;\n        title?: string;\n        align?: string;\n    }\n\n    interface CellChange {\n        value: CellValue;\n        oldValue: CellValue;\n        x: string;\n        y: string;\n    }\n\n    interface HistoryRecord {\n        action: string;\n        [key: string]: any;\n    }\n\n    /**\n     * Item compared in column ordering.\n     *\n     * It is composed respectively by the index of the row it represents and by the value of that row in the sorting column.\n     */\n    type SortingItem = [number, CellValue];\n\n    type MetaInformation = Record<string, any>;\n\n    type ContextMenuItem = {\n        type?: 'line' | 'divisor' | 'default';\n        title: string;\n        icon?: string;\n        id?: string;\n        disabled?: boolean;\n        onclick?: (instance: any, e: MouseEvent) => void;\n        shortcut?: string;\n        tooltip?: string;\n        submenu?: Array<ContextMenuItem>;\n    };\n\n    type ContextMenuRole =\n        | 'select-all'\n        | 'fill-handle'\n        | 'row'\n        | 'nested'\n        | 'tabs'\n        | 'toolbar'\n        | 'pagination'\n        | 'cell'\n        | 'grid'\n        | 'footer'\n        | 'header'\n        | 'applications';\n\n    interface SpreadsheetOptions {\n        /**\n         * Show or not the \"about\" item in the context menu.\n         * @default true\n         */\n        about?: boolean;\n\n        /**\n         * Allow table export as csv.\n         * @default true\n         */\n        allowExport?: boolean;\n\n        /**\n         * If true, Jss will try to convert cell contents used in formulas to numbers\n         * @default true\n         */\n        autoCasting?: boolean;\n\n        /**\n         * Auto increment actions when using the dragging corner.\n         * @default true\n         */\n        autoIncrement?: boolean;\n\n        /**\n         * Creates context menu when user clicks with secondary mouse button.\n         * @param instance - Instance of the worksheet on which the click was made.\n         * @param colIndex - Horizontal index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param rowIndex - Vertical index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param event - pointer event that triggered this event.\n         * @param items - jss default context menu.\n         * @param role - indicates in which part of the spreadsheet the click occurred.\n         * @param x - Horizontal index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param y - Vertical index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @returns Context menu configuration that should be created.\n         */\n        contextMenu?: (\n            instance: WorksheetInstance,\n            colIndex: string | number | null,\n            rowIndex: string | number | null,\n            event: PointerEvent,\n            items: ContextMenuItem[],\n            role: ContextMenuRole,\n            x: string | number | null,\n            y: string | number | null\n        ) => ContextMenuItem[] | null | undefined;\n\n        /**\n         * Enable the formula debug notices.\n         * @default false\n         */\n        debugFormulas?: boolean;\n\n        /**\n         * Fullscreen mode.\n         * @default false\n         */\n        fullscreen?: boolean;\n\n        /**\n         * Include header titles on download.\n         * @default false\n         */\n        includeHeadersOnDownload?: boolean;\n\n        /** Spreadsheet namespace */\n        namespace?: string;\n\n        /**\n         * Occurs after all changes are applied in the tables.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param changes - list of changes.\n         */\n        onafterchanges?: (instance: WorksheetInstance, changes: CellChange[]) => void;\n\n        /**\n         * Occurs before a column value is changed. If any value is returned, it will be the cell's new value.\n         * @param instance - Instance of the worksheet where the changes will occur.\n         * @param cell - HTML element that represents the cell being changed.\n         * @param colIndex - Cell column index being changed.\n         * @param rowIndex - Cell row index being changed.\n         * @param newValue - Value being applied to the cell.\n         */\n        onbeforechange?: (\n            instance: WorksheetInstance,\n            cell: HTMLTableCellElement,\n            colIndex: string | number,\n            rowIndex: string | number,\n            newValue: CellValue\n        ) => undefined | CellValue;\n\n        /**\n         * Occurs before a column is excluded. If this method returns false, the removal will be canceled.\n         * @param instance - Instance of the worksheet where columns will be removed.\n         * @param removedColumns - Indexes of the columns to be removed.\n         */\n        onbeforedeletecolumn?: (instance: WorksheetInstance, removedColumns: number[]) => undefined | boolean;\n\n        /**\n         * Occurs before a row is deleted. If this method returns false, the removal will be canceled.\n         * @param instance - Instance of the worksheet where rows will be removed.\n         * @param removedRows - Indexes of the rows to be removed.\n         */\n        onbeforedeleterow?: (instance: WorksheetInstance, removedRows: number[]) => undefined | boolean;\n\n        /**\n         * Intercept and parse a formula just before the execution.\n         * @param instance - Instance of the worksheet.\n         * @param expression - Formula that triggered the event.\n         * @param x - Column index of the cell whose formula triggered the event.\n         * @param y - Row index of the cell whose formula triggered the event\n         */\n        onbeforeformula?: (instance: WorksheetInstance, expression: string, x?: number, y?: number) => false | string | undefined;\n\n        /**\n         * Occurs before a new column is inserted. If this method returns false, the insertion will be canceled.\n         * @param instance - Instance of the worksheet where columns will be added.\n         * @param columns - Settings for columns to be added.\n         */\n        onbeforeinsertcolumn?: (\n            instance: WorksheetInstance,\n            columns: {\n                column: number;\n                options: Column;\n                data?: CellValue[];\n            }[]\n        ) => undefined | boolean;\n\n        /**\n         * Occurs before a new row is inserted. If this method returns false, the insertion will be canceled.\n         * @param instance - Instance of the worksheet where rows will be added.\n         * @param rows - Settings for rows to be added.\n         */\n        onbeforeinsertrow?: (\n            instance: WorksheetInstance,\n            rows: {\n                row: number;\n                data: CellValue[];\n            }[]\n        ) => undefined | boolean;\n\n        /**\n         * Occurs before the paste action is performed.\n         *\n         * If it returns false, the jss cancels the paste.\n         * If it returns a string, it will be the content pasted into the worksheet.\n         *\n         * @param instance - Instance of the worksheet where data will be pasted.\n         * @param copiedText - Text being pasted to the spreadsheet.\n         * @param colIndex - Column index where it will start the paste.\n         * @param rowIndex - Row index where it will start the paste.\n         */\n        onbeforepaste?: (\n            instance: WorksheetInstance,\n            copiedText: { value: CellValue }[][],\n            colIndex: number | string,\n            rowIndex: number | string\n        ) => undefined | boolean | string;\n\n        /**\n         * Occurs before persisting any changes to the server.\n         *\n         * This event is only called when the spreadsheet has the {@link WorksheetOptions.persistence} property set.\n         *\n         * If this event returns false, the change is not persisted on the server.\n         * If it returns a truthy value, that value is persisted instead of the initial value.\n         * @param spreadsheetInstance - Spreadsheet instance.\n         * @param worksheetInstance - Instance of the worksheet to be saved.\n         * @param data - Changed data.\n         */\n        onbeforesave?: (\n            spreadsheetInstance: SpreadsheetInstance,\n            worksheetInstance: WorksheetInstance,\n            data: { row: number; data: Record<number, CellValue> }[]\n        ) => any;\n\n        /**\n         * Occurs before the selection is changed.\n         * @param instance - Worksheet instance where the selection will occur.\n         * @param borderLeftIndex - Index of the first column contained by the selection.\n         * @param borderTopIndex - Index of the first row contained by the selection.\n         * @param borderRightIndex - Index of the last column contained by the selection.\n         * @param borderBottomIndex - Index of the last row contained by the selection.\n         * @param origin - Javascript event that triggered this jss event.\n         */\n        onbeforeselection?: (\n            instance: WorksheetInstance,\n            borderLeftIndex: number,\n            borderTopIndex: number,\n            borderRightIndex: number,\n            borderBottomIndex: number,\n            origin: Event | undefined\n        ) => false | undefined;\n\n        /**\n         * Occurs when the table is blurred.\n         * @param instance - Instance of the worksheet that was blurred.\n         */\n        onblur?: (instance: WorksheetInstance) => void;\n\n        /**\n         * Occurs after a column value is changed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param cell - HTML element that represents the cell being changed.\n         * @param colIndex - Cell column index being changed.\n         * @param rowIndex - Cell row index being changed.\n         * @param newValue - New cell value.\n         * @param oldValue - Old cell value.\n         */\n        onchange?: (\n            instance: WorksheetInstance,\n            cell: HTMLTableCellElement,\n            colIndex: string | number,\n            rowIndex: string | number,\n            newValue: CellValue,\n            oldValue: CellValue\n        ) => void;\n\n        /**\n         * Occurs when a column heading is changed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param colIndex - Index of the column that was renamed.\n         * @param newValue - New column title.\n         * @param oldValue - Old column title.\n         */\n        onchangeheader?: (instance: WorksheetInstance, colIndex: number, newValue: string, oldValue: string) => void;\n\n        /**\n         * Occurs when a \"setMeta\" is called.\n         *\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param cellName - An object with the metadata changes.\n         */\n        onchangemeta?: (instance: WorksheetInstance, cellName: Record<string, any>) => void;\n\n        /**\n         * Occurs when the page is changed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param newPageNumber - Page the worksheet is on.\n         * @param oldPageNumber - Page the worksheet was on.\n         * @param quantityPerPage - Maximum number of lines on pages.\n         */\n        onchangepage?: (instance: WorksheetInstance, newPageNumber: number, oldPageNumber: number, quantityPerPage: number) => void;\n\n        /**\n         * Occurs when a \"setStyle\" is called.\n         *\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param cellName - An object with the changes.\n         */\n        onchangestyle?: (instance: WorksheetInstance, changes: Record<string, string>) => void;\n\n        /**\n         * Occurs when a comment is changed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param newComments - New comments.\n         * @param oldComments - Old comments.\n         */\n        oncomments?: (instance: WorksheetInstance, newComments: Record<string, string | null>, oldComments: Record<string, string | null>) => void;\n\n        /**\n         * Occurs when the contents of one or more cells are copied.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param selectedRange - Copied cell range.\n         * @param copiedData - Data from copied cell range.\n         * @param cut - If true, the action was cut. Otherwise, the action was copy.\n         */\n        oncopy?: (\n            instance: WorksheetInstance,\n            selectedRange: [number, number, number, number],\n            copiedData: string,\n            cut: boolean | undefined\n        ) => string | false | undefined;\n\n        /**\n         * Occurs when a cell is created.\n         * @param instance - Instance of the worksheet where the cell was created.\n         * @param cell - Cell HTML element.\n         * @param colIndex - Cell column index.\n         * @param rowIndex - Cell row index.\n         * @param newValue - Cell value.\n         */\n        oncreatecell?: (instance: WorksheetInstance, cell: HTMLTableCellElement, colIndex: number, rowIndex: number, newValue: CellValue) => void;\n\n        /**\n         * Occurs when an editor is opened.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param td - Td tag of the cell whose editor was opened.\n         * @param colIndex - Column index of the cell whose editor was opened.\n         * @param rowIndex - Row index of the cell whose editor was opened.\n         * @param input - Input of the editor that was opened.\n         * @param options - Column settings.\n         */\n        oncreateeditor?: (instance: WorksheetInstance, td: HTMLTableCellElement, colIndex: number, rowIndex: number, input: null, options: Column) => void;\n\n        /**\n         * Occurs after a column is excluded.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param removedColumns - Indexes of the columns that were removed.\n         */\n        ondeletecolumn?: (instance: WorksheetInstance, removedColumns: number[]) => void;\n\n        /**\n         * Occurs after a row is excluded.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param removedRows - Indexes of the rows that were removed.\n         */\n        ondeleterow?: (instance: WorksheetInstance, removedRows: number[]) => void;\n\n        /**\n         * Occurs when a worksheet is created.\n         * @param worksheet - Instance of the new worksheet.\n         * @param worksheetOptions - Options of the new worksheet.\n         * @param index - Index of the new worksheet.\n         */\n        oncreateworksheet?: (worksheet: WorksheetInstance, worksheetOptions: WorksheetOptions, index: number) => void;\n\n        /**\n         * Occurs when a worksheet is deleted.\n         * @param worksheet - Instance of the deleted worksheet.\n         * @param index - Index of the deleted worksheet.\n         */\n        ondeleteworksheet?: (worksheet: WorksheetInstance, index: number) => void;\n\n        /**\n         * Occurs when a closeEditor is called.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param td - Td tag of the cell whose editor was opened.\n         * @param colIndex - Column index of the cell whose editor was opened.\n         * @param rowIndex - Row index of the cell whose editor was opened.\n         * @param editorValue - Value that was in the editor.\n         * @param wasSaved - Whether the value which was in the editor was saved in the cell or not.\n         */\n        oneditionend?: (\n            instance: WorksheetInstance,\n            td: HTMLTableCellElement,\n            colIndex: number,\n            rowIndex: number,\n            editorValue: CellValue,\n            wasSaved: boolean\n        ) => void;\n\n        /**\n         * Occurs when a openEditor is called.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param td - Td tag of the cell whose editor was opened.\n         * @param colIndex - Column index of the cell whose editor was opened.\n         * @param rowIndex - Row index of the cell whose editor was opened.\n         */\n        oneditionstart?: (instance: WorksheetInstance, td: HTMLTableCellElement, colIndex: number, rowIndex: number) => void;\n\n        /**\n         * Event fired when any other event fires. It runs before the event that was called.\n         *\n         * If the called event has not been defined, the jss considers the value returned by onevent as the value returned by the called event.\n         * @param event - Name of the event that was called.\n         * @param rest - Arguments of the event that was called.\n         */\n        onevent?: (event: string, ...rest: any[]) => any;\n\n        /**\n         * Occurs when the table is focused.\n         * @param instance - Instance of the worksheet that was focused on.\n         */\n        onfocus?: (instance: WorksheetInstance) => void;\n\n        /**\n         * Occurs after a new column is inserted.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param columns - Columns that have been added.\n         */\n        oninsertcolumn?: (\n            instance: WorksheetInstance,\n            columns: {\n                column: number;\n                options: Column;\n                data?: CellValue[];\n            }[]\n        ) => void;\n\n        /**\n         * Occurs after a new row is inserted.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param rows - Rows that have been added.\n         */\n        oninsertrow?: (\n            instance: WorksheetInstance,\n            rows: {\n                row: number;\n                data: CellValue[];\n            }[]\n        ) => void;\n\n        /**\n         * Event fired when a spreadsheet is created.\n         * @param instance - Jspreadsheet instance.\n         */\n        onload?: (instance: SpreadsheetInstance) => void;\n\n        /**\n         * Occurs when a group of cells is merged.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param merges - Merges that were created.\n         */\n        onmerge?: (instance: WorksheetInstance, merges: Record<string, [number, number]>) => void;\n\n        /**\n         * Occurs when a merge is removed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param cellName - Address of the cell that merge was removed.\n         * @param beforeMerges - Merges that were before the merge was removed.\n         */\n        onunmerge?: (instance: WorksheetInstance, cellName: string, beforeMerges: Record<string, [number, number]>) => void;\n\n        /**\n         * Occurs after a column is moved to a new position.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param oldPosition - Column index before movement.\n         * @param newPosition - Column index after movement.\n         * @param quantity - Number of columns that were moved.\n         */\n        onmovecolumn?: (instance: WorksheetInstance, oldPosition: number, newPosition: number, quantity: number) => void;\n\n        /**\n         * Occurs after a row is moved to a new position.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param oldPosition - Row index before movement.\n         * @param newPosition - Row index after movement.\n         * @param quantity - Number of rows that were moved.\n         */\n        onmoverow?: (instance: WorksheetInstance, oldPosition: number, newPosition: number, quantity: number) => void;\n\n        /**\n         * Occurs after a paste action is performed in the javascript table.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param pastedInfo - Information that was pasted into the worksheet.\n         */\n        onpaste?: (\n            instance: WorksheetInstance,\n            pastedInfo: {\n                x: number;\n                y: number;\n                value: CellValue;\n            }[][]\n        ) => void;\n\n        /**\n         * Occurs when a change is redone.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param historyRecord - History item that was redone. If there are no more actions to redo, it takes the value undefined.\n         */\n        onredo?: (instance: WorksheetInstance, historyRecord: HistoryRecord | undefined) => void;\n\n        /**\n         * Occurs after a change in column width.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param colIndex - Index of columns that were resized.\n         * @param newWidth - New column widths.\n         * @param oldWidth - Old column widths.\n         */\n        onresizecolumn?: (instance: WorksheetInstance, colIndex: number | number[], newWidth: number | number[], oldWidth: number | number[]) => void;\n\n        /**\n         * Occurs after a change in row height.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param rowIndex - Index of row being resized.\n         * @param newHeight - New row height.\n         * @param oldHeight - Old row height.\n         */\n        onresizerow?: (instance: WorksheetInstance, rowIndex: number, newHeight: number, oldHeight: number) => void;\n\n        /**\n         * Occurs when persistence on the server succeeds.\n         * @param spreadsheetInstance - Spreadsheet instance.\n         * @param worksheetInstance - Instance of the worksheet to be saved.\n         * @param data - Data that has been sent to the server.\n         */\n        onsave?: (spreadsheetInstance: SpreadsheetInstance, worksheetInstance: WorksheetInstance, data: any) => void;\n\n        /**\n         * Occurs when selection is changed.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param borderLeftIndex - Index of the first column contained by the selection.\n         * @param borderTopIndex - Index of the first row contained by the selection.\n         * @param borderRightIndex - Index of the last column contained by the selection.\n         * @param borderBottomIndex - Index of the last row contained by the selection.\n         * @param origin - Javascript event that triggered this jss event.\n         */\n        onselection?: (\n            instance: WorksheetInstance,\n            borderLeftIndex: number,\n            borderTopIndex: number,\n            borderRightIndex: number,\n            borderBottomIndex: number,\n            origin: Event | undefined\n        ) => void;\n\n        /**\n         * Occurs after a colum is sorted.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param colIndex - Index of the column that was sorted.\n         * @param order - Sorting direction. 0 for ascending and 1 for descending.\n         * @param newOrderValues - The new order of the rows.\n         */\n        onsort?: (instance: WorksheetInstance, colIndex: number, order: 0 | 1, newOrderValues: number[]) => void;\n\n        /**\n         * Occurs when a change is undone.\n         * @param instance - Instance of the worksheet where the change occurred.\n         * @param historyRecord - History item that was undone. If there are no more actions to undo, it takes the value undefined.\n         */\n        onundo?: (instance: WorksheetInstance, historyRecord: HistoryRecord | undefined) => void;\n\n        /**\n         * Enable execution of formulas inside the table.\n         * @default true\n         */\n        parseFormulas?: boolean;\n\n        /**\n         * Function used in sorting columns. If not specified, the default function will be used.\n         * @param order - Sorting direction. 0 for ascending and 1 for descending.\n         */\n        sorting?: (order: 0 | 1) => (itemA: SortingItem, itemB: SortingItem) => number;\n\n        /**\n         * If false, HTML inside cell values will be treated as regular text. If true, the HTML will be treated as HTML.\n         * @default false\n         */\n        parseHTML?: boolean;\n\n        /**\n         * If true, the button to create new worksheets is shown.\n         * @default false\n         */\n        tabs?: boolean;\n\n        /** Add custom toolbars. */\n        toolbar?: boolean | ToolbarItem[] | ((defaultToolbar: ToolbarItem[]) => ToolbarItem[]) | Record<string, any>;\n\n        /**\n         * Worksheet settings.\n         */\n        worksheets: WorksheetOptions[];\n    }\n\n    interface WorksheetOptions {\n        /**\n         * Allow comments over the cells.\n         * @default true\n         */\n        allowComments?: boolean;\n\n        /**\n         * Allow delete a column.\n         * @default true\n         */\n        allowDeleteColumn?: boolean;\n\n        /**\n         * Allow delete a row.\n         * @default true\n         */\n        allowDeleteRow?: boolean;\n\n        /**\n         * Allow remove all rows. Otherwise, at least one row will be kept.\n         * @default false\n         */\n        allowDeletingAllRows?: boolean;\n\n        /**\n         * Allow insert a new column.\n         * @default true\n         */\n        allowInsertColumn?: boolean;\n\n        /**\n         * Allow insert a new row.\n         * @default true\n         */\n        allowInsertRow?: boolean;\n\n        /**\n         * Allow user to insert a new column using tab key on last column.\n         * @default true\n         */\n        allowManualInsertColumn?: boolean;\n\n        /**\n         * Allow user to insert a new row using space key on last row.\n         * @default true\n         */\n        allowManualInsertRow?: boolean;\n\n        /**\n         * Allow rename a column.\n         * @default true\n         */\n        allowRenameColumn?: boolean;\n\n        /**\n         * Css classes to apply to cells. Only one class per cell is accepted.\n         * @example\n         * {\n         *    A1: \"some-class\",\n         *    B3: \"another-class\"\n         * }\n         */\n        classes?: Record<string, string>;\n\n        /**\n         * Allow column dragging.\n         * @default true\n         */\n        columnDrag?: boolean;\n\n        /**\n         * Allow column resizing.\n         * @default true\n         */\n        columnResize?: boolean;\n\n        /** Column settings. */\n        columns?: Column[];\n\n        /**\n         * Allow column sorting.\n         * @default true\n         */\n        columnSorting?: boolean;\n\n        /**\n         * Worksheet comments. Each object key is a cell name and its value is its comment.\n         */\n        comments?: Record<string, string>;\n\n        /**\n         * Load a external CSV file from this URL.\n         */\n        csv?: string;\n\n        /**\n         * Default delimiter for the CSV file. This value is used for both import and export.\n         * @default \",\"\n         */\n        csvDelimiter?: string;\n\n        /**\n         * Default filename for a download method.\n         * @default \"jspreadsheet\"\n         */\n        csvFileName?: string;\n\n        /**\n         * Load header titles from the CSV file.\n         * @default false\n         */\n        csvHeaders?: boolean;\n\n        /** Data loaded into the spreadsheet. */\n        data?: CellValue[][] | Record<string, CellValue>[];\n\n        /**\n         * Default horizontal alignment used when a column does not have its alignment specified.\n         * @default \"center\"\n         */\n        defaultColAlign?: HorizontalAlign;\n\n        /**\n         * Default width for columns with no specified width.\n         * @default 100\n         */\n        defaultColWidth?: number;\n\n        /**\n         * Default row height.\n         */\n        defaultRowHeight?: number;\n\n        /**\n         * Allow table edition.\n         * @default true\n         */\n        editable?: boolean;\n\n        /**\n         * Enable column filters.\n         * @default false\n         */\n        filters?: boolean;\n\n        /**\n         * Set the initial footer of the spreadsheet\n         */\n        footers?: string[][];\n\n        /**\n         * Number of columns frozen at the top of the spreadsheet.\n         */\n        freezeColumns?: number;\n\n        /** Activate the table lazyloading. */\n        lazyLoading?: boolean;\n\n        /** Cells to be merged in the table innitialization. */\n        mergeCells?: Record<string, [number, number]>;\n\n        /**\n         * Meta information.\n         */\n        meta?: Record<string, MetaInformation>;\n\n        /**\n         * Minimum table dimensions: [cols, rows].\n         * @default [0, 0]\n         */\n        minDimensions?: [number, number];\n\n        /**\n         * Minimum number of spare cols.\n         * @default 0\n         */\n        minSpareCols?: number;\n\n        /**\n         * Minimum number of spare rows.\n         * @default 0\n         */\n        minSpareRows?: number;\n\n        /** Define the nested headers. */\n        nestedHeaders?: NestedHeaderCell[][];\n\n        /** Number of rows per page. */\n        pagination?: number;\n\n        /**\n         * Values available in the dropdown for choosing the number of rows per page.\n         *\n         * This dropdown is only visible when the {@link WorksheetOptions.search} option is true and the {@link WorksheetOptions.pagination} option is greater than 0.\n         */\n        paginationOptions?: number[];\n\n        /**\n         * Try to identify the column type when the instance is created with a table tag.\n         * @default false\n         */\n        parseTableAutoCellType?: boolean;\n\n        /**\n         * If creating the instance with a table tag, if this tag has no header, transform the first line into the header.\n         * @default false\n         */\n        parseTableFirstRowAsHeader?: boolean;\n\n        /**\n         * Route where requests for data persistence will be sent. If true, the {@link WorksheetOptions.url} property value will be used instead.\n         */\n        persistence?: boolean | string;\n\n        /**\n         * Spreadsheet plugins.\n         */\n        plugins?: Record<string, () => Plugin>;\n\n        /**\n         * DOM element for binding the javascript events. This property is normally used when JSS is running as a web component.\n         */\n        root?: HTMLElement;\n\n        /**\n         * Allow row dragging.\n         * @default true\n         */\n        rowDrag?: boolean;\n\n        /**\n         * Allow row resizing.\n         * @default true\n         */\n        rowResize?: boolean;\n\n        /** Row settings. */\n        rows?: Row[] | Record<number, Row>;\n\n        /**\n         * Allow search in the table.\n         * @default false\n         */\n        search?: boolean;\n\n        /**\n         * If true, Jss will capitalize characters that are part of formulas. This does not apply to characters enclosed in double quotes, which represent text within formulas.\n         * @default true\n         */\n        secureFormulas?: boolean;\n\n        /**\n         * Display the copy icon in the lower right corner of the selection.\n         * @default true\n         */\n        selectionCopy?: boolean;\n\n        /**\n         * Cell styles.\n         */\n        style?: Record<string, string>;\n\n        /**\n         * Set the max height of the table.\n         * This property is only used when {@link WorksheetOptions.tableOverflow} is allowed.\n         */\n        tableHeight?: string | number;\n\n        /**\n         * Allow table overflow.\n         * @default false\n         */\n        tableOverflow?: boolean;\n\n        /**\n         * Set the max width of the table.\n         * This property is only used when {@link WorksheetOptions.tableOverflow} is allowed.\n         */\n        tableWidth?: string | number;\n\n        /**\n         * If true, cell contents may overflow over empty cells.\n         * @default false\n         */\n        textOverflow?: boolean;\n\n        /** Load a external json file from this URL. */\n        url?: string;\n\n        /**\n         * Global text wrapping.\n         * @default false\n         */\n        wordWrap?: boolean;\n\n        /**\n         * Worksheet name.\n         */\n        worksheetName?: string;\n    }\n\n    interface JspreadsheetInstanceElement extends HTMLDivElement {\n        /**\n         * Jss instance this element belongs to\n         */\n        spreadsheet: SpreadsheetInstance;\n    }\n\n    interface JworksheetInstanceElement extends HTMLDivElement {\n        /**\n         * Jss worksheet instance this element belongs to\n         */\n        jspreadsheet: WorksheetInstance;\n    }\n\n    interface DragInfo {\n        /**\n         * Index where the row or column will be moved.\n         */\n        destination: number;\n    }\n\n    interface DragColumnInfo extends DragInfo {\n        /**\n         * Index of the column being moved.\n         */\n        column: string;\n\n        /**\n         * HTML element that corresponds to the column being moved.\n         */\n        element: HTMLTableCellElement;\n    }\n\n    interface DragRowInfo extends DragInfo {\n        /**\n         * Index of the row being moved.\n         */\n        row: string;\n\n        /**\n         * HTML element that corresponds to the row being moved.\n         */\n        element: HTMLTableRowElement;\n    }\n\n    interface ResizeInfo {\n        /**\n         * Mouse position when resizing started. This position refers to the vertical axis, when resizing a row, or the horizontal axis, when resizing a column.\n         */\n        mousePosition: number;\n    }\n\n    interface ResizeRowInfo extends ResizeInfo {\n        /**\n         * HTML element that represents the row.\n         */\n        element: HTMLTableRowElement;\n\n        /**\n         * Old row height.\n         */\n        height: number;\n\n        /**\n         * Index of the row being resized.\n         */\n        row: string;\n    }\n\n    interface ResizeColumnInfo extends ResizeInfo {\n        /**\n         * Index of the column being resized.\n         */\n        column: string;\n\n        /**\n         * Old column width.\n         */\n        width: number;\n    }\n\n    interface Plugin {\n        /**\n         * This method is called before a worksheet is created.\n         * @param instance - New worksheet instance.\n         */\n        beforeinit?: (instance: WorksheetInstance) => void;\n\n        /**\n         * Get spreadsheet config information.\n         */\n        getConfig: () => SpreadsheetOptions;\n\n        /**\n         * This method is called when a worksheet is created.\n         * @param instance - New worksheet instance.\n         */\n        init?: (instance: WorksheetInstance) => void;\n\n        /**\n         * Event fired when any other event fires.\n         * @param event - Name of the event that was called.\n         * @param rest - Arguments of the event that was called.\n         */\n        onevent?: (event: string, ...rest: any[]) => void;\n\n        /**\n         * This method is called before the spreadsheet sends data to the server.\n         * @param instance - Worksheet instance.\n         * @param method - Name of the method whose execution needs to be saved on the server.\n         * @param data - Arguments of the method whose execution needs to be saved on the server.\n         */\n        persistence?: (instance: WorksheetInstance, method: string, data: any) => void;\n\n        /**\n         * Method called before the context menu is displayed. If this method returns anything other than a falsy value, that value overrides the context menu settings.\n         * @param instance - Instance of the worksheet on which the click was made.\n         * @param colIndex - Horizontal index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param rowIndex - Vertical index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param event - pointer event that triggered this method.\n         * @param items - jss default context menu.\n         * @param role - indicates in which part of the spreadsheet the click occurred.\n         * @param x - Horizontal index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @param y - Vertical index of the element that was clicked. The meaning of this value depends on the {@link role} argument.\n         * @returns Context menu configuration that should be created.\n         */\n        contextMenu?: (\n            instance: WorksheetInstance,\n            colIndex: number | null,\n            rowIndex: number | null,\n            event: PointerEvent,\n            items: ContextMenuItem[],\n            role: ContextMenuRole,\n            x: number | null,\n            y: number | null\n        ) => ContextMenuItem[] | null | undefined;\n\n        /**\n         * Method called before the toolbar is displayed. If this method returns anything other than a falsy value, that value overrides the toolbar settings.\n         * @param defaultToolbar\n         */\n        toolbar?: (defaultToolbar: ToolbarItem[]) => ToolbarItem[] | null | undefined;\n    }\n\n    interface SpreadsheetInstance {\n        /**\n         * Spreadsheet settings.\n         */\n        config: SpreadsheetOptions;\n\n        /**\n         * Jsuites contextmenu of this jss instance\n         */\n        contextMenu: HTMLDivElement;\n\n        /**\n         * Root HTML element of this jss instance.\n         */\n        el: JspreadsheetInstanceElement;\n\n        /**\n         * Alias for el.\n         */\n        element: JspreadsheetInstanceElement;\n\n        /**\n         * Toogle table fullscreen mode.\n         * @param activate - Desired mode. Default: The opposite of the current mode.\n         */\n        fullscreen: (activate?: boolean) => void;\n\n        /**\n         * Get the index of the currently active worksheet\n         */\n        getWorksheetActive: () => number;\n\n        /**\n         * Hide the toolbar.\n         */\n        hideToolbar: () => void;\n\n        /**\n         * If true, the spreadsheet does not emit events.\n         */\n        ignoreEvents?: boolean;\n\n        /**\n         * Spreadsheet plugins.\n         */\n        plugins: Record<string, Plugin>;\n\n        /**\n         * Change the spreadsheet settings.\n         * @param config - New settings.\n         */\n        setConfig: (config: SpreadsheetOptions) => void;\n\n        /**\n         * Add new plugins to the spreadsheet.\n         * @param plugins - New plugins.\n         */\n        setPlugins: (plugins: Record<string, () => Plugin>) => void;\n\n        /**\n         * Show the toolbar using the current settings.\n         */\n        showToolbar: () => void;\n\n        /**\n         * HTML div tag used as the toolbar of this jss instance.\n         */\n        toolbar: HTMLDivElement;\n\n        /**\n         * Instances of the worksheets that make up this spreadsheet.\n         */\n        worksheets: WorksheetInstance[];\n    }\n\n    interface WorksheetInstance {\n        ads: HTMLDivElement;\n\n        /**\n         * Close a cell editor.\n         * @param cell - HTML td tag whose editor must be closed.\n         * @param save - Whether or not to save editor content in cell.\n         */\n        closeEditor: (cell: HTMLTableCellElement, save: boolean) => void;\n\n        /**\n         * List of \"col\" tags for this spreadsheet's table\n         */\n        cols: {\n            colElement: HTMLTableColElement;\n            x: number;\n        }[];\n\n        /**\n         * Colgroup tag for this spreadsheet's table\n         */\n        colgroupContainer: HTMLElement;\n\n        content: HTMLDivElement;\n\n        /**\n         * Copies or cuts the contents of selected cells in the worksheet.\n         * @param cut - If true, the operation is cut, if not, it is copy.\n         */\n        copy: (cut?: boolean) => void;\n\n        /**\n         * HTML element that sits in the lower-right corner of selections.\n         */\n        corner: HTMLDivElement;\n\n        /**\n         * Create a new worksheet.\n         * @param options - Worksheet options.\n         */\n        createWorksheet: (options: WorksheetOptions) => void;\n\n        cursor: null | HTMLElement;\n\n        /**\n         * Last content copied.\n         */\n        data: string;\n\n        /**\n         * Remove columns.\n         *\n         * This method returns false if the {@link SpreadsheetOptions.onbeforedeletecolumn} event returns false or if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.\n         * @param columnNumber - Column index from which removal starts.\n         * @param numOfColumns - Number of columns to be removed.\n         */\n        deleteColumn: (columnNumber?: number, numOfColumns?: number) => false | undefined;\n\n        /**\n         * Remove rows.\n         *\n         * This method returns false if the {@link SpreadsheetOptions.onbeforedeleterow} event returns false or if the dialog cases \"This action will destroy any existing merged cells. Are you sure?\" or \"This action will clear your search results. Are you sure?\" receive a negative response.\n         * @param rowNumber - Row index from which removal starts.\n         * @param numOfRows - Number of rows to be removed.\n         */\n        deleteRow: (rowNumber?: number, numOfRows?: number) => false | undefined;\n\n        /**\n         * Delete a worksheet.\n         * @param position - Worksheet index.\n         */\n        deleteWorksheet: (position: number) => void;\n\n        /**\n         * Remove all merged cells.\n         */\n        destroyMerge: () => void;\n\n        /**\n         * Emit an event.\n         * @param event - Event name.\n         * @param args - Arguments that should be passed to the event.\n         */\n        dispatch: (event: string, ...args: any[]) => any;\n\n        /**\n         * Simulates the action of the \"arrow down\" key.\n         * @param shiftKey - If true, the method simulates the action of the \"arrow down\" key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the \"arrow down\" key while the Ctrl key is pressed.\n         */\n        down: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * Get the current data as a CSV file.\n         * @param includeHeaders - If true, include the header regardless of the {@link SpreadsheetOptions.includeHeadersOnDownload} property value.\n         * @param processed - If true, the result will contain the displayed cell values. Otherwise, the result will contain the actual cell values.\n         */\n        download: (includeHeaders?: boolean, processed?: boolean) => void;\n\n        /**\n         * Stores information about the row or column being moved.\n         */\n        dragging: null | DragColumnInfo | DragRowInfo;\n\n        /**\n         * Currently open editor information. Respectively the cell whose editor is open, its initial value, its column index and its row index.\n         */\n        edition: null | [HTMLTableCellElement, string, string, string];\n\n        /**\n         * Root HTML element of this worksheet instance.\n         */\n        element: JworksheetInstanceElement;\n\n        /**\n         * Execute a formula.\n         * @param expression - Formula to be executed.\n         * @param x - Column index of the cell where the formula is.\n         * @param y - Row index of the cell where the formula is.\n         */\n        executeFormula: (expression: string, x?: number, y?: number) => any;\n\n        /**\n         * Table row containing filter inputs.\n         */\n        filter: null | HTMLTableRowElement;\n\n        /**\n         * Active filters.\n         */\n        filters: (string[] | null)[];\n\n        /**\n         * Simulates the action of the Home key.\n         * @param shiftKey - If true, the method simulates the action of the Home key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the Home key while the Ctrl key is pressed.\n         */\n        first: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * List of formulas that are used within other formulas. Each key is the name of a cell containing a formula, and each value is a list of cells whose formulas use the cell specified in the key.\n         */\n        formula: Record<string, string[]>;\n\n        /**\n         * Get cell DOM element by cell name.\n         * @param cell - Cell name.\n         */\n        getCell(cell: string): HTMLTableCellElement;\n\n        /**\n         * Get cell DOM element by cell coords.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         */\n        getCell(x: number, y: number): HTMLTableCellElement;\n\n        /**\n         * Get cell DOM element by cell coordinates.\n         * @param x - Column index of the cell.\n         * @param y - Row index of the cell.\n         */\n        getCellFromCoords: (x: number, y: number) => HTMLTableCellElement;\n\n        /**\n         * Get the data from one column by its index.\n         * @param columnNumber - Column index.\n         * @param processed - If true, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the {@link WorksheetOptions.data} property. Default: false.\n         */\n        getColumnData: (columnNumber: number, processed?: boolean) => CellValue[];\n\n        /**\n         * Get comments from one or all cells.\n         * @param cell - Cell name. If it is a falsy value, the comments of all cells are returned.\n         */\n        getComments: (cell?: string) => Record<string, string> | string;\n\n        /**\n         * Get worksheet config information.\n         */\n        getConfig: () => WorksheetOptions;\n\n        /**\n         * Get the full or partial table data.\n         * @param highlighted - If true, get only data from highlighted cells. If false, get data from all cells. Default: false.\n         * @param processed - If false, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the {@link WorksheetOptions.data} property. Default: false.\n         * @param delimiter - Column delimiter. If this property is specified, the result will be formatted like a csv.\n         * @param asJson - If this property is true, the result will be formatted as json.\n         */\n        getData: (highlighted?: boolean, processed?: boolean, delimiter?: string, asJson?: boolean) => CellValue[][];\n\n        /**\n         * Get data from a range.\n         * @param range - Range of cells whose values ​​are to be returned.\n         * @param processed - If true, the method returns the values ​​of the HTML elements of the cells. Otherwise, the method returns the values ​​of the cells in the options.data array.\n         */\n        getDataFromRange: (range: string, processed: true) => CellValue[][];\n\n        /**\n         * Get the column title.\n         * @param column - Column index.\n         */\n        getHeader: (column: number) => string;\n\n        /**\n         * Get all header titles.\n         * @param asArray - If true, returns the items in an array, if false, returns them separated by \";\" within a single string.\n         */\n        getHeaders: (asArray?: boolean) => string | string[];\n\n        /**\n         * Get height of all rows.\n         */\n        getHeight(row?: undefined): string[];\n\n        /**\n         * Get height of one row.\n         * @param row - Column index.\n         */\n        getHeight(row: number): string;\n\n        /**\n         * Get the coordinates of the highlighted selections.\n         */\n        getHighlighted: () => [number, number, number, number][];\n\n        /**\n         * Get the innerHTML of a cell.\n         * @param cell - Cell name.\n         */\n        getLabel(cell: string): string;\n\n        /**\n         * Get the innerHTML of a cell.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         */\n        getLabel(x: number, y: number): string;\n\n        /**\n         * Get information from one or all merged cells\n         * @param cellName - Cell name. If it is a falsy value, it returns the information of all merges. If the given cell is not the anchor of a merge, it returns null.\n         */\n        getMerge(cellName?: string): Record<string, [number, number]> | [number, number] | null;\n\n        /**\n         * Get meta information from one or all cells.\n         * @param cell - Cell name. If it is a falsy value, the metadata of all cells is returned.\n         */\n        getMeta: (cell?: string) => any;\n\n        /**\n         * Get the range description of the selected cells.\n         */\n        getRange: () => string;\n\n        /**\n         * Get data from a row by its index.\n         * @param rowNumber - Row index.\n         * @param processed - If true, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the {@link WorksheetOptions.data} property. Default: false.\n         */\n        getRowData: (rowNumber: number, processed?: boolean) => CellValue[] | undefined;\n\n        /**\n         * Get information from selected cells in the worksheet.\n         * @param columnNameOnly - If true, the method returns the names of the selected cells. Otherwise, the method returns the records of the selected cells.\n         */\n        getSelected: (columnNameOnly?: boolean) =>\n            | {\n                  element: HTMLTableCellElement[][];\n                  x: number;\n                  y: number;\n              }[]\n            | string[];\n\n        /**\n         * Get indexes of the columns that have highlighted cells.\n         * @param visibleOnly - If true, the method returns only visible columns.\n         */\n        getSelectedColumns: (visibleOnly?: boolean) => number[];\n\n        /**\n         * Get indexes of the rows that have highlighted cells.\n         * @param visibleOnly - If true, the method returns only visible rows.\n         */\n        getSelectedRows: (visibleOnly?: boolean) => number[];\n\n        /**\n         * Get the coordinates of the range that is selected in the worksheet.\n         */\n        getSelection: () => [number, number, number, number];\n\n        /**\n         * Get styles from one or all cells.\n         * @param cell - Name or coordinate of a cell. If omitted, returns styles for all cells.\n         * @param key - Style property. if specified, returns only that property. Otherwise, it returns all the cell's style properties.\n         */\n        getStyle: (cell?: string | [number, number], key?: string) => string | Record<string, string>;\n\n        /**\n         * Get the value of a cell.\n         * @param cell - Cell name.\n         * @param processedValue - If true, it returns the cell's innerHTML. Otherwise, it returns the value of the cell in the {@link WorksheetOptions.data} property.\n         */\n        getValue: (cell: string, processedValue?: boolean) => CellValue | null;\n\n        /**\n         * Get the value of a cell by its coordinates.\n         * @param x - Column index.\n         * @param y - Row index.\n         * @param processedValue - If true, it returns the cell's innerHTML. Otherwise, it returns the value of the cell in the {@link WorksheetOptions.data} property.\n         */\n        getValueFromCoords: (x: number, y: number, processedValue?: boolean) => CellValue | null;\n\n        /**\n         * Get the width of one or all columns.\n         * @param column - Index of the column. If omitted, returns the widths of all columns.\n         */\n        getWidth: (column?: number) => number | (number | string)[];\n\n        /**\n         * Get the index of the currently active worksheet\n         */\n        getWorksheetActive: () => number;\n\n        /**\n         * @deprecated\n         */\n        hashString: null | number;\n\n        /**\n         * HTML element that corresponds to the header row.\n         */\n        headerContainer: HTMLTableRowElement;\n\n        /**\n         * List of cells that make up the header.\n         */\n        headers: HTMLTableCellElement[];\n\n        /**\n         * Hide a column.\n         * @param colNumber - Column indexes.\n         */\n        hideColumn: (colNumber: number | number[]) => void;\n\n        /**\n         * Hide row count column.\n         */\n        hideIndex: () => void;\n\n        /**\n         * Hide Row.\n         * @param rowNumber - Row indexes.\n         */\n        hideRow: (rowNumber: number | number[]) => void;\n\n        /**\n         * List of highlighted cells.\n         */\n        highlighted: {\n            element: HTMLTableCellElement;\n            x: number;\n            y: number;\n        }[];\n\n        /**\n         * List of actions performed on the worksheet.\n         */\n        history: HistoryRecord[];\n\n        /**\n         * Current position of the {@link WorksheetInstance.history} property. Used to control movement through history.\n         */\n        historyIndex: number;\n\n        /**\n         * If true, the \"setHistory\" method does not create new records in the history.\n         */\n        ignoreHistory: boolean;\n\n        /**\n         * Check if a cell is within the current selection.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         */\n        isSelected: (x: number, y: number) => boolean;\n\n        /**\n         * Insert one or more columns.\n         *\n         * This method returns false if the {@link SpreadsheetOptions.onbeforeinsertcolumn} event returns false or if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.\n         * @param mixed - Number of columns to insert. It can also be an array of values, but in this case, only one column is inserted, whose data is based on the array items. Default: 1.\n         * @param columnNumber - Index of the column used as reference for the insertion. Default: last column.\n         * @param insertBefore - Insert new columns before or after the reference column. Default: false.\n         * @param properties - New column properties.\n         */\n        insertColumn: (mixed?: number | CellValue[], columnNumber?: number, insertBefore?: boolean, properties?: Column[]) => false | undefined;\n\n        /**\n         * Insert one or more rows.\n         *\n         * This method returns false if the {@link SpreadsheetOptions.onbeforeinsertrow} event returns false or if the \"This action will destroy any existing merged cells. Are you sure?\" or \"This action will clear your search results. Are you sure?\" dialogs receive a negative response.\n         * @param mixed - Number of rows to insert. It can also be an array of values, but in this case, only one row is inserted, whose data is based on the array items. Default: 1.\n         * @param rowNumber - Index of the row used as reference for the insertion. Default: last row.\n         * @param insertBefore - Insert new rows before or after the reference row. Default: false.\n         */\n        insertRow: (mixed?: number | CellValue[], rowNumber?: number, insertBefore?: number) => false | undefined;\n\n        /**\n         * Check if a cell is read only.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         */\n        isReadOnly(x: number, y: number): boolean;\n\n        /**\n         * Check if a cell is read only.\n         * @param cellName - Cell name.\n         */\n        isReadOnly(cellName: string): boolean;\n\n        /**\n         * Simulates the action of the End key.\n         * @param shiftKey - If true, the method simulates the action of the End key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the End key while the Ctrl key is pressed.\n         */\n        last: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * Simulates the action of the \"arrow left\" key.\n         * @param shiftKey - If true, the method simulates the action of the \"arrow left\" key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the \"arrow left\" key while the Ctrl key is pressed.\n         */\n        left: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * Move a column.\n         *\n         * This method returns false if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.\n         * @param o - Column index.\n         * @param d - New column index.\n         */\n        moveColumn: (o: number, d: number) => false | undefined;\n\n        /**\n         * Move a row.\n         *\n         * This method returns false if the \"This action will destroy any existing merged cells. Are you sure?\" or \"This action will clear your search results. Are you sure?\" dialogs receive a negative response.\n         * @param o - Row index.\n         * @param d - New row index.\n         */\n        moveRow: (o: number, d: number) => false | undefined;\n\n        /**\n         * Start the edition for one cell.\n         * @param cell - Cell HTML Element.\n         * @param empty - If true, the editor opens without content even if the cell had content.\n         * @param event - Js event that triggered the editor opening. This argument is passed to the \"openEditor\" method of custom editors.\n         */\n        openEditor: (cell: HTMLTableCellElement, empty?: boolean, event?: KeyboardEvent | MouseEvent | TouchEvent) => void;\n\n        /**\n         * Open the column filter.\n         *\n         * This method only runs if the {@link WorksheetOptions.filters} property is true.\n         * @param columnId - Column index.\n         */\n        openFilter: (columnId: number) => void;\n\n        /**\n         * Open the worksheet by index.\n         * @param position - Worksheet index.\n         */\n        openWorksheet: (position: number) => void;\n\n        /**\n         * Spreadsheet settings.\n         */\n        options: WorksheetOptions;\n\n        /**\n         * Reorder rows based on values in a column.\n         *\n         * This method returns false if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response, or returns true if the sort is successful.\n         * @param column - Column index. If the value of this parameter is less than 0, the method returns false and does not perform sorting.\n         * @param order - Sorting direction. 0 for ascending and 1 for descending.\n         */\n        orderBy: (column: number, order: 0 | 1) => boolean | undefined;\n\n        /**\n         * Go to page. Valid only when {@link WorksheetOptions.pagination} is true.\n         * @param pageNumber - Page number (starting at 0).\n         */\n        page: (pageNumber: number) => void;\n\n        /**\n         * Current spreadsheet page.\n         */\n        pageNumber: undefined | number;\n\n        /**\n         * Div with pagination controls.\n         */\n        pagination: HTMLDivElement;\n\n        /**\n         * Spreadsheet of which this worksheet is part.\n         */\n        parent: SpreadsheetInstance;\n\n        /**\n         * Pastes content into one or more cells.\n         * @param x - Column index of the cell from which the content will be pasted.\n         * @param y - Row index of the cell from which the content will be pasted.\n         * @param data - Content to be pasted.\n         */\n        paste: (x: number, y: number, data: string) => false | undefined;\n\n        /**\n         * Get the number of pages of the worksheet.\n         */\n        quantiyOfPages: () => number;\n\n        /**\n         * List of HTML elements representing table cells.\n         */\n        records: {\n            element: HTMLTableCellElement;\n            x: number;\n            y: number;\n        }[][];\n\n        /**\n         * Redo previously undone action\n         */\n        redo: () => void;\n\n        /**\n         * Remove a merge.\n         * @param cellName - Merge anchor cell.\n         * @param data - Data to be placed in cells released from the merge.\n         */\n        removeMerge: (cellName: string, data?: CellValue[]) => void;\n\n        /**\n         * Reset all filters.\n         */\n        resetFilters: () => void;\n\n        /**\n         * Reset search\n         */\n        resetSearch: () => void;\n\n        /**\n         * Reset highlighted cell selection.\n         * @returns If there were highlighted cells, it returns 1, otherwise it returns 0.\n         */\n        resetSelection: () => 0 | 1;\n\n        /**\n         * Reset styles of one or more cells.\n         * @param o - Object whose keys are the names of the cells that must have their styles reset.\n         * @param ignoreHistoryAndEvents - If true, do not add this action to history.\n         */\n        resetStyle: (o: Record<string, any>, ignoreHistoryAndEvents?: boolean) => void;\n\n        /**\n         * Information about the row or column currently being resized.\n         */\n        resizing: undefined | null | ResizeRowInfo | ResizeColumnInfo;\n\n        /**\n         * Indices of the rows that include the searched text.\n         */\n        results: null | number[];\n\n        /**\n         * Simulates the action of the \"arrow right\" key.\n         * @param shiftKey - If true, the method simulates the action of the \"arrow right\" key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the \"arrow right\" key while the Ctrl key is pressed.\n         */\n        right: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * List of rows that make up the table.\n         */\n        rows: {\n            element: HTMLTableRowElement;\n            y: number;\n        }[];\n\n        /**\n         * Search for some text.\n         * @param query - Text to be searched.\n         */\n        search: (query: string) => void;\n\n        /**\n         * Text field used to perform searches.\n         */\n        searchInput: HTMLInputElement;\n\n        /**\n         * Select all table cells.\n         */\n        selectAll: () => void;\n\n        /**\n         * Current selection coordinates.\n         *\n         * The array is composed respectively by the indices of the leftmost column of the selection [0], the topmost row of the selection [1], the rightmost column of the selection [2] and the bottommost row selection [3].\n         */\n        selectedCell: undefined | null | [number, number, number, number] | [string, string, string, string];\n\n        selectedContainer: undefined | null | [number, number, number, number];\n\n        /**\n         * Cells that currently have \"autocomplete selection\".\n         */\n        selection: HTMLTableCellElement[];\n\n        /**\n         * Set the data from one column by index.\n         * @param colNumber - Column index.\n         * @param data - New data. Positions with the null value are not changed in the table.\n         * @param force - If true, the method also changes the contents of readonly columns.\n         */\n        setColumnData: (colNumber: number, data: (CellValue | null)[], force?: boolean) => void;\n\n        /**\n         * Set or remove a comment.\n         * @param cellId - Name of the cell.\n         * @param comments - New comment. If it is a falsy value, the method just uncomments the cell.\n         */\n        setComments(cellId: string, comments: string): void;\n\n        /**\n         * Set or remove comments.\n         * @param cellId - Object whose keys are cell names and values ​​are cell comments. If the value of a key is a falsy value, the cell comment is removed.\n         */\n        setComments(cellId: Record<string, string>): void;\n\n        /**\n         * Change the worksheet or spreadsheet settings.\n         * @param config - New settings.\n         * @param spreadsheetLevel - If true, the settings are applied to the spreadsheet. If not, they are applied to the worksheet.\n         */\n        setConfig: (config: SpreadsheetOptions, spreadsheetLevel?: boolean) => void;\n\n        /**\n         * Set data.\n         * @param data - New data. It can be an array of cell values or an array of objects whose values are cell values.\n         */\n        setData: (data?: CellValue[][] | Record<string, CellValue>[]) => void;\n\n        /**\n         * Set a column title.\n         * @param column - Column index.\n         * @param newValue - New title. Empty string or undefined to reset the header title.\n         */\n        setHeader: (column: number, newValue?: string) => void;\n\n        /**\n         * Change row height.\n         * @param row - Row index.\n         * @param height - New height. An integer greater than zero.\n         */\n        setHeight: (row: number, height: number) => void;\n\n        /**\n         * Merge cells.\n         * @param cellName - Name of a cell. If it is a falsy value, this method merges the selected cells in the table and ignores all parameters of this method.\n         * @param colspan - Number of columns this merge occupies.\n         * @param rowspan - Number of rows this merge occupies.\n         * @returns If the \"cellName\" parameter is a falsy value, and there are no cells selected in the table, this method returns null.\n         */\n        setMerge: (cellName?: string, colspan?: number, rowspan?: number) => null | undefined;\n\n        /**\n         * Set a property on a cell's meta information.\n         * @param o - Cell name.\n         * @param k - Property name.\n         * @param v - Property value.\n         */\n        setMeta(o: string, k: string, v: string): void;\n\n        /**\n         * Remove current and define new meta information for one or more cells.\n         * @param o - Object with the new meta information.\n         */\n        setMeta(o: Record<string, Record<string, any>>): void;\n\n        /**\n         * Change the read only state of a cell.\n         * @param cell - Cell HTML element or its name.\n         * @param state - New read only state.\n         */\n        setReadOnly: (cell: string | HTMLTableCellElement, state: boolean) => void;\n\n        /**\n         * Set a row data by index.\n         * @param rowNumber - Row index.\n         * @param data - New data. Positions with the null value are not changed in the table.\n         * @param force - If true, the method also changes the contents of readonly columns.\n         */\n        setRowData: (rowNumber: number, data: (CellValue | null)[], force?: boolean) => void;\n\n        /**\n         * Change a single style of one or more cells.\n         * @param o - Name of a cell.\n         * @param k - property to be changed.\n         * @param v - New property value. If equal to the property's current value and the \"force\" parameter is false, removes that property from the style.\n         * @param force - If true, changes the value of the property even if the cell is read-only. Also, if true, even if the new value of the property is the same as the current one, the property is not removed.\n         */\n        setStyle(o: string, k: string, v: string, force?: boolean): void;\n\n        /**\n         * Change cell styles.\n         * @param o - Object where each key is the name of a cell and each value is the style changes for that cell. Each value can be a string with css styles separated by semicolons or an array where each item is a string with a css style.\n         * @param k - It is not used.\n         * @param v - It is not used.\n         * @param force - If true, changes the value of the property even if the cell is read-only. Also, if true, even if the new value of the property is the same as the current one, the property is not removed.\n         */\n        setStyle(o: Record<string, string | string[]>, k?: null | undefined, v?: null | undefined, force?: boolean): void;\n\n        /**\n         * Change the value of one or more cells.\n         * @param cell - Name of a cell, HTML element that represents a cell or an array whose items can be any of the previous alternatives or objects. When an array item is an object, it must have the cell coordinates (\"x\" and \"y\") and can have the cell's new value (\"value\"), but if does not have it, the \"value\" parameter is used instead.\n         * @param value - New cell value.\n         * @param force - If true, changes the value of even read-only cells.\n         */\n        setValue: (\n            cell: string | HTMLTableCellElement | (string | { x: number; y: number; value?: CellValue } | HTMLTableCellElement)[],\n            value?: CellValue,\n            force?: boolean\n        ) => void;\n\n        /**\n         * Set a cell value based on its coordinates.\n         * @param x - Cell column index.\n         * @param y - Cell row index.\n         * @param value - New value.\n         * @param force - If true, changes the value of even read-only cells.\n         */\n        setValueFromCoords: (x: number, y: number, value: CellValue, force?: boolean) => void;\n\n        /**\n         * Set the width of a column.\n         * @param column - Column index.\n         * @param width - New width.\n         */\n        setWidth(column: number, width: number): void;\n\n        /**\n         * Set the width of one or more columns.\n         * @param column - Column indexes.\n         * @param width - New widths.\n         */\n        setWidth(column: number[], width: number | number[]): void;\n\n        /**\n         * Show hidden column.\n         * @param colNumber - Column index.\n         */\n        showColumn: (colNumber: number | number[]) => void;\n\n        /**\n         * Show row count column.\n         */\n        showIndex: () => void;\n\n        /**\n         * Show hidden row.\n         * @param rowNumber - Row index.\n         */\n        showRow: (rowNumber: number | number[]) => void;\n\n        /**\n         * Styles of the cells that were copied.\n         */\n        style: string[];\n\n        /**\n         * HTML table tag of this jss instance.\n         */\n        table: HTMLTableElement;\n\n        /**\n         * HTML tbody tag of this jss instance.\n         */\n        tbody: HTMLTableSectionElement;\n\n        /**\n         * HTML textarea tag used internally when copying cells.\n         */\n        textarea: HTMLTextAreaElement;\n\n        /**\n         * HTML thead tag of this jss instance.\n         */\n        thead: HTMLTableSectionElement;\n\n        /**\n         * Undo last action.\n         */\n        undo: () => void;\n\n        /**\n         * Simulates the action of the \"arrow up\" key.\n         * @param shiftKey - If true, the method simulates the action of the \"arrow up\" key while the Shift key is pressed.\n         * @param ctrlKey - If true, the method simulates the action of the \"arrow up\" key while the Ctrl key is pressed.\n         */\n        up: (shiftKey?: boolean, ctrlKey?: boolean) => void;\n\n        /**\n         * Select cells.\n         * @param x1 - Column index of the first cell of the selection. If omitted or null, rows \"y1\" through \"y2\" are selected.\n         * @param y1 - Row index of the first cell of the selection. If omitted or null, columns \"x1\" through \"x2\" are selected.\n         * @param x2 - Column index of the last cell of the selection. Default: Parameter \"x1\".\n         * @param y2 - Row index of the last cell of the selection. Default: Parameter \"y1\".\n         */\n        updateSelectionFromCoords: (x1: number | null, y1: number | null, x2?: number | null, y2?: number | null) => false | undefined;\n\n        /**\n         * Get the page index of a row.\n         * @param cell - Row index.\n         */\n        whichPage: (cell: number) => number;\n    }\n\n    type Version = () => {\n        host: string;\n        license: string;\n        print: () => [string];\n        type: string;\n        version: string;\n    };\n\n    interface JssHelpers {\n        /**\n         * Extract the configuration to create a new spreadsheet from a static HTML element.\n         * @param element - Table element.\n         * @param options - Worksheet options.\n         */\n        createFromTable: (element: HTMLTableElement, options: WorksheetOptions) => WorksheetOptions;\n\n        /**\n         * Internal method.\n         */\n        getCaretIndex: (e: any) => number;\n\n        /**\n         * Get the column letter(s) based on its index.\n         * @param i - Column index.\n         */\n        getColumnName: (i: number) => string;\n\n        /**\n         * Get \"A1\" style coordinates based on column and row indices.\n         * @param x - Column index.\n         * @param y - Row index.\n         */\n        getCellNameFromCoords: (x: number, y: number) => string;\n\n        /**\n         * Get column and row indices based on coordinate in \"A1\" style.\n         * @param columnName - Coordinate in \"A1\" style.\n         */\n        getCoordsFromCellName: (columnName: string) => [number, number | null] | undefined;\n\n        /**\n         * Get coordinates from a range.\n         * @param range - Range in \"A1:B2\" style.\n         * @returns Array filled with the x and y coordinates of the first and last cells in the range.\n         */\n        getCoordsFromRange: (range: string) => [number, number, number, number];\n\n        /**\n         * Internal method.\n         */\n        invert: (o: object) => any[] & Record<string, any>;\n\n        /**\n         * Parse CSV string to JS array.\n         * @param str - Text in csv format.\n         * @param delimiter - Csv delimiter.\n         */\n        parseCSV: (str: string, delimiter?: string) => string[][];\n    }\n\n    interface JSpreadsheet {\n        (element: HTMLDivElement | HTMLTableElement, options: SpreadsheetOptions): WorksheetInstance[];\n\n        /**\n         * Current instance of jss.\n         */\n        current: null | WorksheetInstance;\n\n        /**\n         * Destroy an instance of jss.\n         * @param element - Root element of jss instance.\n         * @param destroyEventHandlers - Remove event listeners. Default: false.\n         */\n        destroy: (element: JspreadsheetInstanceElement, destroyEventHandlers?: boolean) => void;\n\n        /**\n         * Destroy all instances of jss.\n         */\n        destroyAll: () => void;\n\n        /**\n         * Get a worksheet instance by name and namespace.\n         * @param worksheetName - Name of the searched worksheet. If null or undefined, the method returns the found namespace.\n         * @param namespace - Namespace name.\n         */\n        getWorksheetInstanceByName: (worksheetName: string | null | undefined, namespace: string) => WorksheetInstance | Record<string, WorksheetInstance>;\n\n        helpers: JssHelpers;\n\n        /**\n         * Internal method.\n         */\n        isMouseAction: boolean;\n\n        /**\n         * Defines translations.\n         * @param o - Translations.\n         */\n        setDictionary: (o: Record<string, string>) => void;\n\n        spreadsheet: SpreadsheetInstance[];\n\n        /**\n         * Internal method.\n         */\n        timeControl: null | number;\n\n        /**\n         * Internal method.\n         */\n        timeControlLoading: null | number;\n\n        /**\n         * Basic version information.\n         */\n        version: Version;\n\n        [key: string]: any;\n    }\n}\n"
  },
  {
    "path": "dist/index.js",
    "content": "if (! jSuites && typeof(require) === 'function') {\n    var jSuites = require('jsuites');\n}\n\nif (! formula && typeof(require) === 'function') {\n    var formula = require('@jspreadsheet/formula');\n}\n\n;(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    global.jspreadsheet = factory();\n}(this, (function () {\n\nvar jspreadsheet;(function(){\"use strict\";var __webpack_modules__={805:function(e,t){const s=function(e){const t=this,s=[];for(let n=0;n<e.length;n++){const o=e[n].x,r=e[n].y,l=t.options.columns[o].name?t.options.columns[o].name:o;s[r]||(s[r]={row:r,data:{}}),s[r].data[l]=e[n].value}return s.filter((function(e){return null!=e}))},n=function(e,t){const s=this,n=o.call(s.parent,\"onbeforesave\",s.parent,s,t);if(n)t=n;else if(!1===n)return!1;jSuites.ajax({url:e,method:\"POST\",dataType:\"json\",data:{data:JSON.stringify(t)},success:function(e){o.call(s,\"onsave\",s.parent,s,t)}})},o=function(e){const t=this;let o=null,r=t.parent?t.parent:t;if(!r.ignoreEvents&&(\"function\"==typeof r.config.onevent&&(o=r.config.onevent.apply(this,arguments)),\"function\"==typeof r.config[e]&&(o=r.config[e].apply(this,Array.prototype.slice.call(arguments,1))),\"object\"==typeof r.plugins)){const e=Object.keys(r.plugins);for(let t=0;t<e.length;t++){const s=e[t],n=r.plugins[s];\"function\"==typeof n.onevent&&(o=n.onevent.apply(this,arguments))}}if(\"onafterchanges\"==e){const e=arguments;if(\"object\"==typeof r.plugins&&Object.entries(r.plugins).forEach((function([,s]){\"function\"==typeof s.persistence&&s.persistence(t,\"setValue\",{data:e[2]})})),t.options.persistence){const e=1==t.options.persistence?t.options.url:t.options.persistence,o=s.call(t,arguments[2]);n.call(t,e,o)}}return o};t.A=o},829:function(e,t,s){s.d(t,{F8:function(){return l},N$:function(){return r},dr:function(){return i}});var n=s(530),o=s(657);const r=function(e){const t=this;if(t.options.filters){e=parseInt(e),t.resetSelection();let s=[];if(\"checkbox\"==t.options.columns[e].type)s.push({id:\"true\",name:\"True\"}),s.push({id:\"false\",name:\"False\"});else{const n=[];let o=!1;for(let s=0;s<t.options.data.length;s++){const r=t.options.data[s][e],l=t.records[s][e].element.innerHTML;r&&l?n[r]=l:o=!0}const r=Object.keys(n);s=[];for(let e=0;e<r.length;e++)s.push({id:r[e],name:n[r[e]]});o&&s.push({value:\"\",id:\"\",name:\"(Blanks)\"})}const n=document.createElement(\"div\");t.filter.children[e+1].innerHTML=\"\",t.filter.children[e+1].appendChild(n),t.filter.children[e+1].style.paddingLeft=\"0px\",t.filter.children[e+1].style.paddingRight=\"0px\",t.filter.children[e+1].style.overflow=\"initial\";const r={data:s,multiple:!0,autocomplete:!0,opened:!0,value:void 0!==t.filters[e]?t.filters[e]:null,width:\"100%\",position:1==t.options.tableOverflow||1==t.parent.config.fullscreen,onclose:function(s){i.call(t),t.filters[e]=s.dropdown.getValue(!0),t.filter.children[e+1].innerHTML=s.dropdown.getText(),t.filter.children[e+1].style.paddingLeft=\"\",t.filter.children[e+1].style.paddingRight=\"\",t.filter.children[e+1].style.overflow=\"\",l.call(t,e),o.G9.call(t)}};jSuites.dropdown(n,r)}else console.log(\"Jspreadsheet: filters not enabled.\")},l=function(e){const t=this;if(!e)for(let s=0;s<t.filter.children.length;s++)t.filters[s]&&(e=s);const s=function(e,s,n){for(let o=0;o<e.length;o++){const r=\"\"+t.options.data[n][s],l=\"\"+t.records[n][s].element.innerHTML;if(e[o]==r||e[o]==l)return!0}return!1},o=t.filters[e];t.results=[];for(let n=0;n<t.options.data.length;n++)s(o,e,n)&&t.results.push(n);t.results.length||(t.results=null),n.hG.call(t)},i=function(){const e=this;if(e.options.filters)for(let t=0;t<e.filter.children.length;t++)e.filter.children[t].innerHTML=\"&nbsp;\",e.filters[t]=null;e.results=null,n.hG.call(e)}},160:function(e,t,s){s.d(t,{e:function(){return o}});var n=s(530);const o=function(e){const t=this;if(e&&(t.options.footers=e),t.options.footers){t.tfoot||(t.tfoot=document.createElement(\"tfoot\"),t.table.appendChild(t.tfoot));for(let e=0;e<t.options.footers.length;e++){let s;if(t.tfoot.children[e])s=t.tfoot.children[e];else{s=document.createElement(\"tr\");const e=document.createElement(\"td\");s.appendChild(e),t.tfoot.appendChild(s)}for(let o=0;o<t.headers.length;o++){let r;if(t.options.footers[e][o]||(t.options.footers[e][o]=\"\"),t.tfoot.children[e].children[o+1])r=t.tfoot.children[e].children[o+1];else{r=document.createElement(\"td\"),s.appendChild(r);const e=t.options.columns[o].align||t.options.defaultColAlign||\"center\";r.style.textAlign=e}r.textContent=n.$x.call(t,+t.records.length+o,e,t.options.footers[e][o]),r.style.display=t.cols[o].colElement.style.display}}}}},296:function(e,t,s){s.d(t,{w:function(){return n}});const n=function(){const e=this;let t=0;if(e.options.freezeColumns>0)for(let s=0;s<e.options.freezeColumns;s++){let n;n=e.options.columns&&e.options.columns[s]&&void 0!==e.options.columns[s].width?parseInt(e.options.columns[s].width):void 0!==e.options.defaultColWidth?parseInt(e.options.defaultColWidth):100,t+=n}return t}},978:function(e,t,s){s.r(t),s.d(t,{createFromTable:function(){return u},getCaretIndex:function(){return o},getCellNameFromCoords:function(){return i},getColumnName:function(){return l},getCoordsFromCellName:function(){return a},getCoordsFromRange:function(){return c},invert:function(){return r},parseCSV:function(){return d}});var n=s(689);const o=function(e){let t;t=this.config.root?this.config.root:window;let s=0;const n=t.getSelection();if(n&&0!==n.rangeCount){const t=n.getRangeAt(0),o=t.cloneRange();o.selectNodeContents(e),o.setEnd(t.endContainer,t.endOffset),s=o.toString().length}return s},r=function(e){const t=[],s=Object.keys(e);for(let n=0;n<s.length;n++)t[e[s[n]]]=s[n];return t},l=function(e){let t,s=e+1,n=\"\";for(;s>0;)t=(s-1)%26,n=String.fromCharCode(65+t).toString()+n,s=parseInt((s-t)/26);return n},i=function(e,t){return l(parseInt(e))+(parseInt(t)+1)},a=function(e){const t=/^[a-zA-Z]+/.exec(e);if(t){let s=0;for(let e=0;e<t[0].length;e++)s+=parseInt(t[0].charCodeAt(e)-64)*Math.pow(26,t[0].length-1-e);s--,s<0&&(s=0);let n=parseInt(/[0-9]+$/.exec(e))||null;return n>0&&n--,[s,n]}},c=function(e){const[t,s]=e.split(\":\");return[...a(t),...a(s)]},d=function(e,t){t=t||\",\",e=e.replace(/\\r?\\n$|\\r$|\\n$/g,\"\");const s=[];let n=!1,o=0,r=0,l=0;for(let i=0;i<e.length;i++){const a=e[i],c=e[i+1];s[r]=s[r]||[],s[r][l]=s[r][l]||\"\",'\"'==a&&n&&'\"'==c?(s[r][l]+=a,++i):'\"'!=a?a!=t||n?\"\\r\"!=a||\"\\n\"!=c||n?\"\\n\"==a&&!n||\"\\r\"==a&&!n?(++r,o=Math.max(o,l),l=0):s[r][l]+=a:(++r,o=Math.max(o,l),l=0,++i):++l:n=!n}return s.forEach(((e,t)=>{for(let t=e.length;t<=o;t++)e.push(\"\")})),s},u=function(e,t){if(\"TABLE\"==e.tagName){t||(t={}),t.columns=[],t.data=[];const s=e.querySelectorAll(\"colgroup > col\");if(s.length)for(let e=0;e<s.length;e++){let n=s[e].style.width;n||(n=s[e].getAttribute(\"width\")),n&&(t.columns[e]||(t.columns[e]={}),t.columns[e].width=n)}const o=function(e,s){let n=e.getBoundingClientRect();const o=n.width>50?n.width:50;t.columns[s]||(t.columns[s]={}),e.getAttribute(\"data-celltype\")?t.columns[s].type=e.getAttribute(\"data-celltype\"):t.columns[s].type=\"text\",t.columns[s].width=o+\"px\",t.columns[s].title=e.innerHTML,e.style.textAlign&&(t.columns[s].align=e.style.textAlign),(n=e.getAttribute(\"name\"))&&(t.columns[s].name=n),(n=e.getAttribute(\"id\"))&&(t.columns[s].id=n),(n=e.getAttribute(\"data-mask\"))&&(t.columns[s].mask=n)},r=[];let l=e.querySelectorAll(\":scope > thead > tr\");if(l.length){for(let e=0;e<l.length-1;e++){const t=[];for(let s=0;s<l[e].children.length;s++){const n={title:l[e].children[s].textContent,colspan:l[e].children[s].getAttribute(\"colspan\")||1};t.push(n)}r.push(t)}l=l[l.length-1].children;for(let e=0;e<l.length;e++)o(l[e],e)}let i=0;const a={},c={},d={},u={};let p=e.querySelectorAll(\":scope > tr, :scope > tbody > tr\");for(let e=0;e<p.length;e++)if(t.data[i]=[],1!=t.parseTableFirstRowAsHeader||l.length||0!=e){for(let s=0;s<p[e].children.length;s++){let o=p[e].children[s].getAttribute(\"data-formula\");o?\"=\"!=o.substr(0,1)&&(o=\"=\"+o):o=p[e].children[s].innerHTML,t.data[i].push(o);const r=(0,n.t3)([s,e]),l=p[e].children[s].getAttribute(\"class\");l&&(u[r]=l);const c=parseInt(p[e].children[s].getAttribute(\"colspan\"))||0,h=parseInt(p[e].children[s].getAttribute(\"rowspan\"))||0;(c||h)&&(a[r]=[c||1,h||1]),p[e].children[s].style&&\"none\"==p[e].children[s].style.display&&(p[e].children[s].style.display=\"\");const m=p[e].children[s].getAttribute(\"style\");m&&(d[r]=m),p[e].children[s].classList.contains(\"styleBold\")&&(d[r]?d[r]+=\"; font-weight:bold;\":d[r]=\"font-weight:bold;\")}p[e].style&&p[e].style.height&&(c[e]={height:p[e].style.height}),i++}else for(let t=0;t<p[e].children.length;t++)o(p[e].children[t],t);if(Object.keys(r).length>0&&(t.nestedHeaders=r),Object.keys(d).length>0&&(t.style=d),Object.keys(a).length>0&&(t.mergeCells=a),Object.keys(c).length>0&&(t.rows=c),Object.keys(u).length>0&&(t.classes=u),p=e.querySelectorAll(\"tfoot tr\"),p.length){const e=[];for(let t=0;t<p.length;t++){let s=[];for(let e=0;e<p[t].children.length;e++)s.push(p[t].children[e].textContent);e.push(s)}Object.keys(e).length>0&&(t.footers=e)}if(1==t.parseTableAutoCellType){const e=[];for(let s=0;s<t.columns.length;s++){let n=!0,o=!0;e[s]=[];for(let r=0;r<t.data.length;r++){const l=t.data[r][s];e[s][l]||(e[s][l]=0),e[s][l]++,l.length>25&&(n=!1),10==l.length&&\"-\"==l.substr(4,1)&&\"-\"==l.substr(7,1)||(o=!1)}const r=Object.keys(e[s]).length;o?t.columns[s].type=\"calendar\":1==n&&r>1&&r<=parseInt(.1*t.data.length)&&(t.columns[s].type=\"dropdown\",t.columns[s].source=Object.keys(e[s]))}}return t}console.log(\"Element is not a table\")}},911:function(e,t,s){s.d(t,{Dh:function(){return c},ZS:function(){return h},tN:function(){return p}});var n=s(805),o=s(689),r=s(530),l=s(910),i=s(94),a=s(657);const c=function(e){const t=this;if(1!=t.ignoreHistory){const s=++t.historyIndex;t.history=t.history=t.history.slice(0,s+1),t.history[s]=e}},d=function(e,t){const s=this,n=t.insertBefore?+t.rowNumber:t.rowNumber+1;if(1==s.options.search&&s.results&&s.results.length!=s.rows.length&&s.resetSearch(),1==e){const e=t.numOfRows;for(let t=n;t<e+n;t++)s.rows[t].element.parentNode.removeChild(s.rows[t].element);s.records.splice(n,e),s.options.data.splice(n,e),s.rows.splice(n,e),a.at.call(s,1,n,e+n-1)}else{const e=t.rowRecords.map((e=>[...e]));s.records=(0,o.Hh)(s.records,n,e);const r=t.rowData.map((e=>[...e]));s.options.data=(0,o.Hh)(s.options.data,n,r),s.rows=(0,o.Hh)(s.rows,n,t.rowNode);let l=0;for(let e=n;e<t.numOfRows+n;e++)s.tbody.insertBefore(t.rowNode[l].element,s.tbody.children[e]),l++}for(let e=n;e<s.rows.length;e++)s.rows[e].y=e;for(let e=n;e<s.records.length;e++)for(let t=0;t<s.records[e].length;t++)s.records[e][t].y=e;s.options.pagination>0&&s.page(s.pageNumber),r.o8.call(s)},u=function(e,t){const s=this,n=t.insertBefore?t.columnNumber:t.columnNumber+1;if(1==e){const e=t.numOfColumns;s.options.columns.splice(n,e);for(let t=n;t<e+n;t++)s.headers[t].parentNode.removeChild(s.headers[t]),s.cols[t].colElement.parentNode.removeChild(s.cols[t].colElement);s.headers.splice(n,e),s.cols.splice(n,e);for(let o=0;o<t.data.length;o++){for(let t=n;t<e+n;t++)s.records[o][t].element.parentNode.removeChild(s.records[o][t].element);s.records[o].splice(n,e),s.options.data[o].splice(n,e)}if(s.options.footers)for(let t=0;t<s.options.footers.length;t++)s.options.footers[t].splice(n,e)}else{s.options.columns=(0,o.Hh)(s.options.columns,n,t.columns),s.headers=(0,o.Hh)(s.headers,n,t.headers),s.cols=(0,o.Hh)(s.cols,n,t.cols);let e=0;for(let o=n;o<t.numOfColumns+n;o++)s.headerContainer.insertBefore(t.headers[e],s.headerContainer.children[o+1]),s.colgroupContainer.insertBefore(t.cols[e].colElement,s.colgroupContainer.children[o+1]),e++;for(let e=0;e<t.data.length;e++){s.options.data[e]=(0,o.Hh)(s.options.data[e],n,t.data[e]),s.records[e]=(0,o.Hh)(s.records[e],n,t.records[e]);let r=0;for(let o=n;o<t.numOfColumns+n;o++)s.rows[e].element.insertBefore(t.records[e][r].element,s.rows[e].element.children[o+1]),r++}if(s.options.footers)for(let e=0;e<s.options.footers.length;e++)s.options.footers[e]=(0,o.Hh)(s.options.footers[e],n,t.footers[e])}for(let e=n;e<s.cols.length;e++)s.cols[e].x=e;for(let e=0;e<s.records.length;e++)for(let t=n;t<s.records[e].length;t++)s.records[e][t].x=t;if(s.options.nestedHeaders&&s.options.nestedHeaders.length>0&&s.options.nestedHeaders[0]&&s.options.nestedHeaders[0][0])for(let n=0;n<s.options.nestedHeaders.length;n++){let o;o=1==e?parseInt(s.options.nestedHeaders[n][s.options.nestedHeaders[n].length-1].colspan)-t.numOfColumns:parseInt(s.options.nestedHeaders[n][s.options.nestedHeaders[n].length-1].colspan)+t.numOfColumns,s.options.nestedHeaders[n][s.options.nestedHeaders[n].length-1].colspan=o,s.thead.children[n].children[s.thead.children[n].children.length-1].setAttribute(\"colspan\",o)}r.o8.call(s)},p=function(){const e=this,t=!!e.parent.ignoreEvents,s=!!e.ignoreHistory;e.parent.ignoreEvents=!0,e.ignoreHistory=!0;const o=[];let r;if(e.historyIndex>=0)if(r=e.history[e.historyIndex--],\"insertRow\"==r.action)d.call(e,1,r);else if(\"deleteRow\"==r.action)d.call(e,0,r);else if(\"insertColumn\"==r.action)u.call(e,1,r);else if(\"deleteColumn\"==r.action)u.call(e,0,r);else if(\"moveRow\"==r.action)e.moveRow(r.newValue,r.oldValue);else if(\"moveColumn\"==r.action)e.moveColumn(r.newValue,r.oldValue);else if(\"setMerge\"==r.action)e.removeMerge(r.column,r.data);else if(\"setStyle\"==r.action)e.setStyle(r.oldValue,null,null,1);else if(\"setWidth\"==r.action)e.setWidth(r.column,r.oldValue);else if(\"setHeight\"==r.action)e.setHeight(r.row,r.oldValue);else if(\"setHeader\"==r.action)e.setHeader(r.column,r.oldValue);else if(\"setComments\"==r.action)e.setComments(r.oldValue);else if(\"orderBy\"==r.action){let t=[];for(let e=0;e<r.rows.length;e++)t[r.rows[e]]=e;i.Th.call(e,r.column,r.order?0:1),i.iY.call(e,t)}else if(\"setValue\"==r.action){for(let t=0;t<r.records.length;t++)o.push({x:r.records[t].x,y:r.records[t].y,value:r.records[t].oldValue}),r.oldStyle&&e.resetStyle(r.oldStyle);e.setValue(o),r.selection&&e.updateSelectionFromCoords(r.selection[0],r.selection[1],r.selection[2],r.selection[3])}e.parent.ignoreEvents=t,e.ignoreHistory=s,n.A.call(e,\"onundo\",e,r)},h=function(){const e=this,t=!!e.parent.ignoreEvents,s=!!e.ignoreHistory;let o;if(e.parent.ignoreEvents=!0,e.ignoreHistory=!0,e.historyIndex<e.history.length-1)if(o=e.history[++e.historyIndex],\"insertRow\"==o.action)d.call(e,0,o);else if(\"deleteRow\"==o.action)d.call(e,1,o);else if(\"insertColumn\"==o.action)u.call(e,0,o);else if(\"deleteColumn\"==o.action)u.call(e,1,o);else if(\"moveRow\"==o.action)e.moveRow(o.oldValue,o.newValue);else if(\"moveColumn\"==o.action)e.moveColumn(o.oldValue,o.newValue);else if(\"setMerge\"==o.action)l.FU.call(e,o.column,o.colspan,o.rowspan,1);else if(\"setStyle\"==o.action)e.setStyle(o.newValue,null,null,1);else if(\"setWidth\"==o.action)e.setWidth(o.column,o.newValue);else if(\"setHeight\"==o.action)e.setHeight(o.row,o.newValue);else if(\"setHeader\"==o.action)e.setHeader(o.column,o.newValue);else if(\"setComments\"==o.action)e.setComments(o.newValue);else if(\"orderBy\"==o.action)i.Th.call(e,o.column,o.order),i.iY.call(e,o.rows);else if(\"setValue\"==o.action){e.setValue(o.records);for(let t=0;t<o.records.length;t++)o.oldStyle&&e.resetStyle(o.newStyle);o.selection&&e.updateSelectionFromCoords(o.selection[0],o.selection[1],o.selection[2],o.selection[3])}e.parent.ignoreEvents=t,e.ignoreHistory=s,n.A.call(e,\"onredo\",e,o)}},530:function(__unused_webpack_module,__webpack_exports__,__webpack_require__){__webpack_require__.d(__webpack_exports__,{$O:function(){return getWorksheetActive},$x:function(){return parseValue},C6:function(){return showIndex},Em:function(){return executeFormula},P9:function(){return createCell},Rs:function(){return updateScroll},TI:function(){return hideIndex},Xr:function(){return getCellFromCoords},Y5:function(){return fullscreen},am:function(){return updateTable},dw:function(){return isFormula},eN:function(){return getWorksheetInstance},hG:function(){return updateResult},ju:function(){return createNestedHeader},k9:function(){return updateCell},o8:function(){return updateTableReferences},p9:function(){return getLabel},rS:function(){return getMask},tT:function(){return getCell},xF:function(){return updateFormulaChain},yB:function(){return updateFormula}});var _dispatch_js__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(805),_selection_js__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(657),_helpers_js__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(978),_meta_js__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(654),_freeze_js__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(296),_pagination_js__WEBPACK_IMPORTED_MODULE_7__=__webpack_require__(167),_footer_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(160),_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(689);const updateTable=function(){const e=this;if(e.options.minSpareRows>0){let t=0;for(let s=e.rows.length-1;s>=0;s--){let n=!1;for(let t=0;t<e.headers.length;t++)e.options.data[s][t]&&(n=!0);if(n)break;t++}e.options.minSpareRows-t>0&&e.insertRow(e.options.minSpareRows-t)}if(e.options.minSpareCols>0){let t=0;for(let s=e.headers.length-1;s>=0;s--){let n=!1;for(let t=0;t<e.rows.length;t++)e.options.data[t][s]&&(n=!0);if(n)break;t++}e.options.minSpareCols-t>0&&e.insertColumn(e.options.minSpareCols-t)}e.options.footers&&_footer_js__WEBPACK_IMPORTED_MODULE_0__.e.call(e),e.options.columns.length<e.options.minDimensions[0]&&(e.options.minDimensions[0]=e.options.columns.length),setTimeout((function(){_selection_js__WEBPACK_IMPORTED_MODULE_1__.Aq.call(e)}),0)},parseNumber=function(e,t){const s=t&&this.options.columns[t].decimal?this.options.columns[t].decimal:\".\";let n=\"\"+e;return n=n.split(s),n[0]=n[0].match(/[+-]?[0-9]/g),n[0]&&(n[0]=n[0].join(\"\")),n[1]&&(n[1]=n[1].match(/[0-9]*/g).join(\"\")),n[0]&&Number.isInteger(Number(n[0]))?n[1]?Number(n[0]+\".\"+n[1]):Number(n[0]+\".00\"):null},executeFormula=function(expression,x,y){const obj=this,formulaResults=[],formulaLoopProtection=[],execute=function(expression,x,y){const parentId=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([x,y]);if(formulaLoopProtection[parentId])return console.error(\"Reference loop detected\"),\"#ERROR\";formulaLoopProtection[parentId]=!0;const tokensUpdate=function(e){for(let t=0;t<e.length;t++){const s=[],n=e[t].split(\":\"),o=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(n[0],!0),r=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(n[1],!0);let l,i,a,c;o[0]<=r[0]?(l=o[0],i=r[0]):(l=r[0],i=o[0]),o[1]<=r[1]?(a=o[1],c=r[1]):(a=r[1],c=o[1]);for(let e=a;e<=c;e++)for(let t=l;t<=i;t++)s.push((0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([t,e]));expression=expression.replace(e[t],s.join(\",\"))}};expression=expression.replace(/\\$?([A-Z]+)\\$?([0-9]+)/g,\"$1$2\");let tokens=expression.match(/([A-Z]+[0-9]+):([A-Z]+[0-9]+)/g);if(tokens&&tokens.length&&tokensUpdate(tokens),tokens=expression.match(/([A-Z]+[0-9]+)/g),tokens&&tokens.indexOf(parentId)>-1)return console.error(\"Self Reference detected\"),\"#ERROR\";{const formulaExpressions={};if(tokens)for(let i=0;i<tokens.length;i++)if(obj.formula[tokens[i]]||(obj.formula[tokens[i]]=[]),obj.formula[tokens[i]].indexOf(parentId)<0&&obj.formula[tokens[i]].push(parentId),eval(\"typeof(\"+tokens[i]+') == \"undefined\"')){const e=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(tokens[i],1);let t;if(t=void 0!==obj.options.data[e[1]]&&void 0!==obj.options.data[e[1]][e[0]]?obj.options.data[e[1]][e[0]]:\"\",\"=\"==(\"\"+t).substr(0,1)&&(void 0!==formulaResults[tokens[i]]?t=formulaResults[tokens[i]]:(t=execute(t,e[0],e[1]),formulaResults[tokens[i]]=t)),\"\"==(\"\"+t).trim())formulaExpressions[tokens[i]]=null;else if(t==Number(t)&&0!=obj.parent.config.autoCasting)formulaExpressions[tokens[i]]=Number(t);else{const s=parseNumber.call(obj,t,e[0]);0!=obj.parent.config.autoCasting&&s?formulaExpressions[tokens[i]]=s:formulaExpressions[tokens[i]]='\"'+t+'\"'}}const ret=_dispatch_js__WEBPACK_IMPORTED_MODULE_3__.A.call(obj,\"onbeforeformula\",obj,expression,x,y);if(!1===ret)return expression;let res;ret&&(expression=ret);try{res=formula(expression.substr(1),formulaExpressions,x,y,obj),\"function\"==typeof res&&(res=\"#ERROR\")}catch(e){res=\"#ERROR\",!0===obj.parent.config.debugFormulas&&console.log(expression.substr(1),formulaExpressions,e)}return res}};return execute(expression,x,y)},parseValue=function(e,t,s,n){const o=this;\"=\"==(\"\"+s).substr(0,1)&&0!=o.parent.config.parseFormulas&&(s=executeFormula.call(o,s,e,t));const r=o.options.columns&&o.options.columns[e];if(r&&!isFormula(s)){let e=null;if(e=getMask(r)){s&&s==Number(s)&&(s=Number(s));let t=jSuites.mask.render(s,e,!0);if(n&&e.mask){const o=e.mask.split(\";\");o[1]&&(o[1].match(new RegExp(\"\\\\[Red\\\\]\",\"gi\"))&&(s<0?n.classList.add(\"red\"):n.classList.remove(\"red\")),o[1].match(new RegExp(\"\\\\(\",\"gi\"))&&s<0&&(t=\"(\"+t+\")\"))}t&&(s=t)}}return s},getDropDownValue=function(e,t){const s=this,n=[];if(s.options.columns&&s.options.columns[e]&&s.options.columns[e].source){const o=[],r=s.options.columns[e].source;for(let e=0;e<r.length;e++)\"object\"==typeof r[e]?o[r[e].id]=r[e].name:o[r[e]]=r[e];const l=Array.isArray(t)?t:(\"\"+t).split(\";\");for(let e=0;e<l.length;e++)\"object\"==typeof l[e]?n.push(o[l[e].id]):o[l[e]]&&n.push(o[l[e]])}else console.error(\"Invalid column\");return n.length>0?n.join(\"; \"):\"\"},validDate=function(e){return\"-\"==(e=\"\"+e).substr(4,1)&&\"-\"==e.substr(7,1)||4==(e=e.split(\"-\"))[0].length&&e[0]==Number(e[0])&&2==e[1].length&&e[1]==Number(e[1])},stripScript=function(e){const t=new Option;t.innerHTML=e;let s=null;for(e=t.getElementsByTagName(\"script\");s=e[0];)s.parentNode.removeChild(s);return t.innerHTML},createCell=function(e,t,s){const n=this;let o=document.createElement(\"td\");if(o.setAttribute(\"data-x\",e),o.setAttribute(\"data-y\",t),\"none\"===n.headers[e].style.display&&(o.style.display=\"none\"),\"=\"==(\"\"+s).substr(0,1)&&1==n.options.secureFormulas){const e=secureFormula(s);e!=s&&(s=e)}if(n.options.columns&&n.options.columns[e]&&\"object\"==typeof n.options.columns[e].type)!0===n.parent.config.parseHTML?o.innerHTML=s:o.textContent=s,\"function\"==typeof n.options.columns[e].type.createCell&&n.options.columns[e].type.createCell(o,s,parseInt(e),parseInt(t),n,n.options.columns[e]);else if(n.options.columns&&n.options.columns[e]&&\"hidden\"==n.options.columns[e].type)o.style.display=\"none\",o.textContent=s;else if(n.options.columns&&n.options.columns[e]&&(\"checkbox\"==n.options.columns[e].type||\"radio\"==n.options.columns[e].type)){const r=document.createElement(\"input\");r.type=n.options.columns[e].type,r.name=\"c\"+e,r.checked=1==s||1==s||\"true\"==s,r.onclick=function(){n.setValue(o,this.checked)},1!=n.options.columns[e].readOnly&&0!=n.options.editable||r.setAttribute(\"disabled\",\"disabled\"),o.appendChild(r),n.options.data[t][e]=r.checked}else if(n.options.columns&&n.options.columns[e]&&\"calendar\"==n.options.columns[e].type){let t=null;if(!validDate(s)){const o=jSuites.calendar.extractDateFromString(s,n.options.columns[e].options&&n.options.columns[e].options.format||\"YYYY-MM-DD\");o&&(t=o)}o.textContent=jSuites.calendar.getDateString(t||s,n.options.columns[e].options&&n.options.columns[e].options.format)}else if(n.options.columns&&n.options.columns[e]&&\"dropdown\"==n.options.columns[e].type)o.classList.add(\"jss_dropdown\"),o.textContent=getDropDownValue.call(n,e,s);else if(n.options.columns&&n.options.columns[e]&&\"color\"==n.options.columns[e].type)if(\"square\"==n.options.columns[e].render){const e=document.createElement(\"div\");e.className=\"color\",e.style.backgroundColor=s,o.appendChild(e)}else o.style.color=s,o.textContent=s;else if(n.options.columns&&n.options.columns[e]&&\"image\"==n.options.columns[e].type){if(s&&\"data:image\"==s.substr(0,10)){const e=document.createElement(\"img\");e.src=s,o.appendChild(e)}}else n.options.columns&&n.options.columns[e]&&\"html\"==n.options.columns[e].type||!0===n.parent.config.parseHTML?o.innerHTML=stripScript(parseValue.call(this,e,t,s,o)):o.textContent=parseValue.call(this,e,t,s,o);n.options.columns&&n.options.columns[e]&&1==n.options.columns[e].readOnly&&(o.className=\"readonly\");const r=n.options.columns&&n.options.columns[e]&&n.options.columns[e].align||n.options.defaultColAlign||\"center\";return o.style.textAlign=r,n.options.columns&&n.options.columns[e]&&0==n.options.columns[e].wordWrap||!(1==n.options.wordWrap||n.options.columns&&n.options.columns[e]&&1==n.options.columns[e].wordWrap||o.innerHTML.length>200)||(o.style.whiteSpace=\"pre-wrap\"),e>0&&1==this.options.textOverflow&&(s||o.innerHTML?n.records[t][e-1].element.style.overflow=\"hidden\":e==n.options.columns.length-1&&(o.style.overflow=\"hidden\")),_dispatch_js__WEBPACK_IMPORTED_MODULE_3__.A.call(n,\"oncreatecell\",n,o,e,t,s),o},updateCell=function(e,t,s,n){const o=this;let r;if(1!=o.records[t][e].element.classList.contains(\"readonly\")||n){if(\"=\"==(\"\"+s).substr(0,1)&&1==o.options.secureFormulas){const e=secureFormula(s);e!=s&&(s=e)}const n=_dispatch_js__WEBPACK_IMPORTED_MODULE_3__.A.call(o,\"onbeforechange\",o,o.records[t][e].element,e,t,s);if(null!=n&&(s=n),o.options.columns&&o.options.columns[e]&&\"object\"==typeof o.options.columns[e].type&&\"function\"==typeof o.options.columns[e].type.updateCell){const n=o.options.columns[e].type.updateCell(o.records[t][e].element,s,parseInt(e),parseInt(t),o,o.options.columns[e]);void 0!==n&&(s=n)}r={x:e,y:t,col:e,row:t,value:s,oldValue:o.options.data[t][e]};let l=o.options.columns&&o.options.columns[e]&&\"object\"==typeof o.options.columns[e].type?o.options.columns[e].type:null;if(l)o.options.data[t][e]=s,\"function\"==typeof l.setValue&&l.setValue(o.records[t][e].element,s);else if(o.options.columns&&o.options.columns[e]&&(\"checkbox\"==o.options.columns[e].type||\"radio\"==o.options.columns[e].type)){if(\"radio\"==o.options.columns[e].type)for(let t=0;t<o.options.data.length;t++)o.options.data[t][e]=!1;o.records[t][e].element.children[0].checked=1==s||1==s||\"true\"==s||\"TRUE\"==s,o.options.data[t][e]=o.records[t][e].element.children[0].checked}else if(o.options.columns&&o.options.columns[e]&&\"dropdown\"==o.options.columns[e].type)o.options.data[t][e]=s,o.records[t][e].element.textContent=getDropDownValue.call(o,e,s);else if(o.options.columns&&o.options.columns[e]&&\"calendar\"==o.options.columns[e].type){let n=null;if(!validDate(s)){const t=jSuites.calendar.extractDateFromString(s,o.options.columns[e].options&&o.options.columns[e].options.format||\"YYYY-MM-DD\");t&&(n=t)}o.options.data[t][e]=s,o.records[t][e].element.textContent=jSuites.calendar.getDateString(n||s,o.options.columns[e].options&&o.options.columns[e].options.format)}else if(o.options.columns&&o.options.columns[e]&&\"color\"==o.options.columns[e].type)if(o.options.data[t][e]=s,\"square\"==o.options.columns[e].render){const n=document.createElement(\"div\");n.className=\"color\",n.style.backgroundColor=s,o.records[t][e].element.textContent=\"\",o.records[t][e].element.appendChild(n)}else o.records[t][e].element.style.color=s,o.records[t][e].element.textContent=s;else if(o.options.columns&&o.options.columns[e]&&\"image\"==o.options.columns[e].type){if(s=\"\"+s,o.options.data[t][e]=s,o.records[t][e].element.innerHTML=\"\",s&&\"data:image\"==s.substr(0,10)){const n=document.createElement(\"img\");n.src=s,o.records[t][e].element.appendChild(n)}}else o.options.data[t][e]=s,o.options.columns&&o.options.columns[e]&&\"html\"==o.options.columns[e].type?o.records[t][e].element.innerHTML=stripScript(parseValue.call(o,e,t,s)):!0===o.parent.config.parseHTML?o.records[t][e].element.innerHTML=stripScript(parseValue.call(o,e,t,s,o.records[t][e].element)):o.records[t][e].element.textContent=parseValue.call(o,e,t,s,o.records[t][e].element),o.options.columns&&o.options.columns[e]&&0==o.options.columns[e].wordWrap||!(1==o.options.wordWrap||o.options.columns&&o.options.columns[e]&&1==o.options.columns[e].wordWrap||o.records[t][e].element.innerHTML.length>200)?o.records[t][e].element.style.whiteSpace=\"\":o.records[t][e].element.style.whiteSpace=\"pre-wrap\";e>0&&(o.records[t][e-1].element.style.overflow=s?\"hidden\":\"\"),o.options.columns&&o.options.columns[e]&&\"function\"==typeof o.options.columns[e].render&&o.options.columns[e].render(o.records[t]&&o.records[t][e]?o.records[t][e].element:null,s,parseInt(e),parseInt(t),o,o.options.columns[e]),_dispatch_js__WEBPACK_IMPORTED_MODULE_3__.A.call(o,\"onchange\",o,o.records[t]&&o.records[t][e]?o.records[t][e].element:null,e,t,s,r.oldValue)}else r={x:e,y:t,col:e,row:t};return r},isFormula=function(e){const t=(\"\"+e)[0];return\"=\"==t||\"#\"==t},getMask=function(e){if(e.format||e.mask||e.locale){const t={};return e.mask?t.mask=e.mask:e.format?t.mask=e.format:(t.locale=e.locale,t.options=e.options),e.decimal&&(t.options||(t.options={}),t.options={decimal:e.decimal}),t}return null},secureFormula=function(e){let t=\"\",s=0;for(let n=0;n<e.length;n++)'\"'==e[n]&&(s=0==s?1:0),t+=1==s?e[n]:e[n].toUpperCase();return t};let chainLoopProtection=[];const updateFormulaChain=function(e,t,s){const n=this,o=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([e,t]);if(n.formula[o]&&n.formula[o].length>0)if(chainLoopProtection[o])n.records[t][e].element.innerHTML=\"#ERROR\",n.formula[o]=\"\";else{chainLoopProtection[o]=!0;for(let e=0;e<n.formula[o].length;e++){const t=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(n.formula[o][e],!0),r=\"\"+n.options.data[t[1]][t[0]];\"=\"==r.substr(0,1)?s.push(updateCell.call(n,t[0],t[1],r,!0)):Object.keys(n.formula)[e]=null,updateFormulaChain.call(n,t[0],t[1],s)}}chainLoopProtection=[]},updateFormula=function(e,t){const s=/[A-Z]/,n=/[0-9]/;let o=\"\",r=null,l=null,i=\"\";for(let a=0;a<e.length;a++)s.exec(e[a])?(r=1,l=0,i+=e[a]):n.exec(e[a])?(l=r?1:0,i+=e[a]):(r&&l&&(i=t[i]?t[i]:i),o+=i,o+=e[a],r=0,l=0,i=\"\");return i&&(r&&l&&(i=t[i]?t[i]:i),o+=i),o},updateFormulas=function(e){const t=this;for(let s=0;s<t.options.data.length;s++)for(let n=0;n<t.options.data[0].length;n++){const o=\"\"+t.options.data[s][n];if(\"=\"==o.substr(0,1)){const r=updateFormula(o,e);r!=o&&(t.options.data[s][n]=r)}}const s=[],n=Object.keys(t.formula);for(let o=0;o<n.length;o++){let r=n[o];const l=t.formula[r];e[r]&&(r=e[r]),s[r]=[];for(let t=0;t<l.length;t++){let n=l[t];e[n]&&(n=e[n]),s[r].push(n)}}t.formula=s},updateTableReferences=function(){const e=this;if(e.skipUpdateTableReferences)return;for(let t=0;t<e.headers.length;t++)e.headers[t].getAttribute(\"data-x\")!=t&&(e.headers[t].setAttribute(\"data-x\",t),e.headers[t].getAttribute(\"title\")||(e.headers[t].innerHTML=(0,_helpers_js__WEBPACK_IMPORTED_MODULE_4__.getColumnName)(t)));for(let t=0;t<e.rows.length;t++)e.rows[t]&&e.rows[t].element.getAttribute(\"data-y\")!=t&&(e.rows[t].element.setAttribute(\"data-y\",t),e.rows[t].element.children[0].setAttribute(\"data-y\",t),e.rows[t].element.children[0].innerHTML=t+1);const t=[],s=[],n=function(s,n,o,r){if(s!=o&&e.records[r][o].element.setAttribute(\"data-x\",o),n!=r&&e.records[r][o].element.setAttribute(\"data-y\",r),s!=o||n!=r){const e=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([s,n]),l=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([o,r]);t[e]=l}};for(let t=0;t<e.records.length;t++)for(let o=0;o<e.records[0].length;o++)if(e.records[t][o]){const r=e.records[t][o].element.getAttribute(\"data-x\"),l=e.records[t][o].element.getAttribute(\"data-y\");if(e.records[t][o].element.getAttribute(\"data-merged\")){const e=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([r,l]),n=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.t3)([o,t]);if(null==s[e])if(e==n)s[e]=!1;else{const i=parseInt(o-r),a=parseInt(t-l);s[e]=[n,i,a]}}else n(r,l,o,t)}const o=Object.keys(s);if(o.length)for(let t=0;t<o.length;t++)if(s[o[t]]){const r=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(o[t],!0);let l=r[0],i=r[1];n(l,i,l+s[o[t]][1],i+s[o[t]][2]);const a=o[t],c=s[o[t]][0];for(let n=0;n<e.options.mergeCells[a][2].length;n++)l=parseInt(e.options.mergeCells[a][2][n].getAttribute(\"data-x\")),i=parseInt(e.options.mergeCells[a][2][n].getAttribute(\"data-y\")),e.options.mergeCells[a][2][n].setAttribute(\"data-x\",l+s[o[t]][1]),e.options.mergeCells[a][2][n].setAttribute(\"data-y\",i+s[o[t]][2]);e.options.mergeCells[c]=e.options.mergeCells[a],delete e.options.mergeCells[a]}updateFormulas.call(e,t),_meta_js__WEBPACK_IMPORTED_MODULE_5__.hs.call(e,t),_selection_js__WEBPACK_IMPORTED_MODULE_1__.G9.call(e),updateTable.call(e)},updateScroll=function(e){const t=this,s=t.content.getBoundingClientRect(),n=s.left,o=s.top,r=s.width,l=s.height,i=t.records[t.selectedCell[3]][t.selectedCell[2]].element.getBoundingClientRect(),a=i.left,c=i.top,d=i.width,u=i.height;let p,h;0==e||1==e?(p=a-n+t.content.scrollLeft,h=c-o+t.content.scrollTop-2):(p=a-n+t.content.scrollLeft+d,h=c-o+t.content.scrollTop+u),h>t.content.scrollTop+30&&h<t.content.scrollTop+l||(h<t.content.scrollTop+30?t.content.scrollTop=h-u:t.content.scrollTop=h-(l-2));const m=_freeze_js__WEBPACK_IMPORTED_MODULE_6__.w.call(t);p>t.content.scrollLeft+m&&p<t.content.scrollLeft+r||(p<t.content.scrollLeft+30?(t.content.scrollLeft=p,t.content.scrollLeft<50&&(t.content.scrollLeft=0)):p<t.content.scrollLeft+m?t.content.scrollLeft=p-m-1:t.content.scrollLeft=p-(r-20))},updateResult=function(){const e=this;let t=0,s=0;for(t=1==e.options.lazyLoading?100:e.options.pagination>0?e.options.pagination:e.results?e.results.length:e.rows.length;e.tbody.firstChild;)e.tbody.removeChild(e.tbody.firstChild);for(let n=0;n<e.rows.length;n++)!e.results||e.results.indexOf(n)>-1?(s<t&&(e.tbody.appendChild(e.rows[n].element),s++),e.rows[n].element.style.display=\"\"):e.rows[n].element.style.display=\"none\";return e.options.pagination>0&&_pagination_js__WEBPACK_IMPORTED_MODULE_7__.IV.call(e),_selection_js__WEBPACK_IMPORTED_MODULE_1__.Aq.call(e),_dispatch_js__WEBPACK_IMPORTED_MODULE_3__.A.call(e,\"onupdateresult\",e,e.results),t},getCell=function(e,t){if(\"string\"==typeof e){const s=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(e,!0);e=s[0],t=s[1]}return this.records[t][e].element},getCellFromCoords=function(e,t){return this.records[t][e].element},getLabel=function(e,t){if(\"string\"==typeof e){const s=(0,_internalHelpers_js__WEBPACK_IMPORTED_MODULE_2__.vu)(e,!0);e=s[0],t=s[1]}return this.records[t][e].element.innerHTML},fullscreen=function(e){const t=this;null==e&&(e=!t.config.fullscreen),t.config.fullscreen!=e&&(t.config.fullscreen=e,1==e?t.element.classList.add(\"fullscreen\"):t.element.classList.remove(\"fullscreen\"))},showIndex=function(){this.table.classList.remove(\"jss_hidden_index\")},hideIndex=function(){this.table.classList.add(\"jss_hidden_index\")},createNestedHeader=function(e){const t=this,s=document.createElement(\"tr\");s.classList.add(\"jss_nested\");const n=document.createElement(\"td\");n.classList.add(\"jss_selectall\"),s.appendChild(n),e.element=s;let o=0;for(let n=0;n<e.length;n++){e[n].colspan||(e[n].colspan=1),e[n].title||(e[n].title=\"\"),e[n].id||(e[n].id=\"\");let r=e[n].colspan;const l=[];for(let e=0;e<r;e++)t.options.columns[o]&&\"hidden\"==t.options.columns[o].type&&r++,l.push(o),o++;const i=document.createElement(\"td\");i.setAttribute(\"data-column\",l.join(\",\")),i.setAttribute(\"colspan\",e[n].colspan),i.setAttribute(\"align\",e[n].align||\"center\"),i.setAttribute(\"id\",e[n].id),i.textContent=e[n].title,s.appendChild(i)}return s},getWorksheetActive=function(){const e=this.parent?this.parent:this;return e.element.tabs?e.element.tabs.getActive():0},getWorksheetInstance=function(e){const t=void 0!==e?e:getWorksheetActive.call(this);return this.worksheets[t]}},689:function(e,t,s){s.d(t,{Hh:function(){return o},t3:function(){return l},vu:function(){return r}});var n=s(978);const o=function(e,t,s){if(t<=e.length)return e.slice(0,t).concat(s).concat(e.slice(t));const n=e.slice(0,e.length);for(;t>n.length;)n.push(void 0);return n.concat(s)},r=function(e,t){const s=/^[a-zA-Z]+/.exec(e);if(s){let n=0;for(let e=0;e<s[0].length;e++)n+=parseInt(s[0].charCodeAt(e)-64)*Math.pow(26,s[0].length-1-e);n--,n<0&&(n=0);let o=parseInt(/[0-9]+$/.exec(e));o>0&&o--,e=1==t?[n,o]:n+\"-\"+o}return e},l=function(e){return Array.isArray(e)||(e=e.split(\"-\")),(0,n.getColumnName)(parseInt(e[0]))+(parseInt(e[1])+1)}},497:function(e,t,s){s.d(t,{AG:function(){return o},G_:function(){return r},p6:function(){return l},wu:function(){return n}});const n=function(e){const t=this;let s;s=1!=t.options.search&&1!=t.options.filters||!t.results?t.rows:t.results;const n=100;null!=e&&-1!=e||(e=Math.ceil(s.length/n)-1);let o=e*n,r=e*n+n;r>s.length&&(r=s.length),o=r-100,o<0&&(o=0);for(let e=o;e<r;e++)1!=t.options.search&&1!=t.options.filters||!t.results?t.tbody.appendChild(t.rows[e].element):t.tbody.appendChild(t.rows[s[e]].element),t.tbody.children.length>n&&t.tbody.removeChild(t.tbody.firstChild)},o=function(){const e=this;if(e.selectedCell){const t=parseInt(e.tbody.firstChild.getAttribute(\"data-y\"))/100,s=parseInt(e.selectedCell[3]/100),n=parseInt(e.rows.length/100);if(t!=s&&s<=n&&!Array.prototype.indexOf.call(e.tbody.children,e.rows[e.selectedCell[3]].element))return e.loadPage(s),!0}return!1},r=function(){const e=this;let t;t=1!=e.options.search&&1!=e.options.filters||!e.results?e.rows:e.results;let s=0;if(t.length>100){let n=parseInt(e.tbody.firstChild.getAttribute(\"data-y\"));if(1!=e.options.search&&1!=e.options.filters||!e.results||(n=t.indexOf(n)),n>0)for(let o=0;o<30;o++)n-=1,n>-1&&(1!=e.options.search&&1!=e.options.filters||!e.results?e.tbody.insertBefore(e.rows[n].element,e.tbody.firstChild):e.tbody.insertBefore(e.rows[t[n]].element,e.tbody.firstChild),e.tbody.children.length>100&&(e.tbody.removeChild(e.tbody.lastChild),s=1))}return s},l=function(){const e=this;let t;t=1!=e.options.search&&1!=e.options.filters||!e.results?e.rows:e.results;let s=0;if(t.length>100){let n=parseInt(e.tbody.lastChild.getAttribute(\"data-y\"));if(1!=e.options.search&&1!=e.options.filters||!e.results||(n=t.indexOf(n)),n<e.rows.length-1)for(let o=0;o<=30;o++)n<t.length&&(1!=e.options.search&&1!=e.options.filters||!e.results?e.tbody.appendChild(e.rows[n].element):e.tbody.appendChild(e.rows[t[n]].element),e.tbody.children.length>100&&(e.tbody.removeChild(e.tbody.firstChild),s=1)),n+=1}return s}},910:function(e,t,s){s.d(t,{D0:function(){return c},FU:function(){return u},Lt:function(){return a},VP:function(){return h},Zp:function(){return p},fd:function(){return d}});var n=s(689),o=s(530),r=s(911),l=s(805),i=s(657);const a=function(e,t){const s=this,o=[];if(s.options.mergeCells){const r=Object.keys(s.options.mergeCells);for(let l=0;l<r.length;l++){const i=(0,n.vu)(r[l],!0),a=s.options.mergeCells[r[l]][0],c=i[0],d=i[0]+(a>1?a-1:0);null==t?c<=e&&d>=e&&o.push(r[l]):t?c<e&&d>=e&&o.push(r[l]):c<=e&&d>e&&o.push(r[l])}}return o},c=function(e,t){const s=this,o=[];if(s.options.mergeCells){const r=Object.keys(s.options.mergeCells);for(let l=0;l<r.length;l++){const i=(0,n.vu)(r[l],!0),a=s.options.mergeCells[r[l]][1],c=i[1],d=i[1]+(a>1?a-1:0);null==t?c<=e&&d>=e&&o.push(r[l]):t?c<e&&d>=e&&o.push(r[l]):c<=e&&d>e&&o.push(r[l])}}return o},d=function(e){const t=this;let s={};if(e)s=t.options.mergeCells&&t.options.mergeCells[e]?[t.options.mergeCells[e][0],t.options.mergeCells[e][1]]:null;else if(t.options.mergeCells){t.options.mergeCells;const e=Object.keys(t.options.mergeCells);for(let n=0;n<e.length;n++)s[e[n]]=[t.options.mergeCells[e[n]][0],t.options.mergeCells[e[n]][1]]}return s},u=function(e,t,s,a){const c=this;let d=!1;if(e){if(\"string\"!=typeof e)return null}else{if(!c.highlighted.length)return alert(jSuites.translate(\"No cells selected\")),null;{const o=parseInt(c.highlighted[0].getAttribute(\"data-x\")),r=parseInt(c.highlighted[0].getAttribute(\"data-y\")),l=parseInt(c.highlighted[c.highlighted.length-1].getAttribute(\"data-x\")),i=parseInt(c.highlighted[c.highlighted.length-1].getAttribute(\"data-y\"));e=(0,n.t3)([o,r]),t=l-o+1,s=i-r+1}}const u=(0,n.vu)(e,!0);if(c.options.mergeCells&&c.options.mergeCells[e])c.records[u[1]][u[0]].element.getAttribute(\"data-merged\")&&(d=\"Cell already merged\");else if((!t||t<2)&&(!s||s<2))d=\"Invalid merged properties\";else for(let e=u[1];e<u[1]+s;e++)for(let s=u[0];s<u[0]+t;s++)(0,n.t3)([s,e]),c.records[e][s].element.getAttribute(\"data-merged\")&&(d=\"There is a conflict with another merged cell\");if(d)alert(jSuites.translate(d));else{t>1?c.records[u[1]][u[0]].element.setAttribute(\"colspan\",t):t=1,s>1?c.records[u[1]][u[0]].element.setAttribute(\"rowspan\",s):s=1,c.options.mergeCells||(c.options.mergeCells={}),c.options.mergeCells[e]=[t,s,[]],c.records[u[1]][u[0]].element.setAttribute(\"data-merged\",\"true\"),c.records[u[1]][u[0]].element.style.overflow=\"hidden\";const n=[];for(let r=u[1];r<u[1]+s;r++)for(let s=u[0];s<u[0]+t;s++)u[0]==s&&u[1]==r||(n.push(c.options.data[r][s]),o.k9.call(c,s,r,\"\",!0),c.options.mergeCells[e][2].push(c.records[r][s].element),c.records[r][s].element.style.display=\"none\",c.records[r][s].element=c.records[u[1]][u[0]].element);i.c6.call(c,c.records[u[1]][u[0]].element),a||(r.Dh.call(c,{action:\"setMerge\",column:e,colspan:t,rowspan:s,data:n}),l.A.call(c,\"onmerge\",c,{[e]:[t,s]}))}},p=function(e,t,s){const r=this;if(r.options.mergeCells&&r.options.mergeCells[e]){const a={[e]:r.options.mergeCells[e]},c=(0,n.vu)(e,!0);r.records[c[1]][c[0]].element.removeAttribute(\"colspan\"),r.records[c[1]][c[0]].element.removeAttribute(\"rowspan\"),r.records[c[1]][c[0]].element.removeAttribute(\"data-merged\");const d=r.options.mergeCells[e];let u,p,h=0;for(u=0;u<d[1];u++)for(p=0;p<d[0];p++)(u>0||p>0)&&(r.records[c[1]+u][c[0]+p].element=d[2][h],r.records[c[1]+u][c[0]+p].element.style.display=\"\",t&&t[h]&&o.k9.call(r,c[0]+p,c[1]+u,t[h]),h++);i.c6.call(r,r.records[c[1]][c[0]].element,r.records[c[1]+u-1][c[0]+p-1].element),s||delete r.options.mergeCells[e],l.A.call(r,\"onunmerge\",r,e,a)}},h=function(e){const t=this;if(t.options.mergeCells){t.options.mergeCells;const s=Object.keys(t.options.mergeCells);for(let n=0;n<s.length;n++)p.call(t,s[n],null,e)}}},654:function(e,t,s){s.d(t,{IQ:function(){return o},hs:function(){return r},iZ:function(){return l}});var n=s(805);const o=function(e,t){const s=this;return e?t?s.options.meta&&s.options.meta[e]&&s.options.meta[e][t]?s.options.meta[e][t]:null:s.options.meta&&s.options.meta[e]?s.options.meta[e]:null:s.options.meta},r=function(e){const t=this;if(t.options.meta){const s={},n=Object.keys(t.options.meta);for(let o=0;o<n.length;o++)e[n[o]]?s[e[n[o]]]=t.options.meta[n[o]]:s[n[o]]=t.options.meta[n[o]];t.options.meta=s}},l=function(e,t,s){const o=this;if(o.options.meta||(o.options.meta={}),t&&s)o.options.meta[e]||(o.options.meta[e]={}),o.options.meta[e][t]=s,n.A.call(o,\"onchangemeta\",o,{[e]:{[t]:s}});else{const t=Object.keys(e);for(let s=0;s<t.length;s++){o.options.meta[t[s]]||(o.options.meta[t[s]]={});const n=Object.keys(e[t[s]]);for(let r=0;r<n.length;r++)o.options.meta[t[s]][n[r]]=e[t[s]][n[r]]}n.A.call(o,\"onchangemeta\",o,e)}}},94:function(e,t,s){s.d(t,{My:function(){return d},Th:function(){return a},iY:function(){return c}});var n=s(911),o=s(805),r=s(530),l=s(497),i=s(829);const a=function(e,t){const s=this;for(let e=0;e<s.headers.length;e++)s.headers[e].classList.remove(\"arrow-up\"),s.headers[e].classList.remove(\"arrow-down\");t?s.headers[e].classList.add(\"arrow-up\"):s.headers[e].classList.add(\"arrow-down\")},c=function(e){const t=this;let s=[];for(let n=0;n<e.length;n++)s[n]=t.options.data[e[n]];t.options.data=s,s=[];for(let n=0;n<e.length;n++){s[n]=t.records[e[n]];for(let e=0;e<s[n].length;e++)s[n][e].y=n}t.records=s,s=[];for(let n=0;n<e.length;n++)s[n]=t.rows[e[n]],s[n].y=n;if(t.rows=s,r.o8.call(t),t.results&&t.results.length)t.searchInput.value?t.search(t.searchInput.value):i.F8.call(t);else if(t.results=null,t.pageNumber=0,t.options.pagination>0)t.page(0);else if(1==t.options.lazyLoading)l.wu.call(t,0);else for(let e=0;e<t.rows.length;e++)t.tbody.appendChild(t.rows[e].element)},d=function(e,t){const s=this;if(e>=0){if(s.options.mergeCells&&Object.keys(s.options.mergeCells).length>0){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;s.destroyMerge()}t=null==t?s.headers[e].classList.contains(\"arrow-down\")?1:0:t?1:0;let r=[];if(s.options.columns&&s.options.columns[e]&&(\"number\"==s.options.columns[e].type||\"numeric\"==s.options.columns[e].type||\"percentage\"==s.options.columns[e].type||\"autonumber\"==s.options.columns[e].type||\"color\"==s.options.columns[e].type))for(let t=0;t<s.options.data.length;t++)r[t]=[t,Number(s.options.data[t][e])];else if(s.options.columns&&s.options.columns[e]&&(\"calendar\"==s.options.columns[e].type||\"checkbox\"==s.options.columns[e].type||\"radio\"==s.options.columns[e].type))for(let t=0;t<s.options.data.length;t++)r[t]=[t,s.options.data[t][e]];else for(let t=0;t<s.options.data.length;t++)r[t]=[t,s.records[t][e].element.textContent.toLowerCase()];\"function\"!=typeof s.parent.config.sorting&&(s.parent.config.sorting=function(e){return function(t,s){const n=t[1],o=s[1];return e?\"\"===n&&\"\"!==o?1:\"\"!==n&&\"\"===o||n>o?-1:n<o?1:0:\"\"===n&&\"\"!==o?1:\"\"!==n&&\"\"===o?-1:n>o?1:n<o?-1:0}}),r=r.sort(s.parent.config.sorting(t));const l=[];for(let e=0;e<r.length;e++)l[e]=r[e][0];return n.Dh.call(s,{action:\"orderBy\",rows:l,column:e,order:t}),a.call(s,e,t),c.call(s,l),o.A.call(s,\"onsort\",s,e,t,l.map((e=>e))),!0}}},167:function(e,t,s){s.d(t,{$f:function(){return a},IV:function(){return l},MY:function(){return i},ho:function(){return r}});var n=s(805),o=s(657);const r=function(e){const t=this;return 1!=t.options.search&&1!=t.options.filters||!t.results||(e=t.results.indexOf(e)),Math.ceil((parseInt(e)+1)/parseInt(t.options.pagination))-1},l=function(){const e=this;if(e.pagination.children[0].innerHTML=\"\",e.pagination.children[1].innerHTML=\"\",e.options.pagination){let t;if(t=1!=e.options.search&&1!=e.options.filters||!e.results?e.rows.length:e.results.length,t){const s=Math.ceil(t/e.options.pagination);let n,o;if(e.pageNumber<6?(n=1,o=s<10?s:10):s-e.pageNumber<5?(n=s-9,o=s,n<1&&(n=1)):(n=e.pageNumber-4,o=e.pageNumber+5),n>1){const t=document.createElement(\"div\");t.className=\"jss_page\",t.innerHTML=\"<\",t.title=1,e.pagination.children[1].appendChild(t)}for(let t=n;t<=o;t++){const s=document.createElement(\"div\");s.className=\"jss_page\",s.innerHTML=t,e.pagination.children[1].appendChild(s),e.pageNumber==t-1&&s.classList.add(\"jss_page_selected\")}if(o<s){const t=document.createElement(\"div\");t.className=\"jss_page\",t.innerHTML=\">\",t.title=s,e.pagination.children[1].appendChild(t)}const r=function(e){const t=Array.prototype.slice.call(arguments,1);return e.replace(/{(\\d+)}/g,(function(e,s){return void 0!==t[s]?t[s]:e}))};e.pagination.children[0].innerHTML=r(jSuites.translate(\"Showing page {0} of {1} entries\"),e.pageNumber+1,s)}else e.pagination.children[0].innerHTML=jSuites.translate(\"No records found\")}},i=function(e){const t=this,s=t.pageNumber;let r;r=1!=t.options.search&&1!=t.options.filters||!t.results?t.rows:t.results;const i=parseInt(t.options.pagination);null!=e&&-1!=e||(e=Math.ceil(r.length/i)-1),t.pageNumber=e;let a=e*i,c=e*i+i;for(c>r.length&&(c=r.length),a<0&&(a=0);t.tbody.firstChild;)t.tbody.removeChild(t.tbody.firstChild);for(let e=a;e<c;e++)1!=t.options.search&&1!=t.options.filters||!t.results?t.tbody.appendChild(t.rows[e].element):t.tbody.appendChild(t.rows[r[e]].element);t.options.pagination>0&&l.call(t),o.Aq.call(t),n.A.call(t,\"onchangepage\",t,e,s,t.options.pagination)},a=function(){const e=this;let t;return t=1!=e.options.search&&1!=e.options.filters||!e.results?e.rows.length:e.results.length,Math.ceil(t/e.options.pagination)}},657:function(e,t,s){s.d(t,{AH:function(){return m},Aq:function(){return d},G9:function(){return g},Jg:function(){return f},Lo:function(){return v},R5:function(){return _},Ub:function(){return B},at:function(){return w},c6:function(){return p},eO:function(){return x},ef:function(){return A},gE:function(){return u},gG:function(){return y},kA:function(){return h},kF:function(){return C},kV:function(){return L},sp:function(){return E},tW:function(){return j}});var n=s(805),o=s(296),r=s(978),l=s(911),i=s(530),a=s(689),c=s(392);const d=function(){const e=this;if(e.highlighted&&e.highlighted.length){const t=e.highlighted[e.highlighted.length-1].element,s=t.getAttribute(\"data-x\"),n=e.content.getBoundingClientRect(),r=n.left,l=n.top,i=t.getBoundingClientRect(),a=i.left,c=i.top,d=i.width,u=i.height,p=a-r+e.content.scrollLeft+d-4,h=c-l+e.content.scrollTop+u-4;if(e.corner.style.top=h+\"px\",e.corner.style.left=p+\"px\",e.options.freezeColumns){const t=o.w.call(e);s>e.options.freezeColumns-1&&a-r+d<t?e.corner.style.display=\"none\":0!=e.options.selectionCopy&&(e.corner.style.display=\"\")}else 0!=e.options.selectionCopy&&(e.corner.style.display=\"\")}else e.corner.style.top=\"-2000px\",e.corner.style.left=\"-2000px\";(0,c.nK)(e)},u=function(e){const t=this;let s;if(t.highlighted&&t.highlighted.length){s=1;for(let e=0;e<t.highlighted.length;e++){t.highlighted[e].element.classList.remove(\"highlight\"),t.highlighted[e].element.classList.remove(\"highlight-left\"),t.highlighted[e].element.classList.remove(\"highlight-right\"),t.highlighted[e].element.classList.remove(\"highlight-top\"),t.highlighted[e].element.classList.remove(\"highlight-bottom\"),t.highlighted[e].element.classList.remove(\"highlight-selected\");const s=parseInt(t.highlighted[e].element.getAttribute(\"data-x\")),n=parseInt(t.highlighted[e].element.getAttribute(\"data-y\"));let o,r;if(t.highlighted[e].element.getAttribute(\"data-merged\")){const l=parseInt(t.highlighted[e].element.getAttribute(\"colspan\")),i=parseInt(t.highlighted[e].element.getAttribute(\"rowspan\"));o=l>0?s+(l-1):s,r=i>0?n+(i-1):n}else o=s,r=n;for(let e=s;e<=o;e++)t.headers[e]&&t.headers[e].classList.remove(\"selected\");for(let e=n;e<=r;e++)t.rows[e]&&t.rows[e].element.classList.remove(\"selected\")}}else s=0;return t.highlighted=[],t.selectedCell=null,t.corner.style.top=\"-2000px\",t.corner.style.left=\"-2000px\",1==e&&1==s&&n.A.call(t,\"onblur\",t),s},p=function(e,t,s){const n=e.getAttribute(\"data-x\"),o=e.getAttribute(\"data-y\");let r,l;t?(r=t.getAttribute(\"data-x\"),l=t.getAttribute(\"data-y\")):(r=n,l=o),m.call(this,n,o,r,l,s)},h=function(){const e=document.querySelectorAll(\".jss_worksheet .copying\");for(let t=0;t<e.length;t++)e[t].classList.remove(\"copying\"),e[t].classList.remove(\"copying-left\"),e[t].classList.remove(\"copying-right\"),e[t].classList.remove(\"copying-top\"),e[t].classList.remove(\"copying-bottom\")},m=function(e,t,s,o,r){const l=this;if(null==t){if(t=0,o=l.rows.length-1,null==e)return}else null==e&&(e=0,s=l.options.data[0].length-1);null==s&&(s=e),null==o&&(o=t),e>=l.headers.length&&(e=l.headers.length-1),t>=l.rows.length&&(t=l.rows.length-1),s>=l.headers.length&&(s=l.headers.length-1),o>=l.rows.length&&(o=l.rows.length-1);let i,a,c,u,p=null,m=null,f=null,g=null;parseInt(e)<parseInt(s)?(i=parseInt(e),a=parseInt(s)):(i=parseInt(s),a=parseInt(e)),parseInt(t)<parseInt(o)?(c=parseInt(t),u=parseInt(o)):(c=parseInt(o),u=parseInt(t));for(let e=i;e<=a;e++)for(let t=c;t<=u;t++)if(l.records[t][e]&&l.records[t][e].element.getAttribute(\"data-merged\")){const s=parseInt(l.records[t][e].element.getAttribute(\"data-x\")),n=parseInt(l.records[t][e].element.getAttribute(\"data-y\")),o=parseInt(l.records[t][e].element.getAttribute(\"colspan\")),r=parseInt(l.records[t][e].element.getAttribute(\"rowspan\"));o>1&&(s<i&&(i=s),s+o>a&&(a=s+o-1)),r&&(n<c&&(c=n),n+r>u&&(u=n+r-1))}for(let e=c;e<=u;e++)\"none\"!=l.rows[e].element.style.display&&(null==f&&(f=e),g=e);for(let e=i;e<=a;e++)for(let t=c;t<=u;t++)l.options.columns&&l.options.columns[e]&&\"hidden\"==l.options.columns[e].type||(null==p&&(p=e),m=e);if(p||(p=0),m||(m=0),!1===n.A.call(l,\"onbeforeselection\",l,p,f,m,g,r))return!1;const y=l.resetSelection();l.selectedCell=[e,t,s,o],l.records[t][e]&&l.records[t][e].element.classList.add(\"highlight-selected\");for(let e=i;e<=a;e++)for(let t=c;t<=u;t++)\"none\"!=l.rows[t].element.style.display&&\"none\"!=l.records[t][e].element.style.display&&(l.records[t][e].element.classList.add(\"highlight\"),l.highlighted.push(l.records[t][e]));for(let e=p;e<=m;e++)l.options.columns&&l.options.columns[e]&&\"hidden\"==l.options.columns[e].type||!l.cols[e].colElement.style||\"none\"==l.cols[e].colElement.style.display||(l.records[f]&&l.records[f][e]&&l.records[f][e].element.classList.add(\"highlight-top\"),l.records[g]&&l.records[g][e]&&l.records[g][e].element.classList.add(\"highlight-bottom\"),l.headers[e].classList.add(\"selected\"));for(let e=f;e<=g;e++)l.rows[e]&&\"none\"!=l.rows[e].element.style.display&&(l.records[e][p].element.classList.add(\"highlight-left\"),l.records[e][m].element.classList.add(\"highlight-right\"),l.rows[e].element.classList.add(\"selected\"));l.selectedContainer=[p,f,m,g],0==y&&(n.A.call(l,\"onfocus\",l),h()),n.A.call(l,\"onselection\",l,p,f,m,g,r),d.call(l)},f=function(e){const t=this;if(!t.selectedCell)return[];const s=[];for(let n=Math.min(t.selectedCell[0],t.selectedCell[2]);n<=Math.max(t.selectedCell[0],t.selectedCell[2]);n++)e&&\"none\"==t.headers[n].style.display||s.push(n);return s},g=function(){const e=this;e.selectedCell&&e.updateSelectionFromCoords(e.selectedCell[0],e.selectedCell[1],e.selectedCell[2],e.selectedCell[3])},y=function(){const e=this;for(let t=0;t<e.selection.length;t++)e.selection[t].classList.remove(\"selection\"),e.selection[t].classList.remove(\"selection-left\"),e.selection[t].classList.remove(\"selection-right\"),e.selection[t].classList.remove(\"selection-top\"),e.selection[t].classList.remove(\"selection-bottom\");e.selection=[]},b=function(e){return 1==(e=\"\"+e).length&&(e=\"0\"+e),e},C=function(e,t){const s=this,o=s.getData(!0,!1),r=s.selectedContainer,c=parseInt(e.getAttribute(\"data-x\")),d=parseInt(e.getAttribute(\"data-y\")),u=parseInt(t.getAttribute(\"data-x\")),p=parseInt(t.getAttribute(\"data-y\")),h=[];let m,f,g=!1;r[0]==c?(m=d<r[1]?d-r[1]:1,f=0):(f=c<r[0]?c-r[0]:1,m=0);let y=0,C=0;for(let e=d;e<=p;e++)if(!s.rows[e]||\"none\"!=s.rows[e].element.style.display){null==o[C]&&(C=0),y=0,r[0]!=c&&(f=c<r[0]?c-r[0]:1);for(let t=c;t<=u;t++){if(s.records[e][t]&&!s.records[e][t].element.classList.contains(\"readonly\")&&\"none\"!=s.records[e][t].element.style.display&&0==g){if(!s.selection.length&&\"\"!=s.options.data[e][t]){g=!0;continue}(null==o[C]||null==o[C][y])&&(y=0);let n=o[C][y];if(n&&!o[1]&&0!=s.parent.config.autoIncrement)if(!s.options.columns||!s.options.columns[t]||s.options.columns[t].type&&\"text\"!=s.options.columns[t].type&&\"number\"!=s.options.columns[t].type){if(s.options.columns&&s.options.columns[t]&&\"calendar\"==s.options.columns[t].type){const e=new Date(n);e.setDate(e.getDate()+m),n=e.getFullYear()+\"-\"+b(parseInt(e.getMonth()+1))+\"-\"+b(e.getDate())+\" 00:00:00\"}}else if(\"=\"==(\"\"+n).substr(0,1)){const e=n.match(/([A-Z]+[0-9]+)/g);if(e){const t=[];for(let s=0;s<e.length;s++){const n=(0,a.vu)(e[s],1);n[0]+=f,n[1]+=m,n[1]<0&&(n[1]=0);const o=(0,a.t3)([n[0],n[1]]);o!=e[s]&&(t[e[s]]=o)}t&&(n=(0,i.yB)(n,t))}}else n==Number(n)&&(n=Number(n)+m);h.push(i.k9.call(s,t,e,n)),i.xF.call(s,t,e,h)}y++,r[0]!=c&&f++}C++,m++}l.Dh.call(s,{action:\"setValue\",records:h,selection:s.selectedCell}),i.am.call(s);const j=h.map((function(e){return{x:e.x,y:e.y,value:e.value,oldValue:e.oldValue}}));n.A.call(s,\"onafterchanges\",s,j)},j=function(e){let t,s,n=0;if(!e||0===e.length)return n;for(t=0;t<e.length;t++)s=e.charCodeAt(t),n=(n<<5)-n+s,n|=0;return n},w=function(e,t,s){const n=this;if(1==e){if(n.selectedCell&&(t>=n.selectedCell[1]&&t<=n.selectedCell[3]||s>=n.selectedCell[1]&&s<=n.selectedCell[3]))return void n.resetSelection()}else if(n.selectedCell&&(t>=n.selectedCell[0]&&t<=n.selectedCell[2]||s>=n.selectedCell[0]&&s<=n.selectedCell[2]))return void n.resetSelection()},_=function(e){const t=this;if(!t.selectedCell)return[];const s=[];for(let n=Math.min(t.selectedCell[1],t.selectedCell[3]);n<=Math.max(t.selectedCell[1],t.selectedCell[3]);n++)e&&\"none\"==t.rows[n].element.style.display||s.push(n);return s},B=function(){const e=this;e.selectedCell||(e.selectedCell=[]),e.selectedCell[0]=0,e.selectedCell[1]=0,e.selectedCell[2]=e.headers.length-1,e.selectedCell[3]=e.records.length-1,e.updateSelectionFromCoords(e.selectedCell[0],e.selectedCell[1],e.selectedCell[2],e.selectedCell[3])},v=function(){const e=this;return e.selectedCell?[Math.min(e.selectedCell[0],e.selectedCell[2]),Math.min(e.selectedCell[1],e.selectedCell[3]),Math.max(e.selectedCell[0],e.selectedCell[2]),Math.max(e.selectedCell[1],e.selectedCell[3])]:null},A=function(e){const t=this,s=v.call(t);if(!s)return[];const n=[];for(let o=s[1];o<=s[3];o++)for(let l=s[0];l<=s[2];l++)e?n.push((0,r.getCellNameFromCoords)(l,o)):n.push(t.records[o][l]);return n},x=function(){const e=this,t=v.call(e);if(!t)return\"\";const s=(0,r.getCellNameFromCoords)(t[0],t[1]),n=(0,r.getCellNameFromCoords)(t[2],t[3]);return s===n?e.options.worksheetName+\"!\"+s:e.options.worksheetName+\"!\"+s+\":\"+n},E=function(e,t){const s=v.call(this);return e>=s[0]&&e<=s[2]&&t>=s[1]&&t<=s[3]},L=function(){const e=v.call(this);return e?[e]:[]}},392:function(e,t,s){s.d(t,{Ar:function(){return u},ll:function(){return d},nK:function(){return c}});var n=s(978),o=s(530);const r=function(e,t){0!=t.options.editable?e.classList.remove(\"jtoolbar-disabled\"):e.classList.add(\"jtoolbar-disabled\")},l=function(){const e=[],t=this,s=function(){return o.eN.call(t)};e.push({content:\"undo\",onclick:function(){s().undo()}}),e.push({content:\"redo\",onclick:function(){s().redo()}}),e.push({content:\"save\",onclick:function(){const e=s();e&&e.download()}}),e.push({type:\"divisor\"}),e.push({type:\"select\",width:\"120px\",options:[\"Default\",\"Verdana\",\"Arial\",\"Courier New\"],render:function(e){return'<span style=\"font-family:'+e+'\">'+e+\"</span>\"},onchange:function(e,t,n,o,r){const l=s();let i=l.getSelected(!0);if(i){let e=r?o:\"\";l.setStyle(Object.fromEntries(i.map((function(t){return[t,\"font-family: \"+e]}))))}},updateState:function(e,t,n){r(n,s())}}),e.push({type:\"select\",width:\"48px\",content:\"format_size\",options:[\"x-small\",\"small\",\"medium\",\"large\",\"x-large\"],render:function(e){return'<span style=\"font-size:'+e+'\">'+e+\"</span>\"},onchange:function(e,t,n,o){const r=s();let l=r.getSelected(!0);l&&r.setStyle(Object.fromEntries(l.map((function(e){return[e,\"font-size: \"+o]}))))},updateState:function(e,t,n){r(n,s())}}),e.push({type:\"select\",options:[\"left\",\"center\",\"right\",\"justify\"],render:function(e){return'<i class=\"material-icons\">format_align_'+e+\"</i>\"},onchange:function(e,t,n,o){const r=s();let l=r.getSelected(!0);l&&r.setStyle(Object.fromEntries(l.map((function(e){return[e,\"text-align: \"+o]}))))},updateState:function(e,t,n){r(n,s())}}),e.push({content:\"format_bold\",onclick:function(){const e=s();let t=e.getSelected(!0);t&&e.setStyle(Object.fromEntries(t.map((function(e){return[e,\"font-weight:bold\"]}))))},updateState:function(e,t,n){r(n,s())}}),e.push({type:\"color\",content:\"format_color_text\",k:\"color\",updateState:function(e,t,n){r(n,s())}}),e.push({type:\"color\",content:\"format_color_fill\",k:\"background-color\",updateState:function(e,t,n,o){r(n,s())}});let l=[\"top\",\"middle\",\"bottom\"];return e.push({type:\"select\",options:[\"vertical_align_top\",\"vertical_align_center\",\"vertical_align_bottom\"],render:function(e){return'<i class=\"material-icons\">'+e+\"</i>\"},value:1,onchange:function(e,t,n,o,r){const i=s();let a=i.getSelected(!0);a&&i.setStyle(Object.fromEntries(a.map((function(e){return[e,\"vertical-align: \"+l[r]]}))))},updateState:function(e,t,n){r(n,s())}}),e.push({content:\"web\",tooltip:jSuites.translate(\"Merge the selected cells\"),onclick:function(){const e=s();if(e.selectedCell&&confirm(jSuites.translate(\"The merged cells will retain the value of the top-left cell only. Are you sure?\"))){const t=[Math.min(e.selectedCell[0],e.selectedCell[2]),Math.min(e.selectedCell[1],e.selectedCell[3]),Math.max(e.selectedCell[0],e.selectedCell[2]),Math.max(e.selectedCell[1],e.selectedCell[3])];let s=(0,n.getCellNameFromCoords)(t[0],t[1]);if(e.records[t[1]][t[0]].element.getAttribute(\"data-merged\"))e.removeMerge(s);else{let n=t[2]-t[0]+1,o=t[3]-t[1]+1;1===n&&1===o||e.setMerge(s,n,o)}}},updateState:function(e,t,n){r(n,s())}}),e.push({type:\"select\",options:[\"border_all\",\"border_outer\",\"border_inner\",\"border_horizontal\",\"border_vertical\",\"border_left\",\"border_top\",\"border_right\",\"border_bottom\",\"border_clear\"],columns:5,render:function(e){return'<i class=\"material-icons\">'+e+\"</i>\"},right:!0,onchange:function(e,t,o,r){const l=s();if(l.selectedCell){const e=[Math.min(l.selectedCell[0],l.selectedCell[2]),Math.min(l.selectedCell[1],l.selectedCell[3]),Math.max(l.selectedCell[0],l.selectedCell[2]),Math.max(l.selectedCell[1],l.selectedCell[3])];let s=r;if(e){let o=t.thickness||1,r=t.color||\"black\";const i=t.style||\"solid\";\"double\"===i&&(o+=2);let a={},c=e[0],d=e[1],u=e[2],p=e[3];const h=function(e,t,n){let l=[\"\",\"\",\"\",\"\"];l[0]=(\"border_top\"===s||\"border_outer\"===s)&&n===d||(\"border_inner\"===s||\"border_horizontal\"===s)&&n>d||\"border_all\"===s?\"border-top: \"+o+\"px \"+i+\" \"+r:\"border-top: \",l[1]=\"border_all\"!==s&&\"border_right\"!==s&&\"border_outer\"!==s||t!==u?\"border-right: \":\"border-right: \"+o+\"px \"+i+\" \"+r,l[2]=\"border_all\"!==s&&\"border_bottom\"!==s&&\"border_outer\"!==s||n!==p?\"border-bottom: \":\"border-bottom: \"+o+\"px \"+i+\" \"+r,l[3]=(\"border_left\"===s||\"border_outer\"===s)&&t===c||(\"border_inner\"===s||\"border_vertical\"===s)&&t>c||\"border_all\"===s?\"border-left: \"+o+\"px \"+i+\" \"+r:\"border-left: \",a[e]=l.join(\";\")};for(let t=e[1];t<=e[3];t++)for(let s=e[0];s<=e[2];s++)h((0,n.getCellNameFromCoords)(s,t),s,t),l.records[t][s].element.getAttribute(\"data-merged\")&&h((0,n.getCellNameFromCoords)(e[0],e[1]),s,t);Object.keys(a)&&l.setStyle(a)}}},onload:function(e,t){let s=document.createElement(\"div\"),n=document.createElement(\"div\");s.appendChild(n);let o=jSuites.color(n,{closeOnChange:!1,onchange:function(e,s){e.parentNode.children[1].style.color=s,t.color=s}}),r=document.createElement(\"i\");r.classList.add(\"material-icons\"),r.innerHTML=\"color_lens\",r.onclick=function(){o.open()},s.appendChild(r),e.children[1].appendChild(s),n=document.createElement(\"div\"),jSuites.picker(n,{type:\"select\",data:[1,2,3,4,5],render:function(e){return'<div style=\"height: '+e+'px; width: 30px; background-color: black;\"></div>'},onchange:function(e,s,n,o){t.thickness=o},width:\"50px\"}),e.children[1].appendChild(n);const l=document.createElement(\"div\");jSuites.picker(l,{type:\"select\",data:[\"solid\",\"dotted\",\"dashed\",\"double\"],render:function(e){return\"double\"===e?'<div style=\"width: 30px; border-top: 3px '+e+' black;\"></div>':'<div style=\"width: 30px; border-top: 2px '+e+' black;\"></div>'},onchange:function(e,s,n,o){t.style=o},width:\"50px\"}),e.children[1].appendChild(l),n=document.createElement(\"div\"),n.style.flex=\"1\",e.children[1].appendChild(n)},updateState:function(e,t,n){r(n,s())}}),e.push({type:\"divisor\"}),e.push({content:\"fullscreen\",tooltip:\"Toggle Fullscreen\",onclick:function(e,s,n){\"fullscreen\"===n.children[0].textContent?(t.fullscreen(!0),n.children[0].textContent=\"fullscreen_exit\"):(t.fullscreen(!1),n.children[0].textContent=\"fullscreen\")},updateState:function(e,t,s,n){!0===n.parent.config.fullscreen?s.children[0].textContent=\"fullscreen_exit\":s.children[0].textContent=\"fullscreen\"}}),e},i=function(e){const t=this,s=e.items;for(let e=0;e<s.length;e++)s[e].tooltip&&(s[e].title=s[e].tooltip,delete s[e].tooltip),\"select\"==s[e].type?s[e].options?(s[e].data=s[e].options,delete s[e].options):(s[e].data=s[e].v,delete s[e].v,s[e].k&&!s[e].onchange&&(s[e].onchange=function(n,r,l){const i=o.eN.call(t),a=i.getSelected(!0);i.setStyle(Object.fromEntries(a.map((function(t){return[t,s[e].k+\": \"+l]}))))})):\"color\"==s[e].type&&(s[e].type=\"i\",s[e].onclick=function(n,r,l){l.color||(jSuites.color(l,{onchange:function(n,r){const l=o.eN.call(t),i=l.getSelected(!0);l.setStyle(Object.fromEntries(i.map((function(t){return[t,s[e].k+\": \"+r]}))))},onopen:function(e){e.color.select(\"\")}}),l.color.open())})},a=function(e){const t=this,s=document.createElement(\"div\");return s.classList.add(\"jss_toolbar\"),i.call(t,e),\"object\"==typeof t.plugins&&Object.entries(t.plugins).forEach((function([,t]){if(\"function\"==typeof t.toolbar){const s=t.toolbar(e);s&&(e=s)}})),jSuites.toolbar(s,e),s},c=function(e){e.parent.toolbar&&e.parent.toolbar.toolbar.update(e)},d=function(){const e=this;if(e.config.toolbar&&!e.toolbar){let t;Array.isArray(e.config.toolbar)?t={items:e.config.toolbar}:\"object\"==typeof e.config.toolbar?t=e.config.toolbar:(t={items:l.call(e)},\"function\"==typeof e.config.toolbar&&(t=e.config.toolbar(t))),e.toolbar=e.element.insertBefore(a.call(e,t),e.element.children[1])}},u=function(){const e=this;e.toolbar&&(e.toolbar.parentNode.removeChild(e.toolbar),delete e.toolbar)}}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var s=__webpack_module_cache__[e]={exports:{}};return __webpack_modules__[e](s,s.exports,__webpack_require__),s.exports}__webpack_require__.d=function(e,t){for(var s in t)__webpack_require__.o(t,s)&&!__webpack_require__.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},__webpack_require__.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},__webpack_require__.r=function(e){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})};var __webpack_exports__={};__webpack_require__.d(__webpack_exports__,{default:function(){return src}});const lib={jspreadsheet:{}};var libraryBase=lib,dispatch=__webpack_require__(805),internal=__webpack_require__(530),utils_history=__webpack_require__(911);const openEditor=function(e,t,s){const n=this,o=e.getAttribute(\"data-y\"),r=e.getAttribute(\"data-x\");dispatch.A.call(n,\"oneditionstart\",n,e,parseInt(r),parseInt(o)),r>0&&(n.records[o][r-1].element.style.overflow=\"hidden\");const l=function(t){const s=e.getBoundingClientRect(),n=document.createElement(t);return n.style.width=s.width+\"px\",n.style.height=s.height-2+\"px\",n.style.minHeight=s.height-2+\"px\",e.classList.add(\"editor\"),e.innerHTML=\"\",e.appendChild(n),n};if(1==e.classList.contains(\"readonly\"));else if(n.edition=[n.records[o][r].element,n.records[o][r].element.innerHTML,r,o],n.options.columns&&n.options.columns[r]&&\"object\"==typeof n.options.columns[r].type)n.options.columns[r].type.openEditor(e,n.options.data[o][r],parseInt(r),parseInt(o),n,n.options.columns[r],s),dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]);else if(n.options.columns&&n.options.columns[r]&&\"hidden\"==n.options.columns[r].type);else if(n.options.columns&&n.options.columns[r]&&(\"checkbox\"==n.options.columns[r].type||\"radio\"==n.options.columns[r].type)){const t=!e.children[0].checked;n.setValue(e,t),n.edition=null}else if(n.options.columns&&n.options.columns[r]&&\"dropdown\"==n.options.columns[r].type){let t,s=n.options.data[o][r];n.options.columns[r].multiple&&!Array.isArray(s)&&(s=s.split(\";\")),t=\"function\"==typeof n.options.columns[r].filter?n.options.columns[r].filter(n.element,e,r,o,n.options.columns[r].source):n.options.columns[r].source;const i=[];if(t)for(let e=0;e<t.length;e++)i.push(t[e]);const a=l(\"div\");dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]);const c={data:i,multiple:!!n.options.columns[r].multiple,autocomplete:!!n.options.columns[r].autocomplete,opened:!0,value:s,width:\"100%\",height:a.style.minHeight,position:1==n.options.tableOverflow||1==n.parent.config.fullscreen,onclose:function(){closeEditor.call(n,e,!0)}};n.options.columns[r].options&&n.options.columns[r].options.type&&(c.type=n.options.columns[r].options.type),jSuites.dropdown(a,c)}else if(n.options.columns&&n.options.columns[r]&&(\"calendar\"==n.options.columns[r].type||\"color\"==n.options.columns[r].type)){const t=n.options.data[o][r],s=l(\"input\");dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]),s.value=t;const i=n.options.columns[r].options?{...n.options.columns[r].options}:{};if(1!=n.options.tableOverflow&&1!=n.parent.config.fullscreen||(i.position=!0),i.value=n.options.data[o][r],i.opened=!0,i.onclose=function(t,s){closeEditor.call(n,e,!0)},\"color\"==n.options.columns[r].type){jSuites.color(s,i);const t=e.getBoundingClientRect();i.position&&(s.nextSibling.children[1].style.top=t.top+t.height+\"px\",s.nextSibling.children[1].style.left=t.left+\"px\")}else i.format||(i.format=\"YYYY-MM-DD\"),jSuites.calendar(s,i);s.focus()}else if(n.options.columns&&n.options.columns[r]&&\"html\"==n.options.columns[r].type){const t=n.options.data[o][r],s=l(\"div\");dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]),s.style.position=\"relative\";const i=document.createElement(\"div\");i.classList.add(\"jss_richtext\"),s.appendChild(i),jSuites.editor(i,{focus:!0,value:t});const a=e.getBoundingClientRect(),c=i.getBoundingClientRect();window.innerHeight<a.bottom+c.height?i.style.top=a.bottom-(c.height+2)+\"px\":i.style.top=a.top+\"px\",window.innerWidth<a.left+c.width?i.style.left=a.right-(c.width+2)+\"px\":i.style.left=a.left+\"px\"}else if(n.options.columns&&n.options.columns[r]&&\"image\"==n.options.columns[r].type){const t=e.children[0],s=l(\"div\");dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]),s.style.position=\"relative\";const i=document.createElement(\"div\");i.classList.add(\"jclose\"),t&&t.src&&i.appendChild(t),s.appendChild(i),jSuites.image(i,n.options.columns[r]);const a=e.getBoundingClientRect(),c=i.getBoundingClientRect();window.innerHeight<a.bottom+c.height?i.style.top=a.top-(c.height+2)+\"px\":i.style.top=a.top+\"px\",i.style.left=a.left+\"px\"}else{const s=1==t?\"\":n.options.data[o][r];let i;i=n.options.columns&&n.options.columns[r]&&0==n.options.columns[r].wordWrap||!(1==n.options.wordWrap||n.options.columns&&n.options.columns[r]&&1==n.options.columns[r].wordWrap)?l(\"input\"):l(\"textarea\"),dispatch.A.call(n,\"oncreateeditor\",n,e,parseInt(r),parseInt(o),null,n.options.columns[r]),i.focus(),i.value=s;const a=n.options.columns&&n.options.columns[r];if(!(0,internal.dw)(s)&&a){const e=(0,internal.rS)(a);if(e){if(!a.disabledMaskOnEdition)if(a.mask){const e=a.mask.split(\";\");i.setAttribute(\"data-mask\",e[0])}else a.locale&&i.setAttribute(\"data-locale\",a.locale);e.input=i,i.mask=e,jSuites.mask.render(s,e,!1)}}i.onblur=function(){closeEditor.call(n,e,!0)},i.scrollLeft=i.scrollWidth}},closeEditor=function(e,t){const s=this,n=parseInt(e.getAttribute(\"data-x\")),o=parseInt(e.getAttribute(\"data-y\"));let r;if(1==t){if(s.options.columns&&s.options.columns[n]&&\"object\"==typeof s.options.columns[n].type)r=s.options.columns[n].type.closeEditor(e,t,parseInt(n),parseInt(o),s,s.options.columns[n]);else if(s.options.columns&&s.options.columns[n]&&(\"checkbox\"==s.options.columns[n].type||\"radio\"==s.options.columns[n].type||\"hidden\"==s.options.columns[n].type));else if(s.options.columns&&s.options.columns[n]&&\"dropdown\"==s.options.columns[n].type)r=e.children[0].dropdown.close(!0);else if(s.options.columns&&s.options.columns[n]&&\"calendar\"==s.options.columns[n].type)r=e.children[0].calendar.close(!0);else if(s.options.columns&&s.options.columns[n]&&\"color\"==s.options.columns[n].type)r=e.children[0].color.close(!0);else if(s.options.columns&&s.options.columns[n]&&\"html\"==s.options.columns[n].type)r=e.children[0].children[0].editor.getData();else if(s.options.columns&&s.options.columns[n]&&\"image\"==s.options.columns[n].type){const t=e.children[0].children[0].children[0];r=t&&\"IMG\"==t.tagName?t.src:\"\"}else if(s.options.columns&&s.options.columns[n]&&\"numeric\"==s.options.columns[n].type)r=e.children[0].value,\"=\"!=(\"\"+r).substr(0,1)&&\"\"==r&&(r=s.options.columns[n].allowEmpty?\"\":0),e.children[0].onblur=null;else{r=e.children[0].value,e.children[0].onblur=null;const t=s.options.columns&&s.options.columns[n];if(t){const e=(0,internal.rS)(t);if(e&&\"\"!==r&&!(0,internal.dw)(r)&&\"number\"!=typeof r){const t=jSuites.mask.extract(r,e,!0);t&&\"\"!==t.value&&(r=t.value)}}}s.options.data[o][n]==r?e.innerHTML=s.edition[1]:s.setValue(e,r)}else s.options.columns&&s.options.columns[n]&&\"object\"==typeof s.options.columns[n].type?s.options.columns[n].type.closeEditor(e,t,parseInt(n),parseInt(o),s,s.options.columns[n]):s.options.columns&&s.options.columns[n]&&\"dropdown\"==s.options.columns[n].type?e.children[0].dropdown.close(!0):s.options.columns&&s.options.columns[n]&&\"calendar\"==s.options.columns[n].type?e.children[0].calendar.close(!0):s.options.columns&&s.options.columns[n]&&\"color\"==s.options.columns[n].type?e.children[0].color.close(!0):e.children[0].onblur=null,e.innerHTML=s.edition&&s.edition[1]?s.edition[1]:\"\";dispatch.A.call(s,\"oneditionend\",s,e,n,o,r,t),e.classList.remove(\"editor\"),s.edition=null},setCheckRadioValue=function(){const e=this,t=[],s=Object.keys(e.highlighted);for(let n=0;n<s.length;n++){const s=e.highlighted[n].element.getAttribute(\"data-x\"),o=e.highlighted[n].element.getAttribute(\"data-y\");\"checkbox\"!=e.options.columns[s].type&&\"radio\"!=e.options.columns[s].type||t.push(internal.k9.call(e,s,o,!e.options.data[o][s]))}if(t.length){utils_history.Dh.call(e,{action:\"setValue\",records:t,selection:e.selectedCell});const s=t.map((function(e){return{x:e.x,y:e.y,value:e.value,oldValue:e.oldValue}}));dispatch.A.call(e,\"onafterchanges\",e,s)}};var lazyLoading=__webpack_require__(497);const upGet=function(e,t){const s=this;e=parseInt(e);for(let n=(t=parseInt(t))-1;n>=0;n--)if(\"none\"!=s.records[n][e].element.style.display&&\"none\"!=s.rows[n].element.style.display){if(s.records[n][e].element.getAttribute(\"data-merged\")&&s.records[n][e].element==s.records[t][e].element)continue;t=n;break}return t},upVisible=function(e,t){const s=this;let n,o;if(0==e?(n=parseInt(s.selectedCell[0]),o=parseInt(s.selectedCell[1])):(n=parseInt(s.selectedCell[2]),o=parseInt(s.selectedCell[3])),0==t){for(let e=0;e<o;e++)if(\"none\"!=s.records[e][n].element.style.display&&\"none\"!=s.rows[e].element.style.display){o=e;break}}else o=upGet.call(s,n,o);0==e?(s.selectedCell[0]=n,s.selectedCell[1]=o):(s.selectedCell[2]=n,s.selectedCell[3]=o)},up=function(e,t){const s=this;if(e?s.selectedCell[3]>0&&upVisible.call(s,1,t?0:1):(s.selectedCell[1]>0&&upVisible.call(s,0,t?0:1),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),1==s.options.lazyLoading)if(0==s.selectedCell[1]||0==s.selectedCell[3])lazyLoading.wu.call(s,0),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]);else if(lazyLoading.AG.call(s))s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]);else{const e=parseInt(s.tbody.firstChild.getAttribute(\"data-y\"));s.selectedCell[1]-e<30&&(lazyLoading.G_.call(s),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]))}else if(s.options.pagination>0){const e=s.whichPage(s.selectedCell[3]);e!=s.pageNumber&&s.page(e)}internal.Rs.call(s,1)},rightGet=function(e,t){const s=this;e=parseInt(e),t=parseInt(t);for(let n=e+1;n<s.headers.length;n++)if(\"none\"!=s.records[t][n].element.style.display){if(s.records[t][n].element.getAttribute(\"data-merged\")&&s.records[t][n].element==s.records[t][e].element)continue;e=n;break}return e},rightVisible=function(e,t){const s=this;let n,o;if(0==e?(n=parseInt(s.selectedCell[0]),o=parseInt(s.selectedCell[1])):(n=parseInt(s.selectedCell[2]),o=parseInt(s.selectedCell[3])),0==t){for(let e=s.headers.length-1;e>n;e--)if(\"none\"!=s.records[o][e].element.style.display){n=e;break}}else n=rightGet.call(s,n,o);0==e?(s.selectedCell[0]=n,s.selectedCell[1]=o):(s.selectedCell[2]=n,s.selectedCell[3]=o)},right=function(e,t){const s=this;e?s.selectedCell[2]<s.headers.length-1&&rightVisible.call(s,1,t?0:1):(s.selectedCell[0]<s.headers.length-1&&rightVisible.call(s,0,t?0:1),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),internal.Rs.call(s,2)},downGet=function(e,t){const s=this;e=parseInt(e);for(let n=(t=parseInt(t))+1;n<s.rows.length;n++)if(\"none\"!=s.records[n][e].element.style.display&&\"none\"!=s.rows[n].element.style.display){if(s.records[n][e].element.getAttribute(\"data-merged\")&&s.records[n][e].element==s.records[t][e].element)continue;t=n;break}return t},downVisible=function(e,t){const s=this;let n,o;if(0==e?(n=parseInt(s.selectedCell[0]),o=parseInt(s.selectedCell[1])):(n=parseInt(s.selectedCell[2]),o=parseInt(s.selectedCell[3])),0==t){for(let e=s.rows.length-1;e>o;e--)if(\"none\"!=s.records[e][n].element.style.display&&\"none\"!=s.rows[e].element.style.display){o=e;break}}else o=downGet.call(s,n,o);0==e?(s.selectedCell[0]=n,s.selectedCell[1]=o):(s.selectedCell[2]=n,s.selectedCell[3]=o)},down=function(e,t){const s=this;if(e?s.selectedCell[3]<s.records.length-1&&downVisible.call(s,1,t?0:1):(s.selectedCell[1]<s.records.length-1&&downVisible.call(s,0,t?0:1),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),1==s.options.lazyLoading)s.selectedCell[1]==s.records.length-1||s.selectedCell[3]==s.records.length-1?(lazyLoading.wu.call(s,-1),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3])):lazyLoading.AG.call(s)?s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]):parseInt(s.tbody.lastChild.getAttribute(\"data-y\"))-s.selectedCell[3]<30&&(lazyLoading.p6.call(s),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]));else if(s.options.pagination>0){const e=s.whichPage(s.selectedCell[3]);e!=s.pageNumber&&s.page(e)}internal.Rs.call(s,3)},leftGet=function(e,t){const s=this;e=parseInt(e),t=parseInt(t);for(let n=e-1;n>=0;n--)if(\"none\"!=s.records[t][n].element.style.display){if(s.records[t][n].element.getAttribute(\"data-merged\")&&s.records[t][n].element==s.records[t][e].element)continue;e=n;break}return e},leftVisible=function(e,t){const s=this;let n,o;if(0==e?(n=parseInt(s.selectedCell[0]),o=parseInt(s.selectedCell[1])):(n=parseInt(s.selectedCell[2]),o=parseInt(s.selectedCell[3])),0==t){for(let e=0;e<n;e++)if(\"none\"!=s.records[o][e].element.style.display){n=e;break}}else n=leftGet.call(s,n,o);0==e?(s.selectedCell[0]=n,s.selectedCell[1]=o):(s.selectedCell[2]=n,s.selectedCell[3]=o)},left=function(e,t){const s=this;e?s.selectedCell[2]>0&&leftVisible.call(s,1,t?0:1):(s.selectedCell[0]>0&&leftVisible.call(s,0,t?0:1),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),internal.Rs.call(s,0)},first=function(e,t){const s=this;if(e?t?s.selectedCell[3]=0:leftVisible.call(s,1,0):(t?s.selectedCell[1]=0:leftVisible.call(s,0,0),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),1!=s.options.lazyLoading||0!=s.selectedCell[1]&&0!=s.selectedCell[3]){if(s.options.pagination>0){const e=s.whichPage(s.selectedCell[3]);e!=s.pageNumber&&s.page(e)}}else lazyLoading.wu.call(s,0);s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),internal.Rs.call(s,1)},last=function(e,t){const s=this;if(e?t?s.selectedCell[3]=s.records.length-1:rightVisible.call(s,1,0):(t?s.selectedCell[1]=s.records.length-1:rightVisible.call(s,0,0),s.selectedCell[2]=s.selectedCell[0],s.selectedCell[3]=s.selectedCell[1]),1!=s.options.lazyLoading||s.selectedCell[1]!=s.records.length-1&&s.selectedCell[3]!=s.records.length-1){if(s.options.pagination>0){const e=s.whichPage(s.selectedCell[3]);e!=s.pageNumber&&s.page(e)}}else lazyLoading.wu.call(s,-1);s.updateSelectionFromCoords(s.selectedCell[0],s.selectedCell[1],s.selectedCell[2],s.selectedCell[3]),internal.Rs.call(s,3)};var merges=__webpack_require__(910),selection=__webpack_require__(657),helpers=__webpack_require__(978),internalHelpers=__webpack_require__(689);const copy=function(e,t,s,n,o,r,l){const i=this;t||(t=\"\\t\");const a=new RegExp(t,\"ig\"),c=[];let d=[],u=[];const p=[],h=[],m=i.options.data[0].length,f=i.options.data.length;let g=\"\",y=!1,b=\"\",C=\"\",j=0,w=0,_=0,B=0,v=!0;for(let t=0;t<f;t++)for(let s=0;s<m;s++)e&&!i.records[t][s].element.classList.contains(\"highlight\")||(_<=s&&(_=s),B<=t&&(B=t));if(m===_+1&&f===B+1&&!1,o&&(1==i.parent.config.includeHeadersOnDownload||n)){if(i.options.nestedHeaders&&i.options.nestedHeaders.length>0){g=i.options.nestedHeaders;for(let e=0;e<g.length;e++){const s=[];for(let t=0;t<g[e].length;t++){const n=parseInt(g[e][t].colspan);s.push(g[e][t].title);for(let e=0;e<n-1;e++)s.push(\"\")}C+=s.join(t)+\"\\r\\n\"}}y=!0}i.style=[];for(let s=0;s<f;s++){d=[],u=[];for(let t=0;t<m;t++)if(!e||i.records[s][t].element.classList.contains(\"highlight\")){1==y&&c.push(i.headers[t].textContent);let e,n=i.options.data[s][t];n.match&&(n.match(a)||n.match(/,/g)||n.match(/\\n/)||n.match(/\"/))&&(n=n.replace(new RegExp('\"',\"g\"),'\"\"'),n='\"'+n+'\"'),d.push(n),i.options.columns&&i.options.columns[t]&&(\"checkbox\"==i.options.columns[t].type||\"radio\"==i.options.columns[t].type)?e=n:(e=i.records[s][t].element.innerHTML,e.match&&(e.match(a)||e.match(/,/g)||e.match(/\\n/)||e.match(/\"/))&&(e=e.replace(new RegExp('\"',\"g\"),'\"\"'),e='\"'+e+'\"')),u.push(e),g=i.records[s][t].element.getAttribute(\"style\"),g=g.replace(\"display: none;\",\"\"),i.style.push(g||\"\")}d.length&&(y&&(j=d.length,p.push(c.join(t))),p.push(d.join(t))),u.length&&(w++,y&&(h.push(c.join(t)),y=!1),h.push(u.join(t)))}m==j&&f==w&&(b=C);const A=b+p.join(\"\\r\\n\");let x=b+h.join(\"\\r\\n\");if(!s){const e=[Math.min(i.selectedCell[0],i.selectedCell[2]),Math.min(i.selectedCell[1],i.selectedCell[3]),Math.max(i.selectedCell[0],i.selectedCell[2]),Math.max(i.selectedCell[1],i.selectedCell[3])],t=dispatch.A.call(i,\"oncopy\",i,e,x,r);if(t)x=t;else if(!1===t)return!1;i.textarea.value=x,i.textarea.select(),document.execCommand(\"copy\")}if(i.data=1==l?x:A,i.hashString=selection.tW.call(i,i.data),!s&&(selection.kA.call(i),i.highlighted))for(let e=0;e<i.highlighted.length;e++)i.highlighted[e].element.classList.add(\"copying\"),i.highlighted[e].element.classList.contains(\"highlight-left\")&&i.highlighted[e].element.classList.add(\"copying-left\"),i.highlighted[e].element.classList.contains(\"highlight-right\")&&i.highlighted[e].element.classList.add(\"copying-right\"),i.highlighted[e].element.classList.contains(\"highlight-top\")&&i.highlighted[e].element.classList.add(\"copying-top\"),i.highlighted[e].element.classList.contains(\"highlight-bottom\")&&i.highlighted[e].element.classList.add(\"copying-bottom\");return i.data},paste=function(e,t,s){const n=this,o=(0,selection.tW)(s);let r=o==n.hashString?n.style:null;o==n.hashString&&(s=n.data),s=(0,helpers.parseCSV)(s,\"\\t\");const l=n.selectedCell[2]-e+1,i=n.selectedCell[3]-t+1,a=s[0].length;if(l>1&Number.isInteger(l/a)){const e=l/a;if(r){const t=[];for(let s=0;s<r.length;s+=a){const n=r.slice(s,s+a);for(let s=0;s<e;s++)t.push(...n)}r=t}const t=s.map((function(t,s){const n=Array.apply(null,{length:e*t.length}).map((function(e,s){return t[s%t.length]}));return n}));s=t}const c=s.length;if(i>1&Number.isInteger(i/c)){const e=i/c;if(r){const t=[];for(let s=0;s<e;s++)t.push(...r);r=t}const t=Array.apply(null,{length:e*c}).map((function(e,t){return s[t%c]}));s=t}const d=dispatch.A.call(n,\"onbeforepaste\",n,s.map((function(e){return e.map((function(e){return{value:e}}))})),e,t);if(!1===d)return!1;if(d&&(s=d),null!=e&&null!=t&&s){let o=0,l=0;const i=[],a={},c={};let d=0,u=parseInt(e),p=parseInt(t),h=null;const m=n.headers.slice(u).filter((e=>\"none\"===e.style.display)).length,f=u+m+s[0].length,g=n.headers.length;f>g&&(n.skipUpdateTableReferences=!0,n.insertColumn(f-g));const y=n.rows.slice(p).filter((e=>\"none\"===e.element.style.display)).length,b=p+y+s.length,C=n.rows.length;for(b>C&&(n.skipUpdateTableReferences=!0,n.insertRow(b-C)),n.skipUpdateTableReferences&&(n.skipUpdateTableReferences=!1,internal.o8.call(n));h=s[l];){for(o=0,u=parseInt(e);null!=h[o];){let e=h[o];n.options.columns&&n.options.columns[o]&&\"calendar\"==n.options.columns[o].type&&(e=jSuites.calendar.extractDateFromString(e,n.options.columns[o].options&&n.options.columns[o].options.format||\"YYYY-MM-DD\"));const t=internal.k9.call(n,u,p,e);if(i.push(t),internal.xF.call(n,u,p,i),r&&r[d]){const e=(0,internalHelpers.t3)([u,p]);a[e]=r[d],c[e]=n.getStyle(e),n.records[p][u].element.setAttribute(\"style\",r[d]),d++}if(o++,null!=h[o]){if(u>=n.headers.length-1){if(0==n.options.allowInsertColumn)break;n.insertColumn()}u=rightGet.call(n,u,p)}}if(l++,s[l]){if(p>=n.rows.length-1){if(0==n.options.allowInsertRow)break;n.insertRow()}p=downGet.call(n,e,p)}}selection.AH.call(n,e,t,u,p),utils_history.Dh.call(n,{action:\"setValue\",records:i,selection:n.selectedCell,newStyle:a,oldStyle:c}),internal.am.call(n);const j=[];for(let n=0;n<s.length;n++)for(let o=0;o<s[n].length;o++)j.push({x:o+e,y:n+t,value:s[n][o]});dispatch.A.call(n,\"onpaste\",n,j);const w=i.map((function(e){return{x:e.x,y:e.y,value:e.value,oldValue:e.oldValue}}));dispatch.A.call(n,\"onafterchanges\",n,w)}(0,selection.kA)()};var filter=__webpack_require__(829),footer=__webpack_require__(160);const getNumberOfColumns=function(){const e=this;let t=e.options.columns&&e.options.columns.length||0;if(e.options.data&&void 0!==e.options.data[0]){const s=Object.keys(e.options.data[0]);s.length>t&&(t=s.length)}return e.options.minDimensions&&e.options.minDimensions[0]>t&&(t=e.options.minDimensions[0]),t},createCellHeader=function(e){const t=this,s=t.options.columns&&t.options.columns[e]&&t.options.columns[e].width||t.options.defaultColWidth||100,n=t.options.columns&&t.options.columns[e]&&t.options.columns[e].align||t.options.defaultColAlign||\"center\";t.headers[e]=document.createElement(\"td\"),t.headers[e].textContent=t.options.columns&&t.options.columns[e]&&t.options.columns[e].title||(0,helpers.getColumnName)(e),t.headers[e].setAttribute(\"data-x\",e),t.headers[e].style.textAlign=n,t.options.columns&&t.options.columns[e]&&t.options.columns[e].title&&t.headers[e].setAttribute(\"title\",t.headers[e].innerText),t.options.columns&&t.options.columns[e]&&t.options.columns[e].id&&t.headers[e].setAttribute(\"id\",t.options.columns[e].id);const o=document.createElement(\"col\");o.setAttribute(\"width\",s),t.cols[e]={colElement:o,x:e},t.options.columns&&t.options.columns[e]&&\"hidden\"==t.options.columns[e].type&&(t.headers[e].style.display=\"none\",o.style.display=\"none\")},insertColumn=function(e,t,s,n){const o=this;if(0!=o.options.allowInsertColumn){let r,l=[];Array.isArray(e)?(r=1,e&&(l=e)):r=\"number\"==typeof e?e:1,s=!!s;const i=Math.max(o.options.columns.length,...o.options.data.map((function(e){return e.length})))-1;(null==t||t>=parseInt(i)||t<0)&&(t=i),n||(n=[]);for(let e=0;e<r;e++)n[e]||(n[e]={});const a=[];if(Array.isArray(e)){const r=[];for(let t=0;t<o.options.data.length;t++)r.push(t<e.length?e[t]:\"\");const l={column:t+(s?0:1),options:Object.assign({},n[0]),data:r};a.push(l)}else for(let o=0;o<e;o++){const e={column:t+o+(s?0:1),options:Object.assign({},n[o])};a.push(e)}if(!1===dispatch.A.call(o,\"onbeforeinsertcolumn\",o,a))return!1;if(o.options.mergeCells&&Object.keys(o.options.mergeCells).length>0&&merges.Lt.call(o,t,s).length){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;o.destroyMerge()}const c=s?t:t+1;o.options.columns=(0,internalHelpers.Hh)(o.options.columns,c,n);const d=o.headers.splice(c),u=o.cols.splice(c),p=[],h=[],m=[],f=[],g=[];for(let e=c;e<r+c;e++)createCellHeader.call(o,e),o.headerContainer.insertBefore(o.headers[e],o.headerContainer.children[e+1]),o.colgroupContainer.insertBefore(o.cols[e].colElement,o.colgroupContainer.children[e+1]),p.push(o.headers[e]),h.push(o.cols[e]);if(o.options.footers)for(let e=0;e<o.options.footers.length;e++){g[e]=[];for(let t=0;t<r;t++)g[e].push(\"\");o.options.footers[e].splice(c,0,g[e])}for(let e=0;e<o.options.data.length;e++){const t=o.options.data[e].splice(c),s=o.records[e].splice(c);f[e]=[],m[e]=[];for(let t=c;t<r+c;t++){const s=l[e]?l[e]:\"\";o.options.data[e][t]=s;const n=internal.P9.call(o,t,e,o.options.data[e][t]);o.records[e][t]={element:n,y:e},o.rows[e]&&o.rows[e].element.insertBefore(n,o.rows[e].element.children[t+1]),o.options.columns&&o.options.columns[t]&&\"function\"==typeof o.options.columns[t].render&&o.options.columns[t].render(n,s,parseInt(t),parseInt(e),o,o.options.columns[t]),f[e].push(s),m[e].push({element:n,x:t,y:e})}Array.prototype.push.apply(o.options.data[e],t),Array.prototype.push.apply(o.records[e],s)}Array.prototype.push.apply(o.headers,d),Array.prototype.push.apply(o.cols,u);for(let e=c;e<o.cols.length;e++)o.cols[e].x=e;for(let e=0;e<o.records.length;e++)for(let t=0;t<o.records[e].length;t++)o.records[e][t].x=t;if(o.options.nestedHeaders&&o.options.nestedHeaders.length>0&&o.options.nestedHeaders[0]&&o.options.nestedHeaders[0][0])for(let e=0;e<o.options.nestedHeaders.length;e++){const t=parseInt(o.options.nestedHeaders[e][o.options.nestedHeaders[e].length-1].colspan)+r;o.options.nestedHeaders[e][o.options.nestedHeaders[e].length-1].colspan=t,o.thead.children[e].children[o.thead.children[e].children.length-1].setAttribute(\"colspan\",t);let s=o.thead.children[e].children[o.thead.children[e].children.length-1].getAttribute(\"data-column\");s=s.split(\",\");for(let e=c;e<r+c;e++)s.push(e);o.thead.children[e].children[o.thead.children[e].children.length-1].setAttribute(\"data-column\",s)}utils_history.Dh.call(o,{action:\"insertColumn\",columnNumber:t,numOfColumns:r,insertBefore:s,columns:n,headers:p,cols:h,records:m,footers:g,data:f}),internal.o8.call(o),dispatch.A.call(o,\"oninsertcolumn\",o,a)}},moveColumn=function(e,t){const s=this;if(s.options.mergeCells&&Object.keys(s.options.mergeCells).length>0){let n;if(n=e>t?1:0,merges.Lt.call(s,e).length||merges.Lt.call(s,t,n).length){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;s.destroyMerge()}}if((e=parseInt(e))>(t=parseInt(t))){s.headerContainer.insertBefore(s.headers[e],s.headers[t]),s.colgroupContainer.insertBefore(s.cols[e].colElement,s.cols[t].colElement);for(let n=0;n<s.rows.length;n++)s.rows[n].element.insertBefore(s.records[n][e].element,s.records[n][t].element)}else{s.headerContainer.insertBefore(s.headers[e],s.headers[t].nextSibling),s.colgroupContainer.insertBefore(s.cols[e].colElement,s.cols[t].colElement.nextSibling);for(let n=0;n<s.rows.length;n++)s.rows[n].element.insertBefore(s.records[n][e].element,s.records[n][t].element.nextSibling)}s.options.columns.splice(t,0,s.options.columns.splice(e,1)[0]),s.headers.splice(t,0,s.headers.splice(e,1)[0]),s.cols.splice(t,0,s.cols.splice(e,1)[0]);const n=Math.min(e,t),o=Math.max(e,t);for(let n=0;n<s.rows.length;n++)s.options.data[n].splice(t,0,s.options.data[n].splice(e,1)[0]),s.records[n].splice(t,0,s.records[n].splice(e,1)[0]);for(let e=n;e<=o;e++)s.cols[e].x=e;for(let e=0;e<s.records.length;e++)for(let t=n;t<=o;t++)s.records[e][t].x=t;if(s.options.footers)for(let n=0;n<s.options.footers.length;n++)s.options.footers[n].splice(t,0,s.options.footers[n].splice(e,1)[0]);utils_history.Dh.call(s,{action:\"moveColumn\",oldValue:e,newValue:t}),internal.o8.call(s),dispatch.A.call(s,\"onmovecolumn\",s,e,t,1)},deleteColumn=function(e,t){const s=this;if(0!=s.options.allowDeleteColumn)if(s.headers.length>1){if(null==e){const n=s.getSelectedColumns(!0);n.length?(e=parseInt(n[0]),t=parseInt(n.length)):(e=s.headers.length-1,t=1)}const n=s.options.data[0].length-1;(null==e||e>n||e<0)&&(e=n),t||(t=1),t>s.options.data[0].length-e&&(t=s.options.data[0].length-e);const o=[];for(let s=0;s<t;s++)o.push(s+e);if(!1===dispatch.A.call(s,\"onbeforedeletecolumn\",s,o))return!1;if(parseInt(e)>-1){let n=!1;if(s.options.mergeCells&&Object.keys(s.options.mergeCells).length>0)for(let o=e;o<e+t;o++)merges.Lt.call(s,o,null).length&&(n=!0);if(n){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;s.destroyMerge()}const r=s.options.columns?s.options.columns.splice(e,t):void 0;for(let n=e;n<e+t;n++)s.cols[n].colElement.className=\"\",s.headers[n].className=\"\",s.cols[n].colElement.parentNode.removeChild(s.cols[n].colElement),s.headers[n].parentNode.removeChild(s.headers[n]);const l=s.headers.splice(e,t),i=s.cols.splice(e,t),a=[],c=[],d=[];for(let n=0;n<s.options.data.length;n++)for(let o=e;o<e+t;o++)s.records[n][o].element.className=\"\",s.records[n][o].element.parentNode.removeChild(s.records[n][o].element);for(let n=0;n<s.options.data.length;n++)c[n]=s.options.data[n].splice(e,t),a[n]=s.records[n].splice(e,t);for(let t=e;t<s.cols.length;t++)s.cols[t].x=t;for(let t=0;t<s.records.length;t++)for(let n=e;n<s.records[t].length;n++)s.records[t][n].x=n;if(s.options.footers)for(let n=0;n<s.options.footers.length;n++)d[n]=s.options.footers[n].splice(e,t);if(selection.at.call(s,0,e,e+t-1),s.options.nestedHeaders&&s.options.nestedHeaders.length>0&&s.options.nestedHeaders[0]&&s.options.nestedHeaders[0][0])for(let e=0;e<s.options.nestedHeaders.length;e++){const n=parseInt(s.options.nestedHeaders[e][s.options.nestedHeaders[e].length-1].colspan)-t;s.options.nestedHeaders[e][s.options.nestedHeaders[e].length-1].colspan=n,s.thead.children[e].children[s.thead.children[e].children.length-1].setAttribute(\"colspan\",n)}utils_history.Dh.call(s,{action:\"deleteColumn\",columnNumber:e,numOfColumns:t,insertBefore:1,columns:r,headers:l,cols:i,records:a,footers:d,data:c}),internal.o8.call(s),dispatch.A.call(s,\"ondeletecolumn\",s,o)}}else console.error(\"Jspreadsheet: It is not possible to delete the last column\")},getWidth=function(e){const t=this;let s;if(void 0===e){s=[];for(let e=0;e<t.headers.length;e++)s.push(t.options.columns&&t.options.columns[e]&&t.options.columns[e].width||t.options.defaultColWidth||100)}else s=parseInt(t.cols[e].colElement.getAttribute(\"width\"));return s},setWidth=function(e,t,s){const n=this;if(t){if(Array.isArray(e)){s||(s=[]);for(let o=0;o<e.length;o++){s[o]||(s[o]=parseInt(n.cols[e[o]].colElement.getAttribute(\"width\")));const r=Array.isArray(t)&&t[o]?t[o]:t;n.cols[e[o]].colElement.setAttribute(\"width\",r),n.options.columns||(n.options.columns=[]),n.options.columns[e[o]]||(n.options.columns[e[o]]={}),n.options.columns[e[o]].width=r}}else s||(s=parseInt(n.cols[e].colElement.getAttribute(\"width\"))),n.cols[e].colElement.setAttribute(\"width\",t),n.options.columns||(n.options.columns=[]),n.options.columns[e]||(n.options.columns[e]={}),n.options.columns[e].width=t;utils_history.Dh.call(n,{action:\"setWidth\",column:e,oldValue:s,newValue:t}),dispatch.A.call(n,\"onresizecolumn\",n,e,t,s),selection.Aq.call(n)}},showColumn=function(e){const t=this;Array.isArray(e)||(e=[e]);for(let s=0;s<e.length;s++){const n=e[s];t.headers[n].style.display=\"\",t.cols[n].colElement.style.display=\"\",t.filter&&t.filter.children.length>n+1&&(t.filter.children[n+1].style.display=\"\");for(let e=0;e<t.options.data.length;e++)t.records[e][n].element.style.display=\"\"}t.options.footers&&footer.e.call(t),t.resetSelection()},hideColumn=function(e){const t=this;Array.isArray(e)||(e=[e]);for(let s=0;s<e.length;s++){const n=e[s];t.headers[n].style.display=\"none\",t.cols[n].colElement.style.display=\"none\",t.filter&&t.filter.children.length>n+1&&(t.filter.children[n+1].style.display=\"none\");for(let e=0;e<t.options.data.length;e++)t.records[e][n].element.style.display=\"none\"}t.options.footers&&footer.e.call(t),t.resetSelection()},getColumnData=function(e,t){const s=this,n=[];for(let o=0;o<s.options.data.length;o++)t?n.push(s.records[o][e].element.innerHTML):n.push(s.options.data[o][e]);return n},setColumnData=function(e,t,s){const n=this;for(let o=0;o<n.rows.length;o++){const r=(0,internalHelpers.t3)([e,o]);null!=t[o]&&n.setValue(r,t[o],s)}},createRow=function(e,t){const s=this;s.records[e]||(s.records[e]=[]),t||(t=s.options.data[e]);const n={element:document.createElement(\"tr\"),y:e};s.rows[e]=n,n.element.setAttribute(\"data-y\",e);let o=null;s.options.defaultRowHeight&&(n.element.style.height=s.options.defaultRowHeight+\"px\"),s.options.rows&&s.options.rows[e]&&(s.options.rows[e].height&&(n.element.style.height=s.options.rows[e].height),s.options.rows[e].title&&(o=s.options.rows[e].title)),o||(o=parseInt(e+1));const r=document.createElement(\"td\");r.innerHTML=o,r.setAttribute(\"data-y\",e),r.className=\"jss_row\",n.element.appendChild(r);const l=getNumberOfColumns.call(s);for(let o=0;o<l;o++)s.records[e][o]={element:internal.P9.call(this,o,e,t[o]),x:o,y:e},n.element.appendChild(s.records[e][o].element),s.options.columns&&s.options.columns[o]&&\"function\"==typeof s.options.columns[o].render&&s.options.columns[o].render(s.records[e][o].element,t[o],parseInt(o),parseInt(e),s,s.options.columns[o]);return n},insertRow=function(e,t,s){const n=this;if(0!=n.options.allowInsertRow){let o,r=[];Array.isArray(e)?(o=1,e&&(r=e)):o=void 0!==e?e:1,s=!!s;const l=n.options.data.length-1;(null==t||t>=parseInt(l)||t<0)&&(t=l);const i=[];for(let e=0;e<o;e++){const o=[];for(let e=0;e<n.options.columns.length;e++)o[e]=r[e]?r[e]:\"\";i.push({row:e+t+(s?0:1),data:o})}if(!1===dispatch.A.call(n,\"onbeforeinsertrow\",n,i))return!1;if(n.options.mergeCells&&Object.keys(n.options.mergeCells).length>0&&merges.D0.call(n,t,s).length){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;n.destroyMerge()}if(1==n.options.search){if(n.results&&n.results.length!=n.rows.length){if(!confirm(jSuites.translate(\"This action will clear your search results. Are you sure?\")))return!1;n.resetSearch()}n.results=null}const a=s?t:t+1,c=n.records.splice(a),d=n.options.data.splice(a),u=n.rows.splice(a),p=[],h=[],m=[];for(let e=a;e<o+a;e++){n.options.data[e]=[];for(let t=0;t<n.options.columns.length;t++)n.options.data[e][t]=r[t]?r[t]:\"\";const s=createRow.call(n,e,n.options.data[e]);u[0]?Array.prototype.indexOf.call(n.tbody.children,u[0].element)>=0&&n.tbody.insertBefore(s.element,u[0].element):Array.prototype.indexOf.call(n.tbody.children,n.rows[t].element)>=0&&n.tbody.appendChild(s.element),p.push([...n.records[e]]),h.push([...n.options.data[e]]),m.push(s)}Array.prototype.push.apply(n.records,c),Array.prototype.push.apply(n.options.data,d),Array.prototype.push.apply(n.rows,u);for(let e=a;e<n.rows.length;e++)n.rows[e].y=e;for(let e=a;e<n.records.length;e++)for(let t=0;t<n.records[e].length;t++)n.records[e][t].y=e;n.options.pagination>0&&n.page(n.pageNumber),utils_history.Dh.call(n,{action:\"insertRow\",rowNumber:t,numOfRows:o,insertBefore:s,rowRecords:p,rowData:h,rowNode:m}),internal.o8.call(n),dispatch.A.call(n,\"oninsertrow\",n,i)}},moveRow=function(e,t,s){const n=this;if(n.options.mergeCells&&Object.keys(n.options.mergeCells).length>0){let s;if(s=e>t?1:0,merges.D0.call(n,e).length||merges.D0.call(n,t,s).length){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;n.destroyMerge()}}if(1==n.options.search){if(n.results&&n.results.length!=n.rows.length){if(!confirm(jSuites.translate(\"This action will clear your search results. Are you sure?\")))return!1;n.resetSearch()}n.results=null}s||(Array.prototype.indexOf.call(n.tbody.children,n.rows[t].element)>=0?e>t?n.tbody.insertBefore(n.rows[e].element,n.rows[t].element):n.tbody.insertBefore(n.rows[e].element,n.rows[t].element.nextSibling):n.tbody.removeChild(n.rows[e].element)),n.rows.splice(t,0,n.rows.splice(e,1)[0]),n.records.splice(t,0,n.records.splice(e,1)[0]),n.options.data.splice(t,0,n.options.data.splice(e,1)[0]);const o=Math.min(e,t),r=Math.max(e,t);for(let e=o;e<=r;e++)n.rows[e].y=e;for(let e=o;e<=r;e++)for(let t=0;t<n.records[e].length;t++)n.records[e][t].y=e;n.options.pagination>0&&n.tbody.children.length!=n.options.pagination&&n.page(n.pageNumber),utils_history.Dh.call(n,{action:\"moveRow\",oldValue:e,newValue:t}),internal.o8.call(n),dispatch.A.call(n,\"onmoverow\",n,parseInt(e),parseInt(t),1)},deleteRow=function(e,t){const s=this;if(0!=s.options.allowDeleteRow)if(1==s.options.allowDeletingAllRows||s.options.data.length>1){if(null==e){const n=selection.R5.call(s);0===n.length?(e=s.options.data.length-1,t=1):(e=n[0],t=n.length)}let n=s.options.data.length-1;(null==e||e>n||e<0)&&(e=n),t||(t=1),e+t>=s.options.data.length&&(t=s.options.data.length-e);const o=[];for(let s=0;s<t;s++)o.push(s+e);if(!1===dispatch.A.call(s,\"onbeforedeleterow\",s,o))return!1;if(parseInt(e)>-1){let r=!1;if(s.options.mergeCells&&Object.keys(s.options.mergeCells).length>0)for(let n=e;n<e+t;n++)merges.D0.call(s,n,!1).length&&(r=!0);if(r){if(!confirm(jSuites.translate(\"This action will destroy any existing merged cells. Are you sure?\")))return!1;s.destroyMerge()}if(1==s.options.search){if(s.results&&s.results.length!=s.rows.length){if(!confirm(jSuites.translate(\"This action will clear your search results. Are you sure?\")))return!1;s.resetSearch()}s.results=null}1!=s.options.allowDeletingAllRows&&n+1===t&&(t--,console.error(\"Jspreadsheet: It is not possible to delete the last row\"));for(let n=e;n<e+t;n++)Array.prototype.indexOf.call(s.tbody.children,s.rows[n].element)>=0&&(s.rows[n].element.className=\"\",s.rows[n].element.parentNode.removeChild(s.rows[n].element));const l=s.records.splice(e,t),i=s.options.data.splice(e,t),a=s.rows.splice(e,t);for(let t=e;t<s.rows.length;t++)s.rows[t].y=t;for(let t=e;t<s.records.length;t++)for(let e=0;e<s.records[t].length;e++)s.records[t][e].y=t;s.options.pagination>0&&s.tbody.children.length!=s.options.pagination&&s.page(s.pageNumber),selection.at.call(s,1,e,e+t-1),utils_history.Dh.call(s,{action:\"deleteRow\",rowNumber:e,numOfRows:t,insertBefore:1,rowRecords:l,rowData:i,rowNode:a}),internal.o8.call(s),dispatch.A.call(s,\"ondeleterow\",s,o)}}else console.error(\"Jspreadsheet: It is not possible to delete the last row\")},getHeight=function(e){const t=this;let s;if(void 0===e){s=[];for(let e=0;e<t.rows.length;e++){const n=t.rows[e].element.style.height;n&&(s[e]=n)}}else\"object\"==typeof e&&(e=e.getAttribute(\"data-y\")),s=t.rows[e].element.style.height;return s},setHeight=function(e,t,s){const n=this;t>0&&(s||(s=n.rows[e].element.getAttribute(\"height\"))||(s=n.rows[e].element.getBoundingClientRect().height),t=parseInt(t),n.rows[e].element.style.height=t+\"px\",n.options.rows||(n.options.rows=[]),n.options.rows[e]||(n.options.rows[e]={}),n.options.rows[e].height=t,utils_history.Dh.call(n,{action:\"setHeight\",row:e,oldValue:s,newValue:t}),dispatch.A.call(n,\"onresizerow\",n,e,t,s),selection.Aq.call(n))},showRow=function(e){const t=this;Array.isArray(e)||(e=[e]),e.forEach((function(e){t.rows[e].element.style.display=\"\"}))},hideRow=function(e){const t=this;Array.isArray(e)||(e=[e]),e.forEach((function(e){t.rows[e].element.style.display=\"none\"}))},getRowData=function(e,t){return t?this.records[e].map((function(e){return e.element.innerHTML})):this.options.data[e]},setRowData=function(e,t,s){const n=this;for(let o=0;o<n.headers.length;o++){const r=(0,internalHelpers.t3)([o,e]);null!=t[o]&&n.setValue(r,t[o],s)}};var version={version:\"5.0.0\",host:\"https://bossanova.uk/jspreadsheet\",license:\"MIT\",print:function(){return[[\"Jspreadsheet CE\",this.version,this.host,this.license].join(\"\\r\\n\")]}};const getElement=function(e){let t=0,s=0;return function e(n){n.className&&(n.classList.contains(\"jss_container\")&&(s=n),n.classList.contains(\"jss_spreadsheet\")&&(s=n.querySelector(\":scope > .jtabs-content > .jtabs-selected\"))),\"THEAD\"==n.tagName?t=1:\"TBODY\"==n.tagName&&(t=2),n.parentNode&&(s||e(n.parentNode))}(e),[s,t]},mouseUpControls=function(e){if(libraryBase.jspreadsheet.current)if(libraryBase.jspreadsheet.current.resizing){if(libraryBase.jspreadsheet.current.resizing.column){const e=parseInt(libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.getAttribute(\"width\")),t=libraryBase.jspreadsheet.current.getSelectedColumns();if(t.length>1){const s=[];for(let e=0;e<t.length;e++)s.push(parseInt(libraryBase.jspreadsheet.current.cols[t[e]].colElement.getAttribute(\"width\")));s[t.indexOf(parseInt(libraryBase.jspreadsheet.current.resizing.column))]=libraryBase.jspreadsheet.current.resizing.width,setWidth.call(libraryBase.jspreadsheet.current,t,e,s)}else setWidth.call(libraryBase.jspreadsheet.current,parseInt(libraryBase.jspreadsheet.current.resizing.column),e,libraryBase.jspreadsheet.current.resizing.width);libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.resizing.column].classList.remove(\"resizing\");for(let e=0;e<libraryBase.jspreadsheet.current.records.length;e++)libraryBase.jspreadsheet.current.records[e][libraryBase.jspreadsheet.current.resizing.column]&&libraryBase.jspreadsheet.current.records[e][libraryBase.jspreadsheet.current.resizing.column].element.classList.remove(\"resizing\")}else{libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.children[0].classList.remove(\"resizing\");let e=libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.getAttribute(\"height\");setHeight.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.resizing.row,e,libraryBase.jspreadsheet.current.resizing.height),libraryBase.jspreadsheet.current.resizing.element.classList.remove(\"resizing\")}libraryBase.jspreadsheet.current.resizing=null}else if(libraryBase.jspreadsheet.current.dragging){if(libraryBase.jspreadsheet.current.dragging){if(libraryBase.jspreadsheet.current.dragging.column){const t=e.target.getAttribute(\"data-x\");libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.dragging.column].classList.remove(\"dragging\");for(let e=0;e<libraryBase.jspreadsheet.current.rows.length;e++)libraryBase.jspreadsheet.current.records[e][libraryBase.jspreadsheet.current.dragging.column]&&libraryBase.jspreadsheet.current.records[e][libraryBase.jspreadsheet.current.dragging.column].element.classList.remove(\"dragging\");for(let e=0;e<libraryBase.jspreadsheet.current.headers.length;e++)libraryBase.jspreadsheet.current.headers[e].classList.remove(\"dragging-left\"),libraryBase.jspreadsheet.current.headers[e].classList.remove(\"dragging-right\");t&&libraryBase.jspreadsheet.current.dragging.column!=libraryBase.jspreadsheet.current.dragging.destination&&libraryBase.jspreadsheet.current.moveColumn(libraryBase.jspreadsheet.current.dragging.column,libraryBase.jspreadsheet.current.dragging.destination)}else{let e;libraryBase.jspreadsheet.current.dragging.element.nextSibling?(e=parseInt(libraryBase.jspreadsheet.current.dragging.element.nextSibling.getAttribute(\"data-y\")),libraryBase.jspreadsheet.current.dragging.row<e&&(e-=1)):e=parseInt(libraryBase.jspreadsheet.current.dragging.element.previousSibling.getAttribute(\"data-y\")),libraryBase.jspreadsheet.current.dragging.row!=libraryBase.jspreadsheet.current.dragging.destination&&moveRow.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.dragging.row,e,!0),libraryBase.jspreadsheet.current.dragging.element.classList.remove(\"dragging\")}libraryBase.jspreadsheet.current.dragging=null}}else libraryBase.jspreadsheet.current.selectedCorner&&(libraryBase.jspreadsheet.current.selectedCorner=!1,libraryBase.jspreadsheet.current.selection.length>0&&(selection.kF.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.selection[0],libraryBase.jspreadsheet.current.selection[libraryBase.jspreadsheet.current.selection.length-1]),selection.gG.call(libraryBase.jspreadsheet.current)));libraryBase.jspreadsheet.timeControl&&(clearTimeout(libraryBase.jspreadsheet.timeControl),libraryBase.jspreadsheet.timeControl=null),libraryBase.jspreadsheet.isMouseAction=!1},mouseDownControls=function(e){let t;t=(e=e||window.event).buttons?e.buttons:e.button?e.button:e.which;const s=getElement(e.target);if(s[0]?libraryBase.jspreadsheet.current!=s[0].jssWorksheet&&(libraryBase.jspreadsheet.current&&(libraryBase.jspreadsheet.current.edition&&closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0),libraryBase.jspreadsheet.current.resetSelection()),libraryBase.jspreadsheet.current=s[0].jssWorksheet):libraryBase.jspreadsheet.current&&(libraryBase.jspreadsheet.current.edition&&closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0),e.target.classList.contains(\"jss_object\")||(selection.gE.call(libraryBase.jspreadsheet.current,!0),libraryBase.jspreadsheet.current=null)),libraryBase.jspreadsheet.current&&1==t){if(e.target.classList.contains(\"jss_selectall\"))libraryBase.jspreadsheet.current&&selection.Ub.call(libraryBase.jspreadsheet.current);else if(e.target.classList.contains(\"jss_corner\"))0!=libraryBase.jspreadsheet.current.options.editable&&(libraryBase.jspreadsheet.current.selectedCorner=!0);else{if(1==s[1]){const t=e.target.getAttribute(\"data-x\");if(t){const s=e.target.getBoundingClientRect();if(0!=libraryBase.jspreadsheet.current.options.columnResize&&s.width-e.offsetX<6){libraryBase.jspreadsheet.current.resizing={mousePosition:e.pageX,column:t,width:s.width},libraryBase.jspreadsheet.current.headers[t].classList.add(\"resizing\");for(let e=0;e<libraryBase.jspreadsheet.current.records.length;e++)libraryBase.jspreadsheet.current.records[e][t]&&libraryBase.jspreadsheet.current.records[e][t].element.classList.add(\"resizing\")}else if(0!=libraryBase.jspreadsheet.current.options.columnDrag&&s.height-e.offsetY<6)if(merges.Lt.call(libraryBase.jspreadsheet.current,t).length)console.error(\"Jspreadsheet: This column is part of a merged cell.\");else{libraryBase.jspreadsheet.current.resetSelection(),libraryBase.jspreadsheet.current.dragging={element:e.target,column:t,destination:t},libraryBase.jspreadsheet.current.headers[t].classList.add(\"dragging\");for(let e=0;e<libraryBase.jspreadsheet.current.records.length;e++)libraryBase.jspreadsheet.current.records[e][t]&&libraryBase.jspreadsheet.current.records[e][t].element.classList.add(\"dragging\")}else{let s,n;libraryBase.jspreadsheet.current.selectedHeader&&(e.shiftKey||e.ctrlKey)?(s=libraryBase.jspreadsheet.current.selectedHeader,n=t):(libraryBase.jspreadsheet.current.selectedHeader==t&&0!=libraryBase.jspreadsheet.current.options.allowRenameColumn&&(libraryBase.jspreadsheet.timeControl=setTimeout((function(){libraryBase.jspreadsheet.current.setHeader(t)}),800)),libraryBase.jspreadsheet.current.selectedHeader=t,s=t,n=t),selection.AH.call(libraryBase.jspreadsheet.current,s,0,n,libraryBase.jspreadsheet.current.options.data.length-1,e)}}else if(e.target.parentNode.classList.contains(\"jss_nested\")){let t,s;if(e.target.getAttribute(\"data-column\")){const n=e.target.getAttribute(\"data-column\").split(\",\");t=parseInt(n[0]),s=parseInt(n[n.length-1])}else t=0,s=libraryBase.jspreadsheet.current.options.columns.length-1;selection.AH.call(libraryBase.jspreadsheet.current,t,0,s,libraryBase.jspreadsheet.current.options.data.length-1,e)}}else libraryBase.jspreadsheet.current.selectedHeader=!1;if(2==s[1]){const t=parseInt(e.target.getAttribute(\"data-y\"));if(e.target.classList.contains(\"jss_row\")){const s=e.target.getBoundingClientRect();if(0!=libraryBase.jspreadsheet.current.options.rowResize&&s.height-e.offsetY<6)libraryBase.jspreadsheet.current.resizing={element:e.target.parentNode,mousePosition:e.pageY,row:t,height:s.height},e.target.parentNode.classList.add(\"resizing\");else if(0!=libraryBase.jspreadsheet.current.options.rowDrag&&s.width-e.offsetX<6)merges.D0.call(libraryBase.jspreadsheet.current,t).length?console.error(\"Jspreadsheet: This row is part of a merged cell\"):1==libraryBase.jspreadsheet.current.options.search&&libraryBase.jspreadsheet.current.results?console.error(\"Jspreadsheet: Please clear your search before perform this action\"):(libraryBase.jspreadsheet.current.resetSelection(),libraryBase.jspreadsheet.current.dragging={element:e.target.parentNode,row:t,destination:t},e.target.parentNode.classList.add(\"dragging\"));else{let s,n;null!=libraryBase.jspreadsheet.current.selectedRow&&(e.shiftKey||e.ctrlKey)?(s=libraryBase.jspreadsheet.current.selectedRow,n=t):(libraryBase.jspreadsheet.current.selectedRow=t,s=t,n=t),selection.AH.call(libraryBase.jspreadsheet.current,null,s,null,n,e)}}else if(e.target.classList.contains(\"jclose\")&&e.target.clientWidth-e.offsetX<50&&e.offsetY<50)closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0);else{const t=function(e){const s=e.getAttribute(\"data-x\"),n=e.getAttribute(\"data-y\");return s&&n?[s,n]:e.parentNode?t(e.parentNode):void 0},s=t(e.target);if(s){const t=s[0],n=s[1];libraryBase.jspreadsheet.current.edition&&(libraryBase.jspreadsheet.current.edition[2]==t&&libraryBase.jspreadsheet.current.edition[3]==n||closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0)),libraryBase.jspreadsheet.current.edition||(e.shiftKey?selection.AH.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.selectedCell[0],libraryBase.jspreadsheet.current.selectedCell[1],t,n,e):selection.AH.call(libraryBase.jspreadsheet.current,t,n,void 0,void 0,e)),libraryBase.jspreadsheet.current.selectedHeader=null,libraryBase.jspreadsheet.current.selectedRow=null}}}else libraryBase.jspreadsheet.current.selectedRow=!1;e.target.classList.contains(\"jss_page\")&&(\"<\"==e.target.textContent?libraryBase.jspreadsheet.current.page(0):\">\"==e.target.textContent?libraryBase.jspreadsheet.current.page(e.target.getAttribute(\"title\")-1):libraryBase.jspreadsheet.current.page(e.target.textContent-1))}libraryBase.jspreadsheet.current.edition?libraryBase.jspreadsheet.isMouseAction=!1:libraryBase.jspreadsheet.isMouseAction=!0}else libraryBase.jspreadsheet.isMouseAction=!1},mouseMoveControls=function(e){let t;if(t=(e=e||window.event).buttons?e.buttons:e.button?e.button:e.which,t||(libraryBase.jspreadsheet.isMouseAction=!1),libraryBase.jspreadsheet.current)if(1==libraryBase.jspreadsheet.isMouseAction){if(libraryBase.jspreadsheet.current.resizing)if(libraryBase.jspreadsheet.current.resizing.column){const t=e.pageX-libraryBase.jspreadsheet.current.resizing.mousePosition;if(libraryBase.jspreadsheet.current.resizing.width+t>0){const e=libraryBase.jspreadsheet.current.resizing.width+t;libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.setAttribute(\"width\",e),selection.Aq.call(libraryBase.jspreadsheet.current)}}else{const t=e.pageY-libraryBase.jspreadsheet.current.resizing.mousePosition;if(libraryBase.jspreadsheet.current.resizing.height+t>0){const e=libraryBase.jspreadsheet.current.resizing.height+t;libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.setAttribute(\"height\",e),selection.Aq.call(libraryBase.jspreadsheet.current)}}else if(libraryBase.jspreadsheet.current.dragging)if(libraryBase.jspreadsheet.current.dragging.column){const t=e.target.getAttribute(\"data-x\");if(t)if(merges.Lt.call(libraryBase.jspreadsheet.current,t).length)console.error(\"Jspreadsheet: This column is part of a merged cell.\");else{for(let e=0;e<libraryBase.jspreadsheet.current.headers.length;e++)libraryBase.jspreadsheet.current.headers[e].classList.remove(\"dragging-left\"),libraryBase.jspreadsheet.current.headers[e].classList.remove(\"dragging-right\");libraryBase.jspreadsheet.current.dragging.column==t?libraryBase.jspreadsheet.current.dragging.destination=parseInt(t):e.target.clientWidth/2>e.offsetX?(libraryBase.jspreadsheet.current.dragging.column<t?libraryBase.jspreadsheet.current.dragging.destination=parseInt(t)-1:libraryBase.jspreadsheet.current.dragging.destination=parseInt(t),libraryBase.jspreadsheet.current.headers[t].classList.add(\"dragging-left\")):(libraryBase.jspreadsheet.current.dragging.column<t?libraryBase.jspreadsheet.current.dragging.destination=parseInt(t):libraryBase.jspreadsheet.current.dragging.destination=parseInt(t)+1,libraryBase.jspreadsheet.current.headers[t].classList.add(\"dragging-right\"))}}else{const t=e.target.getAttribute(\"data-y\");if(t)if(merges.D0.call(libraryBase.jspreadsheet.current,t).length)console.error(\"Jspreadsheet: This row is part of a merged cell.\");else{const t=e.target.clientHeight/2>e.offsetY?e.target.parentNode.nextSibling:e.target.parentNode;libraryBase.jspreadsheet.current.dragging.element!=t&&(e.target.parentNode.parentNode.insertBefore(libraryBase.jspreadsheet.current.dragging.element,t),libraryBase.jspreadsheet.current.dragging.destination=Array.prototype.indexOf.call(libraryBase.jspreadsheet.current.dragging.element.parentNode.children,libraryBase.jspreadsheet.current.dragging.element))}}}else{const t=e.target.getAttribute(\"data-x\"),s=e.target.getAttribute(\"data-y\"),n=e.target.getBoundingClientRect();libraryBase.jspreadsheet.current.cursor&&(libraryBase.jspreadsheet.current.cursor.style.cursor=\"\",libraryBase.jspreadsheet.current.cursor=null),e.target.parentNode.parentNode&&e.target.parentNode.parentNode.className&&(e.target.parentNode.parentNode.classList.contains(\"resizable\")&&(e.target&&t&&!s&&n.width-(e.clientX-n.left)<6?(libraryBase.jspreadsheet.current.cursor=e.target,libraryBase.jspreadsheet.current.cursor.style.cursor=\"col-resize\"):e.target&&!t&&s&&n.height-(e.clientY-n.top)<6&&(libraryBase.jspreadsheet.current.cursor=e.target,libraryBase.jspreadsheet.current.cursor.style.cursor=\"row-resize\")),e.target.parentNode.parentNode.classList.contains(\"draggable\")&&(e.target&&!t&&s&&n.width-(e.clientX-n.left)<6||e.target&&t&&!s&&n.height-(e.clientY-n.top)<6)&&(libraryBase.jspreadsheet.current.cursor=e.target,libraryBase.jspreadsheet.current.cursor.style.cursor=\"move\"))}},updateCopySelection=function(e,t){const s=this;selection.gG.call(s);const n=s.selectedContainer[0],o=s.selectedContainer[1],r=s.selectedContainer[2],l=s.selectedContainer[3];if(null!=e&&null!=t){let i,a,c,d;e-r>0?(i=parseInt(r)+1,a=parseInt(e)):(i=parseInt(e),a=parseInt(n)-1),t-l>0?(c=parseInt(l)+1,d=parseInt(t)):(c=parseInt(t),d=parseInt(o)-1),a-i<=d-c?(i=parseInt(n),a=parseInt(r)):(c=parseInt(o),d=parseInt(l));for(let e=c;e<=d;e++)for(let t=i;t<=a;t++)s.records[e][t]&&\"none\"!=s.rows[e].element.style.display&&\"none\"!=s.records[e][t].element.style.display&&(s.records[e][t].element.classList.add(\"selection\"),s.records[c][t].element.classList.add(\"selection-top\"),s.records[d][t].element.classList.add(\"selection-bottom\"),s.records[e][i].element.classList.add(\"selection-left\"),s.records[e][a].element.classList.add(\"selection-right\"),s.selection.push(s.records[e][t].element))}},mouseOverControls=function(e){let t;if(t=(e=e||window.event).buttons?e.buttons:e.button?e.button:e.which,t||(libraryBase.jspreadsheet.isMouseAction=!1),libraryBase.jspreadsheet.current&&1==libraryBase.jspreadsheet.isMouseAction){const t=getElement(e.target);if(t[0]){if(libraryBase.jspreadsheet.current!=t[0].jssWorksheet&&libraryBase.jspreadsheet.current)return!1;let s=e.target.getAttribute(\"data-x\");const n=e.target.getAttribute(\"data-y\");if(libraryBase.jspreadsheet.current.resizing||libraryBase.jspreadsheet.current.dragging);else{if(1==t[1]&&libraryBase.jspreadsheet.current.selectedHeader){s=e.target.getAttribute(\"data-x\");const t=libraryBase.jspreadsheet.current.selectedHeader,n=s;selection.AH.call(libraryBase.jspreadsheet.current,t,0,n,libraryBase.jspreadsheet.current.options.data.length-1,e)}if(2==t[1])if(e.target.classList.contains(\"jss_row\")){if(null!=libraryBase.jspreadsheet.current.selectedRow){const t=libraryBase.jspreadsheet.current.selectedRow,s=n;selection.AH.call(libraryBase.jspreadsheet.current,0,t,libraryBase.jspreadsheet.current.options.data[0].length-1,s,e)}}else libraryBase.jspreadsheet.current.edition||s&&n&&(libraryBase.jspreadsheet.current.selectedCorner?updateCopySelection.call(libraryBase.jspreadsheet.current,s,n):libraryBase.jspreadsheet.current.selectedCell&&selection.AH.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.selectedCell[0],libraryBase.jspreadsheet.current.selectedCell[1],s,n,e))}}}libraryBase.jspreadsheet.timeControl&&(clearTimeout(libraryBase.jspreadsheet.timeControl),libraryBase.jspreadsheet.timeControl=null)},doubleClickControls=function(e){if(libraryBase.jspreadsheet.current)if(e.target.classList.contains(\"jss_corner\")){if(libraryBase.jspreadsheet.current.highlighted.length>0){const e=libraryBase.jspreadsheet.current.highlighted[0].element.getAttribute(\"data-x\"),t=parseInt(libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length-1].element.getAttribute(\"data-y\"))+1,s=libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length-1].element.getAttribute(\"data-x\"),n=libraryBase.jspreadsheet.current.records.length-1;selection.kF.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.records[t][e].element,libraryBase.jspreadsheet.current.records[n][s].element)}}else if(e.target.classList.contains(\"jss_column_filter\")){const t=e.target.getAttribute(\"data-x\");filter.N$.call(libraryBase.jspreadsheet.current,t)}else{const t=getElement(e.target);if(1==t[1]&&0!=libraryBase.jspreadsheet.current.options.columnSorting){const t=e.target.getAttribute(\"data-x\");t&&libraryBase.jspreadsheet.current.orderBy(parseInt(t))}if(2==t[1]&&0!=libraryBase.jspreadsheet.current.options.editable&&!libraryBase.jspreadsheet.current.edition){const t=function(e){if(e.parentNode){const s=e.getAttribute(\"data-x\"),n=e.getAttribute(\"data-y\");return s&&n?e:t(e.parentNode)}},s=t(e.target);s&&s.classList.contains(\"highlight\")&&openEditor.call(libraryBase.jspreadsheet.current,s,void 0,e)}}},pasteControls=function(e){libraryBase.jspreadsheet.current&&libraryBase.jspreadsheet.current.selectedCell&&(libraryBase.jspreadsheet.current.edition||0!=libraryBase.jspreadsheet.current.options.editable&&(e&&e.clipboardData?(paste.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.selectedCell[0],libraryBase.jspreadsheet.current.selectedCell[1],e.clipboardData.getData(\"text\")),e.preventDefault()):window.clipboardData&&paste.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.selectedCell[0],libraryBase.jspreadsheet.current.selectedCell[1],window.clipboardData.getData(\"text\"))))},getRole=function(e){if(e.classList.contains(\"jss_selectall\"))return\"select-all\";if(e.classList.contains(\"jss_corner\"))return\"fill-handle\";let t=e;for(;!t.classList.contains(\"jss_spreadsheet\");){if(t.classList.contains(\"jss_row\"))return\"row\";if(t.classList.contains(\"jss_nested\"))return\"nested\";if(t.classList.contains(\"jtabs-headers\"))return\"tabs\";if(t.classList.contains(\"jtoolbar\"))return\"toolbar\";if(t.classList.contains(\"jss_pagination\"))return\"pagination\";if(\"TBODY\"===t.tagName)return\"cell\";if(\"TFOOT\"===t.tagName)return 0===getElementIndex(e)?\"grid\":\"footer\";if(\"THEAD\"===t.tagName)return\"header\";t=t.parentElement}return\"applications\"},defaultContextMenu=function(e,t,s,n){const o=[];if(\"header\"===n&&(0!=e.options.allowInsertColumn&&o.push({title:jSuites.translate(\"Insert a new column before\"),onclick:function(){e.insertColumn(1,parseInt(t),1)}}),0!=e.options.allowInsertColumn&&o.push({title:jSuites.translate(\"Insert a new column after\"),onclick:function(){e.insertColumn(1,parseInt(t),0)}}),0!=e.options.allowDeleteColumn&&o.push({title:jSuites.translate(\"Delete selected columns\"),onclick:function(){e.deleteColumn(e.getSelectedColumns().length?void 0:parseInt(t))}}),0!=e.options.allowRenameColumn&&o.push({title:jSuites.translate(\"Rename this column\"),onclick:function(){const s=e.getHeader(t),n=prompt(jSuites.translate(\"Column name\"),s);e.setHeader(t,n)}}),0!=e.options.columnSorting&&(o.push({type:\"line\"}),o.push({title:jSuites.translate(\"Order ascending\"),onclick:function(){e.orderBy(t,0)}}),o.push({title:jSuites.translate(\"Order descending\"),onclick:function(){e.orderBy(t,1)}}))),\"row\"!==n&&\"cell\"!==n||(0!=e.options.allowInsertRow&&(o.push({title:jSuites.translate(\"Insert a new row before\"),onclick:function(){e.insertRow(1,parseInt(s),1)}}),o.push({title:jSuites.translate(\"Insert a new row after\"),onclick:function(){e.insertRow(1,parseInt(s))}})),0!=e.options.allowDeleteRow&&o.push({title:jSuites.translate(\"Delete selected rows\"),onclick:function(){e.deleteRow(e.getSelectedRows().length?void 0:parseInt(s))}})),\"cell\"===n&&0!=e.options.allowComments){o.push({type:\"line\"});const n=e.records[s][t].element.getAttribute(\"title\")||\"\";o.push({title:jSuites.translate(n?\"Edit comments\":\"Add comments\"),onclick:function(){const o=prompt(jSuites.translate(\"Comments\"),n);o&&e.setComments((0,helpers.getCellNameFromCoords)(t,s),o)}}),n&&o.push({title:jSuites.translate(\"Clear comments\"),onclick:function(){e.setComments((0,helpers.getCellNameFromCoords)(t,s),\"\")}})}return 0!==o.length&&o.push({type:\"line\"}),\"header\"!==n&&\"row\"!==n&&\"cell\"!==n||(o.push({title:jSuites.translate(\"Copy\")+\"...\",shortcut:\"Ctrl + C\",onclick:function(){copy.call(e,!0)}}),navigator&&navigator.clipboard&&o.push({title:jSuites.translate(\"Paste\")+\"...\",shortcut:\"Ctrl + V\",onclick:function(){e.selectedCell&&navigator.clipboard.readText().then((function(t){t&&paste.call(e,e.selectedCell[0],e.selectedCell[1],t)}))}})),0!=e.parent.config.allowExport&&o.push({title:jSuites.translate(\"Save as\")+\"...\",shortcut:\"Ctrl + S\",onclick:function(){e.download()}}),0!=e.parent.config.about&&o.push({title:jSuites.translate(\"About\"),onclick:function(){void 0===e.parent.config.about||!0===e.parent.config.about?alert(version.print()):alert(e.parent.config.about)}}),o},getElementIndex=function(e){const t=e.parentElement.children;for(let s=0;s<t.length;s++)if(e===t[s])return s;return-1},contextMenuControls=function(e){let t;if(t=\"buttons\"in(e=e||window.event)?e.buttons:e.which||e.button,libraryBase.jspreadsheet.current){const t=libraryBase.jspreadsheet.current.parent;if(libraryBase.jspreadsheet.current.edition)e.preventDefault();else if(t.contextMenu.contextmenu.close(),libraryBase.jspreadsheet.current){const s=getRole(e.target);let n=null,o=null;if(\"cell\"===s){let t=e.target;for(;\"TD\"!==t.tagName;)t=t.parentNode;o=t.getAttribute(\"data-y\"),n=t.getAttribute(\"data-x\"),(!libraryBase.jspreadsheet.current.selectedCell||n<parseInt(libraryBase.jspreadsheet.current.selectedCell[0])||n>parseInt(libraryBase.jspreadsheet.current.selectedCell[2])||o<parseInt(libraryBase.jspreadsheet.current.selectedCell[1])||o>parseInt(libraryBase.jspreadsheet.current.selectedCell[3]))&&selection.AH.call(libraryBase.jspreadsheet.current,n,o,n,o,e)}else if(\"row\"===s||\"header\"===s)\"row\"===s?o=e.target.getAttribute(\"data-y\"):n=e.target.getAttribute(\"data-x\"),(!libraryBase.jspreadsheet.current.selectedCell||n<parseInt(libraryBase.jspreadsheet.current.selectedCell[0])||n>parseInt(libraryBase.jspreadsheet.current.selectedCell[2])||o<parseInt(libraryBase.jspreadsheet.current.selectedCell[1])||o>parseInt(libraryBase.jspreadsheet.current.selectedCell[3]))&&selection.AH.call(libraryBase.jspreadsheet.current,n,o,n,o,e);else if(\"nested\"===s){const t=e.target.getAttribute(\"data-column\").split(\",\");n=getElementIndex(e.target)-1,o=getElementIndex(e.target.parentElement),libraryBase.jspreadsheet.current.selectedCell&&t[0]==parseInt(libraryBase.jspreadsheet.current.selectedCell[0])&&t[t.length-1]==parseInt(libraryBase.jspreadsheet.current.selectedCell[2])&&null==libraryBase.jspreadsheet.current.selectedCell[1]&&null==libraryBase.jspreadsheet.current.selectedCell[3]||selection.AH.call(libraryBase.jspreadsheet.current,t[0],null,t[t.length-1],null,e)}else\"select-all\"===s?selection.Ub.call(libraryBase.jspreadsheet.current):\"tabs\"===s?n=getElementIndex(e.target):\"footer\"===s&&(n=getElementIndex(e.target)-1,o=getElementIndex(e.target.parentElement));let r=defaultContextMenu(libraryBase.jspreadsheet.current,parseInt(n),parseInt(o),s);if(\"function\"==typeof t.config.contextMenu){const l=t.config.contextMenu(libraryBase.jspreadsheet.current,n,o,e,r,s,n,o);if(l)r=l;else if(!1===l)return}\"object\"==typeof t.plugins&&Object.entries(t.plugins).forEach((function([,t]){if(\"function\"==typeof t.contextMenu){const l=t.contextMenu(libraryBase.jspreadsheet.current,null!==n?parseInt(n):null,null!==o?parseInt(o):null,e,r,s,null!==n?parseInt(n):null,null!==o?parseInt(o):null);l&&(r=l)}})),t.contextMenu.contextmenu.open(e,r),e.preventDefault()}}},touchStartControls=function(e){const t=getElement(e.target);if(t[0]?libraryBase.jspreadsheet.current!=t[0].jssWorksheet&&(libraryBase.jspreadsheet.current&&libraryBase.jspreadsheet.current.resetSelection(),libraryBase.jspreadsheet.current=t[0].jssWorksheet):libraryBase.jspreadsheet.current&&(libraryBase.jspreadsheet.current.resetSelection(),libraryBase.jspreadsheet.current=null),libraryBase.jspreadsheet.current&&!libraryBase.jspreadsheet.current.edition){const t=e.target.getAttribute(\"data-x\"),s=e.target.getAttribute(\"data-y\");t&&s&&(selection.AH.call(libraryBase.jspreadsheet.current,t,s,void 0,void 0,e),libraryBase.jspreadsheet.timeControl=setTimeout((function(){\"color\"==libraryBase.jspreadsheet.current.options.columns[t].type?libraryBase.jspreadsheet.tmpElement=null:libraryBase.jspreadsheet.tmpElement=e.target,openEditor.call(libraryBase.jspreadsheet.current,e.target,!1,e)}),500))}},touchEndControls=function(e){libraryBase.jspreadsheet.timeControl&&(clearTimeout(libraryBase.jspreadsheet.timeControl),libraryBase.jspreadsheet.timeControl=null,libraryBase.jspreadsheet.tmpElement&&\"INPUT\"==libraryBase.jspreadsheet.tmpElement.children[0].tagName&&libraryBase.jspreadsheet.tmpElement.children[0].focus(),libraryBase.jspreadsheet.tmpElement=null)},cutControls=function(e){libraryBase.jspreadsheet.current&&(libraryBase.jspreadsheet.current.edition||(copy.call(libraryBase.jspreadsheet.current,!0,void 0,void 0,void 0,void 0,!0),0!=libraryBase.jspreadsheet.current.options.editable&&libraryBase.jspreadsheet.current.setValue(libraryBase.jspreadsheet.current.highlighted.map((function(e){return e.element})),\"\")))},copyControls=function(e){libraryBase.jspreadsheet.current&&copyControls.enabled&&(libraryBase.jspreadsheet.current.edition||copy.call(libraryBase.jspreadsheet.current,!0))},isMac=function(){return navigator.platform.toUpperCase().indexOf(\"MAC\")>=0},isCtrl=function(e){return isMac()?e.metaKey:e.ctrlKey},keyDownControls=function(e){if(libraryBase.jspreadsheet.current){if(libraryBase.jspreadsheet.current.edition)if(27==e.which)libraryBase.jspreadsheet.current.edition&&closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!1),e.preventDefault();else if(13==e.which)if(libraryBase.jspreadsheet.current.options.columns&&libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]]&&\"calendar\"==libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type)closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0);else if(libraryBase.jspreadsheet.current.options.columns&&libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]]&&\"dropdown\"==libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type);else if((1==libraryBase.jspreadsheet.current.options.wordWrap||libraryBase.jspreadsheet.current.options.columns&&libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]]&&1==libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].wordWrap||libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][libraryBase.jspreadsheet.current.edition[2]]&&libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][libraryBase.jspreadsheet.current.edition[2]].length>200)&&e.altKey){const e=libraryBase.jspreadsheet.current.edition[0].children[0];let t=libraryBase.jspreadsheet.current.edition[0].children[0].value;const s=e.selectionStart;t=t.slice(0,s)+\"\\n\"+t.slice(s),e.value=t,e.focus(),e.selectionStart=s+1,e.selectionEnd=s+1}else libraryBase.jspreadsheet.current.edition[0].children[0].blur();else 9==e.which&&(libraryBase.jspreadsheet.current.options.columns&&libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]]&&[\"calendar\",\"html\"].includes(libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type)?closeEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.edition[0],!0):libraryBase.jspreadsheet.current.edition[0].children[0].blur());if(!libraryBase.jspreadsheet.current.edition&&libraryBase.jspreadsheet.current.selectedCell)if(37==e.which)left.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(39==e.which)right.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(38==e.which)up.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(40==e.which)down.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(36==e.which)first.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(35==e.which)last.call(libraryBase.jspreadsheet.current,e.shiftKey,e.ctrlKey),e.preventDefault();else if(46==e.which||8==e.which)0!=libraryBase.jspreadsheet.current.options.editable&&(null!=libraryBase.jspreadsheet.current.selectedRow?0!=libraryBase.jspreadsheet.current.options.allowDeleteRow&&confirm(jSuites.translate(\"Are you sure to delete the selected rows?\"))&&libraryBase.jspreadsheet.current.deleteRow():libraryBase.jspreadsheet.current.selectedHeader?0!=libraryBase.jspreadsheet.current.options.allowDeleteColumn&&confirm(jSuites.translate(\"Are you sure to delete the selected columns?\"))&&libraryBase.jspreadsheet.current.deleteColumn():libraryBase.jspreadsheet.current.setValue(libraryBase.jspreadsheet.current.highlighted.map((function(e){return e.element})),\"\"));else if(13==e.which)e.shiftKey?up.call(libraryBase.jspreadsheet.current):(0!=libraryBase.jspreadsheet.current.options.allowInsertRow&&0!=libraryBase.jspreadsheet.current.options.allowManualInsertRow&&libraryBase.jspreadsheet.current.selectedCell[1]==libraryBase.jspreadsheet.current.options.data.length-1&&libraryBase.jspreadsheet.current.insertRow(),down.call(libraryBase.jspreadsheet.current)),e.preventDefault();else if(9==e.which)e.shiftKey?left.call(libraryBase.jspreadsheet.current):(0!=libraryBase.jspreadsheet.current.options.allowInsertColumn&&0!=libraryBase.jspreadsheet.current.options.allowManualInsertColumn&&libraryBase.jspreadsheet.current.selectedCell[0]==libraryBase.jspreadsheet.current.options.data[0].length-1&&libraryBase.jspreadsheet.current.insertColumn(),right.call(libraryBase.jspreadsheet.current)),e.preventDefault();else if(!e.ctrlKey&&!e.metaKey||e.shiftKey){if(libraryBase.jspreadsheet.current.selectedCell&&0!=libraryBase.jspreadsheet.current.options.editable){const t=libraryBase.jspreadsheet.current.selectedCell[1],s=libraryBase.jspreadsheet.current.selectedCell[0];32==e.keyCode?(e.preventDefault(),\"checkbox\"==libraryBase.jspreadsheet.current.options.columns[s].type||\"radio\"==libraryBase.jspreadsheet.current.options.columns[s].type?setCheckRadioValue.call(libraryBase.jspreadsheet.current):openEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.records[t][s].element,!0,e)):113==e.keyCode?openEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.records[t][s].element,!1,e):1!==e.key.length&&\"Process\"!==e.key||e.altKey||isCtrl(e)||(openEditor.call(libraryBase.jspreadsheet.current,libraryBase.jspreadsheet.current.records[t][s].element,!0,e),libraryBase.jspreadsheet.current.options.columns&&libraryBase.jspreadsheet.current.options.columns[s]&&\"calendar\"==libraryBase.jspreadsheet.current.options.columns[s].type&&e.preventDefault())}}else 65==e.which?(selection.Ub.call(libraryBase.jspreadsheet.current),e.preventDefault()):83==e.which?(libraryBase.jspreadsheet.current.download(),e.preventDefault()):89==e.which?(libraryBase.jspreadsheet.current.redo(),e.preventDefault()):90==e.which?(libraryBase.jspreadsheet.current.undo(),e.preventDefault()):67==e.which?(copy.call(libraryBase.jspreadsheet.current,!0),e.preventDefault()):88==e.which?(0!=libraryBase.jspreadsheet.current.options.editable?cutControls():copyControls(),e.preventDefault()):86==e.which&&pasteControls();else e.target.classList.contains(\"jss_search\")&&(libraryBase.jspreadsheet.timeControl&&clearTimeout(libraryBase.jspreadsheet.timeControl),libraryBase.jspreadsheet.timeControl=setTimeout((function(){libraryBase.jspreadsheet.current.search(e.target.value)}),200))}},wheelControls=function(e){const t=this;1==t.options.lazyLoading&&null==libraryBase.jspreadsheet.timeControlLoading&&(libraryBase.jspreadsheet.timeControlLoading=setTimeout((function(){t.content.scrollTop+t.content.clientHeight>=t.content.scrollHeight-10?lazyLoading.p6.call(t)&&(t.content.scrollTop+t.content.clientHeight>t.content.scrollHeight-10&&(t.content.scrollTop=t.content.scrollTop-t.content.clientHeight),selection.Aq.call(t)):t.content.scrollTop<=t.content.clientHeight&&lazyLoading.G_.call(t)&&(t.content.scrollTop<10&&(t.content.scrollTop=t.content.scrollTop+t.content.clientHeight),selection.Aq.call(t)),libraryBase.jspreadsheet.timeControlLoading=null}),100))};let scrollLeft=0;const updateFreezePosition=function(){const e=this;scrollLeft=e.content.scrollLeft;let t=0;if(scrollLeft>50)for(let s=0;s<e.options.freezeColumns;s++){if(s>0&&(!e.options.columns||!e.options.columns[s-1]||\"hidden\"!==e.options.columns[s-1].type)){let n;n=e.options.columns&&e.options.columns[s-1]&&void 0!==e.options.columns[s-1].width?parseInt(e.options.columns[s-1].width):void 0!==e.options.defaultColWidth?parseInt(e.options.defaultColWidth):100,t+=parseInt(n)}e.headers[s].classList.add(\"jss_freezed\"),e.headers[s].style.left=t+\"px\";for(let t=0;t<e.rows.length;t++)if(e.rows[t]&&e.records[t][s]){const n=scrollLeft+(s>0?e.records[t][s-1].element.style.width:0)-51+\"px\";e.records[t][s].element.classList.add(\"jss_freezed\"),e.records[t][s].element.style.left=n}}else for(let t=0;t<e.options.freezeColumns;t++){e.headers[t].classList.remove(\"jss_freezed\"),e.headers[t].style.left=\"\";for(let s=0;s<e.rows.length;s++)e.records[s][t]&&(e.records[s][t].element.classList.remove(\"jss_freezed\"),e.records[s][t].element.style.left=\"\")}selection.Aq.call(e)},scrollControls=function(e){const t=this;wheelControls.call(t),t.options.freezeColumns>0&&t.content.scrollLeft!=scrollLeft&&updateFreezePosition.call(t),1!=t.options.lazyLoading&&1!=t.options.tableOverflow||t.edition&&\"jdropdown\"!=e.target.className.substr(0,9)&&closeEditor.call(t,t.edition[0],!0)},setEvents=function(e){destroyEvents(e),e.addEventListener(\"mouseup\",mouseUpControls),e.addEventListener(\"mousedown\",mouseDownControls),e.addEventListener(\"mousemove\",mouseMoveControls),e.addEventListener(\"mouseover\",mouseOverControls),e.addEventListener(\"dblclick\",doubleClickControls),e.addEventListener(\"paste\",pasteControls),e.addEventListener(\"contextmenu\",contextMenuControls),e.addEventListener(\"touchstart\",touchStartControls),e.addEventListener(\"touchend\",touchEndControls),e.addEventListener(\"touchcancel\",touchEndControls),e.addEventListener(\"touchmove\",touchEndControls),document.addEventListener(\"keydown\",keyDownControls)},destroyEvents=function(e){e.removeEventListener(\"mouseup\",mouseUpControls),e.removeEventListener(\"mousedown\",mouseDownControls),e.removeEventListener(\"mousemove\",mouseMoveControls),e.removeEventListener(\"mouseover\",mouseOverControls),e.removeEventListener(\"dblclick\",doubleClickControls),e.removeEventListener(\"paste\",pasteControls),e.removeEventListener(\"contextmenu\",contextMenuControls),e.removeEventListener(\"touchstart\",touchStartControls),e.removeEventListener(\"touchend\",touchEndControls),e.removeEventListener(\"touchcancel\",touchEndControls),document.removeEventListener(\"keydown\",keyDownControls)};var toolbar=__webpack_require__(392),pagination=__webpack_require__(167);const setData=function(e){const t=this;if(e&&(t.options.data=e),t.options.data||(t.options.data=[]),t.options.data&&t.options.data[0]&&!Array.isArray(t.options.data[0])){e=[];for(let s=0;s<t.options.data.length;s++){const n=[];for(let e=0;e<t.options.columns.length;e++)n[e]=t.options.data[s][t.options.columns[e].name];e.push(n)}t.options.data=e}let s=0,n=0;const o=t.options.columns&&t.options.columns.length||0,r=t.options.data.length,l=t.options.minDimensions[0],i=t.options.minDimensions[1],a=l>o?l:o,c=i>r?i:r;for(s=0;s<c;s++)for(n=0;n<a;n++)null==t.options.data[s]&&(t.options.data[s]=[]),null==t.options.data[s][n]&&(t.options.data[s][n]=\"\");let d,u;for(t.rows=[],t.results=null,t.records=[],t.history=[],t.historyIndex=-1,t.tbody.innerHTML=\"\",1==t.options.lazyLoading?(d=0,u=t.options.data.length<100?t.options.data.length:100,t.options.pagination&&(t.options.pagination=!1,console.error(\"Jspreadsheet: Pagination will be disable due the lazyLoading\"))):t.options.pagination?(t.pageNumber||(t.pageNumber=0),t.options.pagination,d=t.options.pagination*t.pageNumber,u=t.options.pagination*t.pageNumber+t.options.pagination,t.options.data.length<u&&(u=t.options.data.length)):(d=0,u=t.options.data.length),s=0;s<t.options.data.length;s++){const e=createRow.call(t,s,t.options.data[s]);s>=d&&s<u&&t.tbody.appendChild(e.element)}if(1==t.options.lazyLoading||t.options.pagination&&pagination.IV.call(t),t.options.mergeCells){const e=Object.keys(t.options.mergeCells);for(let s=0;s<e.length;s++){const n=t.options.mergeCells[e[s]];merges.FU.call(t,e[s],n[0],n[1],1)}}internal.am.call(t)},getValue=function(e,t){const s=this;let n,o;if(\"string\"!=typeof e)return null;n=(e=(0,internalHelpers.vu)(e,!0))[0],o=e[1];let r=null;return null!=n&&null!=o&&(s.records[o]&&s.records[o][n]&&t?r=s.records[o][n].element.innerHTML:s.options.data[o]&&\"undefined\"!=s.options.data[o][n]&&(r=s.options.data[o][n])),r},getValueFromCoords=function(e,t,s){const n=this;let o=null;return null!=e&&null!=t&&(n.records[t]&&n.records[t][e]&&s?o=n.records[t][e].element.innerHTML:n.options.data[t]&&\"undefined\"!=n.options.data[t][e]&&(o=n.options.data[t][e])),o},setValue=function(e,t,s){const n=this,o=[];if(\"string\"==typeof e){const r=(0,internalHelpers.vu)(e,!0),l=r[0],i=r[1];o.push(internal.k9.call(n,l,i,t,s)),internal.xF.call(n,l,i,o)}else{let r=null,l=null;if(e&&e.getAttribute&&(r=e.getAttribute(\"data-x\"),l=e.getAttribute(\"data-y\")),null!=r&&null!=l)o.push(internal.k9.call(n,r,l,t,s)),internal.xF.call(n,r,l,o);else{const r=Object.keys(e);if(r.length>0)for(let l=0;l<r.length;l++){let r,i;if(\"string\"==typeof e[l]){const t=(0,internalHelpers.vu)(e[l],!0);r=t[0],i=t[1]}else null!=e[l].x&&null!=e[l].y?(r=e[l].x,i=e[l].y,null!=e[l].value&&(t=e[l].value)):(r=e[l].getAttribute(\"data-x\"),i=e[l].getAttribute(\"data-y\"));null!=r&&null!=i&&(o.push(internal.k9.call(n,r,i,t,s)),internal.xF.call(n,r,i,o))}}}utils_history.Dh.call(n,{action:\"setValue\",records:o,selection:n.selectedCell}),internal.am.call(n);const r=o.map((function(e){return{x:e.x,y:e.y,value:e.value,oldValue:e.oldValue}}));dispatch.A.call(n,\"onafterchanges\",n,r)},setValueFromCoords=function(e,t,s,n){const o=this,r=[];r.push(internal.k9.call(o,e,t,s,n)),internal.xF.call(o,e,t,r),utils_history.Dh.call(o,{action:\"setValue\",records:r,selection:o.selectedCell}),internal.am.call(o);const l=r.map((function(e){return{x:e.x,y:e.y,value:e.value,oldValue:e.oldValue}}));dispatch.A.call(o,\"onafterchanges\",o,l)},getData=function(e,t,s,n){const o=this,r=[];let l=0,i=0;const a=Math.max(...o.options.data.map((function(e){return e.length}))),c=o.options.data.length;for(let s=0;s<c;s++){l=0;for(let n=0;n<a;n++)e&&!o.records[s][n].element.classList.contains(\"highlight\")||(r[i]||(r[i]=[]),r[i][l]=t?o.records[s][n].element.innerHTML:o.options.data[s][n],l++);l>0&&i++}return s?r.map((function(e){return e.join(s)})).join(\"\\r\\n\")+\"\\r\\n\":n?r.map((function(e){const t={};return e.forEach((function(e,s){t[s]=e})),t})):r},getDataFromRange=function(e,t){const s=this,n=(0,helpers.getCoordsFromRange)(e),o=[];for(let e=n[1];e<=n[3];e++){o.push([]);for(let r=n[0];r<=n[2];r++)t?o[o.length-1].push(s.records[e][r].element.innerHTML):o[o.length-1].push(s.options.data[e][r])}return o},search=function(e){const t=this;if(t.options.filters&&filter.dr.call(t),t.resetSelection(),t.pageNumber=0,t.results=[],e){t.searchInput.value!==e&&(t.searchInput.value=e);const s=function(e,s,n){for(let o=0;o<e.length;o++)if((\"\"+e[o]).toLowerCase().search(s)>=0||(\"\"+t.records[n][o].element.innerHTML).toLowerCase().search(s)>=0)return!0;return!1},n=function(e){-1==t.results.indexOf(e)&&t.results.push(e)};let o=e.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,\"\\\\$&\");o=new RegExp(o,\"i\"),t.options.data.forEach((function(e,r){if(s(e,o,r)){const e=merges.D0.call(t,r);if(e.length)for(let s=0;s<e.length;s++){const o=(0,internalHelpers.vu)(e[s],!0);for(let r=0;r<t.options.mergeCells[e[s]][1];r++)n(o[1]+r)}else n(r)}}))}else t.results=null;internal.hG.call(t)},resetSearch=function(){const e=this;e.searchInput.value=\"\",e.search(\"\"),e.results=null},getHeader=function(e){return this.headers[e].textContent},getHeaders=function(e){const t=this,s=[];for(let e=0;e<t.headers.length;e++)s.push(t.getHeader(e));return e?s:s.join(t.options.csvDelimiter)},setHeader=function(e,t){const s=this;if(s.headers[e]){const n=s.headers[e].textContent,o=s.options.columns&&s.options.columns[e]&&s.options.columns[e].title||\"\";t||(t=(0,helpers.getColumnName)(e)),s.headers[e].textContent=t,s.headers[e].setAttribute(\"title\",t),s.options.columns||(s.options.columns=[]),s.options.columns[e]||(s.options.columns[e]={}),s.options.columns[e].title=t,utils_history.Dh.call(s,{action:\"setHeader\",column:e,oldValue:n,newValue:t}),dispatch.A.call(s,\"onchangeheader\",s,parseInt(e),t,o)}},getStyle=function(e,t){const s=this;if(e)return e=(0,internalHelpers.vu)(e,!0),t?s.records[e[1]][e[0]].element.style[t]:s.records[e[1]][e[0]].element.getAttribute(\"style\");{const e={},n=s.options.data[0].length,o=s.options.data.length;for(let r=0;r<o;r++)for(let o=0;o<n;o++){const n=t?s.records[r][o].element.style[t]:s.records[r][o].element.getAttribute(\"style\");n&&(e[(0,internalHelpers.t3)([o,r])]=n)}return e}},setStyle=function(e,t,s,n,o){const r=this,l={},i={},a=function(e,t,s){const o=(0,internalHelpers.vu)(e,!0);if(r.records[o[1]]&&r.records[o[1]][o[0]]&&(0==r.records[o[1]][o[0]].element.classList.contains(\"readonly\")||n)){const a=r.records[o[1]][o[0]].element.style[t];a!=s||n?r.records[o[1]][o[0]].element.style[t]=s:(s=\"\",r.records[o[1]][o[0]].element.style[t]=\"\"),i[e]||(i[e]=[]),l[e]||(l[e]=[]),i[e].push([t+\":\"+a]),l[e].push([t+\":\"+s])}};if(t&&s)\"string\"==typeof e&&a(e,t,s);else{const t=Object.keys(e);for(let s=0;s<t.length;s++){let n=e[t[s]];\"string\"==typeof n&&(n=n.split(\";\"));for(let e=0;e<n.length;e++)\"string\"==typeof n[e]&&(n[e]=n[e].split(\":\")),n[e][0].trim()&&a(t[s],n[e][0].trim(),n[e][1])}}let c=Object.keys(i);for(let e=0;e<c.length;e++)i[c[e]]=i[c[e]].join(\";\");c=Object.keys(l);for(let e=0;e<c.length;e++)l[c[e]]=l[c[e]].join(\";\");o||utils_history.Dh.call(r,{action:\"setStyle\",oldValue:i,newValue:l}),dispatch.A.call(r,\"onchangestyle\",r,l)},resetStyle=function(e,t){const s=this,n=Object.keys(e);for(let e=0;e<n.length;e++){const t=(0,internalHelpers.vu)(n[e],!0);s.records[t[1]]&&s.records[t[1]][t[0]]&&s.records[t[1]][t[0]].element.setAttribute(\"style\",\"\")}s.setStyle(e,null,null,null,t)},download=function(e,t){const s=this;if(0==s.parent.config.allowExport)console.error(\"Export not allowed\");else{let n=\"\";n+=copy.call(s,!1,s.options.csvDelimiter,!0,e,!0,void 0,t);const o=new Blob([\"\\ufeff\"+n],{type:\"text/csv;charset=utf-8;\"});if(window.navigator&&window.navigator.msSaveOrOpenBlob)window.navigator.msSaveOrOpenBlob(o,(s.options.csvFileName||s.options.worksheetName)+\".csv\");else{const e=document.createElement(\"a\");e.setAttribute(\"target\",\"_top\");const t=URL.createObjectURL(o);e.href=t,e.setAttribute(\"download\",(s.options.csvFileName||s.options.worksheetName)+\".csv\"),document.body.appendChild(e),e.click(),e.parentNode.removeChild(e)}}},getComments=function(e){const t=this;if(e)return\"string\"!=typeof e?getComments.call(t):(e=(0,internalHelpers.vu)(e,!0),t.records[e[1]][e[0]].element.getAttribute(\"title\")||\"\");{const e={};for(let s=0;s<t.options.data.length;s++)for(let n=0;n<t.options.columns.length;n++){const o=t.records[s][n].element.getAttribute(\"title\");o&&(e[(0,internalHelpers.t3)([n,s])]=o)}return e}},setComments=function(e,t){const s=this;let n;n=\"string\"==typeof e?{[e]:t}:e;const o={};Object.entries(n).forEach((function([e,t]){const n=(0,helpers.getCoordsFromCellName)(e);o[e]=s.records[n[1]][n[0]].element.getAttribute(\"title\"),s.records[n[1]][n[0]].element.setAttribute(\"title\",t||\"\"),t?(s.records[n[1]][n[0]].element.classList.add(\"jss_comments\"),s.options.comments||(s.options.comments={}),s.options.comments[e]=t):(s.records[n[1]][n[0]].element.classList.remove(\"jss_comments\"),s.options.comments&&s.options.comments[e]&&delete s.options.comments[e])})),utils_history.Dh.call(s,{action:\"setComments\",newValue:n,oldValue:o}),dispatch.A.call(s,\"oncomments\",s,n,o)};var orderBy=__webpack_require__(94);const getWorksheetConfig=function(){return this.options},getSpreadsheetConfig=function(){return this.config},setConfig=function(e,t){const s=this,n=Object.keys(e);let o;s.parent?o=s.parent:(t=!0,o=s),n.forEach((function(n){t?(o.config[n]=e[n],\"toolbar\"===n&&(!0===e[n]?o.showToolbar():!1===e[n]&&o.hideToolbar())):s.options[n]=e[n]}))};var meta=__webpack_require__(654);const setReadOnly=function(e,t){const s=this;let n;if(\"string\"==typeof e){const t=(0,helpers.getCoordsFromCellName)(e);n=s.records[t[1]][t[0]]}else{const t=parseInt(e.getAttribute(\"data-x\")),o=parseInt(e.getAttribute(\"data-y\"));n=s.records[o][t]}t?n.element.classList.add(\"readonly\"):n.element.classList.remove(\"readonly\")},isReadOnly=function(e,t){if(\"string\"==typeof e&&void 0===t){const s=(0,helpers.getCoordsFromCellName)(e);[e,t]=s}return this.records[t][e].element.classList.contains(\"readonly\")},setWorksheetFunctions=function(e){for(let t=0;t<worksheetPublicMethodsLength;t++){const[s,n]=worksheetPublicMethods[t];e[s]=n.bind(e)}},createTable=function(){let e=this;setWorksheetFunctions(e),e.table=document.createElement(\"table\"),e.thead=document.createElement(\"thead\"),e.tbody=document.createElement(\"tbody\"),e.headers=[],e.cols=[],e.content=document.createElement(\"div\"),e.content.classList.add(\"jss_content\"),e.content.onscroll=function(t){scrollControls.call(e,t)},e.content.onwheel=function(t){wheelControls.call(e,t)};const t=document.createElement(\"div\"),s=document.createElement(\"label\");s.innerHTML=jSuites.translate(\"Search\")+\": \",t.appendChild(s),e.searchInput=document.createElement(\"input\"),e.searchInput.classList.add(\"jss_search\"),s.appendChild(e.searchInput),e.searchInput.onfocus=function(){e.resetSelection()};const n=document.createElement(\"div\");if(e.options.pagination>0&&e.options.paginationOptions&&e.options.paginationOptions.length>0){e.paginationDropdown=document.createElement(\"select\"),e.paginationDropdown.classList.add(\"jss_pagination_dropdown\"),e.paginationDropdown.onchange=function(){e.options.pagination=parseInt(this.value),e.page(0)};for(let t=0;t<e.options.paginationOptions.length;t++){const s=document.createElement(\"option\");s.value=e.options.paginationOptions[t],s.innerHTML=e.options.paginationOptions[t],e.paginationDropdown.appendChild(s)}e.paginationDropdown.value=e.options.pagination,n.appendChild(document.createTextNode(jSuites.translate(\"Show \"))),n.appendChild(e.paginationDropdown),n.appendChild(document.createTextNode(jSuites.translate(\"entries\")))}const o=document.createElement(\"div\");o.classList.add(\"jss_filter\"),o.appendChild(n),o.appendChild(t),e.colgroupContainer=document.createElement(\"colgroup\");let r=document.createElement(\"col\");if(r.setAttribute(\"width\",\"50\"),e.colgroupContainer.appendChild(r),e.options.nestedHeaders&&e.options.nestedHeaders.length>0&&e.options.nestedHeaders[0]&&e.options.nestedHeaders[0][0])for(let t=0;t<e.options.nestedHeaders.length;t++)e.thead.appendChild(internal.ju.call(e,e.options.nestedHeaders[t]));e.headerContainer=document.createElement(\"tr\"),r=document.createElement(\"td\"),r.classList.add(\"jss_selectall\"),e.headerContainer.appendChild(r);const l=getNumberOfColumns.call(e);for(let t=0;t<l;t++)createCellHeader.call(e,t),e.headerContainer.appendChild(e.headers[t]),e.colgroupContainer.appendChild(e.cols[t].colElement);if(e.thead.appendChild(e.headerContainer),1==e.options.filters){e.filter=document.createElement(\"tr\");const t=document.createElement(\"td\");e.filter.appendChild(t);for(let t=0;t<e.options.columns.length;t++){const s=document.createElement(\"td\");s.innerHTML=\"&nbsp;\",s.setAttribute(\"data-x\",t),s.className=\"jss_column_filter\",\"hidden\"==e.options.columns[t].type&&(s.style.display=\"none\"),e.filter.appendChild(s)}e.thead.appendChild(e.filter)}e.table=document.createElement(\"table\"),e.table.classList.add(\"jss_worksheet\"),e.table.setAttribute(\"cellpadding\",\"0\"),e.table.setAttribute(\"cellspacing\",\"0\"),e.table.setAttribute(\"unselectable\",\"yes\"),e.table.appendChild(e.colgroupContainer),e.table.appendChild(e.thead),e.table.appendChild(e.tbody),e.options.textOverflow||e.table.classList.add(\"jss_overflow\"),e.corner=document.createElement(\"div\"),e.corner.className=\"jss_corner\",e.corner.setAttribute(\"unselectable\",\"on\"),e.corner.setAttribute(\"onselectstart\",\"return false\"),0==e.options.selectionCopy&&(e.corner.style.display=\"none\"),e.textarea=document.createElement(\"textarea\"),e.textarea.className=\"jss_textarea\",e.textarea.id=\"jss_textarea\",e.textarea.tabIndex=\"-1\",e.textarea.ariaHidden=\"true\";const i=document.createElement(\"a\");i.setAttribute(\"href\",\"https://bossanova.uk/jspreadsheet/\"),e.ads=document.createElement(\"div\"),e.ads.className=\"jss_about\";const a=document.createElement(\"span\");a.innerHTML=\"Jspreadsheet CE\",i.appendChild(a),e.ads.appendChild(i),document.createElement(\"div\").classList.add(\"jss_table\"),e.pagination=document.createElement(\"div\"),e.pagination.classList.add(\"jss_pagination\");const c=document.createElement(\"div\"),d=document.createElement(\"div\");if(e.pagination.appendChild(c),e.pagination.appendChild(d),e.options.pagination||(e.pagination.style.display=\"none\"),1==e.options.search&&e.element.appendChild(o),e.content.appendChild(e.table),e.content.appendChild(e.corner),e.content.appendChild(e.textarea),e.element.appendChild(e.content),e.element.appendChild(e.pagination),e.element.appendChild(e.ads),e.element.classList.add(\"jss_container\"),e.element.jssWorksheet=e,e.element.jspreadsheet=e,1==e.options.tableOverflow&&(e.options.tableHeight&&(e.content.style[\"overflow-y\"]=\"auto\",e.content.style[\"box-shadow\"]=\"rgb(221 221 221) 2px 2px 5px 0.1px\",e.content.style.maxHeight=\"string\"==typeof e.options.tableHeight?e.options.tableHeight:e.options.tableHeight+\"px\"),e.options.tableWidth&&(e.content.style[\"overflow-x\"]=\"auto\",e.content.style.width=\"string\"==typeof e.options.tableWidth?e.options.tableWidth:e.options.tableWidth+\"px\")),1!=e.options.tableOverflow&&e.parent.config.toolbar&&e.element.classList.add(\"with-toolbar\"),0!=e.options.columnDrag&&e.thead.classList.add(\"draggable\"),0!=e.options.columnResize&&e.thead.classList.add(\"resizable\"),0!=e.options.rowDrag&&e.tbody.classList.add(\"draggable\"),0!=e.options.rowResize&&e.tbody.classList.add(\"resizable\"),e.setData.call(e),e.options.style&&(e.setStyle(e.options.style,null,null,1,1),delete e.options.style),Object.defineProperty(e.options,\"style\",{enumerable:!0,configurable:!0,get(){return e.getStyle()}}),e.options.comments&&e.setComments(e.options.comments),e.options.classes){const t=Object.keys(e.options.classes);for(let s=0;s<t.length;s++){const n=(0,internalHelpers.vu)(t[s],!0);e.records[n[1]][n[0]].element.classList.add(e.options.classes[t[s]])}}},prepareTable=function(){const e=this;1==e.options.lazyLoading&&1!=e.options.tableOverflow&&1!=e.parent.config.fullscreen&&(console.error(\"Jspreadsheet: The lazyloading only works when tableOverflow = yes or fullscreen = yes\"),e.options.lazyLoading=!1),e.options.columns||(e.options.columns=[]);let t,s=e.options.columns.length;if(e.options.data&&void 0!==e.options.data[0])if(Array.isArray(e.options.data[0])){const t=e.options.data[0].length;t>s&&(s=t)}else t=Object.keys(e.options.data[0]),t.length>s&&(s=t.length);e.options.minDimensions||(e.options.minDimensions=[0,0]),e.options.minDimensions[0]>s&&(s=e.options.minDimensions[0]);const n=[];for(let o=0;o<s;o++)e.options.columns[o]||(e.options.columns[o]={}),!e.options.columns[o].name&&t&&t[o]&&(e.options.columns[o].name=t[o]),\"dropdown\"==e.options.columns[o].type&&e.options.columns[o].url&&n.push({url:e.options.columns[o].url,index:o,method:\"GET\",dataType:\"json\",success:function(t){e.options.columns[this.index].source||(e.options.columns[this.index].source=[]);for(let s=0;s<t.length;s++)e.options.columns[this.index].source.push(t[s])}});n.length?jSuites.ajax(n,(function(){createTable.call(e)})):createTable.call(e)},getNextDefaultWorksheetName=function(e){const t=/^Sheet(\\d+)$/;let s=0;return e.worksheets.forEach((function(e){const n=t.exec(e.options.worksheetName);n&&(s=Math.max(s,parseInt(n[1])))})),\"Sheet\"+(s+1)},buildWorksheet=async function(){const e=this,t=(e.element,e.parent);\"object\"==typeof t.plugins&&Object.entries(t.plugins).forEach((function([,t]){\"function\"==typeof t.beforeinit&&t.beforeinit(e)})),libraryBase.jspreadsheet.current=e;const s=[];if(e.options.csv){const t=new Promise((t=>{jSuites.ajax({url:e.options.csv,method:\"GET\",dataType:\"text\",success:function(s){const n=(0,helpers.parseCSV)(s,e.options.csvDelimiter);if(1==e.options.csvHeaders&&n.length>0){const t=n.shift();if(t.length>0){e.options.columns||(e.options.columns=[]);for(let s=0;s<t.length;s++)e.options.columns[s]||(e.options.columns[s]={}),void 0===e.options.columns[s].title&&(e.options.columns[s].title=t[s])}}e.options.data=n,prepareTable.call(e),t()}})}));s.push(t)}else if(e.options.url){const t=new Promise((t=>{jSuites.ajax({url:e.options.url,method:\"GET\",dataType:\"json\",success:function(s){e.options.data=s.data?s.data:s,prepareTable.call(e),t()}})}));s.push(t)}else prepareTable.call(e);await Promise.all(s),\"object\"==typeof t.plugins&&Object.entries(t.plugins).forEach((function([,t]){\"function\"==typeof t.init&&t.init(e)}))},createWorksheetObj=function(e){const t=this.parent;e.worksheetName||(e.worksheetName=getNextDefaultWorksheetName(this.parent));const s={parent:t,options:e,filters:[],formula:[],history:[],selection:[],historyIndex:-1};return t.config.worksheets.push(s.options),t.worksheets.push(s),s},createWorksheet=function(e){const t=this.parent;t.creationThroughJss=!0,createWorksheetObj.call(this,e),t.element.tabs.create(e.worksheetName)},openWorksheet=function(e){this.parent.element.tabs.open(e)},deleteWorksheet=function(e){const t=this;t.parent.element.tabs.remove(e);const s=t.parent.worksheets.splice(e,1)[0];dispatch.A.call(t.parent,\"ondeleteworksheet\",s,e)},worksheetPublicMethods=[[\"selectAll\",selection.Ub],[\"updateSelectionFromCoords\",function(e,t,s,n){return selection.AH.call(this,e,t,s,n)}],[\"resetSelection\",function(){return selection.gE.call(this)}],[\"getSelection\",selection.Lo],[\"getSelected\",selection.ef],[\"getSelectedColumns\",selection.Jg],[\"getSelectedRows\",selection.R5],[\"getData\",getData],[\"setData\",setData],[\"getValue\",getValue],[\"getValueFromCoords\",getValueFromCoords],[\"setValue\",setValue],[\"setValueFromCoords\",setValueFromCoords],[\"getWidth\",getWidth],[\"setWidth\",function(e,t){return setWidth.call(this,e,t)}],[\"insertRow\",insertRow],[\"moveRow\",function(e,t){return moveRow.call(this,e,t)}],[\"deleteRow\",deleteRow],[\"hideRow\",hideRow],[\"showRow\",showRow],[\"getRowData\",getRowData],[\"setRowData\",setRowData],[\"getHeight\",getHeight],[\"setHeight\",function(e,t){return setHeight.call(this,e,t)}],[\"getMerge\",merges.fd],[\"setMerge\",function(e,t,s){return merges.FU.call(this,e,t,s)}],[\"destroyMerge\",function(){return merges.VP.call(this)}],[\"removeMerge\",function(e,t){return merges.Zp.call(this,e,t)}],[\"search\",search],[\"resetSearch\",resetSearch],[\"getHeader\",getHeader],[\"getHeaders\",getHeaders],[\"setHeader\",setHeader],[\"getStyle\",getStyle],[\"setStyle\",function(e,t,s,n){return setStyle.call(this,e,t,s,n)}],[\"resetStyle\",resetStyle],[\"insertColumn\",insertColumn],[\"moveColumn\",moveColumn],[\"deleteColumn\",deleteColumn],[\"getColumnData\",getColumnData],[\"setColumnData\",setColumnData],[\"whichPage\",pagination.ho],[\"page\",pagination.MY],[\"download\",download],[\"getComments\",getComments],[\"setComments\",setComments],[\"orderBy\",orderBy.My],[\"undo\",utils_history.tN],[\"redo\",utils_history.ZS],[\"getCell\",internal.tT],[\"getCellFromCoords\",internal.Xr],[\"getLabel\",internal.p9],[\"getConfig\",getWorksheetConfig],[\"setConfig\",setConfig],[\"getMeta\",function(e){return meta.IQ.call(this,e)}],[\"setMeta\",meta.iZ],[\"showColumn\",showColumn],[\"hideColumn\",hideColumn],[\"showIndex\",internal.C6],[\"hideIndex\",internal.TI],[\"getWorksheetActive\",internal.$O],[\"openEditor\",openEditor],[\"closeEditor\",closeEditor],[\"createWorksheet\",createWorksheet],[\"openWorksheet\",openWorksheet],[\"deleteWorksheet\",deleteWorksheet],[\"copy\",function(e){e?cutControls():copy.call(this,!0)}],[\"paste\",paste],[\"executeFormula\",internal.Em],[\"getDataFromRange\",getDataFromRange],[\"quantiyOfPages\",pagination.$f],[\"getRange\",selection.eO],[\"isSelected\",selection.sp],[\"setReadOnly\",setReadOnly],[\"isReadOnly\",isReadOnly],[\"getHighlighted\",selection.kV],[\"dispatch\",dispatch.A],[\"down\",down],[\"first\",first],[\"last\",last],[\"left\",left],[\"right\",right],[\"up\",up],[\"openFilter\",filter.N$],[\"resetFilters\",filter.dr]],worksheetPublicMethodsLength=worksheetPublicMethods.length,factory=function(){},createWorksheets=async function(e,t,s){let n=t.worksheets;if(!n)throw new Error(\"JSS: worksheets are not defined\");{let o={animation:!0,onbeforecreate:function(t,s){return s||getNextDefaultWorksheetName(e)},oncreate:function(s,n){if(e.creationThroughJss)e.creationThroughJss=!1;else{const t=s.tabs.headers.children[s.tabs.headers.children.length-2].innerHTML;createWorksheetObj.call(e.worksheets[0],{minDimensions:[10,15],worksheetName:t})}const o=e.worksheets[e.worksheets.length-1];o.element=n,buildWorksheet.call(o).then((function(){(0,toolbar.nK)(o),dispatch.A.call(o,\"oncreateworksheet\",o,t,e.worksheets.length-1)}))},onchange:function(t,s,n){0!=e.worksheets.length&&e.worksheets[n]&&(0,toolbar.nK)(e.worksheets[n])}};1==t.tabs?o.allowCreate=!0:o.hideHeaders=!0,o.data=[];let r=1;for(let e=0;e<n.length;e++)n[e].worksheetName||(n[e].worksheetName=\"Sheet\"+r++),o.data.push({title:n[e].worksheetName,content:\"\"});s.classList.add(\"jss_spreadsheet\"),s.tabIndex=0;const l=jSuites.tabs(s,o),i=t.style;delete t.style;for(let t=0;t<n.length;t++)n[t].style&&Object.entries(n[t].style).forEach((function([e,s]){\"number\"==typeof s&&(n[t].style[e]=i[s])})),e.worksheets.push({parent:e,element:l.content.children[t],options:n[t],filters:[],formula:[],history:[],selection:[],historyIndex:-1}),await buildWorksheet.call(e.worksheets[t])}};factory.spreadsheet=async function(e,t,s){if(\"TABLE\"==e.tagName){t||(t={}),t.worksheets||(t.worksheets=[]);const s=(0,helpers.createFromTable)(e,t.worksheets[0]);t.worksheets[0]=s;const n=document.createElement(\"div\");e.parentNode.insertBefore(n,e),e.remove(),e=n}let n={worksheets:s,config:t,element:e,el:e};return n.contextMenu=document.createElement(\"div\"),n.contextMenu.className=\"jss_contextmenu\",n.getWorksheetActive=internal.$O.bind(n),n.fullscreen=internal.Y5.bind(n),n.showToolbar=toolbar.ll.bind(n),n.hideToolbar=toolbar.Ar.bind(n),n.getConfig=getSpreadsheetConfig.bind(n),n.setConfig=setConfig.bind(n),n.setPlugins=function(e){n.plugins||(n.plugins={}),\"object\"==typeof e&&Object.entries(e).forEach((function([e,t]){n.plugins[e]=t.call(libraryBase.jspreadsheet,n,{},n.config)}))},n.setPlugins(t.plugins),await createWorksheets(n,t,e),n.element.appendChild(n.contextMenu),jSuites.contextmenu(n.contextMenu,{onclick:function(){n.contextMenu.contextmenu.close(!1)}}),1==n.config.fullscreen&&n.element.classList.add(\"fullscreen\"),toolbar.ll.call(n),t.root?setEvents(t.root):setEvents(document),e.spreadsheet=n,n},factory.worksheet=function(e,t,s){let n={parent:e,options:{}};return void 0===s?e.worksheets.push(n):e.worksheets.splice(s,0,n),Object.assign(n.options,t),n};var utils_factory=factory;libraryBase.jspreadsheet=function(e,t){try{let s=[];return utils_factory.spreadsheet(e,t,s).then((e=>{libraryBase.jspreadsheet.spreadsheet.push(e),dispatch.A.call(e,\"onload\",e)})),s}catch(e){console.error(e)}},libraryBase.jspreadsheet.getWorksheetInstanceByName=function(e,t){const s=libraryBase.jspreadsheet.spreadsheet.find((e=>e.config.namespace===t));if(s)return{};if(null==e){const e=s.worksheets.map((e=>[e.options.worksheetName,e]));return Object.fromEntries(e)}return s.worksheets.find((t=>t.options.worksheetName===e))},libraryBase.jspreadsheet.setDictionary=function(e){jSuites.setDictionary(e)},libraryBase.jspreadsheet.destroy=function(e,t){if(e.spreadsheet){const s=libraryBase.jspreadsheet.spreadsheet.indexOf(e.spreadsheet);libraryBase.jspreadsheet.spreadsheet.splice(s,1);const n=e.spreadsheet.config.root||document;e.spreadsheet=null,e.innerHTML=\"\",t&&destroyEvents(n)}},libraryBase.jspreadsheet.destroyAll=function(){for(let e=0;e<libraryBase.jspreadsheet.spreadsheet.length;e++){const t=libraryBase.jspreadsheet.spreadsheet[e];libraryBase.jspreadsheet.destroy(t.element)}},libraryBase.jspreadsheet.current=null,libraryBase.jspreadsheet.spreadsheet=[],libraryBase.jspreadsheet.helpers={},libraryBase.jspreadsheet.version=function(){return version},Object.entries(helpers).forEach((([e,t])=>{libraryBase.jspreadsheet.helpers[e]=t}));var src=libraryBase.jspreadsheet;jspreadsheet=__webpack_exports__.default})();\n\n    return jspreadsheet;\n})));"
  },
  {
    "path": "dist/jspreadsheet.css",
    "content": ":root {\n    --jss-border-color: #000;\n}\n\n.jss_spreadsheet {\n    outline: none;\n}\n\n.jss_container {\n    display: inline-block;\n    padding-right: 2px;\n    box-sizing: border-box;\n    overscroll-behavior: contain;\n    outline: none;\n}\n\n.fullscreen {\n    position: fixed !important;\n    top: 0px;\n    left: 0px;\n    width: 100%;\n    height: 100%;\n    z-index: 21;\n\n    display: flex;\n    flex-direction: column;\n\n    background-color: #ffffff;\n}\n\n.fullscreen .jtabs-content {\n    flex: 1;\n    overflow: hidden;\n}\n\n.fullscreen .jss_content {\n    overflow: auto;\n    width: 100% !important;\n    height: 100%;\n    max-height: 100% !important;\n}\n\n.fullscreen .jss_container {\n    height: 100%;\n}\n\n.jss_content {\n    display: inline-block;\n    box-sizing: border-box;\n    padding-right: 3px;\n    padding-bottom: 3px;\n    position: relative;\n    scrollbar-width: thin;\n    scrollbar-color: #666 transparent;\n}\n\n@supports (-moz-appearance: none) {\n    .jss_content {\n        padding-right: 10px;\n    }\n}\n\n.jss_content::-webkit-scrollbar {\n    width: 8px;\n    height: 8px;\n}\n\n.jss_content::-webkit-scrollbar-track {\n    background: #eee;\n}\n\n.jss_content::-webkit-scrollbar-thumb {\n    background: #666;\n}\n\n.jss_worksheet {\n    border-collapse: separate;\n    table-layout: fixed;\n    white-space: nowrap;\n    empty-cells: show;\n    border: 0px;\n    background-color: #fff;\n    width: 0;\n\n    border-top: 1px solid transparent;\n    border-left: 1px solid transparent;\n    border-right: 1px solid #ccc;\n    border-bottom: 1px solid #ccc;\n}\n\n.jss_worksheet > thead > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    background-color: #f3f3f3;\n    padding: 2px;\n    cursor: pointer;\n    box-sizing: border-box;\n    overflow: hidden;\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 2;\n}\n\n.jss_worksheet > thead > tr > td.dragging {\n    opacity: 0.5;\n}\n\n.jss_worksheet > thead > tr > td.selected {\n    background-color: #dcdcdc;\n}\n\n.jss_worksheet > thead > tr > td.arrow-up {\n    background-repeat: no-repeat;\n    background-position: center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration: underline;\n}\n\n.jss_worksheet > thead > tr > td.arrow-down {\n    background-repeat: no-repeat;\n    background-position: center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration: underline;\n}\n\n.jss_worksheet > tbody > tr > td:first-child {\n    position: relative;\n    background-color: #f3f3f3;\n    text-align: center;\n}\n\n.jss_worksheet > tbody.resizable > tr > td:first-child::before {\n    content: '\\00a0';\n    width: 100%;\n    height: 3px;\n    position: absolute;\n    bottom: 0px;\n    left: 0px;\n    cursor: row-resize;\n}\n\n.jss_worksheet > tbody.draggable > tr > td:first-child::after {\n    content: '\\00a0';\n    width: 3px;\n    height: 100%;\n    position: absolute;\n    top: 0px;\n    right: 0px;\n    cursor: move;\n}\n\n.jss_worksheet > tbody > tr.dragging > td {\n    background-color: #eee;\n    opacity: 0.5;\n}\n\n.jss_worksheet > tbody > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    padding: 4px;\n    white-space: nowrap;\n    box-sizing: border-box;\n    line-height: 1em;\n}\n\n.jss_overflow > tbody > tr > td {\n    overflow: hidden;\n}\n\n.jss_worksheet > tbody > tr > td:last-child {\n    overflow: hidden;\n}\n\n.jss_worksheet > tbody > tr > td > img {\n    display: inline-block;\n    max-width: 100px;\n}\n\n.jss_worksheet > tbody > tr > td.readonly {\n    color: rgba(0, 0, 0, 0.3);\n}\n.jss_worksheet > tbody > tr.selected > td:first-child {\n    background-color: #dcdcdc;\n}\n.jss_worksheet > tbody > tr > td > select,\n.jss_worksheet > tbody > tr > td > input,\n.jss_worksheet > tbody > tr > td > textarea {\n    border: 0px;\n    border-radius: 0px;\n    outline: 0px;\n    width: 100%;\n    margin: 0px;\n    padding: 0px;\n    padding-right: 2px;\n    background-color: transparent;\n    box-sizing: border-box;\n}\n\n.jss_worksheet > tbody > tr > td > textarea {\n    resize: none;\n    padding-top: 6px !important;\n}\n\n.jss_worksheet > tbody > tr > td > input[type='checkbox'] {\n    width: 12px;\n    margin-top: 2px;\n}\n.jss_worksheet > tbody > tr > td > input[type='radio'] {\n    width: 12px;\n    margin-top: 2px;\n}\n\n.jss_worksheet > tbody > tr > td > select {\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    appearance: none;\n    background-repeat: no-repeat;\n    background-position-x: 100%;\n    background-position-y: 40%;\n    background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+);\n}\n\n.jss_worksheet > tbody > tr > td.jss_dropdown {\n    background-repeat: no-repeat;\n    background-position: top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    overflow-x: hidden;\n}\n\n.jss_worksheet > tbody > tr > td.jss_dropdown.jss_comments {\n    background: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\")\n            top 50% right 5px no-repeat,\n        url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=')\n            top right no-repeat;\n}\n\n.jss_worksheet > tbody > tr > td > .color {\n    width: 90%;\n    height: 10px;\n    margin: auto;\n}\n\n.jss_worksheet > tbody > tr > td > a {\n    text-decoration: underline;\n}\n\n.jss_worksheet > tbody > tr > td.highlight > a {\n    color: blue;\n    cursor: pointer;\n}\n\n.jss_worksheet > tfoot > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    background-color: #f3f3f3;\n    padding: 2px;\n    cursor: pointer;\n    box-sizing: border-box;\n    overflow: hidden;\n}\n\n.jss_worksheet .highlight {\n    background-color: rgba(0, 0, 0, 0.05);\n}\n\n.jss_worksheet .highlight-top {\n    border-top: 1px solid #000;\n    box-shadow: 0px -1px #ccc;\n}\n\n.jss_worksheet .highlight-left {\n    border-left: 1px solid #000;\n    box-shadow: -1px 0px #ccc;\n}\n\n.jss_worksheet .highlight-right {\n    border-right: 1px solid #000;\n}\n\n.jss_worksheet .highlight-bottom {\n    border-bottom: 1px solid #000;\n}\n\n.jss_worksheet .highlight-top.highlight-left {\n    box-shadow: -1px -1px #ccc;\n    -webkit-box-shadow: -1px -1px #ccc;\n    -moz-box-shadow: -1px -1px #ccc;\n}\n\n.jss_worksheet .highlight-selected {\n    background-color: rgba(0, 0, 0, 0);\n}\n.jss_worksheet .selection {\n    background-color: rgba(0, 0, 0, 0.05);\n}\n.jss_worksheet .selection-left {\n    border-left: 1px dotted #000;\n}\n.jss_worksheet .selection-right {\n    border-right: 1px dotted #000;\n}\n.jss_worksheet .selection-top {\n    border-top: 1px dotted #000;\n}\n.jss_worksheet .selection-bottom {\n    border-bottom: 1px dotted #000;\n}\n.jss_corner {\n    position: absolute;\n    background-color: rgb(0, 0, 0);\n    height: 1px;\n    width: 1px;\n    border: 1px solid rgb(255, 255, 255);\n    top: -2000px;\n    left: -2000px;\n    cursor: crosshair;\n    box-sizing: initial;\n    z-index: 20;\n    padding: 2px;\n}\n\n.jss_worksheet .editor {\n    outline: 0px solid transparent;\n    overflow: visible;\n    white-space: nowrap;\n    text-align: left;\n    padding: 0px;\n    box-sizing: border-box;\n    overflow: visible !important;\n}\n\n.jss_worksheet .editor > input {\n    padding-left: 4px;\n}\n\n.jss_worksheet .editor .jupload {\n    position: fixed;\n    top: 100%;\n    z-index: 40;\n    user-select: none;\n    -webkit-font-smoothing: antialiased;\n    font-size: 0.875rem;\n    letter-spacing: 0.2px;\n    -webkit-border-radius: 4px;\n    border-radius: 4px;\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    padding: 10px;\n    background-color: #fff;\n    width: 300px;\n    min-height: 225px;\n    margin-top: 2px;\n}\n\n.jss_worksheet .editor .jupload img {\n    width: 100%;\n    height: auto;\n}\n\n.jss_worksheet .editor .jss_richtext {\n    position: fixed;\n    top: 100%;\n    z-index: 40;\n    user-select: none;\n    -webkit-font-smoothing: antialiased;\n    font-size: 0.875rem;\n    letter-spacing: 0.2px;\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    padding: 10px;\n    background-color: #fff;\n    width: 358px;\n    margin-top: 2px;\n    text-align: left;\n    white-space: initial;\n}\n\n.jss_worksheet .editor .jclose:after {\n    position: absolute;\n    top: 0;\n    right: 0;\n    margin: 10px;\n    content: 'close';\n    font-family: 'Material icons';\n    font-size: 24px;\n    width: 24px;\n    height: 24px;\n    line-height: 24px;\n    cursor: pointer;\n    text-shadow: 0px 0px 5px #fff;\n}\n\n.jss_worksheet,\n.jss_worksheet td,\n.jss_corner {\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    -webkit-user-drag: none;\n    -khtml-user-drag: none;\n    -moz-user-drag: none;\n    -o-user-drag: none;\n    user-drag: none;\n}\n\n.jss_textarea {\n    position: absolute;\n    top: -999px;\n    left: -999px;\n    width: 1px;\n    height: 1px;\n}\n.jss_worksheet .dragline {\n    position: absolute;\n}\n.jss_worksheet .dragline div {\n    position: relative;\n    top: -6px;\n    height: 5px;\n    width: 22px;\n}\n.jss_worksheet .dragline div:hover {\n    cursor: move;\n}\n\n.jss_worksheet .onDrag {\n    background-color: rgba(0, 0, 0, 0.6);\n}\n\n.jss_worksheet .error {\n    border: 1px solid red;\n}\n\n.jss_worksheet thead td.resizing {\n    border-right-style: dotted !important;\n    border-right-color: red !important;\n}\n\n.jss_worksheet tbody tr.resizing > td {\n    border-bottom-style: dotted !important;\n    border-bottom-color: red !important;\n}\n\n.jss_worksheet tbody td.resizing {\n    border-right-style: dotted !important;\n    border-right-color: red !important;\n}\n\n.jss_worksheet .jdropdown-header {\n    border: 0px !important;\n    outline: none !important;\n    width: 100% !important;\n    height: 100% !important;\n    padding: 0px !important;\n    padding-left: 8px !important;\n}\n\n.jss_worksheet .jdropdown-container {\n    margin-top: 1px;\n}\n\n.jss_worksheet .jdropdown-container-header {\n    padding: 0px;\n    margin: 0px;\n    height: inherit;\n}\n\n.jss_worksheet .jdropdown-picker {\n    border: 0px !important;\n    padding: 0px !important;\n    width: inherit;\n    height: inherit;\n}\n\n.jss_worksheet .jss_comments {\n    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=');\n    background-repeat: no-repeat;\n    background-position: top right;\n}\n\n.jss_worksheet .sp-replacer {\n    margin: 2px;\n    border: 0px;\n}\n\n.jss_worksheet > thead > tr.jss_filter > td > input {\n    border: 0px;\n    width: 100%;\n    outline: none;\n}\n\n.jss_about {\n    float: right;\n    font-size: 0.7em;\n    padding: 2px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    display: none;\n}\n.jss_about a {\n    color: #ccc;\n    text-decoration: none;\n}\n\n.jss_about img {\n    display: none;\n}\n\n.jss_filter {\n    display: flex;\n    justify-content: space-between;\n    margin-bottom: 4px;\n}\n\n.jss_filter > div {\n    padding: 8px;\n    align-items: center;\n}\n\n.jss_pagination {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n}\n\n.jss_pagination > div {\n    display: flex;\n    padding: 10px;\n}\n\n.jss_pagination > div:last-child {\n    padding-right: 10px;\n    padding-top: 10px;\n}\n\n.jss_pagination > div > div {\n    text-align: center;\n    width: 36px;\n    height: 36px;\n    line-height: 34px;\n    border: 1px solid #ccc;\n    box-sizing: border-box;\n    margin-left: 2px;\n    cursor: pointer;\n}\n\n.jss_page {\n    font-size: 0.8em;\n}\n\n.jss_page_selected {\n    font-weight: bold;\n    background-color: #f3f3f3;\n}\n\n.jss_toolbar {\n    display: flex;\n    background-color: #f3f3f3;\n    border: 1px solid #ccc;\n    padding: 4px;\n    margin: 0px 2px 4px 1px;\n}\n\n.jss_toolbar:empty {\n    display: none;\n}\n\n.jss_worksheet .dragging-left {\n    background-repeat: no-repeat;\n    background-position: top 50% left 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E\");\n}\n\n.jss_worksheet .dragging-right {\n    background-repeat: no-repeat;\n    background-position: top 50% right 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E\");\n}\n\n.jss_hidden_index > tbody > tr > td:first-child,\n.jss_hidden_index > thead > tr > td:first-child,\n.jss_hidden_index > tfoot > tr > td:first-child,\n.jss_hidden_index > colgroup > col:first-child {\n    display: none;\n}\n\n.jss_worksheet .jrating {\n    display: inline-flex;\n}\n.jss_worksheet .jrating > div {\n    zoom: 0.55;\n}\n\n.jss_worksheet .copying-top {\n    border-top: 1px dashed #000;\n}\n\n.jss_worksheet .copying-left {\n    border-left: 1px dashed #000;\n}\n\n.jss_worksheet .copying-right {\n    border-right: 1px dashed #000;\n}\n\n.jss_worksheet .copying-bottom {\n    border-bottom: 1px dashed #000;\n}\n\n.jss_worksheet .jss_column_filter {\n    background-repeat: no-repeat;\n    background-position: top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='gray' width='18px' height='18px'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    overflow: hidden;\n    padding: 0px;\n    padding-left: 6px;\n    padding-right: 20px;\n}\n\n.jss_worksheet thead .jss_freezed,\n.jss_worksheet tfoot .jss_freezed {\n    left: 0px;\n    z-index: 3 !important;\n    box-shadow: 2px 0px 2px 0.2px #ccc !important;\n    -webkit-box-shadow: 2px 0px 2px 0.2px #ccc !important;\n    -moz-box-shadow: 2px 0px 2px 0.2px #ccc !important;\n}\n\n.jss_worksheet tbody .jss_freezed {\n    position: relative;\n    background-color: #fff;\n    box-shadow: 1px 1px 1px 1px #ccc !important;\n    -webkit-box-shadow: 2px 4px 4px 0.1px #ccc !important;\n    -moz-box-shadow: 2px 4px 4px 0.1px #ccc !important;\n}\n\n.red {\n    color: red;\n}\n\n.jss_worksheet > tbody > tr > td.readonly > input[type='checkbox'],\n.jss_worksheet > tbody > tr > td.readonly > input[type='radio'] {\n    pointer-events: none;\n    opacity: 0.5;\n}\n"
  },
  {
    "path": "dist/jspreadsheet.themes.css",
    "content": ".jss_worksheet > thead > tr > td {\n    border-top: 1px solid var(--border_color, #ccc);\n    border-left: 1px solid var(--border_color, #ccc);\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n}\n\n.jss_worksheet > thead > tr > td.selected {\n    background-color: var(--header_background_highlighted, #dcdcdc);\n    color: var(--header_color_highlighted, #000);\n}\n\n.jss_worksheet > tbody > tr > td:first-child {\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n}\n\n.jss_worksheet > tbody > tr > td {\n    background-color: var(--content_background, #fff);\n    color: var(--content_color, #000);\n    border-top: 1px solid var(--border_color, #ccc);\n    border-left: 1px solid var(--border_color, #ccc);\n}\n\n.jss_worksheet > tbody > tr.selected > td:first-child {\n    background-color: var(--header_background_highlighted, #dcdcdc);\n    color: var(--header_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight {\n    background-color: var(--selection, rgba(0, 0, 0, 0.05));\n}\n\n.jss_worksheet .highlight-top {\n    border-top: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-left {\n    border-left: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-right {\n    border-right: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-bottom {\n    border-bottom: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-selected {\n    background-color: var(--cursor, #eee);\n}\n\n.jss_pagination > div > div {\n    color: var(--header_color, #000);\n    background: var(--header_background, #f3f3f3);\n    border: 1px solid var(--border_color, #ccc);\n}\n\n.jss_toolbar {\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n    border: 1px solid var(--border_color, #ccc);\n}\n\n.jss_toolbar .jtoolbar-item i {\n    color: var(--content_color, #000);\n}\n\n.jss_toolbar .jtoolbar-item:not(.jtoolbar-divisor):hover,\n.jss_toolbar .jtoolbar-item.jpicker:hover > .jpicker-header {\n    background-color: var(--content_background_highlighted, #f3f3f3);\n    color: var(--content_color_highlighted, #000);\n}\n\n.jss_toolbar .jtoolbar-divisor {\n    background: var(--header_color, #ddd);\n}\n\n.jss_contextmenu {\n    border: 1px solid var(--border_color, #ccc);\n    background: var(--menu_background, #fff);\n    color: var(--menu_color, #555);\n    box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n    -webkit-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n    -moz-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n}\n\n.jss_contextmenu > div a {\n    color: var(--menu_color, #555);\n}\n\n.jss_contextmenu > div:not(.contextmenu-line):hover a {\n    color: var(--menu_color_highlighted, #555);\n}\n\n.jss_contextmenu > div:not(.contextmenu-line):hover {\n    background: var(--menu_background_highlighted, #ebebeb);\n}\n\n.jss_container input {\n    color: var(--header_color, #000);\n    background: var(--header_background, #f3f3f3);\n}\n"
  },
  {
    "path": "docs/jspreadsheet/contact.md",
    "content": "title: Jspreadsheet - Contact Us  \nkeywords: Contact Jspreadsheet, Get in Touch, Technical Support, Sales Inquiries  \ndescription: Reach out to us for any technical support or sales inquiries related to Jspreadsheet. We're here to help with your questions and needs.\ncanonical: https://bossanova.uk/jspreadsheet/contact\n\n<div class=\"home\">\n\n\n{data-text=\"Jspreadsheet\" .center}\n# Contact Us\n\n<br><br>\n\n<div class=\"box shadow center\" data-number=\"3\">\n<div>\n\n#### Upgrades\n\nLooking for more information? \n\nEmail: [contact@jspreadsheet.com](mailto:contact@jspreadsheet.com)\n\n[Contact Us](mailto:contact@jspreadsheet.com){.button .dark target=\"_blank\"}\n\n</div><div>\n\n#### Technical Support\n\nProfessional Technical Support\n\nEmail: [support@jspreadsheet.com](mailto:support@jspreadsheet.com)\n\n[Talk to us](mailto:support@jspreadsheet.com){.button .dark target=\"_blank\"}\n\n</div><div>\n\n#### Github\n\nGitHub repository: \n\n[https://github.com/jspreadsheet/ce](https://github.com/jspreadsheet/ce)\n\n[![GitHub](img/github-logo.svg)](https://github.com/jspreadsheet/ce){target=\"_blank\"}\n\n</div>\n</div>\n\n</div>\n\n<div class=\"space200 line\"></div>\n"
  },
  {
    "path": "docs/jspreadsheet/demo.md",
    "content": "title: Jspreadsheet Demo Page  \nkeywords: Jspreadsheet, JavaScript data grid, spreadsheet controls, interactive data grid, web-based applications, lightweight, responsive controls, agnostic platform, Angular, React, Vue, spreadsheet-like plugin, documentation, examples, license  \ndescription: Explore working examples of Jspreadsheet in action. See how it delivers interactive, customizable data grid solutions for web-based applications.  \ncanonical: https://bossanova.uk/jspreadsheet/demo\n\n<div class=\"center\">  \n\n{data-text=\"Demo\"}\n# Data Grid Demo\n\nExplore the Jspreadsheet data grid through these interactive demos. Learn how its versatile spreadsheet controls integrate seamlessly with Angular, React, Vue, and other platforms. See practical use cases for building responsive, lightweight, and powerful web-based applications.\n\n<lm-demo></lm-demo>\n\n<div class=\"space200\"></div>  \n\n</div>  \n"
  },
  {
    "path": "docs/jspreadsheet/docs/angular.md",
    "content": "title: Angular Spreadsheet\nkeywords: Angular, Jspreadsheet, data grid, spreadsheet controls, TypeScript, Angular integration, JavaScript grid, Angular application, interactive spreadsheets, data grid functionality  \ndescription: Create advanced data grids in Angular applications using Jspreadsheet. Integrate spreadsheet-like controls with TypeScript support for enhanced interactivity and data management.\n\n# Angular Spreadsheet\n\n## Overview\n\nCreate advanced JavaScript Data Grids with Angular using Jspreadsheet, featuring spreadsheet-like controls optimized for TypeScript. This guide provides integration steps and practical examples tailored for Angular projects.\n\n## Documentation\n\n### Installation\n\nInstall Jspreadsheet for Angular using npm:\n\n```bash\nnpm install jspreadsheet-ce@5.0.0-beta.3\n```\n\n### Importing Stylesheets\n\nInclude the necessary styles for Jspreadsheet and Jsuites in your Angular project. Add the following to the styles array in your angular.json file:\n\n```json\n\"styles\": [\n    \"jsuites/dist/jsuites.css\",\n    \"jspreadsheet-ce/dist/jspreadsheet.css\"\n]\n```\n\n## Examples\n\n### Basic Angular Spreadsheet\n\nHere's a simple example of integrating Jspreadsheet in an Angular component. The spreadsheet allows dropdowns and colour pickers within cells, demonstrating adding comments and using basic formulas.\n\n```typescript\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\n@Component({\n  selector: \"app-root\",\n  templateUrl: \"./app.component.html\",\n  styleUrls: [\"./app.component.css\"]\n})\nexport class AppComponent {\n  @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n\n  ngAfterViewInit() {\n    // Create the spreadsheet\n    jspreadsheet(this.spreadsheet.nativeElement, {\n      worksheets: [\n        {\n          data: [\n            ['Yes', '#ff0000'],\n            ['No', '#00ff00']\n          ],\n          minDimensions: [6, 4],\n          columns: [\n            {\n              type: \"dropdown\",\n              width: 100,\n              source: [\"Yes\", \"No\"]\n            },\n            {\n              type: \"colour\",\n              width: 100,\n              render: \"square\"\n            }\n          ],\n          allowComments: true,\n          comments: {\n            A1: \"Select Yes or No\",\n            B1: \"Choose a colour.\"\n          }\n        }\n      ]\n    });\n  }\n}\n```\n\n### Data from External Sources\n\nUse Angular's HttpClient to load or save data dynamically from an API and pass it to Jspreadsheet.\n\n{.ignore}\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport { HttpClient } from '@angular/common/http';\nimport jspreadsheet from \"jspreadsheet-ce\";\n\n@Component({\n  selector: \"app-root\",\n  templateUrl: \"./app.component.html\",\n  styleUrls: [\"./app.component.css\"]\n})\nexport class AppComponent {\n  @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n\n  constructor(private http: HttpClient) {}\n\n  ngAfterViewInit() {\n    // Fetch data from an external API and initialize the spreadsheet\n    this.http.get('https://api.example.com/spreadsheet-data')\n      .subscribe((data: any[][]) => {\n        jspreadsheet(this.spreadsheet.nativeElement, {\n          worksheets: [\n            {\n              data: data,\n              columns: [\n                { type: \"dropdown\", width: 100, source: [\"Yes\", \"No\"] },\n                { type: \"checkbox\", width: 50 }\n              ],\n              minDimensions: [10, 5]\n            }\n          ]\n        });\n      });\n  }\n}\n```\n\n### Online Spreadsheet with Angular\n\nA basic Angular spreadsheet with export to XLSX.\n\n- [Angular Online Spreadsheet](https://codesandbox.io/s/angular-spreadsheet-lbtcwf)\n- [Angular Data Grid with Spreadsheet Controls](https://stackblitz.com/edit/data-grid-with-spreadsheet-controls)\n- [Dynamic Spreadsheets](https://stackblitz.com/edit/online-spreadsheets)\n\n### Online XLSX Editor with Angular\n\nImport XLSX files and edit them online using Jspreadsheet Pro.\n\n- [Angular Spreadsheet XLSX Editor](https://codesandbox.io/s/online-angular-excel-spreadsheet-lk5bnc)\n\n### More Spreadsheet Angular Examples\n\n- [Data Grid Custom Cell Editor](https://stackblitz.com/~/github.com/nicolasjesse/jss-custom-column-ng)\n- [Angular Spreadsheet with Data Validation](https://stackblitz.com/edit/angular-data-validation-example)\n\n{.pro}\n> **Jspreadsheet Pro for Angular - Professional Spreadsheet Components**\n>\n> While Jspreadsheet CE provides core spreadsheet functionality for Angular, **Jspreadsheet Pro** offers enhanced Angular integration and enterprise features:\n>\n> **Enhanced Angular Integration:**\n> - **Full TypeScript Support:** Complete type definitions for type-safe Angular development\n> - **Angular Modules:** NgModule structure for easy integration into Angular apps\n> - **Standalone Components:** Support for Angular standalone components (Angular 14+)\n> - **Dependency Injection:** Full DI support for spreadsheet services\n> - **RxJS Integration:** Observable streams for reactive data handling\n> - **Angular Signals:** Support for Angular Signals (Angular 16+)\n>\n> **Professional Components:**\n> - **Advanced Editors:** Conditional dropdowns, rich text, HTML editors with Angular forms\n> - **Formula System:** 500+ Excel functions with Angular change detection\n> - **Conditional Formatting:** Visual rules, data bars, color scales, icon sets\n> - **Data Validation:** Real-time validation with Angular form validators\n> - **Import/Export:** Full Excel (.xlsx) import/export with formatting preservation\n> - **Charts & Graphs:** Built-in Angular chart components for data visualization\n>\n> **Angular Forms Integration:**\n> - **Reactive Forms:** Full integration with Angular Reactive Forms\n> - **Template-Driven Forms:** Support for template-driven form bindings\n> - **Form Validators:** Built-in and custom Angular validators\n> - **Form Control:** Spreadsheet as FormControl with validation\n> - **Two-Way Binding:** [(ngModel)] support for spreadsheet data\n> - **Form States:** Track pristine, dirty, touched, valid states\n>\n> **Performance & Scale:**\n> - **Virtual Scrolling:** Handle 100K+ rows with smooth scrolling\n> - **Lazy Loading:** Load data on-demand for optimal performance\n> - **Zone.js Optimized:** Efficient change detection with Angular zones\n> - **OnPush Strategy:** Support for OnPush change detection strategy\n> - **Web Workers:** Background processing for heavy calculations\n> - **AOT Compatible:** Fully compatible with Ahead-of-Time compilation\n>\n> **Developer Experience:**\n> - **Angular-Specific Documentation:** Examples with TypeScript and Angular patterns\n> - **Professional Support:** Priority support for Angular integration issues\n> - **Regular Updates:** Continuous Angular version compatibility (14, 15, 16, 17+)\n> - **Migration Tools:** Easy migration from CE to Pro with Angular examples\n>\n> **Angular-Specific Pro Features:**\n> - **Custom Angular Components:** Use Angular components as cell editors\n> - **Pipes Support:** Use Angular pipes for cell value transformation\n> - **Directives:** Custom directives for cell customization\n> - **Services:** Spreadsheet services for shared state management\n> - **Router Integration:** Deep linking and navigation integration\n> - **Testing Support:** Jasmine/Karma and Jest integration examples\n>\n> Perfect for Angular applications requiring enterprise-grade spreadsheet functionality with professional support.\n>\n> **[Explore Jspreadsheet Pro Angular →](https://jspreadsheet.com/docs/angular)** | **[Compare CE vs Pro →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n"
  },
  {
    "path": "docs/jspreadsheet/docs/cells.md",
    "content": "title: Spreadsheet Cells\nkeywords: JavaScript, Jspreadsheet, Jexcel, Data Grid, Spreadsheet Features, Excel-like Functionality, Table Management, Cell Settings, Cell Configuration\ndescription: Detailed documentation on Jspreadsheet cell configuration, covering events, methods, and settings for advanced customization.\n\n# Spreadsheet Cells\n\nThis guide provides an in-depth overview of data grid cells in Jspreadsheet, including key methods, configuration options, and a basic implementation example. \n\n{.pro}\n> #### Differences in the Pro Version\n>\n> The Jspreadsheet Pro enables cell-level editor customization and allows dynamic programmatic changes to cell types.\\\n> \\\n> [Learn more](https://jspreadsheet.com/docs/cells){.button}\n\n## Documentation\n\n### Methods\n\nThis section outlines methods to facilitate interaction with cells and their attributes within the data grid.\n\n| Method                       | Description                                                                                                                                          |\n|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getCell`                    | Get cell DOM element by cell name.<br/>@param `cell` - Cell name.<br>`worksheetInstance.getCell(cell: string): HTMLTableCellElement;`<br/><br/>Get cell DOM element by cell coords.<br/>@param `x` - Cell column index.<br/>@param `y` - Cell row index.<br/>`worksheetInstance.getCell(x: number, y: number): HTMLTableCellElement;`                                   |\n| `getCellFromCoords`{.nowrap} | Get cell DOM element by cell coordinates.<br/>@param `x` - Column index of the cell.<br/>@param `y` - Row index of the cell.<br>`worksheetInstance.getCellFromCoords(x: number, y: number): HTMLTableCellElement;` |\n\n### Example\n\nThe following example is a car loan calculation spreadsheet showcasing styles, merged cells, and row properties.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<input type=\"button\" value=\"Apply Style\" id=\"btn1\" class=\"jss_object\" />\n\n<script>\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Car Loan', ''],\n            ['Purchase price', '19700'],\n            ['Down payment', '1000'],\n            ['Trade-in value', '500'],\n            ['Interest rate', '0.0305'],\n            ['Length of loan (in months)', '60'],\n            ['', ''],\n            ['Monthly payment', '=PMT(B5/12,B6,B2-(B3+B4))'],\n            ['Total cost', '=-(B8*B6)+(B3+B4)'],\n        ],\n        columns: [\n            { width:'300px' },\n            { width:'200px' },\n        ],\n        mergeCells: {\n            A1: [2, 1]\n        },\n        rows: {\n            0: { height:'200px' }\n        },\n        style: {\n            'A1': 'font-weight: bold'\n        }\n    }]\n});\n\nbtn1.addEventListener('click', function() {\n    worksheets[0].getCell('A1').style.backgroundColor = 'orange'; \n})\n\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Data\n    const data = [\n        ['Car loan', ''],\n        ['Purchase price', '19700'],\n        ['Down payment', '1000'],\n        ['Trade-in value', '500'],\n        ['Interest rate', '0.0305'],\n        ['Length of loan (in months)', '60'],\n        ['', ''],\n        ['Monthly payment', '=PMT(B5/12,B6,B2-(B3+B4))'],\n        ['Total cost', '=-(B8*B6)+(B3+B4)'],\n    ];\n\n    // Columns\n    const columns = [\n        { width:'300px' },\n        { width:'200px' },\n    ];\n\n    // Merge cells\n    const mergeCells = {\n        A1: [2, 1],\n    }\n\n    // Rows properties\n    const rows = {\n        0: { height:'200px' }\n    }\n\n    const style = {\n        A1: 'font-weight: bold'\n    }\n\n    const applyStyle = function() {\n        if (spreadsheet.current) {\n            spreadsheet.current[0].getCell('A1').style.backgroundColor = 'orange';\n        }\n    }\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} mergeCells={mergeCells} rows={rows} style={style} />\n            </Spreadsheet>\n            <input type={\"button\"} value={\"Apply external style\"} onClick={applyStyle} className={\"jss_object\"} />\n        </>\n    );\n}\n```\n```vue\n<template>\n  <div>\n      <Spreadsheet ref=\"spreadsheetRef\">\n          <Worksheet \n              :data=\"data\" \n              :columns=\"columns\" \n              :merge-cells=\"mergeCells\" \n              :rows=\"rows\" \n              :style=\"style\" \n          />\n      </Spreadsheet>\n      <input \n          type=\"button\" \n          value=\"Apply external style\" \n          @click=\"applyStyle\" \n          class=\"jss_object\" \n      />\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = [\n  ['Car loan', ''],\n  ['Purchase price', '19700'],\n  ['Down payment', '1000'],\n  ['Trade-in value', '500'],\n  ['Interest rate', '0.0305'],\n  ['Length of loan (in months)', '60'],\n  ['', ''],\n  ['Monthly payment', '=PMT(B5/12,B6,B2-(B3+B4))'],\n  ['Total cost', '=-(B8*B6)+(B3+B4)'],\n];\n\n// Columns\nconst columns = [\n  { width:'300px' },\n  { width:'200px' },\n];\n\n// Merge cells\nconst mergeCells = {\n  A1: [2, 1],\n};\n\n// Rows properties\nconst rows = {\n  0: { height:'200px' }\n};\n\nconst style = {\n  A1: 'font-weight: bold'\n};\n\nconst applyStyle = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].getCell('A1').style.backgroundColor = 'orange';\n  }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create the data grid component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <input type=\"button\" value=\"Apply Style\" class=\"jss_object\" (click)=\"applyStyle()\" />`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Car Loan', ''],\n                    ['Purchase price', '19700'],\n                    ['Down payment', '1000'],\n                    ['Trade-in value', '500'],\n                    ['Interest rate', '0.0305'],\n                    ['Length of loan (in months)', '60'],\n                    ['', ''],\n                    ['Monthly payment', '=PMT(B5/12,B6,B2-(B3+B4))'],\n                    ['Total cost', '=-(B8*B6)+(B3+B4)'],\n                ],\n                columns: [\n                    { width:'300px' },\n                    { width:'200px' },\n                ],\n                mergeCells: {\n                    A1: [2, 1]\n                },\n                rows: {\n                    0: { height:'200px' }\n                },\n                style: {\n                    'A1': 'font-weight: bold'\n                }\n            }]\n        });\n    }\n\n    applyStyle() {\n        if (this.worksheets) {\n            this.worksheets[0].getCell('A1').style.backgroundColor = 'orange';\n        }\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Cell Features**\n>\n> Jspreadsheet CE provides basic cell operations and styling. **Jspreadsheet Pro** adds powerful cell-level capabilities:\n>\n> **Cell-Level Validation:**\n> - **Required Cells:** Mark specific cells as required with custom error messages\n> - **Custom Validators:** Define validation functions per cell (not just column-level)\n> - **Cross-Cell Validation:** Validate based on values in other cells\n> - **Real-Time Validation:** Instant feedback with visual indicators as users type\n> - **Validation Rules:** Min/max values, regex patterns, custom logic per cell\n> - **Error Highlighting:** Automatic visual highlighting of invalid cells\n>\n> **Advanced Cell Formatting:**\n> - **Conditional Cell Formatting:** Auto-apply colors, styles based on cell values\n> - **Data Bars:** In-cell bar charts showing relative values\n> - **Color Scales:** Gradient colors based on value ranges\n> - **Icon Sets:** Visual icons (arrows, flags, traffic lights) based on values\n> - **Rich Text in Cells:** Bold, italic, underline, colors within single cell\n> - **Cell-Specific Masks:** Different format masks per cell (not just column)\n>\n> **Cell Protection & Security:**\n> - **Cell-Level Protection:** Lock specific cells while allowing others to edit\n> - **Conditional Protection:** Protect/unprotect cells based on user roles or values\n> - **Password Protection:** Protect cells with passwords\n> - **Read-Only Cells:** Programmatically set individual cells as read-only\n>\n> **Enhanced Cell Features:**\n> - **Cell Comments/Notes:** Advanced commenting system with threads and mentions\n> - **Cell Metadata:** Store custom business data per cell\n> - **Cell History:** Track changes per cell with audit trail\n> - **Cell Dependencies:** Track which cells depend on each other\n> - **Cell Types:** Dynamic cell type changes at runtime\n> - **Custom Renderers:** Per-cell custom rendering functions\n>\n> Perfect for data entry applications requiring strict validation and conditional formatting.\n>\n> **[Explore Pro Cell Features →](https://jspreadsheet.com/docs/cells)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**"
  },
  {
    "path": "docs/jspreadsheet/docs/clipboard.md",
    "content": "title: Jspreadsheet Clipboard\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like formulas, spreadsheet data, worksheet data, manage spreadsheet data, change cell data, update worksheet data\ndescription: Explore Jspreadsheet’s clipboard methods and events, enabling seamless copy-paste operations for managing and updating data within your spreadsheets.\n\n# Data Grid Clipboard\n\n## Overview\n\nThe clipboard in Jspreadsheet offers a comprehensive set of features that facilitate copying and pasting data between Jspreadsheet instances and other spreadsheet software, such as Excel or Google Sheets. These features include automatic formula updates, data overwriting during paste actions, and custom event handling.\n\nThis section details the methods and events you can use to control clipboard behaviour, customize paste actions, and ensure smooth interoperability between different data grids.\n\n### Key Features\n\n- **Automatic Formula Updates**: When pasting data containing formulas, Jspreadsheet automatically updates the formulas to match the new data context;\n- **Data Overwriting**: Jspreadsheet supports data overwriting during paste actions, giving you fine-grained control over pasted data handling;\n- **Custom Events**: You can hook into specific clipboard-related events to customize the behaviour of copy-paste operations, including validation, transformation, and data filtering;\n\n{.pro}\n> #### What you can find on the Pro Version\n> \n> The **Pro version** of Jspreadsheet includes additional clipboard capabilities, enhancing your experience with formatting, styles, and formula reference updates:\n> - **Copy and Paste Formatting**: When using the Pro version, not only data but also cell formats (like font styles, borders, and colours) and styles are transferred during copy-paste actions.\n> - **Formula Reference Updates**: The Pro version ensures that pasted formulas automatically adjust their references according to the new locations, maintaining the integrity of your spreadsheet formulas.\\\n>\n>\\\n> [Learn more](https://jspreadsheet.com/docs/clipboard){.button}\n\n\n## Documentation\n\n### Clipboard Management Methods\n\nJspreadsheet provides methods to control clipboard operations, allowing smooth data transfers between spreadsheets or external tools like Excel.\n\n| Method                    | Description                                                                                                                                                                                                                                                                                                                                                       |\n|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `copy`                    | Copies or cuts the contents of selected cells in the worksheet.<br/>@param `cut` - If true, the operation is cut, if not, it is copy. <br/>`worksheetInstance.copy(cut?: boolean): void;` <br/> **Example**: `worksheetInstance.copy(true)` to cut the data.                                                                                                      |\n| `paste`                   | Paste data at a coordinate.Pastes content into one or more cells.<br/>@param `x` - Column index of the cell from which the content will be pasted.<br/>@param `y` - Row index of the cell from which the content will be pasted.<br/>@param `data` - Content to be pasted.<br/>`worksheetInstance.paste(x: number, y: number, data: string): false \\| undefined;` |\n\n### Clipboard Operation Events\n\nThese events let you hook into clipboard operations like copy and paste, providing greater control over how data is transferred or manipulated.\n\n| Event                    | Description                                                                                                                                                                                                                                                                                                     |\n|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `oncopy`                 | Fires when data is copied to the clipboard. Use this to inspect or modify the copied data before it goes to the clipboard. <br/>`oncopy(instance: WorksheetInstance, selectedRange: [number, number, number, number], copiedData: string, cut: boolean \\| undefined): string \\| false \\| undefined;`            |\n| `onbeforepaste`{.nowrap} | Fires before pasting occurs, allowing you to validate or transform the incoming data. You can also cancel the paste if needed. <br/>`onbeforepaste(instance: WorksheetInstance, copiedText: { value: CellValue }[][], colIndex: number \\| string, rowIndex: number \\| string): undefined \\| boolean \\| string;` |\n| `onpaste`                | Fires after the paste action is completed. Useful for post-processing or triggering additional actions after the paste. <br/>`onpaste(instance: WorksheetInstance, pastedInfo: { x: number, y: number, value: CellValue }[][]): void;`                                                                          |\n\n> **Note**\n> In Jspreadsheet CE, it is not possible to cancel or modify the clipboard value during the oncopy event.\n\n## Examples\n\n### Intercept Pasted Data\n\nYou can intercept and cancel the paste event if needed.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n    \n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n    }],\n    onbeforepaste: function() {\n        alert('Not allowed to paste');\n        return false;\n    }\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ];\n    \n    const onbeforepaste = function() {\n        alert('Not allowed to paste');\n        return false;\n    }\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet} onbeforepaste={onbeforepaste}>\n            <Worksheet data={data} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\" :onbeforepaste=\"onbeforepaste\">\n      <Worksheet :data=\"data\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = [\n  ['Mazda', 2001, 2000],\n  ['Peugeot', 2010, 5000],\n  ['Honda Fit', 2009, 3000],\n  ['Honda CRV', 2010, 6000],\n];\n\n// Paste interception method\nconst onbeforepaste = () => {\n  alert('Not allowed to paste');\n  return false;\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create the data grid component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [6,6],\n            }],\n            onbeforepaste: function() {\n                alert('Not allowed to paste');\n                return false;\n            }\n        });\n    }\n}\n```"
  },
  {
    "path": "docs/jspreadsheet/docs/columns.md",
    "content": "title: Spreadsheet Columns\nkeywords: Jspreadsheet, column management, JavaScript spreadsheets, Excel-like columns, data grid customization, column settings, spreadsheet methods, column events\ndescription: Dive into the comprehensive guide on managing spreadsheet columns in Jspreadsheet, covering column settings, programmable methods, and associated events for complete control and customization.\n\n# Spreadsheet Columns\n\nThe Jspreadsheet column settings control all the cell attributes within a column, including data types, read-only status, data masks, and rendering options. This section covers settings, events, and methods in Jspreadsheet to customize spreadsheet functionality for your application needs.\n\n## Documentation\n\n### Methods\n\nThe following methods allow for programmatic interaction with spreadsheet columns in Jspreadsheet.\n\n| Method                  | Description                                                                                                                                             |\n|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getWidth`              | Get the width of one or all columns.<br/>@param `column` - Index of the column. If omitted, returns the widths of all columns.<br/>`worksheetInstance.getWidth(column?: number): number \\| (number \\| string)[];`                 |\n| `setWidth`              | Set the width of a column.<br/>@param `column` - Column index.<br/>@param `width` - New width.<br/>`worksheetInstance.setWidth(column: number, width: number): void;`<br/><br/>Set the width of one or more columns.<br/>@param `column` - Column indexes.<br/>@param `width` - New widths.<br/>`worksheetInstance.setWidth(column: number[], width: number \\| number[]): void;`              |\n| `getColumn`             | Retrieves settings for a specified column.<br/>`worksheetInstance.getColumn(colNumber: Number): Object`                                                                 |\n| `moveColumn`            | Move a column. This method returns false if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.<br/>@param o - Column index.<br/>@param `d` - New column index.<br/>`worksheetInstance.moveColumn(columnNumber: number, newPositionNumber: number): false \\| undefined;`      |\n| `insertColumn`          | Insert one or more columns. This method returns false if the onbeforeinsertcolumn event returns false or if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.<br/>@param `mixed` - Number of columns to insert. It can also be an array of values, but in this case, only one column is inserted, whose data is based on the array items. Default: 1.<br/>@param `columnNumber` - Index of the column used as reference for the insertion. Default: last column.<br/>@param `insertBefore` - Insert new columns before or after the reference column. Default: false.<br/>@param `properties` - New column properties.<br/>`worksheetInstance.insertColumn(mixed?: number \\| CellValue[], columnNumber?: number, insertBefore?: boolean, properties?: Column[]): false \\| undefined;` |\n| `deleteColumn`{.nowrap} | Remove columns. This method returns false if the onbeforedeletecolumn event returns false or if the \"This action will destroy any existing merged cells. Are you sure?\" dialog receives a negative response.<br/>@param `columnNumber` - Column index from which removal starts.<br/>@param `numOfColumns` - Number of columns to be removed.<br/>`worksheetInstance.deleteColumn(columnNumber?: number, numOfColumns?: number): false \\| undefined;`                             |\n\n### Events\n\nSeveral events are available for handling column actions in your spreadsheet, including `onbefore` events that let you intercept, validate, or cancel user actions.\n\n| Event                           | Description                                                                                                                                                |\n|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onbeforeinsertcolumn`{.nowrap} | Triggered before a new column is inserted. Return `false` to cancel the action.<br/>`onbeforeinsertcolumn(instance: WorksheetInstance, columns: { column: number, options: Column, data?: CellValue[] }[]): undefined \\| boolean;` |\n| `oninsertcolumn`                | Triggered after a new column is inserted.<br/>`oninsertcolumn(instance: WorksheetInstance, columns: { column: number, options: Column, data?: CellValue[] }[]): void;`                                                |\n| `onbeforedeletecolumn`          | Triggered before a column is deleted. Return `false` to cancel the action.<br/>`onbeforedeletecolumn(instance: WorksheetInstance, removedColumns: number[]): undefined \\| boolean;` |\n| `ondeletecolumn`                | Triggered after a column is deleted.<br/>`ondeletecolumn(instance: WorksheetInstance, removedColumns: number[]): void;`                                                       |\n| `onmovecolumn`                  | Triggered after a column is moved to a new position.<br/>`ondeletecolumn(instance: WorksheetInstance, removedColumns: number[]): void;`          |\n\n### Initial Settings\n\nThe following column-related properties are configurable during the initialization of the online spreadsheet:\n\n| Property                                    | Description                                                                                            |\n|---------------------------------------------|--------------------------------------------------------------------------------------------------------|\n| `allowInsertColumn: boolean`                | Enables users to add new columns. `Default: true`                                                      |\n| `allowManualInsertColumn: boolean`{.nowrap} | Adds a new column when the user presses the Tab key in the last column. `Default: true`                |\n| `allowDeleteColumn: boolean`                | Allows users to delete columns. `Default: true`                                                        |\n| `allowRenameColumn: boolean`                | Allows users to rename columns. `Default: true`                                                        |\n| `columnDrag: boolean`                       | Enables drag-and-drop for changing column positions. `Default: true`                                   |\n| `columnSorting: boolean`                    | Allows users to sort columns. `Default: true`                                                          |\n| `columnResize: boolean`                     | Allows users to resize columns. `Default: true`                                                        |\n| `defaultColWidth: number`                   | Sets the default column width. `Default: 100px`                                                        |\n| `defaultColAlign: string`                   | Sets the default column text alignment. `Default: center`                                              |\n| `minSpareCols: number`                      | Number of blank columns at the spreadsheet's end. `Default: none`                                      |\n\n\n### Available Properties\n\nEach column type in Jspreadsheet can hold specific properties. Below are some of the most commonly available options:\n\n| Property                   | Description                                                                                                                             |\n|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|\n| `type`                     | Defines the editor type for the column. It can be a string for a native editor or a method for a custom editor plugin.                  |\n| `title`                    | Title of the column.                                                                                                                    |\n| `name`                     | Property name or path when using JSON data.                                                                                             |\n| `width`                    | Width of the column.                                                                                                                    |\n| `align`                    | Alignment of the column content. `Default: center.`                                                                                     |\n| `url`                      | URL to load items for dropdowns in this column.                                                                                         |\n| `source`                   | Items for dropdown or autocomplete fields.                                                                                              |\n| `autocomplete`             | Enables autocomplete for the column.                                                                                                    |\n| `multiple`                 | Allows multiple selections in dropdown or autocomplete fields.                                                                          |\n| `mask`                     | Input mask applied to data cells.                                                                                                       |\n| `decimal`                  | Character used as the decimal separator.                                                                                                |\n| `disabledMaskOnEdition`    | Disables the mask when editing.                                                                                                         |\n| `render`                   | Renderer method or rule for cell content.                                                                                               |\n| `format`                   | Date or number format in the cell. `Default for the calendar: \"DD/MM/YYYY\".`                                                            |\n| `options`                  | Extended configuration for the column.                                                                                                  |\n\n\n#### Adding a New Column\n\nTo insert a new column into the data grid, you can pass an array of objects, each object containing three properties outlined below. This approach allows for the creation of multiple columns in a single operation.\n\n| Method          | Description                                      |\n| ----------------|--------------------------------------------------|\n| data: any[]     | Array with the column data                       |\n| column: number  | The index where the new column will be inserted. |\n| options: object | An object specifying the column's attributes.    |\n\n##### Example\n\nTo add a new column at the beginning of the grid:\n\n{.ignore}\n```javascript\nworksheet.insertColumn([\n    {\n        data: [1,2,3],\n        column: 0,\n        options: {\n            type: 'calendar',\n            title: 'My new column'\n        }\n    }\n]);\n```\n\n## Examples\n\n### Render Method\n\nThe render method modifies visible data before inserting it into a grid cell.\n\n```html\n<html>\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Adding an arbitrary number leading zeros.\nlet pad = function(cell, value, x, y, instance, options) {\n    if (value || Number(value)) {\n        let size = options.digits||0;\n        value = value.toString();\n        while (value.length < size) {\n            value = \"0\" + value;\n        }\n        cell.innerText = value;\n    }\n}\n\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: true,\n    toolbar: true,\n    worksheets: [{\n        data: [[1]],\n        minDimensions: [6,6],\n        columns: [{ render: pad, digits: 6 }]\n    }],\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Adding an arbitrary number leading zeros.\nconst pad = (cell, value, x, y, instance, options) => {\n    if (value !== '') {\n        let size = options.digits||0;\n        value = value.toString();\n        while (value.length < size) {\n            value = \"0\" + value;\n        }\n        cell.innerText = value;\n    }\n}\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [[1]];\n    // Columns\n    const columns = [\n        { render: pad, digits: 6 }\n    ];\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\">\n        <Worksheet \n            :data=\"data\" \n            :columns=\"columns\" \n        />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Adding an arbitrary number leading zeros.\nconst pad = (cell, value, x, y, instance, options) => {\n    if (value !== '') {\n        let size = options.digits || 0;\n        value = value.toString();\n        while (value.length < size) {\n            value = \"0\" + value;\n        }\n        cell.innerText = value;\n    }\n}\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([[1]]);\n\n// Columns\nconst columns = ref([\n    { render: pad, digits: 6 }\n]);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Adding an arbitrary number leading zeros.\nlet pad = function(cell, value, x, y, instance, options) {\n    if (value || Number(value)) {\n        let size = options.digits||0;\n        value = value.toString();\n        while (value.length < size) {\n            value = \"0\" + value;\n        }\n        cell.innerText = value;\n    }\n}\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            tabs: true,\n            toolbar: true,\n            worksheets: [{\n                data: [[1]],\n                minDimensions: [6,6],\n                columns: [{ render: pad, digits: 6 }]\n            }],\n        });\n    }\n}\n```\n\n### Programmatic Methods\n\nA basic spreadsheet example demonstrating various programmatic methods.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div><br/><br/>\n\n<input type=\"button\" value=\"Insert a new blank column at the end\" id=\"btn1\" /><br/><br/>\n<input type=\"button\" value=\"Insert two new blank columns at the beginning\" id=\"btn2\" /><br/><br/>\n<input type=\"button\" value=\"Click to delete the last column\" id=\"btn4\" /><br/><br/>\n<input type=\"button\" value=\"Click to move the first column to the third position\" id=\"btn5\" /><br/><br/>\n<input type=\"button\" value=\"Hide the first column\" id=\"btn6\" /><br/><br/>\n<input type=\"button\" value=\"Show the first column\" id=\"btn7\" /><br/><br/>\n\n<script>\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['US', 'Cheese', 1000 ],\n            ['CA', 'Apples', 1200 ],\n            ['CA', 'Carrots', 2000 ],\n            ['BR', 'Oranges', 3800 ],\n        ]\n    }],\n});\n\ndocument.getElementById('btn1').onclick = function() { worksheets[0].insertColumn(); };\ndocument.getElementById('btn2').onclick = function() { worksheets[0].insertColumn(2, 0, 1); };\ndocument.getElementById('btn4').onclick = function() { worksheets[0].deleteColumn(); };\ndocument.getElementById('btn5').onclick = function() { worksheets[0].moveColumn(0, 2); };\ndocument.getElementById('btn6').onclick = function() { worksheets[0].hideColumn(0); };\ndocument.getElementById('btn7').onclick = function() { worksheets[0].showColumn(0); };\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef(null);\n    // Data\n    const data = [\n        ['US', 'Cheese', 1000 ],\n        ['CA', 'Apples', 1200 ],\n        ['CA', 'Carrots', 2000 ],\n        ['BR', 'Oranges', 3800 ],\n    ];\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} />\n            </Spreadsheet>\n            <button onClick={() => spreadsheet.current[0].insertColumn()}>\n                Click to insert a new blank column at the end of the table\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].insertColumn(2, 0, 1)}>\n                Click to insert two new blank columns at the beginning of the table\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].deleteColumn()}>\n                Click to delete the last column\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].moveColumn(0, 2)}>\n                Click to move the first column to the third position\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].hideColumn(0)}>\n                Hide the first column\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].showColumn(0)}>\n                Show the first column\n            </button>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\">\n      <Worksheet :data=\"data\" />\n  </Spreadsheet>\n  <ul>\n      <li @click=\"insertBlankColumn()\">\n          Click to insert a new blank column at the end of the table\n      </li>\n      <li @click=\"insertMultipleColumns()\">\n          Click to insert two new blank columns at the beginning of the table\n      </li>\n      <li @click=\"deleteLastColumn()\">\n          Click to delete the last column\n      </li>\n      <li @click=\"moveFistColumnToThird()\">\n          Click to move the first column to the third position\n      </li>\n      <li @click=\"hideFirstColumn()\">\n          Hide the first column\n      </li>\n      <li @click=\"showFirstColumn()\">\n          Show the first column\n      </li>\n  </ul>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Data\nconst data = ref([\n  ['US', 'Cheese', 1000 ],\n  ['CA', 'Apples', 1200 ],\n  ['CA', 'Carrots', 2000 ],\n  ['BR', 'Oranges', 3800 ],\n]);\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Column manipulation methods\nconst insertBlankColumn = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].insertColumn();\n  }\n};\n\nconst insertMultipleColumns = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].insertColumn(2, 0, 1);\n  }\n};\n\nconst deleteLastColumn = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].deleteColumn();\n  }\n};\n\nconst moveFistColumnToThird = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].moveColumn(0, 2);\n  }\n};\n\nconst hideFirstColumn = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].hideColumn(0);\n  }\n};\n\nconst showFirstColumn = () => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].showColumn(0);\n  }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <ul>\n            <li (click)=\"this.worksheets[0].insertColumn()\">\n                Click to insert a new blank column at the end of the table\n            </li>\n            <li (click)=\"this.worksheets[0].insertColumn(2, 0, true)\">\n                Click to insert two new blank columns at the beginning of the table\n            </li>\n            <li (click)=\"this.worksheets[0].deleteColumn()\">\n                Click to delete the last column\n            </li>\n            <li (click)=\"this.worksheets[0].moveColumn(0, 2)\">\n                Click to move the first column to the third position\n            </li>\n            <li (click)=\"this.worksheets[0].hideColumn(0)\">\n                Hide the first column\n            </li>\n            <li (click)=\"this.worksheets[0].showColumn(0)\">\n                Show the first column\n            </li>\n        </ul>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            tabs: true,\n            toolbar: true,\n            worksheets: [{\n                data: [\n                    ['US', 'Cheese', 1000 ],\n                    ['CA', 'Apples', 1200 ],\n                    ['CA', 'Carrots', 2000 ],\n                    ['BR', 'Oranges', 3800 ],\n                ]\n            }],\n        });\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Column Features**\n>\n> Jspreadsheet CE provides core column functionality including types, widths, and basic validation. **Jspreadsheet Pro** adds powerful column management capabilities:\n>\n> **Advanced Validation:**\n> - **Column-Level Validation Rules:** Required fields, min/max values, regex patterns, custom validators\n> - **Data Type Validation:** Strict validation for numbers, dates, emails, URLs, and custom types\n> - **Cross-Column Validation:** Validation rules based on values in other columns\n> - **Custom Error Messages:** User-friendly validation messages per column\n> - **Real-Time Validation:** Instant feedback as users type\n>\n> **Column Dependencies:**\n> - **Conditional Logic:** Show/hide columns based on other column values\n> - **Dynamic Dropdowns:** Dropdown options that change based on other selections\n> - **Calculated Columns:** Auto-calculate column values from formulas\n> - **Cascading Updates:** Automatic updates when dependent columns change\n>\n> **Enhanced Column Features:**\n> - **Nested Headers:** Multi-level column headers for complex layouts\n> - **Column Grouping:** Group related columns with expand/collapse\n> - **Fixed Columns:** Freeze columns while scrolling horizontally\n> - **Column Templates:** Reusable column configurations\n> - **Advanced Sorting:** Multi-column sorting with custom sort logic\n> - **Column Filters:** Built-in filter dropdowns per column\n>\n> Perfect for complex data entry forms and enterprise applications requiring sophisticated column management.\n>\n> **[Explore Pro Column Features →](https://jspreadsheet.com/docs/columns)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/comments.md",
    "content": "title: Data Grid Cell Comments\nkeywords: Jspreadsheet, Jexcel, Data Grid, JavaScript, Excel-like features, Spreadsheet, Cell comments, Cell comments methods, Cell comments events, Cell comments settings\ndescription: Learn how to add, manage, and customize comments in Jspreadsheet data grid cells using built-in methods, events, and settings.\n\n# Data Grid Cell Comments\n\n## Overview\n\nThis guide explains how to manage and add comments to data grid cells in Jspreadsheet, similar to the features found in Excel or Google Sheets. You can add comments programmatically or through the context menu, enabling annotations and additional information for specific cell contents.\n\n## Documentation\n\n### Methods\n\nThe following methods allow you to get or set comments on one or multiple data grid cells.\n\n| Method        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getComments` | Retrieve comments from a specific cell or the entire data grid.<br/>@param `cell` - Cell name. If null or undefined, returns comments for all cells.<br/>`worksheetInstance.getComments(cell?: string): Record<string, string> \\| string;`                                                                                                                                                                                                                        |\n| `setComments` | Add, update, or remove a comment.<br/>@param `cellId` - Cell name.<br/>@param `comments` - New comment. If null, removes the comment from the cell.<br/>`worksheetInstance.setComments(cellId: string, comments: string): void;`<br/><br/>Handle multiple cells:<br/>@param `cellId` - Object where keys are cell names and values are cell comments. Null values remove the comments.<br/>`worksheetInstance.setComments(cellId: Record<string, string>): void;` |\n\n\n### Events\n\nThe `onbeforecomments` event allows you to intercept, modify, or cancel the action of adding a new comment.\n\n| Event                 | Description                                                                                                                                                                                                                                                                                                                                                                                   |\n|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `oncomments`{.nowrap} | Triggered when a comment is modified.<br/>@param `instance` - The worksheet instance where the change occurred.<br/>@param `newComments` - Object containing updated comments.<br/>@param `oldComments` - Object containing previous comments.<br/>`oncomments(instance: WorksheetInstance, newComments: Record<string, string \\| null>, oldComments: Record<string, string \\| null>): void;` |\n\n### Initial Settings\n\nThese properties can be configured during the initialization of the spreadsheet.\n\n| Property                 | Description                                                                            |\n|--------------------------|----------------------------------------------------------------------------------------|\n| `allowComments: boolean` | Enable or disable the ability for users to add new comments to cells.                  |\n| `comments: object`       | Object containing the initial comments. Example: `{ A1: 'test', B1: 'another test' }`. |\n\n## Examples\n\n### Data Grid with Comments\n\nInitialize a Jspreadsheet data grid with predefined comments and interact with them programmatically using `setComments` and `getComments`.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n<p>\n<input type=\"button\" id=\"bt1\" value=\"Set A1 comments\"/>\n<input type=\"button\" id=\"bt2\" value=\"Get A1 comments\"/>\n<input type=\"button\" id=\"bt3\" value=\"Reset A1 comments\"/>\n</p>\n\n<script>\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        {\n            data: [\n                ['US', 'Cheese', '2019-02-12'],\n                ['CA', 'Apples', '2019-03-01'],\n                ['CA', 'Carrots', '2018-11-10'],\n                ['BR', 'Oranges', '2019-01-12'],\n            ],\n            columns: [\n                { width: '300px' },\n                { width: '200px' },\n                { width: '200px' },\n            ],\n            allowComments: true,\n            comments: {\n                B1: 'Initial comments on B1',\n                C1: 'Initial comments on C1'\n            },\n        }\n    ],\n    oncomments: function () {\n        console.log(arguments);\n    }\n});\n\ndocument.getElementById('bt1').onclick = function () {\n    worksheets[0].setComments('A1', 'Test');\n}\ndocument.getElementById('bt2').onclick = function () {\n    alert(worksheets[0].getComments('A1'));\n}\ndocument.getElementById('bt3').onclick = function () {\n    worksheets[0].setComments('A1', '');\n}\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\nconst spreadsheet = useRef();\n\n    const data = [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ];\n\n    const columns = [\n        { width: '300px' },\n        { width: '200px' },\n        { width: '200px' },\n    ];\n\n    const comments = {\n        B1: 'Initial comments on B1',\n        C1: 'Initial comments on C1'\n    };\n\n    const oncomments = () => {\n        console.log(arguments);\n    }\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} oncomments={oncomments}>\n                <Worksheet data={data} columns={columns} comments={comments} allowComments />\n            </Spreadsheet>\n            <input type=\"button\" value=\"Set A1 comments\"\n                onClick={() => spreadsheet.current[0].setComments('A1', 'Test')} />\n            <input type=\"button\" value=\"Get A1 comments\"\n                onClick={() => alert(spreadsheet.current[0].getComments('A1'))} />\n            <input type=\"button\" value=\"Reset A1 comments\"\n                onClick={() => spreadsheet.current[0].setComments('A1', '')} />\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\" @comments=\"oncomments\">\n      <Worksheet \n          :data=\"data\" \n          :columns=\"columns\" \n          :comments=\"comments\" \n      />\n  </Spreadsheet>\n  <input \n      type=\"button\" \n      value=\"Set A1 comments\" \n      @click=\"setComments('A1', 'Test')\" \n  />\n  <input \n      type=\"button\" \n      value=\"Get A1 comments\" \n      @click=\"getComments('A1')\" \n  />\n  <input \n      type=\"button\" \n      value=\"Reset A1 comments\" \n      @click=\"setComments('A1','')\"  \n  />\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([\n  [\"US\", \"Cheese\", \"2019-02-12\"],\n  [\"CA\", \"Apples\", \"2019-03-01\"],\n  [\"CA\", \"Carrots\", \"2018-11-10\"],\n  [\"BR\", \"Oranges\", \"2019-01-12\"],\n]);\n\n// Columns configuration\nconst columns = ref([\n  { width: \"300px\" },\n  { width: \"200px\" },\n  { width: \"200px\" }\n]);\n\n// Initial comments\nconst comments = ref({\n  B1: \"Initial comments on B1\",\n  C1: \"Initial comments on C1\",\n});\n\n// Method to get comments\nconst getComments = (cell) => {\n  if (spreadsheetRef.value) {\n      alert(spreadsheetRef.value.current[0].getComments(cell));\n  }\n};\n\n// Method to set comments\nconst setComments = (cell, title) => {\n  if (spreadsheetRef.value) {\n      spreadsheetRef.value.current[0].setComments(cell, title);\n  }\n};\n\n// Comments callback method\nconst oncomments = (worksheet) => {\n  console.log(worksheet);\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <input type=\"button\" value=\"Set A1 comments\" (click)=\"setA1Comments()\"/>\n        <input type=\"button\" value=\"Get A1 comments\" (click)=\"getA1Comments()\"/>\n        <input type=\"button\" value=\"Reset A1 comments\" (click)=\"resetA1Comments()\"/>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    worksheets: jspreadsheet.worksheetInstance[];\n\n    ngAfterViewInit() {\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Cheese', '2019-02-12'],\n                        ['CA', 'Apples', '2019-03-01'],\n                        ['CA', 'Carrots', '2018-11-10'],\n                        ['BR', 'Oranges', '2019-01-12'],\n                    ],\n                    columns: [\n                        { width: '300px' },\n                        { width: '200px' },\n                        { width: '200px' },\n                    ],\n                    allowComments: true,\n                    comments: {\n                        B1: 'Initial comments on B1',\n                        C1: 'Initial comments on C1'\n                    },\n                }\n            ],\n            oncomments: function () {\n                console.log(arguments);\n            }\n        });\n    }\n\n    setA1Comments() {\n        // Set a comment on cell A1\n        this.worksheets[0].setComments('A1', 'This is a comment for A1');\n    }\n\n    getA1Comments() {\n        // Get the comment from cell A1\n        const comment = this.worksheets[0].getComments('A1');\n        console.log('Comment on A1:', comment);\n    }\n\n    resetA1Comments() {\n        // Reset the comment on cell A1\n        this.worksheets[0].setComments('A1', '');\n    }\n}\n```\n\n### Batch Update for Multiple Cells\n\nThe `setComments` method supports adding comments to multiple cells simultaneously by passing an object, optimizing performance and avoiding unnecessary history entries.\n\n{.ignore}\n```javascript\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [10,10],\n        allowComments: true,\n    }],\n});\n\nworksheets[0].setComments({\n    A1: 'Comment on A1',\n    B1: 'Comments on B1'\n});\n```\n\n## Related Tools\n\n- [Jspreadsheet Pro Comments](https://jspreadsheet.com/docs/comments) - Advanced comment features with formatting and attachments (Pro)\n- [Advanced Comments Extension](https://jspreadsheet.com/products/comments) - Multiple comments per cell (Enterprise)\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/comparison.md",
    "content": "title: Jspreadsheet Distributions Comparison\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like, spreadsheet, table, documentation, performance, version differences, distribution comparison\ndescription: Explore a detailed comparison highlighting the key differences between various Jspreadsheet distributions.\n\n# Comparison Table\n\nThe main differences between the JSS distributions:\n\n| Package                                         |    CE distribution    |  Pro distribution   |  Pro distribution   |\n|-------------------------------------------------|:---------------------:|:-------------------:|:-------------------:|\n| Distribution                                    |        **CE**         |      **Base**       |     **Premium**     |\n| License                                         |      Free (MIT)       |      Required       |      Required       |\n| Spreadsheet-like formulas                       |          Yes          |      Advanced       |      Advanced       |\n| Copy and paste features                         |          Yes          |      Advanced       |      Advanced       |\n| Viewport                                        |     Lazy loading      |   Smart loading*    |   Smart loading*    |\n| Plugins support                                 |          Yes          |         Yes         |         Yes         |\n| Advance filtering and multiple column filtering |          No           |         Yes         |         Yes         |\n| Worksheets management                           |        Limited        |         Yes         |         Yes         |\n| Native persistence features                     |        Limited        |         Yes         |         Yes         |\n| Automatic formula update on copy and paste      |          No           |         Yes         |         Yes         |\n| Automatic formula update on corner copy         |          No           |         Yes         |         Yes         |\n| Formula returning DOM objects to cells.         |          No           |         Yes         |         Yes         |\n| Cell renderer                                   |          No           |         Yes         |         Yes         |\n| Editor definitions at a cell level              |          No           |         Yes         |         Yes         |\n| Cross worksheet calculations                    |          No           |         No          |         Yes         |\n| Native validations                              |          No           |         No          |         Yes         |\n| Column and row grouping                         |          No           |         No          |         Yes         |\n| Defined names (Range and variables)             |          No           |         No          |         Yes         |\n| Extensions                                      |          No           |         No          |         Yes         |\n| Formula Picker                                  |          No           |         No          |         Yes         |\n\n## More Details\n\n### Worksheet Management\n\nPro includes dragging-and-drop, deleting or renaming worksheets, adding a new worksheet button, and configuring the context menu.\n\n### Persistence Support\n\nPro offers native persistence features such as internal row IDs and sequences, updated row IDs, saving methods, and integration with persistence plugins.\n\n### Advanced Lazy Loading\n\nProvides efficient DOM management with pagination and lazy loading, creating and displaying DOM elements only when required. From version 8, lazy loading leverages the viewport and extends virtual DOM management to columns.\n\n### Cell-Level Definitions\n\nThe Pro distribution enables developers to configure properties like masking, format, type, and other attributes at the cell level.\n"
  },
  {
    "path": "docs/jspreadsheet/docs/config.md",
    "content": "title: Data Grid Settings\nkeywords: Jspreadsheet, data grid customization, React, Vue, JavaScript spreadsheet, Excel-like functionality, configuration settings, spreadsheet customization, data grid options\ndescription: Explore the configuration settings in Jspreadsheet for customizing data grids. Learn how to enable, disable, and adjust features to enhance flexibility for both spreadsheets and individual worksheets.\n\n# Data Grid Settings\n\nThis section provides an overview of the parameters that define the behaviour and functionality of spreadsheets and worksheets. Configuration options are categorized into two levels:\n\n- Spreadsheet-level settings for overarching configurations.\n- Worksheet-level settings for individual worksheet customizations.\n\n## Documentation\n\n### Methods\n\nThe following methods allow programmatic manipulation of configuration settings:\n\n| Method      | Description                                                                                        |\n|-------------|----------------------------------------------------------------------------------------------------|\n| `getConfig` | Retrieves the configuration of a worksheet.<br/>`getConfig() => Object`                                |\n| `setConfig` | Updates the configuration for a worksheet.<br/>`setConfig(options: Object, level?: Boolean) => void`  |\n\n\n## Examples\n\n### Data Grid Cloning\n\nThis example demonstrates how to copy a data grid configuration and use it to initialize a new spreadsheet.\n\n[See this example on JSFiddle](https://jsfiddle.net/spreadsheet/xz5pfgq7/)\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n<div id=\"spreadsheet-clone\"></div><br>\n\n<textarea id=\"console\" style=\"width: 600px; height: 100px;\"></textarea><br>\n\n<input type=\"button\" value=\"Clone the data grid above\" class=\"button main\" id=\"btn1\" />\n\n<script>\nconst clone = function() {\n    // Get the data grid configuration\n    let config = JSON.stringify(grid[0].parent.getConfig());\n    // Show on the textarea\n    document.getElementById('console').value = config;\n    // Destroy any existing spreadsheet\n    jspreadsheet.destroy(document.getElementById('spreadsheet-clone'));\n    // Parse\n    config = JSON.parse(config);\n    // Create a new spreadsheet\n    jspreadsheet(document.getElementById('spreadsheet-clone'), config);\n}\n\n// Create the JavaScript sample data grid\nlet grid = jspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: true,\n    toolbar: true,\n    worksheets: [{\n        data: [[1,2,3]],\n        minDimensions: [6, 6],\n    }],\n});\n\ndocument.getElementById('btn1').onclick = clone\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    const console = useRef();\n    const copy = useRef();\n\n    // Method to clone the data grid\n    const clone = function() {\n        // Get the data grid configuration\n        let config = JSON.stringify(spreadsheet.current[0].parent.getConfig());\n        // Show on the textarea\n        console.current.value = config;\n        // Destroy any existing spreadsheet\n        jspreadsheet.destroy(copy.current);\n        // Parse\n        config = JSON.parse(config);\n        // Create a new spreadsheet\n        jspreadsheet(copy.current, config);\n    }\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} tabs toolbar>\n                <Worksheet data={[[1,2,3]]} minDimensions={[8,8]} />\n            </Spreadsheet>\n            <div ref={copy}></div><br>\n            <textarea ref={console}></textarea><br>\n            <input type=\"button\" value=\"Clone the data grid above\" onClick={() => clone()} />\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\" tabs toolbar>\n      <Worksheet :data=\"[[1,2,3]]\" :minDimensions=\"[6,6]\" />\n  </Spreadsheet>\n  <div ref=\"copyRef\"></div><br />\n  <textarea ref=\"consoleRef\"></textarea><br />\n  <button @click=\"clone\">Clone the data grid above</button>\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/vue\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nexport default {\n  components: {\n      Spreadsheet,\n      Worksheet,\n  },\n  setup() {\n    const spreadsheetRef = ref(null);\n    const copyRef = ref(null);\n    const consoleRef = ref(null);\n\n\n\n      // Method to clone the data grid\n      const clone = function () {\n          // Get the data grid configuration\n          let config = JSON.stringify(spreadsheetRef.value.current[0].parent.getConfig());\n          // Show on the textarea\n          consoleRef.value.value = config;\n          // Destroy any existing spreadsheet\n          jspreadsheet.destroy(spreadsheetRef.value);\n          // Parse\n          config = JSON.parse(config);\n          // Create a new spreadsheet\n          jspreadsheet(copyRef.value, config);\n      };\n\n      return {\n        spreadsheetRef,\n        copyRef,\n        consoleRef,\n        clone\n      };\n  }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `\n        <div #spreadsheet></div>\n        <div #copy></div><br>\n        <textarea #console></textarea><br>\n        <input type=\"button\" value=\"Clone the data grid above\" (click)=\"this.clone()\" />\n    `,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    @ViewChild(\"console\") console: ElementRef;\n    @ViewChild(\"copy\") copy: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [4,4],\n            }],\n            contextmenu: function() {\n                return false;\n            }\n        });\n    }\n\n    // Clone the data grid\n    clone() {\n        // Get the data grid configuration\n        let config = JSON.stringify(this.worksheets[0].parent.getConfig());\n        // Show on the textarea\n        this.console.nativeElement.value = config;\n        // Destroy any existing spreadsheet\n        jspreadsheet.destroy(this.copy.nativeElement);\n        // Parse\n        config = JSON.parse(config);\n        // Create a new spreadsheet\n        jspreadsheet(this.copy.nativeElement, config);\n    }\n}\n```\n \n"
  },
  {
    "path": "docs/jspreadsheet/docs/contextmenu.md",
    "content": "title: Data Grid Context Menu Customization\nkeywords: Jspreadsheet, Jexcel, Angular data grid, JavaScript, Excel-like spreadsheet, data tables, data grid context menu, context customization, customize context menu, interactive data grid.\ndescription: Define and customize the context menu in Jspreadsheet. Manage default items, section info, and cell coordinates while adding custom menu entries based on application-specific rules.\n\n# Context Menu Customization\n\nJspreadsheet lets you customize the context menu through a context menu method, which provides default items, section info, coordinates, and context, which allows you to add or modify menu options based on dynamic application rules.  \n\n## Documentation\n\n### Item Properties\n\nThe `contextmenu` handler method might return an array of menu items, each represented as an object with the following properties:\n\n| Property   | Description                                                        |\n|------------|--------------------------------------------------------------------|\n| `type`     | Type of menu item: `\"line\"`, `\"divisor\"`, or `\"default\"`.          |\n| `title`    | The title of the menu item.                                        |\n| `icon`     | Icon key for the item (uses Material icon key for identification). |\n| `id`       | `id` attribute for the HTML element.                               |\n| `disabled` | If `true`, the item is disabled.                                   |\n| `onclick`  | `onclick` event handler function for the item.                     |\n| `shortcut` | Short description or shortcut instruction.                         |\n| `tooltip`  | Tooltip text displayed on hover.                                   |\n| `submenu`  | Array of submenu items.                                            |\n\n\n### Translation\n\nYou can define translations for default menu items in Jspreadsheet by using `jspreadsheet.setDictionary` as shown below: \n\n{.ignore}\n```javascript\njspreadsheet.setDictionary({\n    'Rename this worksheet': 'Renomear worksheet',\n    'Delete this worksheet': 'Apagar worksheet',\n    'Are you sure?': 'Tem certeza?',\n    'Rename this cell': 'Renomear essa celula',\n    'Cut': 'Cortar',\n    'Copy': 'Copy',\n    'Paste': 'Colar',\n    'Insert a new column before': 'Inserir uma coluna antes',\n    'Insert a new column after': 'Inserior uma coluna depois',\n    'Insert a new column after': 'Inserior uma coluna depois',\n    'Delete selected columns': 'Apagar colunas selecionadas',\n    'Rename this column': 'Renomar essa coluna',\n    'Create a new row': 'Criar uma nova linha',\n    'Order ascending': 'Ordenar asc',\n    'Order descending': 'Ordenar desc',\n    'Insert a new row before': 'Inserir uma linha antes',\n    'Insert a new row after': 'Inserir uma nova linha depois',\n    'Delete selected rows': 'Apagar linhas selecionadas',\n    'Edit notes': 'Editar notas',\n    'Add notes': 'Adicionar notas',\n    'Notes': 'Notas',\n    'Clear notes': 'Apagar notas',\n    'Save as': 'Salvar como',\n    'About': 'Sobre',\n});\n```\n\n\nJspreadsheet uses the jSuites [contextmenu plugin](https://jsuites.net/docs/contextmenu). For further details and examples, refer to the documentation.\n\n## Examples\n\n### Customize the contextmenu\n\nCustomize the contextmenu with a few basic options depending on the section clicked. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            [\"Apples\", \"Produce\", 1.5, 10, \"=C1*(1-(D1/100))\"],\n            [\"Bread\", \"Bakery\", 3.0, 20, \"=C2*(1-(D2/100))\"],\n            [\"Cheese\", \"Dairy\", 5.0, 15, \"=C3*(1-(D3/100))\"],\n            [\"Eggs\", \"Dairy\", 2.5, 0, \"=C4*(1-(D4/100))\"],\n        ],\n        columns: [\n            { title: 'Food', width: '140px' },\n            { title: 'Category' },\n            { title: 'Unit Price' },\n            { title: 'Discount' },\n            { title: 'Total ' },\n         ],\n         allowComments: true,\n    }],\n    contextMenu: function(o, x, y, e, items, section) {\n         // Reset all items\n         let itemsArr = [];\n\n         // If the click was in the headers\n         if (section == 'header') {\n            // Items to the header only\n            itemsArr.push({\n                title: jSuites.translate('Execute one action'),\n                onclick: function() {\n                    alert('test')\n                }\n            });\n\n            // Add a line\n            itemsArr.push({ type: 'line' });\n         }\n\n         // Save\n         itemsArr.push({\n             title: jSuites.translate('Save as'),\n             shortcut: 'Ctrl + S',\n             icon: 'save',\n             onclick: function () {\n                 o.download();\n             }\n         });\n\n         // About\n         itemsArr.push({\n             title: jSuites.translate('About'),\n             onclick: function() {\n                 alert('https://jspreadsheet.com');\n             }\n         });\n\n         return itemsArr;\n     }\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport jSuites from \"jsuites\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Intercept the context menu\nconst contextMenu = (o, x, y, e, items, section) => {\n     // Reset all items\n     items = [];\n\n     // If the click was in the headers\n     if (section == 'header') {\n        // Items to the header only\n        items.push({\n            title: jSuites.translate('Execute one action'),\n            onclick: function() {\n                alert('test')\n            }\n        });\n\n        // Add a line\n        items.push({ type: 'line' });\n     }\n\n     // Save\n     items.push({\n         title: jSuites.translate('Save as'),\n         shortcut: 'Ctrl + S',\n         icon: 'save',\n         onclick: function () {\n             o.download();\n         }\n     });\n\n     // About\n     items.push({\n         title: jSuites.translate('About'),\n         onclick: function() {\n             alert('https://jspreadsheet.com');\n         }\n     });\n\n     return items;\n}\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [\"Apples\", \"Produce\", 1.5, 10, \"=C1*(1-(D1/100))\"],\n        [\"Bread\", \"Bakery\", 3.0, 20, \"=C2*(1-(D2/100))\"],\n        [\"Cheese\", \"Dairy\", 5.0, 15, \"=C3*(1-(D3/100))\"],\n        [\"Eggs\", \"Dairy\", 2.5, 0, \"=C4*(1-(D4/100))\"],\n    ];\n    // Columns\n    const columns = [\n        { title: 'Food', width: '140px' },\n        { title: 'Category' },\n        { title: 'Unit Price' },\n        { title: 'Discount' },\n        { title: 'Total ' },\n    ];\n\n    return (\n        <Spreadsheet ref={spreadsheet} contextMenu={contextMenu}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\" :context-menu=\"contextMenu\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport jSuites from \"jsuites\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([\n    [\"Apples\", \"Produce\", 1.5, 10, \"=C1*(1-(D1/100))\"],\n    [\"Bread\", \"Bakery\", 3.0, 20, \"=C2*(1-(D2/100))\"],\n    [\"Cheese\", \"Dairy\", 5.0, 15, \"=C3*(1-(D3/100))\"],\n    [\"Eggs\", \"Dairy\", 2.5, 0, \"=C4*(1-(D4/100))\"],\n]);\n\n// Columns\nconst columns = ref([\n    { title: 'Food', width: '140px' },\n    { title: 'Category' },\n    { title: 'Unit Price' },\n    { title: 'Discount' },\n    { title: 'Total ' },\n]);\n\n// Intercept the context menu\nconst contextMenu = (o, x, y, e, items, section) => {\n     // Reset all items\n     items = [];\n\n     // If the click was in the headers\n     if (section == 'header') {\n        // Items to the header only\n        items.push({\n            title: jSuites.translate('Execute one action'),\n            onclick: function() {\n                alert('test')\n            }\n        });\n\n        // Add a line\n        items.push({ type: 'line' });\n     }\n\n     // Save\n     items.push({\n         title: jSuites.translate('Save as'),\n         shortcut: 'Ctrl + S',\n         icon: 'save',\n         onclick: function () {\n             o.download();\n         }\n     });\n\n     // About\n     items.push({\n         title: jSuites.translate('About'),\n         onclick: function() {\n             alert('https://jspreadsheet.com');\n         }\n     });\n\n     return items;\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport jSuites from \"jsuites\"\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    [\"Apples\", \"Produce\", 1.5, 10, \"=C1*(1-(D1/100))\"],\n                    [\"Bread\", \"Bakery\", 3.0, 20, \"=C2*(1-(D2/100))\"],\n                    [\"Cheese\", \"Dairy\", 5.0, 15, \"=C3*(1-(D3/100))\"],\n                    [\"Eggs\", \"Dairy\", 2.5, 0, \"=C4*(1-(D4/100))\"],\n                ],\n                columns: [\n                    { title: 'Food', width: '140px' },\n                    { title: 'Category' },\n                    { title: 'Unit Price' },\n                    { title: 'Discount' },\n                    { title: 'Total ' },\n                 ],\n                 allowComments: true,\n            }],\n            contextMenu: function(o, x, y, e, items, section) {\n                 // Reset all items\n                 items = [];\n\n                 // If the click was in the headers\n                 if (section == 'header') {\n                    // Items to the header only\n                    items.push({\n                        title: jSuites.translate('Execute one action'),\n                        onclick: function() {\n                            alert('test')\n                        }\n                    });\n\n                    // Add a line\n                    items.push({ type: 'line' });\n                 }\n\n                 // Save\n                 items.push({\n                     title: jSuites.translate('Save as'),\n                     shortcut: 'Ctrl + S',\n                     icon: 'save',\n                     onclick: function () {\n                         o.download();\n                     }\n                 });\n\n                 // About\n                 items.push({\n                     title: jSuites.translate('About'),\n                     onclick: function() {\n                         alert('https://jspreadsheet.com');\n                     }\n                 });\n\n                 return items;\n             }\n        });\n    }\n}\n```\n\n\n### Disable the Context Menu\n\nTo disable the context menu in Jspreadsheet, define a contextMenu method that returns false.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n    }],\n    contextMenu: function() {\n        return false;\n    }\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n  // Spreadsheet array of worksheets\n  const spreadsheet = useRef();\n\n  // Disable the context menu\n  const contextMenu = () => {\n    return false;\n  };\n\n  return (\n      <Spreadsheet ref={spreadsheet} contextMenu={contextMenu}>\n        <Worksheet minDimensions={[10,10]} />\n      </Spreadsheet>\n  );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\" :context-menu=\"contextMenu\">\n        <Worksheet :min-dimensions=\"[4,4]\" />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Disable the context menu\nconst contextMenu = () => {\n    return false;\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [4,4],\n            }],\n            // Disable the context menu\n            contextMenu: function() {\n                return false;\n            }\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [JavaScript Context Menu](https://jsuites.net/docs/contextmenu) - Standalone context menu component\n- [LemonadeJS Context Menu](https://lemonadejs.com/docs/plugins/context-menu) - Reactive context menu component\n- [Jspreadsheet Pro Context Menu](https://jspreadsheet.com/docs/contextmenu) - Advanced spreadsheet context menu customization (Pro)\n "
  },
  {
    "path": "docs/jspreadsheet/docs/custom-formulas.md",
    "content": "title: Custom Excel-Like Formulas  \nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like formulas, formulas, calculations, spreadsheet calculations, spreadsheet formulas, spreadsheet-like formulas, Excel formulas, Google Sheet formulas, event handling, customizations, internationalization  \ndescription: Learn how to create custom Excel-like formulas using Jspreadsheet, with examples for implementing advanced calculations and formula features.\n\n# Custom Formulas\n\n## Overview\n\nJspreadsheet allows developers to create custom Excel-like formulas and methods. These formulas can interact with APIs, return specific outputs, and render dynamic content or DOM elements directly within cells. This feature enables the development of highly interactive and dynamic spreadsheet applications.\n\n{.green}\n> **IMPORTANT:** All custom method names must be capitalized.\n\n### Example\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a custom javascript method (capital case)\nconst NEGATIVE = function(v) {\n    return -1 * v;\n}\n\n// Send custom formula to the correct scope\nformula.setFormula({ NEGATIVE })\n\n// Create spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            [ '1000', '=NEGATIVE(A1)' ],\n            [ '2000', '=NEGATIVE(A2)' ],\n            [ '3000', '=NEGATIVE(A3)' ],\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nimport formula from \"@jspreadsheet/formula\";\n\n// Create a custom javascript method (capital case)\nconst NEGATIVE = function(v) {\n    return -1 * v;\n}\n\n// Send custom formula to the correct scope\nformula.setFormula({ NEGATIVE })\n\n// Create the component\nexport default function App() {\n    // Array with all the data grids\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [ '1000', '=NEGATIVE(A1)' ],\n        [ '2000', '=NEGATIVE(A2)' ],\n        [ '3000', '=NEGATIVE(A3)' ],\n    ]\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet} >\n            <Worksheet data={data}  />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nimport formula from \"@jspreadsheet/formula\";\n\n// Custom NEGATIVE formula\nconst NEGATIVE = function(v) {\n  return -1 * v;\n}\n\n// Send the custom formula to the correct scope\nformula.setFormula({ NEGATIVE })\n\n// Create data grid\nconst data = ref([\n    [ '1000', '=NEGATIVE(A1)' ],\n    [ '2000', '=NEGATIVE(A2)' ],\n    [ '3000', '=NEGATIVE(A3)' ],\n])\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create a custom javascript method (capital case)\nconst NEGATIVE = function(v) {\n    return -1 * v;\n}\n\n// Send the custom formula to the correct scope\nformula.setFormula({ NEGATIVE })\n\n// Create the data grid component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    [ '1000', '=NEGATIVE(A1)' ],\n                    [ '2000', '=NEGATIVE(A2)' ],\n                    [ '3000', '=NEGATIVE(A3)' ],\n                ]\n            }]\n        });\n    }\n}\n```\n\n{.pro}\n> #### Formula Pro\n>\n> The Formula Pro extension enhances custom formula creation by providing execution context, including the origin cell (x, y), the worksheet, and the ability for developers to return DOM elements directly to the cell via the formula. \n> <br><br>\n> [Learn more](https://jspreadsheet.com/products/formulas){.button}\n"
  },
  {
    "path": "docs/jspreadsheet/docs/data.md",
    "content": "title: Spreadsheet Data Operations\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like formulas, spreadsheet data, worksheet data, data management, update cell data, manage worksheet data, programmatically update data\ndescription: Learn how to load, manage, and manipulate worksheet data in Jspreadsheet. Explore methods for programmatic data updates and integrate spreadsheet functionality with your web applications using related events.\n\n# Data Management\n\nThis section details the methods, events, and settings for loading, updating, and managing data in Jspreadsheet data grids.\n\n## Loading the Data\n\n### Formats\n\nYou can create spreadsheets using various data formats, including:\n\n- Existing HTML tables\n- CSV files\n- JSON objects\n- JavaScript arrays\n\n\n## Documentation\n\n### Methods\n\nThese methods assist in managing data in your grid or spreadsheet.\n\n#### Read Methods\n\n| Method                        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getValue`                    | Get the value of a cell.<br/>@param `cell` - Cell name.<br/>@param `processedValue` - If true, it returns the cell's innerHTML. Otherwise, it returns the value of the cell in the data property.<br/>`worksheetInstance.getValue(cell: string, processedValue?: boolean): CellValue \\| null;`                                                                                                                                                                                                                                                                                                                                                                                        |\n| `getValueFromCoords`{.nowrap} | Get the value of a cell by its coordinates.<br/>@param `x` - Column index.<br/>@param `y` - Row index.<br/>@param `processedValue` - If true, it returns the cell's innerHTML. Otherwise, it returns the value of the cell in the data property.<br/>`worksheetInstance.getValueFromCoords(x: number, y: number, processedValue?: boolean): CellValue \\| null;`                                                                         |\n| `getData`                     | Get the full or partial table data.<br/>@param `highlighted` - If true, get only data from highlighted cells. If false, get data from all cells. Default: false.<br/>@param `processed` - If false, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the {@link WorksheetOptions.data} property. Default: false.<br/>@param `delimiter` - Column delimiter. If this property is specified, the result will be formatted like a csv.<br/>@param `asJson` - If this property is true, the result will be formatted as json.<br/>`getData(highlighted?: boolean, processed?: boolean, delimiter?: string, asJson?: boolean): CellValue[][];` |\n| `getRowData`                  | Get data from a row by its index.<br/>@param `rowNumber` - Row index.<br/>@param `processed` - If true, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the data property. Default: false.<br/>`worksheetInstance.getRowData(rowNumber: number, processed?: boolean): CellValue[] \\| undefined;`                                                                                                                                                                                                                                                                                                                                       |\n| `getColumnData`               | Get the data from one column by its index.<br/>@param `columnNumber` - Column index.<br/>@param `processed` - If true, the return is constructed using the innerHTML of the cells. Otherwise, it is constructed using the data property. Default: false.<br/>`worksheetInstance.getColumnData(columnNumber: number, processed?: boolean): CellValue[];`                                                                                                                                                                                                                                                                                                                               |\n| `download`                    | Get the current data as a CSV file.<br/>@param `includeHeaders` - If true, include the header regardless of the {@link SpreadsheetOptions.includeHeadersOnDownload} property value.<br/>@param `processed` - If true, the result will contain the displayed cell values. Otherwise, the result will contain the actual cell values.<br/>`worksheetInstance.download(includeHeaders?: boolean, processed?: boolean): void;`                                                                                                                                                                                                                                                            |\n\n#### Write Methods\n\n| Method                        | Description                                                                                                                                  |\n|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|\n| `setValue`                    | Change the value of one or more cells.<br/>@param `cell` - Name of a cell, HTML element that represents a cell or an array whose items can be any of the previous alternatives or objects. When an array item is an object, it must have the cell coordinates (\"x\" and \"y\") and can have the cell's new value (\"value\"), but if does not have it, the \"value\" parameter is used instead.<br/>@param `value` - New cell value.<br/>@param `force` - If true, changes the value of even read-only cells.<br/>`worksheetInstance.setValue(cell: string \\| HTMLTableCellElement \\| (string \\| { x: number; y: number; value?: CellValue } \\| HTMLTableCellElement)[], value?: CellValue, force?: boolean): void;`        |\n| `setValueFromCoords`{.nowrap} | Set a cell value based on its coordinates.<br/>@param `x` - Cell column index.<br/>@param `y` - Cell row index.<br/>@param `value` - New value.<br/>@param `force` - If true, changes the value of even read-only cells.<br/>`worksheetInstance.setValueFromCoords(x: number, y: number, value: CellValue, force?: boolean): void;` |\n| `setData`                     | Set data.<br/>@param `data` - New data. It can be an array of cell values or an array of objects whose values are cell values.<br/>`worksheetInstance.setData(data?: CellValue[][] \\| Record<string, CellValue>[]): void;`                                                                                |\n| `setRowData`                  | Set a row data by index.<br/>@param `rowNumber` - Row index.<br/>@param `data` - New data. Positions with the null value are not changed in the table.<br/>@param `force` - If true, the method also changes the contents of readonly columns.<br/>`worksheetInstance.setRowData(rowNumber: number, data: (CellValue \\| null)[], force?: boolean): void;`                                        |\n| `setColumnData`               | Set the data from one column by index.<br/>@param `colNumber` - Column index.<br/>@param `data` - New data. Positions with the null value are not changed in the table.<br/>@param `force` - If true, the method also changes the contents of readonly columns.<br/>`worksheetInstance.setColumnData(colNumber: number, data: (CellValue \\| null)[], force?: boolean): void;`                               |\n\n### Events\n\nYou can find a list of javascript events related to the data operations.\n\n| Event                     | Description                                                                                                                                           |\n|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| \n| `onbeforechange`{.nowrap} | Occurs before a column value is changed. If any value is returned, it will be the cell's new value.<br/>@param `instance` - Instance of the worksheet where the changes will occur.<br/>@param `cell` - HTML element that represents the cell being changed.<br/>@param `colIndex` - Cell column index being changed.<br/>@param `rowIndex` - Cell row index being changed.<br/>@param `newValue` - Value being applied to the cell<br/>`onbeforechange(instance: WorksheetInstance, cell: HTMLTableCellElement, colIndex: string \\| number, rowIndex: string \\| number, newValue: CellValue): undefined \\| CellValue;`         |\n| `onchange`                | Occurs after a column value is changed.<br/>@param `instance` - Instance of the worksheet where the change occurred.<br/>@param `cell` - HTML element that represents the cell being changed.<br/>@param `colIndex` - Cell column index being changed.<br/>@param `rowIndex` - Cell row index being changed.<br/>@param `newValue` - New cell value.<br/>@param `oldValue` - Old cell value.<br/>`onchange(instance: WorksheetInstance, cell: HTMLTableCellElement, colIndex: string \\| number, rowIndex: string \\| number, newValue: CellValue, oldValue: CellValue): void;` |\n| `onafterchanges`          | Occurs after all changes are applied in the tables.<br/>@param `instance` - Instance of the worksheet where the change occurred.<br/>@param `changes` - list of changes.<br/>`onafterchanges(instance: WorksheetInstance, changes: CellChange[]): void;`                             |\n\n### Initial Settings\n\nA list of settings that can be utilized when initializing the data grid.\n\n| Property                | Description                                       |\n|-------------------------|---------------------------------------------------|\n| `data: Array \\| Object` | Defines initial data from a JSON or array.        |\n| `url: String`           | Loads data from an external file.                 |\n| `csv: String`           | Loads data from an external CSV file.             |\n| `csvHeaders: Boolean`   | The first row of the CSV file is used as headers. |\n| `csvDelimiter: String`  | Sets the CSV delimiter (default: ',').            |\n\n\n## Examples\n\n### Create a data grid from an 2D array.\n\nCreate a spreadsheet from a JavaScript array \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Mazda', 2001, 2000],\n            ['Peugeot', 2010, 5000],\n            ['Honda Fit', 2009, 3000],\n            ['Honda CRV', 2010, 6000],\n        ],\n        columns: [\n            { title:'Model', width:'300px' },\n            { title:'Price', width:'80px' },\n            { title:'Model', width:'100px' }\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ];\n    // Columns\n    const columns = [\n        { title:'Model', width:'300px' },\n        { title:'Price', width:'80px' },\n        { title:'Model', width:'100px' }\n    ];\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([\n    ['Mazda', 2001, 2000],\n    ['Peugeot', 2010, 5000],\n    ['Honda Fit', 2009, 3000],\n    ['Honda CRV', 2010, 6000],\n]);\n\n// Columns\nconst columns = ref([\n    { title:'Model', width:'300px' },\n    { title:'Price', width:'80px' },\n    { title:'Model', width:'100px' }\n]);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Mazda', 2001, 2000],\n                    ['Peugeot', 2010, 5000],\n                    ['Honda Fit', 2009, 3000],\n                    ['Honda CRV', 2010, 6000],\n                ],\n                columns: [\n                    { title:'Model', width:'300px' },\n                    { title:'Price', width:'80px' },\n                    { title:'Model', width:'100px' }\n                ]\n            }]\n        });\n    }\n}\n```\n\n\n### Create a data grid from a CSV file\n\nHow to create a data grid from a remote CSV file \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        // Point to your file\n        csv: '/jspreadsheet/demo.csv',\n        // First line will define the header titles\n        csvHeaders: true,\n        columns: [\n            { width: '200px' },\n            { width: '100px' },\n            { width: '100px' },\n        ],\n        pagination: 10,\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet csv={\"/jspreadsheet/demo.csv\"} csvHeaders />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\">\n        <Worksheet csv=\"/jspreadsheet/demo.csv\" csvHeaders />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nconst spreadsheetRef = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                // Point to your file\n                csv: '/jspreadsheet/demo.csv',\n                // First line will define the header titles\n                csvHeaders: true,\n                columns: [\n                    { width: '200px' },\n                    { width: '100px' },\n                    { width: '100px' },\n                ]\n            }]\n        });\n    }\n}\n```\n\n### Create a data grid from a HTML table\n\nHow to create data grid from an existing HTML table element \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n\n<table id=\"spreadsheet\">\n<thead>\n<tr>\n<td>POS</td>\n<td>TITLE</td>\n<td>ARTIST</td>\n<td>PEAK</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n<td>LEWIS CAPALDI</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>NO 6 COLLABORATIONS PROJECT</td>\n<td>ED SHEERAN</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>THE GREATEST SHOWMAN</td>\n<td>MOTION PICTURE CAST RECORDING</td>\n<td>1</td>\n</tbody>\n</table>\n\n<br>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'));\n</script>\n</html>\n```\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    useEffect(() => {\n        if (spreadsheet.current) {\n            jspreadsheet(spreadsheet.current);\n        }\n    }, [])\n\n    // Render component\n    return (\n        <>\n            <table ref={spreadsheet}>\n                <thead>\n                    <tr>\n                        <td>POS</td>\n                        <td>TITLE</td>\n                        <td>ARTIST</td>\n                        <td>PEAK</td>\n                    </tr>\n                </thead>\n                <tbody>\n                    <tr>\n                        <td>1</td>\n                        <td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n                        <td>LEWIS CAPALDI</td>\n                        <td>1</td>\n                    </tr>\n                    <tr>\n                        <td>2</td>\n                        <td>NO 6 COLLABORATIONS PROJECT</td>\n                        <td>ED SHEERAN</td>\n                        <td>1</td>\n                    </tr>\n                    <tr>\n                        <td>3</td>\n                        <td>THE GREATEST SHOWMAN</td>\n                        <td>MOTION PICTURE CAST RECORDING</td>\n                        <td>1</td>\n                    </tr>\n                </tbody>\n            </table>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <table ref=\"spreadsheetRef\">\n        <thead>\n            <tr>\n                <td>POS</td>\n                <td>TITLE</td>\n                <td>ARTIST</td>\n                <td>PEAK</td>\n            </tr>\n        </thead>\n        <tbody>\n            <tr>\n                <td>1</td>\n                <td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n                <td>LEWIS CAPALDI</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>2</td>\n                <td>NO 6 COLLABORATIONS PROJECT</td>\n                <td>ED SHEERAN</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>3</td>\n                <td>THE GREATEST SHOWMAN</td>\n                <td>MOTION PICTURE CAST RECORDING</td>\n                <td>1</td>\n            </tr>\n        </tbody>\n    </table>\n</template>\n\n<script setup>\nimport { ref, onMounted } from 'vue';\nimport { jspreadsheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Initialize jspreadsheet when component is mounted\nonMounted(() => {\n    if (spreadsheetRef.value) {\n        jspreadsheet(spreadsheetRef.value);\n    }\n});\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<table #spreadsheet>\n        <thead>\n            <tr>\n                <td>POS</td>\n                <td>TITLE</td>\n                <td>ARTIST</td>\n                <td>PEAK</td>\n            </tr>\n        </thead>\n        <tbody>\n            <tr>\n                <td>1</td>\n                <td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n                <td>LEWIS CAPALDI</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>2</td>\n                <td>NO 6 COLLABORATIONS PROJECT</td>\n                <td>ED SHEERAN</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>3</td>\n                <td>THE GREATEST SHOWMAN</td>\n                <td>MOTION PICTURE CAST RECORDING</td>\n                <td>1</td>\n            </tr>\n        </tbody>\n    </table>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement);\n    }\n}\n```\n\n### Batch update\n\nHow to update multiple cells with a single call \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n<div id=\"spreadsheet\"></div>\n\n<p><input type='button' value='Update multiple cells' id='btn1' /></p>\n\n<script>\nconst update = function() {\n    let records = [\n        {\n            x: 0,\n            y: 0,\n            value: 'update A1',\n        },\n        {\n            x: 3,\n            y: 3,\n            value: 'Another cell',\n        }\n    ];\n\n    worksheets[0].setValue(records);\n}\n\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n    }]\n});\n\ndocument.getElementById('btn1').onclick = update\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Update multiple cells\n    const update = () => {\n        let records = [\n            {\n                x: 0,\n                y: 0,\n                value: 'update A1',\n            },\n            {\n                x: 10,\n                y: 10,\n                value: 'Another cell',\n            },\n            // (...)\n        ];\n\n        spreadsheet.current[0].setValue(records);\n    }\n\n    // Render data grid component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet />\n            </Spreadsheet>\n        <input type='button' value='Update multiple cells' onClick={()=>update()} />\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet />\n    </Spreadsheet>\n    <input type=\"button\" value=\"Update multiple cells\" @click=\"update\" />\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const spreadsheetRef = ref(null);\n\n        const update = () => {\n            const records = [\n                {\n                    x: 0,\n                    y: 0,\n                    value: \"update A1\",\n                },\n                {\n                    x: 10,\n                    y: 10,\n                    value: \"Another cell\",\n                },\n                // (...)\n            ];\n\n            if (spreadsheetRef.value) {\n                spreadsheetRef.value.current[0].setValue(records);\n            }\n        };\n\n        return {\n            spreadsheetRef,\n            update,\n        };\n    },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <input type='button' value='Update multiple cells' (click)=\"update\" />`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [10,10],\n            }]\n        });\n    }\n    update() {\n        let records = [\n            {\n                x: 0,\n                y: 0,\n                value: 'update A1',\n            },\n            {\n                x: 10,\n                y: 10,\n                value: 'Another cell',\n            },\n            // (...)\n        ];\n\n        this.worksheets[0].setValue(records);\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Data Operations**\n>\n> Jspreadsheet CE supports basic data loading from arrays, CSV, JSON, and HTML tables. **Jspreadsheet Pro** adds enterprise-grade data management capabilities:\n>\n> **Excel Import/Export:**\n> - **Full Excel Compatibility:** Import/export native .xlsx and .xls files with formulas, formatting, and styles preserved\n> - **Multiple Worksheet Support:** Import/export workbooks with multiple sheets\n> - **Formula Translation:** Excel formulas automatically converted to Jspreadsheet format\n> - **Style Preservation:** Cell colors, fonts, borders, and formatting maintained on import/export\n> - **Chart Import:** Import Excel charts and graphs\n> - **Named Ranges:** Support for Excel named ranges and defined names\n>\n> **Advanced Data Loading:**\n> - **Database Integration:** Direct integration with MySQL, PostgreSQL, SQL Server, MongoDB\n> - **API Integration:** RESTful API support with authentication and pagination\n> - **Real-Time Data:** WebSocket support for live data updates\n> - **Lazy Loading:** Load large datasets incrementally for better performance\n> - **Background Processing:** Async data loading with progress indicators\n>\n> **Data Export Options:**\n> - **PDF Export:** Export spreadsheets to formatted PDF documents\n> - **XML Export:** Export data in XML format\n> - **Custom Formats:** Build custom export formats with templates\n> - **Batch Export:** Export multiple worksheets at once\n> - **Server-Side Export:** Generate exports on the server for large datasets\n>\n> **Data Validation & Quality:**\n> - **Data Validation Rules:** Enforce data integrity with validation rules\n> - **Duplicate Detection:** Find and merge duplicate records\n> - **Data Cleansing:** Auto-trim, case normalization, format correction\n> - **Import Validation:** Validate data during import with error reporting\n>\n> Perfect for business applications requiring Excel compatibility and enterprise data management.\n>\n> **[Explore Pro Data Features →](https://jspreadsheet.com/docs/import-and-export)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n"
  },
  {
    "path": "docs/jspreadsheet/docs/editors.md",
    "content": "title: Spreadsheet Inline Cell Editors\nkeywords: Jspreadsheet, data grid, JavaScript, Excel-like controls, spreadsheet controls, data input editors, custom cell editors, input components, editor methods, editor settings, editor events, interactive data grid, custom data input\ndescription: Jspreadsheet cell editors support integrating third-party JavaScript components, enabling advanced, customizable inputs for dynamic data entry.\n\n# Spreadsheet Cell Editors\n\n## Overview\n\nJspreadsheet editors assist users with cell input during editing. Jspreadsheet includes built-in components, from basic text fields to interactive widgets. The API also allows developers to create custom components, integrating various JavaScript input methods for enhanced data interaction.\n\n### Available Editors\n\n* text\n* numeric\n* hidden\n* dropdown\n* autocomplete\n* checkbox\n* radio\n* calendar\n* image\n* color\n* html\n\n{.pro}\n> #### Differences in the Pro Version\n> The Pro version offers 17 native editors and allows defining cell-level editors and programmatically changing cell types.\\\n>\\\n> [Learn more](https://jspreadsheet.com/docs/editors){.button}\n\n## Documentation\n\n### Methods\n\nMethods related to editors and data grid cell editing:\n\n| Method        | Description                                                                                         |\n|---------------|-----------------------------------------------------------------------------------------------------|\n| `getEditor`   | Retrieve the editor instance and options for a column (`x`) or cell (`x, y`).                       |\n| `openEditor`  | Start editing a cell.<br/>`openEditor(cell: HTMLElement, empty: boolean, e: MouseEvent): void`      |\n| `closeEditor` | Close the editor.<br/>`closeEditor(cell: HTMLElement, save: boolean): void`                         |\n\n### Events\n\nEvents related to editors and data grid editing:\n\n| Event                     | Description                                                                                                                                                      |\n|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `oneditionstart`          | Triggered when cell editing starts.<br/>`oneditionstart(worksheet: Object, cell: HTMLElement, x: number, y: number): void \\| boolean`                            |\n| `oneditionend`            | Triggered when cell editing ends.<br/>`oneditionend(worksheet: Object, cell: HTMLElement, x: number, y: number, v: any, save: boolean): void`                    |\n| `oncreateeditor`{.nowrap} | Triggered when an editor is created.<br/>`oncreateeditor(worksheet: Object, cell: HTMLElement, x: number, y: number, input: HTMLElement, options: object): void` |\n\n<br>\n\n### Declaring the Editors\n\nIn CE, the `type` property declares the editor at the column level. The Pro version allows defining editors at the cell level.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [5,5],\n        columns: [\n            { type: 'text' },\n            { type: 'dropdown', source: ['Male','Female'] }\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Columns\n    const columns = [\n        { type: 'text' },\n        { type: 'dropdown', source: ['Male','Female'] }\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n    <Worksheet :columns=\"columns\" :cells=\"cells\" />\n  </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nexport default {\n  components: {\n    Spreadsheet,\n    Worksheet,\n  },\n  setup() {\n    // Define reactive data\n    const columns = ref([\n      { type: \"text\" },\n      { type: 'dropdown', source: [\"Male\", \"Fermale\"]}\n    ]);\n\n    // Return reactive data to the template\n    return {\n      columns,\n    };\n  },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [5,5],\n                columns: [\n                    { type: 'text' },\n                    { type: 'dropdown', source: ['Male','Female'] }\n                ],\n            }]\n        });\n    }\n}\n```\n\n### Custom Editors  \n\nCustom editors allow enhanced user interaction and data collection in your data grid. A custom editor is defined as a JavaScript object with the following methods:  \n\n| Method        | Description                                                                                                                                                          |\n|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `createCell`  | Triggered when a new cell is created.<br/>`createCell(cell: Object, value: Any, x: Number, y: Number, instance: Object, options: Object): void`                       |\n| `updateCell`  | Triggered when the cell value changes.<br/>`updateCell(cell: Object, value: Any, x: Number, y: Number, instance: Object, options: Object): void`                      |\n| `openEditor`  | Triggered when the user starts editing a cell.<br/>`openEditor(cell: Object, value: Any, x: Number, y: Number, instance: Object, options: Object): void`              |\n| `closeEditor` | Triggered when the user finalizes the cell edit.<br/>`closeEditor(cell: Object, confirmChanges: Boolean, x: Number, y: Number, instance: Object, options: Object): void` |\n| `destroyCell` | Triggered when a cell is destroyed.<br/>`destroyCell(cell: Object, x: Number, y: Number, instance: Object): void`                                                    |\n| `get`         | Converts raw data into processed data, such as displaying text instead of an ID in a dropdown type.<br/>`get(options: Object, value: Any): Any`                      |\n\n## Examples\n\n### Native Editors\n\nA basic example showcasing the use of multiple native editors.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Jazz', 'Honda', '2019-02-12', '/templates/default/img/nophoto.jpg', true, 2000.00, '#777700'],\n            ['Civic', 'Honda', '2018-07-11', '/templates/default/img/nophoto.jpg', true, 4000.01, '#007777'],\n        ],\n        columns: [\n            {\n                type:'text',\n                title:'Car'\n            },\n            {\n                type: 'dropdown',\n                title:'Make',\n                source:[\n                    \"Alfa Romeo\",\n                    \"Audi\",\n                    \"Bmw\",\n                    \"Chevrolet\",\n                    \"Chrysler\",\n                    \"Dodge\",\n                    \"Ferrari\",\n                    \"Fiat\",\n                    \"Ford\",\n                    \"Honda\",\n                    \"Hyundai\",\n                    \"Jaguar\",\n                    \"Jeep\",\n                    \"Kia\",\n                    \"Mazda\",\n                    \"Mercedes-Benz\",\n                    \"Mitsubishi\",\n                    \"Nissan\",\n                    \"Peugeot\",\n                    \"Porsche\",\n                    \"Subaru\",\n                    \"Suzuki\",\n                    \"Toyota\",\n                    \"Volkswagen\"\n                  ]\n            },\n            {\n                type: 'calendar',\n                title:'Available',\n                options:{ format:'DD/MM/YYYY' }\n            },\n            {\n                type: 'image',\n                title:'Photo',\n            },\n            {\n                type: 'checkbox',\n                title:'Stock',\n                width: 80,\n            },\n            {\n                type: 'text',\n                title:'Price',\n                mask:'$ #.##0,00',\n                width: 80,\n                decimal:',',\n                disabledMaskOnEdition: true\n            },\n            {\n                type: 'color',\n                width: 80,\n                render:'square',\n            },\n         ]\n    }],\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Jazz', 'Honda', '2019-02-12', '/templates/default/img/nophoto.jpg', true, 2000.00, '#777700'],\n        ['Civic', 'Honda', '2018-07-11', '/templates/default/img/nophoto.jpg', true, 4000.01, '#007777'],\n    ];\n    // Columns\n    const columns = [\n        {\n            type:'text',\n            title:'Car',\n            width:120\n        },\n        {\n            type: 'dropdown',\n            title:'Make',\n            width:180,\n            source:[\n                \"Alfa Romeo\",\n                \"Audi\",\n                \"Bmw\",\n                \"Chevrolet\",\n                // (...)\n              ]\n        },\n        {\n            type: 'calendar',\n            title:'Available',\n            width:120,\n            options:{ format:'DD/MM/YYYY' }\n        },\n        {\n            type: 'image',\n            title:'Photo',\n            width:120\n        },\n        {\n            type: 'checkbox',\n            title:'Stock',\n            width:80\n        },\n        {\n            type: 'text',\n            title:'Price',\n            mask:'$ #.##0,00',\n            width:100,\n            decimal:',',\n            disabledMaskOnEdition: true\n        },\n        {\n            type: 'color',\n            width:100,\n            render:'square',\n        },\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        // Data\n        const data = [\n            ['Jazz', 'Honda', '2019-02-12', '/templates/default/img/nophoto.jpg', true, 2000.00, '#777700'],\n            ['Civic', 'Honda', '2018-07-11', '/templates/default/img/nophoto.jpg', true, 4000.01, '#007777'],\n        ];\n        // Columns\n        const columns = [\n            {\n                type:'text',\n                title:'Car',\n                width:120\n            },\n            {\n                type: 'dropdown',\n                title:'Make',\n                width:180,\n                source:[\n                    \"Alfa Romeo\",\n                    \"Audi\",\n                    \"Bmw\",\n                    \"Chevrolet\",\n                    // (...)\n                  ]\n            },\n            {\n                type: 'calendar',\n                title:'Available',\n                width:120,\n                options:{ format:'DD/MM/YYYY' }\n            },\n            {\n                type: 'image',\n                title:'Photo',\n                width:120\n            },\n            {\n                type: 'checkbox',\n                title:'Stock',\n                width:80\n            },\n            {\n                type: 'text',\n                title:'Price',\n                mask:'$ #.##0,00',\n                width:100,\n                decimal:',',\n                disabledMaskOnEdition: true\n            },\n            {\n                type: 'color',\n                width:100,\n                render:'square',\n            },\n        ];\n\n        return {\n            data,\n            columns,\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Jazz', 'Honda', '2019-02-12', '/templates/default/img/nophoto.jpg', true, 2000.00, '#777700'],\n                    ['Civic', 'Honda', '2018-07-11', '/templates/default/img/nophoto.jpg', true, 4000.01, '#007777'],\n                ],\n                columns: [\n                    {\n                        type:'text',\n                        title:'Car',\n                        width:120\n                    },\n                    {\n                        type: 'dropdown',\n                        title:'Make',\n                        width:180,\n                        source:[\n                            \"Alfa Romeo\",\n                            \"Audi\",\n                            \"Bmw\",\n                            \"Chevrolet\",\n                            \"Chrysler\",\n                            \"Dodge\",\n                            \"Ferrari\",\n                            \"Fiat\",\n                            \"Ford\",\n                            \"Honda\",\n                            \"Hyundai\",\n                            \"Jaguar\",\n                            \"Jeep\",\n                            \"Kia\",\n                            \"Mazda\",\n                            \"Mercedes-Benz\",\n                            \"Mitsubishi\",\n                            \"Nissan\",\n                            \"Peugeot\",\n                            \"Porsche\",\n                            \"Subaru\",\n                            \"Suzuki\",\n                            \"Toyota\",\n                            \"Volkswagen\"\n                          ]\n                    },\n                    {\n                        type: 'calendar',\n                        title:'Available',\n                        width:120,\n                        options:{ format:'DD/MM/YYYY' }\n                    },\n                    {\n                        type: 'image',\n                        title:'Photo',\n                        width:120\n                    },\n                    {\n                        type: 'checkbox',\n                        title:'Stock',\n                        width:80\n                    },\n                    {\n                        type: 'text',\n                        title:'Price',\n                        mask:'$ #.##0,00',\n                        width:100,\n                        decimal:',',\n                        disabledMaskOnEdition: true\n                    },\n                    {\n                        type: 'color',\n                        width:100,\n                        render:'square',\n                    },\n                 ]\n            }]\n        });\n    }\n}\n```\n\n### Custom Editor\n\nCreate an edit button within a cell.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet action = function() {\n    let methods = {};\n\n    methods.createCell = function(cell, value, x, y, instance, options) {\n        let input = document.createElement('i');\n        input.className = 'material-icons';\n        input.style.cursor = 'pointer';\n        input.style.fontSize = '22px';\n        input.innerHTML = \"search\";\n        input.onclick = function() {\n            let id = instance.getValueFromCoords(0,y);\n            // Do some action\n            alert(id);\n        }\n\n        cell.appendChild(input);\n    }\n\n    return methods;\n}();\n\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            [1, 'Google'],\n            [2, 'Bing'],\n            [3, 'Yahoo'],\n            [4, 'Duckduckgo'],\n        ],\n        columns: [\n            { type: 'text', width:'100px' },\n            { type: 'rating', width:'100px' },\n            { type: action, width:'100px', readOnly: true },\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nconst action = function() {\n    let methods = {};\n\n    methods.createCell = (cell, value, x, y, instance, options) => {\n        let input = document.createElement('i');\n        input.className = 'material-icons';\n        input.style.cursor = 'pointer';\n        input.style.fontSize = '22px';\n        input.innerHTML = \"search\";\n        input.onclick = function() {\n            let id = instance.getValueFromCoords(0,y);\n            // Do some action\n            alert(id);\n        }\n\n        cell.appendChild(input);\n    }\n\n    return methods;\n}();\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [1, 'Google'],\n        [2, 'Bing'],\n        [3, 'Yahoo'],\n        [4, 'Duckduckgo'],\n    ]\n    // Columns\n    const columns = [\n        { type: 'text', width:'100px' },\n        { type: 'rating', width:'100px' },\n        { type: action, width:'100px', readOnly: true },\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nconst Action = function() {\n    let methods = {};\n\n    methods.createCell = (cell, value, x, y, instance, options) => {\n        let input = document.createElement('i');\n        input.className = 'material-icons';\n        input.style.cursor = 'pointer';\n        input.style.fontSize = '22px';\n        input.innerHTML = \"search\";\n        input.onclick = function() {\n          let id = instance.getValueFromCoords(0, y);\n          alert(id);\n        };\n\n        cell.appendChild(input);\n    };\n\n    return methods;\n}();\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const data = ref([\n            [1, 'Google'],\n            [2, 'Bing'],\n            [3, 'Yahoo'],\n            [4, 'Duckduckgo'],\n        ]);\n\n        const columns = ref([\n            { type: 'text', width: '100px' },\n            { type: 'rating', width: '100px' },\n            { type: Action, width: '100px', readOnly: true },\n        ]);\n\n        return {\n            data,\n            columns,\n        };\n    },\n};\n</script>\n\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nconst Action = function() {\n    let methods = {};\n\n    methods.createCell = (cell, value, x, y, instance, options) => {\n        let input = document.createElement('i');\n        input.className = 'material-icons';\n        input.style.cursor = 'pointer';\n        input.style.fontSize = '22px';\n        input.innerHTML = \"search\";\n        input.onclick = function() {\n            let id = instance.getValueFromCoords(0,y);\n            alert(id);\n        }\n\n        cell.appendChild(input);\n    }\n\n    return methods;\n}();\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    [1, 'Google'],\n                    [2, 'Bing'],\n                    [3, 'Yahoo'],\n                    [4, 'Duckduckgo'],\n                ],\n                columns: [\n                    { type: 'text', width:'100px' },\n                    { type: 'rating', width:'100px' },\n                    { type: action, width:'100px', readOnly: true },\n                ]\n            }]\n        });\n    }\n}\n```\n\n## More Resources\n\n### Editors Source Code\n\nThe source code for editors is available for reference. Use these examples as a foundation to create your custom editors.\\\n\\\n[https://github.com/jspreadsheet/editors](https://github.com/jspreadsheet/editors){.button}\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Cell Editors**\n>\n> Jspreadsheet CE provides 12 basic editors. **Jspreadsheet Pro** extends this with 17 professional editors and advanced capabilities:\n>\n> **Advanced Editor Types:**\n> - **Conditional Dropdowns:** Options dynamically change based on other cell values\n> - **Rich Text Editor:** Full HTML formatting with bold, italic, lists, and styling\n> - **HTML Editor:** Embed custom HTML content directly in cells\n> - **Advanced Date/Time:** Enhanced calendar with time selection and custom formats\n> - **Rating Editor:** Star ratings and custom rating systems\n> - **Tags Editor:** Multi-select tags with autocomplete\n> - **Progress Bars:** Visual progress indicators in cells\n> - **Custom Editors:** Build your own cell editors with full API access\n>\n> **Pro-Only Editor Features:**\n> - **Cell-Level Editors:** Define different editors for individual cells (not just columns)\n> - **Programmatic Type Changes:** Dynamically change cell types at runtime\n> - **Enhanced Validation:** Advanced validation rules with custom messages\n> - **Editor Events:** Extended event system for editor lifecycle\n>\n> Perfect for data-intensive applications requiring sophisticated user input controls.\n>\n> **[Explore Pro Editors →](https://jspreadsheet.com/docs/editors)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n## Related Tools\n\n- [JavaScript HTML Editor](https://jsuites.net/docs/javascript-html-editor) - Standalone rich text editor component\n- [Jspreadsheet Pro Editors](https://jspreadsheet.com/docs/editors) - Advanced spreadsheet cell editors (Pro)\n- [Custom Cell Editors](https://jspreadsheet.com/docs/editors/custom) - Build your own custom editors (Pro)\n- [HTML Editor](https://jspreadsheet.com/docs/editors/html) - Rich text editing in cells (Pro)\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/events.md",
    "content": "title: Spreadsheet Events  \nkeywords: Jspreadsheet, JavaScript data grid, spreadsheet events, event handling, interactive spreadsheets, Excel-like features, JavaScript integration, feature customization, event-driven programming, data grid events  \ndescription: Discover Jspreadsheet's robust event system, enabling seamless integration, advanced customization, and dynamic user interaction.  \ncanonical: https://bossanova.uk/jspreadsheet/docs/events\n\n# Spreadsheet Events\n\nJspreadsheet provides a comprehensive event system designed to enhance integration and customization. These events allow developers to create dynamic, interactive spreadsheets by responding to user actions and modifying spreadsheet behaviour in real-time.\n\n## Events\n\n| Event                     | Description                                                                                           |  \n|---------------------------|-------------------------------------------------------------------------------------------------------|  \n| `onload`                  | Triggered when a spreadsheet is loaded.                                                               |  \n| `onbeforechange`          | Triggered before a cell value is changed.                                                             |  \n| `onchange`                | Triggered after a cell value is changed.                                                              |  \n| `onafterchanges`          | Triggered after all pending changes are applied to the table.                                         |  \n| `onpaste`                 | Triggered after a paste action in the table.                                                          |  \n| `onbeforepaste`           | Triggered before a paste action is performed. Useful for parsing input data. It returns the parsed data. |  \n| `oninsertrow`             | Triggered after a new row is inserted.                                                                |  \n| `onbeforeinsertrow`       | Triggered before a new row is inserted. Cancel the insert by returning `false`.                       |  \n| `ondeleterow`             | Triggered after a row is deleted.                                                                     |  \n| `onbeforedeleterow`       | Triggered before a row is deleted. Cancel the delete by returning `false`.                            |  \n| `oninsertcolumn`          | Triggered after a new column is inserted.                                                             |  \n| `onbeforeinsertcolumn`    | Triggered before a new column is inserted. Cancel the insert by returning `false`.                    |  \n| `ondeletecolumn`          | Triggered after a column is deleted.                                                                  |  \n| `onbeforedeletecolumn`    | Triggered before a column is deleted. Cancel the delete by returning `false`.                         |  \n| `onmoverow`               | Triggered after a row is moved.                                                                       |  \n| `onmovecolumn`            | Triggered after a column is moved.                                                                    |  \n| `onresizerow`             | Triggered after a row height change.                                                                  |  \n| `onresizecolumn`          | Triggered after a column width change.                                                                |  \n| `onselection`             | Triggered when selection changes.                                                                     |  \n| `onsort`                  | Triggered after a column is sorted.                                                                   |  \n| `onfocus`                 | Triggered when the table gains focus.                                                                 |  \n| `onblur`                  | Triggered when the table loses focus.                                                                 |  \n| `onmerge`                 | Triggered when cells are merged.                                                                      |  \n| `onunmerge`              | Triggered when merge is removed                                                                      |\n| `onchangeheader`          | Triggered when a header is changed.                                                                   |  \n| `onundo`                  | Triggered when an undo action is applied.                                                             |  \n| `onredo`                  | Triggered when a redo action is applied.                                                              |  \n| `oneditionstart`          | Triggered when `openEditor` is called.                                                                |  \n| `oneditionend`            | Triggered when `closeEditor` is called.                                                               |  \n| `onchangestyle`           | Triggered when `setStyle` is called.                                                                  |  \n| `onchangemeta`            | Triggered when `setMeta` is called.                                                                   |  \n\n\n## Examples  \n\n### Update External Components  \n\nThe following example demonstrates integrating Jspreadsheet CE data with an external chart component using `onchange` events.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n\n<div id=\"container\"></div>\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet data = [\n    ['Tokyo', 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6],\n    ['New York', -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5],\n    ['Berlin', -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0],\n    ['London', 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8],\n];\n\nlet update = function (instance, cell, x, y, value) {\n    // If the related series does not exists create a new one\n    if (! chart.series[y]) {\n        // Create a new series row\n        let row = [];\n        for (i = 1; i < data[y].length; i++) {\n            row.push(parseFloat(data[y][i]));\n        }\n        // Append new series to the chart\n        chart.addSeries({ name:data[y][0], data:row });\n    } else {\n        if (x == 0) {\n            // Update legend\n            chart.series[y].update({ name:value });\n        } else {\n            // Update chart data\n            chart.series[y].data[x-1].update({ y:parseFloat(value) });\n        }\n    }\n}\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data:data,\n        defaultColWidth: 50,\n        columns: [\n            { type: 'text', width:'200' },\n        ],\n    }],\n    onchange: update,\n});\n\nlet chart = null;\n\nchart = Highcharts.chart('container', {\n    title: {\n        text: 'Monthly Average Temperature',\n        x: -20 //center\n    },\n    subtitle: {\n        text: 'Source: WorldClimate.com',\n        x: -20\n    },\n    xAxis: {\n        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n            'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n    },\n    yAxis: {\n        title: {\n            text: 'Temperature (°C)'\n        },\n        plotLines: [{\n            value: 0,\n            width: 1,\n            color: '#808080'\n        }]\n    },\n    tooltip: {\n        valueSuffix: '°C'\n    },\n    legend: {\n        layout: 'vertical',\n        align: 'right',\n        verticalAlign: 'middle',\n        borderWidth: 0\n    },\n    series: [{\n        name: 'Tokyo',\n        data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n    }, {\n        name: 'New York',\n        data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n    }, {\n        name: 'Berlin',\n        data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n    }, {\n        name: 'London',\n        data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nimport HighchartsReact from \"highcharts-react-official\";\nimport Highcharts from \"highcharts\";\n\nconst chartOptions = {\n    title: {\n        text: \"Monthly Average Temperature\",\n        x: -20 //center\n    },\n    subtitle: {\n        text: \"Source: WorldClimate.com\",\n        x: -20\n    },\n    xAxis: {\n        categories: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n    },\n    yAxis: {\n        title: {\n            text: \"Temperature (°C)\"\n        },\n        plotLines: [{\n            value: 0,\n            width: 1,\n            color: \"#808080\"\n        }]\n    },\n    tooltip: {\n        valueSuffix: \"°C\"\n    },\n    legend: {\n        layout: \"vertical\",\n        align: \"right\",\n        verticalAlign: \"middle\",\n        borderWidth: 0\n    },\n    series: [{\n        name: \"Tokyo\",\n        data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n    },\n    {\n        name: \"New York\",\n        data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n    },\n    {\n        name: \"Berlin\",\n        data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n    },\n    {\n        name: \"London\",\n        data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n    }\n    ]\n};\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const chart = useRef();\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [chartOptions.series[0].name, ...chartOptions.series[0].data],\n        [chartOptions.series[1].name, ...chartOptions.series[1].data],\n        [chartOptions.series[2].name, ...chartOptions.series[2].data],\n        [chartOptions.series[3].name, ...chartOptions.series[3].data]\n    ];\n    // Columns\n    const columns = [{\n        type: \"text\",\n        width: \"200\"\n    }];\n    // Updates\n    const updates = (instance, cell, x, y, value) => {\n        const component = chart.current.chart;\n        // If the related series does not exists create a new one\n        if (!component.series[y]) {\n            // Create a new series row\n            let row = [];\n            for (let i = 1; i < data[y].length; i++) {\n                // @ts-ignore\n                row.push(parseFloat(data[y][i]));\n            }\n            // Append new series to the chart\n            component.addSeries({\n                name: data[y][0],\n                data: row\n            });\n        } else {\n            if (x < 1) {\n                // Update legend\n                component.series[y].update({\n                    name: value\n                });\n            } else {\n                // Update chart data\n                component.series[y].data[x - 1].update({\n                    y: parseFloat(value)\n                });\n            }\n        }\n    };\n    \n    // Render data grid component\n    return (\n        <>\n            <HighchartsReact ref={chart} highcharts={Highcharts} options={chartOptions} />\n            <Spreadsheet ref={spreadsheet} onchange={updates}>\n                <Worksheet data={data} columns={columns} minDimensions={[4, 4]} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <div>\n      <highcharts ref=\"chart\" :options=\"chartOptions\" />\n      <spreadsheet ref=\"spreadsheet\" :onchange=\"updates\">\n        <worksheet :data=\"data\" :columns=\"columns\" />\n      </spreadsheet>\n    </div>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Chart } from 'highcharts-vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nconst chartOptions = {\n  title: {\n    text: \"Monthly Average Temperature\",\n    x: -20 //center\n  },\n  subtitle: {\n    text: \"Source: WorldClimate.com\",\n    x: -20\n  },\n  xAxis: {\n    categories: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n  },\n  yAxis: {\n    title: {\n      text: \"Temperature (°C)\"\n    },\n    plotLines: [{\n      value: 0,\n      width: 1,\n      color: \"#808080\"\n    }]\n  },\n  tooltip: {\n    valueSuffix: \"°C\"\n  },\n  legend: {\n    layout: \"vertical\",\n    align: \"right\",\n    verticalAlign: \"middle\",\n    borderWidth: 0\n  },\n  series: [{\n    name: \"Tokyo\",\n    data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n  },\n  {\n    name: \"New York\",\n    data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n  },\n  {\n    name: \"Berlin\",\n    data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n  },\n  {\n    name: \"London\",\n    data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n  }\n  ]\n};\n\nexport default {\n  name: 'App',\n  components: {\n    Spreadsheet,\n    Worksheet,\n    highcharts: Chart,\n  },\n  setup() {\n    const columns = ref([\n      {\n        type: \"text\",\n        width: \"200\"\n      }\n    ]);\n\n    const data = ref([\n      [chartOptions.series[0].name, ...chartOptions.series[0].data],\n      [chartOptions.series[1].name, ...chartOptions.series[1].data],\n      [chartOptions.series[2].name, ...chartOptions.series[2].data],\n      [chartOptions.series[3].name, ...chartOptions.series[3].data]\n    ]);\n\n    const updates = (instance, cell, x, y, value) => {\n      const component = instance.$refs.chart.chart;\n      if (!component.series[y]) {\n        let row = [];\n        for (let i = 1; i < data.value[y].length; i++) {\n          row.push(parseFloat(data.value[y][i]));\n        }\n        component.addSeries({\n          name: data.value[y][0],\n          data: row\n        });\n      } else {\n        if (x < 1) {\n          component.series[y].update({\n            name: value\n          });\n        } else {\n          component.series[y].data[x - 1].update({\n            y: parseFloat(value)\n          });\n        }\n      }\n    };\n\n    return {\n      columns,\n      data,\n      chartOptions,\n      updates,\n    };\n  },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport Highcharts from 'highcharts'\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\nimport charts from \"@jspreadsheet/charts\";\n\nconst chartOptions = {\n    title: {\n        text: \"Monthly Average Temperature\",\n        x: -20 //center\n    },\n    subtitle: {\n        text: \"Source: WorldClimate.com\",\n        x: -20\n    },\n    xAxis: {\n        categories: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n    },\n    yAxis: {\n        title: {\n            text: \"Temperature (°C)\"\n        },\n        plotLines: [{\n            value: 0,\n            width: 1,\n            color: \"#808080\"\n        }]\n    },\n    tooltip: {\n        valueSuffix: \"°C\"\n    },\n    legend: {\n        layout: \"vertical\",\n        align: \"right\",\n        verticalAlign: \"middle\",\n        borderWidth: 0\n    },\n    series: [{\n            name: \"Tokyo\",\n            data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n        },\n        {\n            name: \"New York\",\n            data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n        },\n        {\n            name: \"Berlin\",\n            data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n        },\n        {\n            name: \"London\",\n            data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n        }\n    ]\n};\n\n// Create the data grid component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #chartContainer></div>\n        <div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    @ViewChild(\"chartContainer\") chartContainer: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create summary spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    [ chartOptions.series[0].name, ...chartOptions.series[0].data ],\n                    [ chartOptions.series[1].name, ...chartOptions.series[1].data ],\n                    [ chartOptions.series[2].name, ...chartOptions.series[2].data ],\n                    [ chartOptions.series[3].name, ...chartOptions.series[3].data ]\n                ],\n                columns: [\n                    { type: 'text', width:'200' },\n                ],\n            }],\n            onchange: (instance, cell, x, y, value) => {\n                // If the related series does not exists create a new one\n                if (! this.chart.series[y]) {\n                    // Create a new series row\n                    let row = [];\n                    for (i = 1; i < instance.options.data[y].length; i++) {\n                        row.push(parseFloat(instance.options.data[y][i]));\n                    }\n                    // Append new series to the chart\n                    chart.addSeries({ name: instance.options.data[y][0], data: row });\n                } else {\n                    if (x == 0) {\n                        // Update legend\n                        this.chart.series[y].update({ name:value });\n                    } else {\n                        // Update chart data\n                        this.chart.series[y].data[x-1].update({ y:parseFloat(value) });\n                    }\n                }\n            },\n        });\n\n        // Create external chart component\n        this.chart = Highcharts.chart(this.chartContainer.nativeElement, chartOptions);\n\n    }\n}\n```"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/column-dragging.md",
    "content": "title: Column Dragging  \nkeywords: Jexcel, spreadsheet, JavaScript, JavaScript table, column dragging, data grid customization, interactive columns  \ndescription: Learn how to enable and use column dragging functionality in Jspreadsheet CE to create dynamic and customizable data grids.  \n\n# Column Dragging  \n\nColumn dragging in Jspreadsheet CE is disabled by default. To enable this feature, set the `columnDrag: true` option during initialization, as demonstrated below:  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['BR', 'Cheese', 1],\n            ['CA', 'Apples', 0],\n            ['US', 'Carrots', 1],\n            ['GB', 'Oranges', 0],\n        ],\n        columns: [\n            {\n                type: 'autocomplete',\n                title: 'Country',\n                width: '300',\n                url: '/jspreadsheet/countries.json'\n            },\n            {\n                type: 'dropdown',\n                title: 'Food',\n                width: '150',\n                source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n            },\n            {\n                type: 'checkbox',\n                title: 'Stock',\n                width:'100'\n            },\n        ],\n        columnDrag: true,  \n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Tabs\n    const data = [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ];\n    \n    const columns = [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '300',\n            url: '/jspreadsheet/countries.json'\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width:'100'\n        },\n    ];\n\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} columnDrag={true} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" :columnDrag=\"true\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet\n    },\n    setup() {\n        // Spreadsheet reference\n        const spreadsheet = ref(null);\n\n        // Data for the worksheet\n        const data = ref([\n            ['BR', 'Cheese', 1],\n            ['CA', 'Apples', 0],\n            ['US', 'Carrots', 1],\n            ['GB', 'Oranges', 0],\n        ]);\n\n        // Columns definition for the worksheet\n        const columns = ref([\n            {\n                type: 'autocomplete',\n                title: 'Country',\n                width: '300',\n                url: '/jspreadsheet/countries.json'\n            },\n            {\n                type: 'dropdown',\n                title: 'Food',\n                width: '150',\n                source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n            },\n            {\n                type: 'checkbox',\n                title: 'Stock',\n                width: '100'\n            },\n        ]);\n\n        return {\n            spreadsheet,\n            data,\n            columns\n        };\n    }\n}\n</script>\n\n```\n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/create-from-table.md",
    "content": "title: Create from an HTML Table  \nkeywords: Jexcel, JavaScript, dynamic spreadsheet, create table, HTML table element, Jspreadsheet table, interactive table  \ndescription: Learn how to create a dynamic Jspreadsheet table from a static HTML table element with a complete example.  \n\n# Create from an HTML Table  \n\nStarting with v4+, Jspreadsheet allows you to create an online spreadsheet dynamically from a static HTML table element.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<h4>The Official Top 40 biggest albums of 2019</h4>\n\n<table id=\"spreadsheet\">\n<thead>\n<tr>\n<td colspan='4'>General</td>\n</tr>\n<tr>\n<td colspan='3'>Info</td>\n<td colspan='1'>Stats</td>\n</tr>\n<tr>\n<td id='pos'>POS</td>\n<td id='title'>TITLE</td>\n<td id='artist'>ARTIST</td>\n<td id='peak'>PEAK</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n<td>LEWIS CAPALDI</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>NO 6 COLLABORATIONS PROJECT</td>\n<td>ED SHEERAN</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>THE GREATEST SHOWMAN</td>\n<td>MOTION PICTURE CAST RECORDING</td>\n<td>1</td>\n</tr>\n<tr>\n<td>4</td>\n<td>WHEN WE ALL FALL ASLEEP WHERE DO WE GO</td>\n<td>BILLIE EILISH</td>\n<td>1</td>\n</tr>\n<tr>\n<td>5</td>\n<td>STAYING AT TAMARA'S</td>\n<td>GEORGE EZRA</td>\n<td>1</td>\n</tr>\n<tr>\n<td>6</td>\n<td>BOHEMIAN RHAPSODY - OST</td>\n<td>QUEEN</td>\n<td>3</td>\n</tr>\n<tr>\n<td>7</td>\n<td>THANK U NEXT</td>\n<td>ARIANA GRANDE</td>\n<td>1</td>\n</tr>\n<tr>\n<td>8</td>\n<td>WHAT A TIME TO BE ALIVE</td>\n<td>TOM WALKER</td>\n<td>1</td>\n</tr>\n<tr>\n<td>9</td>\n<td>A STAR IS BORN</td>\n<td>MOTION PICTURE CAST RECORDING</td>\n<td>1</td>\n</tr>\n<tr>\n<td>10</td>\n<td>YOU'RE IN MY HEART</td>\n<td>ROD STEWART</td>\n<td>1</td>\n</tr>\n</tbody>\n<tfoot>\n<tr>\n<td></td>\n<td></td>\n<td></td>\n<td>=SUM(B1,B2,B3)</td>\n</tr>\n</tfoot>\n</table>\n\n<br>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet')); \n</script>\n</html>\n```\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    useEffect(() => {\n        if (spreadsheet.current) {\n            jspreadsheet(spreadsheet.current);\n        }\n    }, [])\n\n    // Render component\n    return (\n        <>\n            <table ref={\"spreadsheet\"}>\n            <thead>\n            <tr>\n            <td colspan='4'>General</td>\n            </tr>\n            <tr>\n            <td colspan='3'>Info</td>\n            <td colspan='1'>Stats</td>\n            </tr>\n            <tr>\n            <td id='pos'>POS</td>\n            <td id='title'>TITLE</td>\n            <td id='artist'>ARTIST</td>\n            <td id='peak'>PEAK</td>\n            </tr>\n            </thead>\n            <tbody>\n            <tr>\n            <td>1</td>\n            <td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n            <td>LEWIS CAPALDI</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>2</td>\n            <td>NO 6 COLLABORATIONS PROJECT</td>\n            <td>ED SHEERAN</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>3</td>\n            <td>THE GREATEST SHOWMAN</td>\n            <td>MOTION PICTURE CAST RECORDING</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>4</td>\n            <td>WHEN WE ALL FALL ASLEEP WHERE DO WE GO</td>\n            <td>BILLIE EILISH</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>5</td>\n            <td>STAYING AT TAMARA'S</td>\n            <td>GEORGE EZRA</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>6</td>\n            <td>BOHEMIAN RHAPSODY - OST</td>\n            <td>QUEEN</td>\n            <td>3</td>\n            </tr>\n            <tr>\n            <td>7</td>\n            <td>THANK U NEXT</td>\n            <td>ARIANA GRANDE</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>8</td>\n            <td>WHAT A TIME TO BE ALIVE</td>\n            <td>TOM WALKER</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>9</td>\n            <td>A STAR IS BORN</td>\n            <td>MOTION PICTURE CAST RECORDING</td>\n            <td>1</td>\n            </tr>\n            <tr>\n            <td>10</td>\n            <td>YOU'RE IN MY HEART</td>\n            <td>ROD STEWART</td>\n            <td>1</td>\n            </tr>\n            </tbody>\n            <tfoot>\n            <tr>\n            <td></td>\n            <td></td>\n            <td></td>\n            <td>=SUM(B1,B2,B3)</td>\n            </tr>\n            </tfoot>\n            </table>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <table ref=\"spreadsheetRef\">\n        <thead>\n            <tr>\n                <td colspan='4'>General</td>\n            </tr>\n            <tr>\n                <td colspan='3'>Info</td>\n                <td colspan='1'>Stats</td>\n            </tr>\n            <tr>\n                <td id='pos'>POS</td>\n                <td id='title'>TITLE</td>\n                <td id='artist'>ARTIST</td>\n                <td id='peak'>PEAK</td>\n            </tr>\n        </thead>\n        <tbody>\n            <tr>\n               <td>1</td>\n               <td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n               <td>LEWIS CAPALDI</td>\n               <td>1</td>\n            </tr>\n            <tr>\n                <td>2</td>\n                <td>NO 6 COLLABORATIONS PROJECT</td>\n                <td>ED SHEERAN</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>3</td>\n                <td>THE GREATEST SHOWMAN</td>\n                <td>MOTION PICTURE CAST RECORDING</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>4</td>\n                <td>WHEN WE ALL FALL ASLEEP WHERE DO WE GO</td>\n                <td>BILLIE EILISH</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>5</td>\n                <td>STAYING AT TAMARA'S</td>\n                <td>GEORGE EZRA</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>6</td>\n                <td>BOHEMIAN RHAPSODY - OST</td>\n                <td>QUEEN</td>\n                <td>3</td>\n            </tr>\n            <tr>\n                <td>7</td>\n                <td>THANK U NEXT</td>\n                <td>ARIANA GRANDE</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>8</td>\n                <td>WHAT A TIME TO BE ALIVE</td>\n                <td>TOM WALKER</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>9</td>\n                <td>A STAR IS BORN</td>\n                <td>MOTION PICTURE CAST RECORDING</td>\n                <td>1</td>\n            </tr>\n            <tr>\n                <td>10</td>\n                <td>YOU'RE IN MY HEART</td>\n                <td>ROD STEWART</td>\n                <td>1</td>\n            </tr>\n        </tbody>\n        <tfoot>\n            <tr>\n                <td></td>\n                <td></td>\n                <td></td>\n                <td>=SUM(B1,B2,B3)</td>\n            </tr>\n        </tfoot>\n    </table>\n</template>\n\n<script setup>\nimport { ref, onMounted } from 'vue';\nimport { jspreadsheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\nonMounted(() => {\n    if (spreadsheetRef.value) {\n        jspreadsheet(spreadsheetRef.value);\n    }\n});\n</script>\n```"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/jquery.md",
    "content": "title: jQuery Spreadsheet  \nkeywords: Jexcel, JavaScript, Jspreadsheet, jQuery integration, JavaScript spreadsheet, interactive table  \ndescription: Learn how to integrate Jspreadsheet with jQuery through a complete example.\n\n# jQuery Spreadsheet\n\nCreate fantastic data grids with spreadsheet controls integrating Jspreadsheet with jQuery.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<br/>\n<input type=\"button\" value=\"Add new row\" id=\"btn1\" />\n\n<script>\nlet table = jspreadsheet($('#spreadsheet')[0], {\n    worksheets: [\n        {\n            minDimensions: [4, 4],\n            tableOverflow: true,\n        }\n    ]\n}); \n\ndocument.getElementById(\"btn1\").addEventListener('click', function() {\n    table[0].insertRow();\n})\n\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/richtext-html-editor.md",
    "content": "title: HTML Editor  \nkeywords: Jexcel, JavaScript, rich text, HTML editor, spreadsheet editor, excel-like features, dynamic columns  \ndescription: Learn how to create a rich text column with an HTML editor in Jspreadsheet for enhanced data input and formatting.  \n\n# Rich Text and HTML Editor Column Type  \n\nAdd an HTML Editor input to your spreadsheet to create rich text columns.  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet options = {\n    worksheets: [{\n        data: [\n            ['the Sorcerer`s Stone', '<img align=\"left\" style=\"width:100px\" src=\"data:image/jpeg;base64,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\"><span>On his 11th birthday, <b>Harry</b> receives a letter inviting him to study magic at the Hogwarts School of Witchcraft and Wizardry. <b>Harry</b> discovers that not only is he a wizard, but he is a famous one. He meets two best friends, Ron Weasley and Hermione Granger, and makes his first enemy, Draco Malfoy.</span>']\n        ],\n        columns: [\n            {type: 'text', width: 200, title: 'Title'},\n            {type: 'html', width: 400, title: 'HTML'},\n        ],\n        minDimensions: [2, 2],\n    }]\n}\n\njspreadsheet(document.getElementById('spreadsheet'), options);\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nconst data = [\n    [ 'the Sorcerer`s Stone', '<img align=\"left\" style=\"width:100px\" src=\"data:image/jpeg;base64,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\"><span>On his 11th birthday, <b>Harry</b> receives a letter inviting him to study magic at the Hogwarts School of Witchcraft and Wizardry. <b>Harry</b> discovers that not only is he a wizard, but he is a famous one. He meets two best friends, Ron Weasley and Hermione Granger, and makes his first enemy, Draco Malfoy.</span>' ]\n];\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Columns\n    const columns = [\n        { type:'text', width:'200px', title:'Title' },\n        { type:'html', width:'400px', title:'HTML' },\n    ];\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} minDimensions={[2,2]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        // Data\n        const data = [\n            [ 'the Sorcerer`s Stone', '<img align=\"left\" style=\"width:100px\" src=\"data:image/jpeg;base64,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\"><span>On his 11th birthday, <b>Harry</b> receives a letter inviting him to study magic at the Hogwarts School of Witchcraft and Wizardry. <b>Harry</b> discovers that not only is he a wizard, but he is a famous one. He meets two best friends, Ron Weasley and Hermione Granger, and makes his first enemy, Draco Malfoy.</span>' ]\n        ];\n\n        // Columns\n        const columns = [\n            { type:'text', width:'200px', title:'Title' },\n            { type:'html', width:'400px', title:'HTML' },\n        ];\n\n        return {\n            data,\n            columns,\n        }\n    }\n}\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/table-overflow.md",
    "content": "title: Table Overflow  \nkeywords: Jexcel, JavaScript, table overflow, fixed dimensions, JavaScript table, spreadsheet customization, table width and height  \ndescription: Learn how to define fixed width and height for Jspreadsheet tables to manage table overflow effectively.\n\n# Table Overflow\n\nSet fixed width and height for your online JavaScript spreadsheet to control table overflow and improve layout management.\n\n### Source code\n\n```html\n<html>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [10, 20],\n        tableOverflow: true,\n        tableWidth: \"600px\",\n        columns: [\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            }\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Columns\n    const columns = [\n        {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n        },\n        {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n        },\n        {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n        },\n        {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n        },\n        {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n        }\n    ];\n\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet tableOverflow={true} tableWidth={\"600px\"} columns={columns} minDimensions={[10,20]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :table-overflow=\"true\" table-width=\"600px\" :columns=\"columns\" :minDimensions=\"[10, 20]\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet\n    },\n    setup() {\n        // Spreadsheet reference\n        const spreadsheet = ref(null);\n\n        // Columns definition for the worksheet\n        const columns = [\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            },\n            {\n                type: \"dropdown\",\n                source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n            }\n        ];\n\n        return {\n            spreadsheet,\n            columns\n        };\n    }\n}\n\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/translations.md",
    "content": "title: How to Translate the Default Messages in Jspreadsheet  \nkeywords: Jexcel, JavaScript, spreadsheet, JavaScript table, translate messages, Jspreadsheet translations, internationalization  \ndescription: Learn how to translate and customize the default messages in Jspreadsheet for internationalization.\n\n# Jspreadsheet Internationalization\n\nLearn how to update and customize the default texts in your online spreadsheet for better internationalization.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet.setDictionary({\n    'Search': '検索',\n    'Show': '表示',\n    'entries': 'エントリ',\n    'Insert a new column before': '新しい列を前に挿入',\n    'Insert a new column after': '新しい列を後に挿入',\n    'Delete selected columns': '選択した列を削除',\n    'Rename this column': 'この列の名前を変更',\n    'Column name': '列名',\n    'Order ascending': '昇順に並べ替え',\n    'Order descending': '降順に並べ替え',\n    'Insert a new row before': '新しい行を前に挿入',\n    'Insert a new row after': '新しい行を後に挿入',\n    'Edit comments': 'コメントを編集',\n    'Add comments': 'コメントを追加',\n    'Comments': 'コメント',\n    'Clear comments': 'コメントをクリア',\n    'Copy': 'コピー',\n    'Paste': '貼り付け',\n    'Save as': '名前を付けて保存',\n    'About': '情報',\n    'Are you sure to delete the selected rows?': '選択した行を削除してもよろしいですか？',\n    'Are you sure to delete the selected columns?': '選択した列を削除してもよろしいですか？',\n    'No cells selected': 'セルが選択されていません',\n    'No records found': '記録が見つかりません',\n    'Showing page {0} of {1} entries': '{1}件中{0}ページを表示',\n    'Merge the selected cells': '選択したセルを結合',\n    'There is a conflict with another merged cell': '別の結合されたセルとの競合があります',\n    'Invalid merged properties': '無効な結合プロパティ',\n    'Cell already merged': 'セルはすでに結合されています',\n    'This action will destroy any existing merged cells. Are you sure?': 'この操作により、既存の結合セルが削除されます。よろしいですか？',\n    'The merged cells will retain the value of the top-left cell only. Are you sure?': '結合セルは左上のセルの値のみを保持します。よろしいですか？',\n    'This action will clear your search results. Are you sure?': 'この操作により検索結果がクリアされます。よろしいですか？'\n});\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        { minDimensions: [6,6] },\n    ]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\njspreadsheet.setDictionary({\n    'Search': '検索',\n    'Show': '表示',\n    'entries': 'エントリ',\n    'Insert a new column before': '新しい列を前に挿入',\n    'Insert a new column after': '新しい列を後に挿入',\n    'Delete selected columns': '選択した列を削除',\n    'Rename this column': 'この列の名前を変更',\n    'Column name': '列名',\n    'Order ascending': '昇順に並べ替え',\n    'Order descending': '降順に並べ替え',\n    'Insert a new row before': '新しい行を前に挿入',\n    'Insert a new row after': '新しい行を後に挿入',\n    'Edit comments': 'コメントを編集',\n    'Add comments': 'コメントを追加',\n    'Comments': 'コメント',\n    'Clear comments': 'コメントをクリア',\n    'Copy': 'コピー',\n    'Paste': '貼り付け',\n    'Save as': '名前を付けて保存',\n    'About': '情報',\n    'Are you sure to delete the selected rows?': '選択した行を削除してもよろしいですか？',\n    'Are you sure to delete the selected columns?': '選択した列を削除してもよろしいですか？',\n    'No cells selected': 'セルが選択されていません',\n    'No records found': '記録が見つかりません',\n    'Showing page {0} of {1} entries': '{1}件中{0}ページを表示',\n    'Merge the selected cells': '選択したセルを結合',\n    'There is a conflict with another merged cell': '別の結合されたセルとの競合があります',\n    'Invalid merged properties': '無効な結合プロパティ',\n    'Cell already merged': 'セルはすでに結合されています',\n    'This action will destroy any existing merged cells. Are you sure?': 'この操作により、既存の結合セルが削除されます。よろしいですか？',\n    'The merged cells will retain the value of the top-left cell only. Are you sure?': '結合セルは左上のセルの値のみを保持します。よろしいですか？',\n    'This action will clear your search results. Are you sure?': 'この操作により検索結果がクリアされます。よろしいですか？'\n});\n\nexport default function App() {\n    const spreadsheet = useRef();\n\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet />\n        </Spreadsheet>\n    );\n};\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\njspreadsheet.setDictionary({\n  'Search': '検索',\n  'Show': '表示',\n  'entries': 'エントリ',\n  'Insert a new column before': '新しい列を前に挿入',\n  'Insert a new column after': '新しい列を後に挿入',\n  'Delete selected columns': '選択した列を削除',\n  'Rename this column': 'この列の名前を変更',\n  'Column name': '列名',\n  'Order ascending': '昇順に並べ替え',\n  'Order descending': '降順に並べ替え',\n  'Insert a new row before': '新しい行を前に挿入',\n  'Insert a new row after': '新しい行を後に挿入',\n  'Edit comments': 'コメントを編集',\n  'Add comments': 'コメントを追加',\n  'Comments': 'コメント',\n  'Clear comments': 'コメントをクリア',\n  'Copy': 'コピー',\n  'Paste': '貼り付け',\n  'Save as': '名前を付けて保存',\n  'About': '情報',\n  'Are you sure to delete the selected rows?': '選択した行を削除してもよろしいですか？',\n  'Are you sure to delete the selected columns?': '選択した列を削除してもよろしいですか？',\n  'No cells selected': 'セルが選択されていません',\n  'No records found': '記録が見つかりません',\n  'Showing page {0} of {1} entries': '{1}件中{0}ページを表示',\n  'Merge the selected cells': '選択したセルを結合',\n  'There is a conflict with another merged cell': '別の結合されたセルとの競合があります',\n  'Invalid merged properties': '無効な結合プロパティ',\n  'Cell already merged': 'セルはすでに結合されています',\n  'This action will destroy any existing merged cells. Are you sure?': 'この操作により、既存の結合セルが削除されます。よろしいですか？',\n  'The merged cells will retain the value of the top-left cell only. Are you sure?': '結合セルは左上のセルの値のみを保持します。よろしいですか？',\n  'This action will clear your search results. Are you sure?': 'この操作により検索結果がクリアされます。よろしいですか？'\n});\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        return {};\n    }\n}\n</script>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/docs/examples/web-component.md",
    "content": "title: Spreadsheet Web Component with Jspreadsheet  \nkeywords: Jexcel, JavaScript, JavaScript plugin, web component, spreadsheet, data grid, table, Excel-like grid, data tables, data visualization  \ndescription: Learn how to use Jspreadsheet CE to create a fully functional spreadsheet as a web component for your applications.\n\n# Spreadsheet Web Component\n\nEasily create a spreadsheet web component using Jspreadsheet CE for seamless integration into your web applications.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n\n<j-spreadsheet />\n\n<script>\nclass Jspreadsheet extends HTMLElement {\n    constructor() {\n        super();\n    }\n\n    init(o) {\n        // Shadow root\n        const shadowRoot = this.attachShadow({mode: 'open'});\n\n        // Style\n        const css = document.createElement('link');\n        css.rel = 'stylesheet';\n        css.type = 'text/css'\n        css.href = 'https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5.0.0-beta.3/dist/jspreadsheet.min.css';\n        shadowRoot.appendChild(css);\n\n        const cssJsuites = document.createElement('link');\n        cssJsuites.rel = 'stylesheet';\n        cssJsuites.type = 'text/css'\n        cssJsuites.href = 'https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css';\n        shadowRoot.appendChild(cssJsuites);\n\n        const cssMaterial = document.createElement('link');\n        cssMaterial.rel = 'stylesheet';\n        cssMaterial.type = 'text/css'\n        cssMaterial.href = 'https://fonts.googleapis.com/css?family=Material+Icons';\n        shadowRoot.appendChild(cssMaterial);\n\n        // JSS container\n        var container = document.createElement('div');\n        shadowRoot.appendChild(container);\n\n        // Properties\n        var toolbar = this.getAttribute('toolbar') == \"true\" ? true : false;\n\n        // Create jexcel element\n        this.el = jspreadsheet(container, {\n            tabs: true,\n            toolbar: toolbar,\n            root: shadowRoot,\n            worksheets: [{\n                filters: true,\n                minDimensions: [6,6],\n            }],\n        });\n    }\n\n    connectedCallback() {\n        this.init(this);\n    }\n\n    disconnectedCallback() {\n    }\n\n    attributeChangedCallback() {\n    }\n}\n\nwindow.customElements.define('j-spreadsheet', Jspreadsheet);\n</script>\n</html>\n```"
  },
  {
    "path": "docs/jspreadsheet/docs/examples.md",
    "content": "title: Jspreadsheet Examples  \nkeywords: Jspreadsheet examples, JavaScript spreadsheet, interactive data grid, spreadsheet columns, spreadsheet integrations, rich text editor, HTML table, internationalization  \ndescription: Explore Jspreadsheet examples, including creating spreadsheets from HTML, rich text editors, column dragging, and integration with web components and jQuery.  \n\n# Examples\n\nExplore practical examples of Jspreadsheet in action. This page showcases how to create and customize spreadsheets, implement advanced column features, and integrate with popular web technologies like web components and jQuery.\n\n## Spreadsheet  \n- [Create a new Spreadsheet from HTML](https://jspreadsheet.com/docs/examples/create-from-table)  \n- [Data Grid Translations](https://jspreadsheet.com/docs/examples/translations)  \n- [Data Grid Table Ooverflow](https://jspreadsheet.com/docs/examples/table-overflow)  \n\n## Columns  \n- [Rich Text Cell Editor](https://jspreadsheet.com/docs/examples/richtext-html-editor)  \n- [Data Grid Column Dragging](https://jspreadsheet.com/docs/examples/column-dragging)  \n\n## Integrations  \n- [Spreadsheet Web Component](https://jspreadsheet.com/docs/examples/web-component)  \n- [Jquery Spreadsheet](https://jspreadsheet.com/docs/examples/jquery)  \n"
  },
  {
    "path": "docs/jspreadsheet/docs/filters.md",
    "content": "title: Data Grid Filters\nkeywords: Jspreadsheet, data grid filters, JavaScript, Excel-like functionality, spreadsheet filters, column filters, filter methods, filter events, filter customization\ndescription: Explore detailed insights into Jspreadsheet data grid column filters, including settings, methods, and related events.\n\n# Spreadsheet Column Filters\n\nThis section provides details about the row filters in Jspreadsheet CE.\n\n{.pro}\n> #### Differences in the Pro Version\n> The Pro version provides multi-column filters, support for filtering specific columns or cell ranges, sorting ranges, additional events, and extensive customization options, with excellent compatibility with other spreadsheet software.\\\n>\\\n> [Learn more](https://jspreadsheet.com/docs/filters){.button}\n\n## Documentation\n\n### Methods\n\n| Method            | Description                                                                                |\n|-------------------|--------------------------------------------------------------------------------------------|\n| `openFilter()`    | Opens the filter input.<br/>`openFilter(columnNumber: Number, getAsSets?: boolean): void`  |\n| `closeFilter()`   | Closes the filter input.<br/>`closeFilter(): void`                                         |\n| `resetFilters()`  | \tResets all applied filters.<br/>`resetFilters(): void`                                    |\n \n\n### Initial Settings\n\n| Property                   | Description                                                      |\n|----------------------------|------------------------------------------------------------------|\n| `filters: boolean\\|string` | Start the spreadsheet with the filters enabled. `Default: false` |\n\n \n\n#### Enabling Spreadsheet Filters\n\nTo activate a filter for a specific column, use the filter attribute in the column object during spreadsheet initialization:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Jazz', 'Honda'],\n            ['Civic', 'Honda'],\n            ['Civic', 'Honda'],\n            ['Picanto', 'Kia'],\n            ['Optima', 'Kia'],\n        ],\n        filters: true,\n     }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef(null);\n    // Data\n    const data = [\n        ['Jazz', 'Honda'],\n        ['Civic', 'Honda'],\n        ['Civic', 'Honda'],\n        ['Picanto', 'Kia'],\n        ['Optima', 'Kia'],\n    ];\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} filters={true} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\">\n      <Worksheet :data=\"data\" :filters=\"true\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Data\nconst data = ref([\n    ['Jazz', 'Honda'],\n    ['Civic', 'Honda'],\n    ['Civic', 'Honda'],\n    ['Picanto', 'Kia'],\n    ['Optima', 'Kia'],\n]);\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Jazz', 'Honda'],\n                    ['Civic', 'Honda'],\n                    ['Civic', 'Honda'],\n                    ['Picanto', 'Kia'],\n                    ['Optima', 'Kia'],\n                ],\n                filters: true,\n            }],\n        });\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Filtering**\n>\n> Jspreadsheet CE provides basic row filtering. **Jspreadsheet Pro** adds Excel-like filtering capabilities for data analysis:\n>\n> **Advanced Filter Types:**\n> - **Text Filters:** Contains, starts with, ends with, equals, custom text matching\n> - **Number Filters:** Greater than, less than, between, top 10, above/below average\n> - **Date Filters:** Today, this week, this month, this year, custom date ranges\n> - **Boolean Filters:** True/false checkbox filters\n> - **Color Filters:** Filter by cell background or text color\n> - **Custom Filters:** Define your own filter logic with JavaScript functions\n>\n> **Multi-Column Filtering:**\n> - **Simultaneous Filters:** Apply filters to multiple columns at once (Excel-like)\n> - **Filter Combinations:** AND/OR logic between multiple column filters\n> - **Filter Priority:** Control which filters apply first\n> - **Dependent Filters:** Filter options change based on other active filters\n>\n> **Professional Filter UI:**\n> - **Dropdown Filter Menus:** Excel-like dropdown on each column header\n> - **Search in Filters:** Quick search within filter dropdown options\n> - **Select All/None:** Bulk select/deselect filter values\n> - **Filter Indicators:** Visual indicators showing which columns are filtered\n> - **Filter Count:** Show count of visible vs total rows\n>\n> **Filter Management:**\n> - **Save Filter States:** Persist filters across sessions\n> - **Filter Templates:** Predefined filter configurations for common scenarios\n> - **Clear All Filters:** One-click to reset all active filters\n> - **Filter History:** Navigate through previously applied filters\n> - **Filter Events:** Track filter changes with advanced event system\n> - **Programmatic API:** Set/get/clear filters via JavaScript API\n>\n> **Performance & Compatibility:**\n> - **Large Dataset Filtering:** Optimized for filtering 100K+ rows\n> - **Excel Compatibility:** Import/export preserves Excel filters\n> - **Filter Cell Ranges:** Filter specific ranges, not just entire columns\n> - **Virtual Scrolling:** Maintain performance with filtered large datasets\n>\n> Perfect for business intelligence dashboards and data analysis applications.\n>\n> **[Explore Pro Filters →](https://jspreadsheet.com/docs/filters)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n## Related Tools\n\n- [Data Grid Search](/jspreadsheet/docs/search) - Search and highlight functionality for data grids\n- [Jspreadsheet Pro Filters](https://jspreadsheet.com/docs/filters) - Advanced filtering with custom rules and date ranges (Pro)\n- [Jspreadsheet Pro Search](https://jspreadsheet.com/docs/search) - Enhanced search with regex and highlighting (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/footers.md",
    "content": "title: Data Grid Footers\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like footer, spreadsheet footers, table footers, summary calculations, status bar, floating footers, worksheet footers\ndescription: Spreadsheet footers let you display information such as formulas and data summaries at the bottom of the grid for easy reference and visibility.\n\n# Data Grid Footers\n\nJspreadsheet footers enable you to display information, including calculations or summaries, at the bottom of a data grid. This section explains implementing footers during initialization or dynamically at runtime using available methods and settings.\n\n## Documentation\n\n### Initial Settings\n\nThe following properties are available during the initialization of the Jspreadsheet data grid:\n\n| Property                      | Description                                                      |\n|-------------------------------|------------------------------------------------------------------|\n| footers?: string[]            | Defines the footers for the grid.                                |\n\n## Examples\n\n### Programmatic updates\n\nHow to change the formulas in the footers after the initialization. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Cheese', 10, 6.00],\n            ['Apples', 5, 4.00],\n            ['Carrots', 5, 1.00],\n            ['Oranges', 6, 2.00],\n        ],\n        footers: [\n            [\n                'Total',\n                '=SUM(B1:B4)',\n                '=SUM(C1:C4)',\n            ]\n        ],\n        columns: [\n            { width:'400px' },\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport jSuites from \"jsuites\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/style.css\";\n\n\n// Create a new data grid\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Cheese', 10, 6.00],\n        ['Apples', 5, 4.00],\n        ['Carrots', 5, 1.00],\n        ['Oranges', 6, 2.00],\n    ];\n    // Columns\n    const columns = [\n        { width:'400px' }\n    ];\n    // Data grid cell definitions\n    const footers = [\n        [\n            'Total',\n            '=SUM(B1:B4)',\n            '=SUM(C1:C4)',\n        ]\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} footers={footers} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" :footers=\"footers\"  />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        // Data\n        const data = ref([\n            ['Cheese', 10, 6.00, \"=B1*C1\"],\n            ['Apples', 5, 4.00, \"=B2*C2\"],\n            ['Carrots', 5, 1.00, \"=B3*C3\"],\n            ['Oranges', 6, 2.00, \"=B4*C4\"],\n        ]);\n\n        // Columns\n        const columns = ref([\n            { width: '400px' }\n        ]);\n\n        // Data grid cell definitions\n        const footers = ref([\n            [\n                'Total',\n                '=SUM(B1:B4)',\n                '=SUM(C1:C4)',\n            ]\n        ]);\n\n        return {\n            data,\n            columns,\n            footers,\n        };\n    }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n        worksheets: [{\n                data: [\n                    ['Cheese', 10, 6.00],\n                    ['Apples', 5, 4.00],\n                    ['Carrots', 5, 1.00],\n                    ['Oranges', 6, 2.00],\n                ],\n                footers: [\n                    [\n                        'Total',\n                        '=SUM(B1:B4)',\n                        '=SUM(C1:C4)',\n                    ]\n                ],\n                columns: [\n                    { width:'400px' }\n                ]\n            }]\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [Spreadsheet Headers](/jspreadsheet/docs/headers) - Customize column headers\n- [Nested Headers](/jspreadsheet/docs/nested-headers) - Multi-level column headers\n- [Jspreadsheet Pro Footers](https://jspreadsheet.com/docs/footers) - Advanced footer features (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/format.md",
    "content": "title: Data Grid Cell Masking\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, cell formatting, currency format, date format, date mask, number formatting, spreadsheet currency, data formatting, format mask, data display customization, Jspreadsheet formatting\ndescription: Learn how to format and customize cell data in Jspreadsheet. Explore techniques for formatting numbers, currency, dates, and more to improve the presentation of your data grid.\n\n# Cell Formatting\n\nJspreadsheet offers flexible cell formatting features similar to Excel and Google Sheets, with customizable options for better data presentation. You can format numbers, currencies, dates, and more to suit specific application requirements, enabling enhanced control over how data is displayed and integrated. \n\n## Overview\n\n### Column Level Settings\n\nThe following properties are applied uniformly to all cells in a column.\n\n| Attribute         | Description                                                                                                           |\n|-------------------|-----------------------------------------------------------------------------------------------------------------------|\n| mask?: object     | This property force the mask during the edition.                                                                      |\n| format?: object   | This property will mask the content of a cell after the edition.                                                      |\n| locale? object    | This property apply the JavaScript Intl.FormatNumber to the cells.                                                    | \n| render?: function | Intercepts and modifies the text of all cells in a column.<br>`render(td, value, x, y, worksheet, options) => void`   |\n\n\n{.pro}\n> #### Differences in the Pro Version\n>\n> Jspreadsheet Pro allows cell-level format and other advanced format customizations.\\\n> \\\n> [Learn more](https://jspreadsheet.com/docs/format){.button}\n\n## Documentation\n\n### Mask and Format Settings\n\nIn Jspreadsheet CE, the `mask` property enforces a specific input pattern during cell editing using spreadsheet-like tokens. The `format` property applies formatting rules to cell content after editing.   Below are examples of tokens that can be used:\n\n| Valid examples |\n|----------------|\n| 0              |\n| 0.00           |\n| 0%             |\n| 0.00%          |\n| #,##0          |\n| #,##0.00       |\n| #,##0          |\n| d-mmm-yy       |\n| d-mmm          |\n| dd/mm/yyyy     |\n| mmm-yy         |\n| h:mm AM/PM     |\n| h:mm:ss AM/PM  |\n| h:mm           |\n| h:mm:ss        |\n| m/d/yy h:mm    |\n| mm:ss          |\n| [h]:mm:ss      |\n\n#### Example\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n        columns: [{\n            type: 'number',\n            // Excel like token to format the currency input\n            mask: 'U$ #.##0,00'\n        }]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const columns = [{\n        type: 'number',\n        // Excel like token to format the currency input\n        mask: 'U$ #.##0,00'\n    }];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet columns={columns} minDimensions={[6,6]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n    <Worksheet :columns=\"columns\" />\n  </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nexport default {\n  components: {\n    Spreadsheet,\n    Worksheet,\n  },\n  setup() {\n    const columns = ref([\n      {\n        type: \"number\",\n        // Excel-like token to format the currency input\n        mask: \"U$ #.##0,00\",\n      },\n    ]);\n\n    return {\n      columns,\n    };\n  },\n};\n</script>\n```\n```angularjs\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [6,6],\n                columns: [{\n                    type: 'number',\n                    // Excel like token to format the currency input\n                    mask: 'U$ #.##0,00'\n                }]\n            }]\n        });\n    }\n}\n```\n\n### Locale\n\n#### Currency Formatting\n\nTo use the `currency` style in Jspreadsheet, specify the `currency` property. Optionally, you can use the `currencyDisplay` and `currencySign` properties to control the display format of the currency unit.\n\n```html\n<div id=\"spreadsheet1\"></div>\n<script>\n// India currency\njspreadsheet(document.getElementById('spreadsheet1'), {\n    worksheets: [{\n        minDimensions: [6,6],\n        columns: [{\n            type: 'number',\n            // Locale will enable number formatting\n            locale: 'en-IN',\n            // Options for the number format class. You can find more about he options on the link above\n            options: { style:'currency', currency: 'INR' }\n        }]\n    }]\n});\n</script>\n```\n```jsx\n// India currency\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Columns\n    const columns = [{\n        type: 'number',\n        // Locale will enable number formatting\n        locale: 'en-IN',\n        // Options for the number format class. You can find more about he options on the link above\n        options: { style:'currency', currency: 'INR' }\n    }];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet columns={columns} minDimensions={[6,6]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n    <Worksheet :columns=\"columns\" />\n  </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nexport default {\n  components: {\n    Spreadsheet,\n    Worksheet,\n  },\n  setup() {\n    const columns = ref([{\n        type: 'number',\n        // Locale will enable number formatting\n        locale: 'en-IN',\n        // Options for the number format class. You can find more about he options on the link above\n        options: { style:'currency', currency: 'INR' }\n    }]);\n\n    return {\n      columns,\n    };\n  },\n};\n</script>\n```\n```angularjs\nexport class AppComponent {\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [6,6],\n                columns: [{\n                    type: 'number',\n                    // Locale will enable number formatting\n                    locale: 'en-IN',\n                    // Options for the number format class. You can find more about he options on the link above\n                    options: { style:'currency', currency: 'INR' }\n                }]\n            }]\n        });\n    }\n}\n```\n\n### Custom Formatting with `render()`\n\nJspreadsheet supports custom masking through the `render` method, allowing you to customize cell content display.\n\n{.ignore}\n```javascript\njspreadsheet(DOMElement, {\n        tabs: true,\n        toolbar: true,\n        worksheets: [{\n            data: [['2022-01-01 12:14:12'],['=TODAY()']],\n            columns: [{\n                width: 300,\n                customFormat: 'MMMM Do YYYY, h:mm:ss a',\n                render: function(td, value, x, y, worksheet, options) {\n                    if (td && td.innerText && options.customFormat) {\n                        td.innerText = moment(td.innerText).format(options.customFormat);\n                    }\n                },\n                align: 'right',\n            }],\n            minDimensions: [4,8],\n        }]\n    });\n```\n\n\n## Examples\n\n### Data Grid with Different Currencies\n\nThe example below demonstrates number formatting using `Intl.NumberFormat` or `mask`.\n\nSee more examples of the [spreadsheet format](https://jsfiddle.net/spreadsheet/L9jxszf3/) on jsfiddle\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: true,\n    worksheets: [\n        {\n            minDimensions:[6, 10],\n            data: [\n                [1024,1024,0.24,1024,1024,1024],\n                [1000.456,1000.456,0.4155,1000.456,1000.456,1000.456],\n                ['547','547,98','7,98','547.98','547,98','547.98'],\n            ],\n            columns: [\n                {\n                    title:\"Currency INR\",\n                    type: \"number\",\n                    locale: 'en-IN',\n                    options: { style:'currency', currency: 'INR' } },\n                {\n                    title: \"Currency BRL\",\n                    type: \"number\",\n                    locale: 'pt-BR',\n                    options: { style: 'currency', currency: 'BRL' } },\n                {\n                    title: \"Percent US\",\n                    type: \"number\",\n                    mask: \"0.00%\" },\n                {\n                    title: \"Units Liter US\",\n                    type: \"number\",\n                    locale: 'en-US',\n                    options: { style: 'unit', unit: 'liter', unitDisplay: 'long' } },\n                {\n                    type: \"number\",\n                    format: '#.##0,00'\n                },\n                {\n                    type: \"number\",\n                    mask: '#,##0'\n                },\n            ],\n            defaultColWidth: '110px',\n        }\n    ]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [1024,1024,0.24,1024,1024,1024],\n        [1000.456,1000.456,0.4155,1000.456,1000.456,1000.456],\n        ['547','547,98','7,98','547.98','547,98','547.98'],\n    ];\n    // Columns\n    const columns = [\n        {\n            title:\"Currency INR\",\n            type: \"number\",\n            locale: 'en-IN',\n            options: { style:'currency', currency: 'INR' } },\n        {\n            title: \"Currency BRL\",\n            type: \"number\",\n            locale: 'pt-BR',\n            options: { style: 'currency', currency: 'BRL' } },\n        {\n            title: \"Percent US\",\n            type: \"number\",\n            mask: \"0.00%\" },\n        {\n            title: \"Units Liter US\",\n            type: \"number\",\n            locale: 'en-US',\n            options: { style: 'unit', unit: 'liter', unitDisplay: 'long' } },\n        {\n            type: \"number\",\n            format: '#.##0,00'\n        },\n        {\n            type: \"number\",\n            mask: '#,##0'\n        }\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet} toolbar>\n            <Worksheet data={data} columns={columns} minDimensions={[6,10]} defaultColWidth=\"110px\" />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :data=\"data\" :columns=\"columns\" :minDimensions=\"[6,10]\" defaultColWidth=\"110px\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Data\nconst data = ref([\n  [1024,1024,0.24,1024,1024,1024],\n  [1000.456,1000.456,0.4155,1000.456,1000.456,1000.456],\n  ['547','547,98','7,98','547.98','547,98','547.98'],\n]);\n\n// Columns\nconst columns = ref([\n  {\n      title:\"Currency INR\",\n      type: \"number\",\n      locale: 'en-IN',\n      options: { style:'currency', currency: 'INR' } \n  },\n  {\n      title: \"Currency BRL\",\n      type: \"number\",\n      locale: 'pt-BR',\n      options: { style: 'currency', currency: 'BRL' } \n  },\n  {\n      title: \"Percent US\",\n      type: \"number\",\n      mask: \"0.00%\" \n  },\n  {\n      title: \"Units Liter US\",\n      type: \"number\",\n      locale: 'en-US',\n      options: { style: 'unit', unit: 'liter', unitDisplay: 'long' } \n  },\n  {\n      type: \"number\",\n      format: '#.##0,00'\n  },\n  {\n      type: \"number\",\n      mask: '#,##0'\n  }\n]);\n\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            toolbar: true,\n            worksheets: [\n                {\n                    minDimensions:[6, 10],\n                    data: [\n                        [1024,1024,0.24,1024,1024,1024],\n                        [1000.456,1000.456,0.4155,1000.456,1000.456,1000.456],\n                        ['547','547,98','7,98','547.98','547,98','547.98'],\n                    ],\n                    columns: [\n                        {\n                            title:\"Currency INR\",\n                            type: \"number\",\n                            locale: 'en-IN',\n                            options: { style:'currency', currency: 'INR' } },\n                        {\n                            title: \"Currency BRL\",\n                            type: \"number\",\n                            locale: 'pt-BR',\n                            options: { style: 'currency', currency: 'BRL' } },\n                        {\n                            title: \"Percent US\",\n                            type: \"number\",\n                            mask: \"0.00%\" },\n                        {\n                            title: \"Units Liter US\",\n                            type: \"number\",\n                            locale: 'en-US',\n                            options: { style: 'unit', unit: 'liter', unitDisplay: 'long' } },\n                        {\n                            type: \"number\",\n                            format: '#.##0,00'\n                        },\n                        {\n                            type: \"number\",\n                            mask: '#,##0'\n                        },\n                    ],\n                    defaultColWidth: '110px',\n                }\n            ]\n        });\n    }\n}\n```\n\n### Custom Formatting with MomentJS\n\nThe example below demonstrates how to apply a mask to a cell using MomentJS.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/moment/moment.min.js\"></script>\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\nconst customRender = function(td, value, x, y, instance, options) {\n    if (td && td.innerText && options.customFormat) {\n        td.innerText = moment(td.innerText).format(options.customFormat);\n    }\n}\n\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: true,\n    toolbar: true,\n    worksheets: [{\n        data: [['2022-01-01 12:14:12'],['=TODAY()']],\n        columns: [{\n            width: 300,\n            customFormat: 'MMMM Do YYYY, h:mm:ss a',\n            render: customRender,\n            align: 'right',\n        }],\n        minDimensions: [4,8],\n    }],\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport moment from \"momentjs\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Create a new spreadsheet\nconst customRender = function(td, value, x, y, instance, options) {\n    if (td && td.innerText && options.customFormat) {\n        td.innerText = moment(td.innerText).format(options.customFormat);\n    }\n}\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [['2022-01-01 12:14:12'],['=TODAY()']];\n    // Columns\n    const columns = [{\n        width: 300,\n        customFormat: 'MMMM Do YYYY, h:mm:ss a',\n        render: customRender,\n        align: 'right',\n    }]\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} minDimensions={[4,8]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport moment from \"momentjs\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        // Data\n        const data = ref([\n            ['2022-01-01 12:14:12'],\n            ['=TODAY()']\n        ]);\n\n        // Columns\n        const columns = ref([\n            {\n                width: 300,\n                customFormat: 'MMMM Do YYYY, h:mm:ss a',\n                render: (td, value, x, y, instance, options) => {\n                    if (td && td.innerText && options.customFormat) {\n                        td.innerText = moment(td.innerText).format(options.customFormat);\n                    }\n                },\n                align: 'right',\n            }\n        ]);\n\n        return {\n            data,\n            columns,\n        };\n    }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n\n\n// Create a new spreadsheet\nconst customRender = function(td, value, x, y, instance, options) {\n    if (td && td.innerText && options.customFormat) {\n        td.innerText = moment(td.innerText).format(options.customFormat);\n    }\n}\n\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            tabs: true,\n            toolbar: true,\n            worksheets: [{\n                data: [['2022-01-01 12:14:12'],['=TODAY()']],\n                columns: [{\n                    width: 300,\n                    customFormat: 'MMMM Do YYYY, h:mm:ss a',\n                    render: customRender,\n                    align: 'right',\n                }],\n                minDimensions: [4,8],\n            }]\n        });\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Conditional Formatting**\n>\n> Jspreadsheet CE provides column-level formatting with masks and locale settings. **Jspreadsheet Pro** adds powerful visual formatting capabilities:\n>\n> **Conditional Formatting Features:**\n> - **Visual Rules:** Automatically highlight cells based on values (greater than, less than, between, equal to)\n> - **Color Scales:** Gradient color scales for data visualization (2-color and 3-color scales)\n> - **Data Bars:** In-cell bar charts showing relative values\n> - **Icon Sets:** Traffic lights, arrows, flags, and custom icons based on cell values\n> - **Custom Rules:** Formula-based rules for complex conditional formatting\n> - **Cell-Level Formatting:** Apply different formats to individual cells, not just columns\n> - **Style Templates:** Pre-defined formatting styles and themes\n>\n> **Advanced Format Options:**\n> - **Rich Text Formatting:** Bold, italic, underline, strikethrough, superscript, subscript\n> - **Cell-Specific Masks:** Different formatting masks for individual cells\n> - **Background Colors & Borders:** Cell-level styling with custom colors and border styles\n> - **Text Alignment:** Per-cell horizontal and vertical alignment\n> - **Font Controls:** Font family, size, and color per cell\n>\n> Perfect for dashboards, reports, and data analysis applications requiring visual data presentation.\n>\n> **[Explore Pro Formatting →](https://jspreadsheet.com/docs/format)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n## Related Tools\n\n- [JavaScript Input Mask](https://jsuites.net/docs/javascript-mask) - Standalone input masks with Excel-like tokens\n- [JavaScript Rich Forms](https://jsuites.net/docs/rich-form) - Form validation and change tracking\n- [LemonadeJS Forms](https://lemonadejs.com/docs/forms) - Reactive form binding\n- [Jspreadsheet Pro Format](https://jspreadsheet.com/docs/format) - Advanced cell formatting and conditional formatting (Pro)\n- [Data Validations](https://jspreadsheet.com/docs/validations) - Cell validation rules (Pro)\n\nThese tools use the same Excel-like mask tokens as Jspreadsheet CE, making it easy to maintain consistent formatting across your application.\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/formulas.md",
    "content": "title: Excel-Like Formulas in Jspreadsheet CE\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like formulas, spreadsheet formulas, calculations, spreadsheet calculations, custom formulas, web-based spreadsheets, internationalization\ndescription: Implement Excel-like formulas in your web app with Jspreadsheet CE for dynamic, browser-based spreadsheets.\n\n# Excel-Like Formulas\n\nJspreadsheet supports Excel-like formulas compatible with popular spreadsheet applications such as Excel and Google Sheets. These features enable advanced calculation capabilities, including:\n\n- Excel-style Formulas: Customizable formulas resembling Excel.\n- Formula Auto-update: Adjusts on cell actions such as copy and paste.\n- Custom Formulas: Create your custom formulas.\n\n{.pro}\n> #### Formula Pro\n>\n> Jspreadsheet Pro's Formula Pro Extension provides a suite of advanced features designed to enhance spreadsheet functionality, including:\n>\n> - Full-stack applications;\n> - Secure private scopes;\n> - Cross-sheet calculations;\n> - complex matrix operations;\n> - Advanced operators ('%', '@');\n> - Dynamic range calculations (e.g., A:A, 1:1);\n>\n>\\\n> [More about Formula Pro](https://jspreadsheet.com/products/formulas){.button}\n\n## Documentation\n\nThis section details the settings, methods, and events associated with spreadsheet calculations in Jspreadsheet. All formula names, including custom ones, should be capitalized for security and standardization.\n\n### Settings\n\nA summary of configurations related to the use of formulas.\n\n| Configuration                    | Description                                                       |\n|----------------------------------|-------------------------------------------------------------------|\n| secureFormulas?: boolean         | Enable formula security. Default: true                            |\n| parseFormulas?: boolean          | Enable formula calculations. Default: true                        |\n| debugFormulas?: boolean          | Enable the formula debug notices. Default: false                  |\n| autoIncrement?: boolean          | Formula variable increment on cloning or copying. Default: true   |\n\n\n### Events\n\nAll events related to formulas.\n\n| Event                      | Description                                                                                                                                  |\n| ---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|\n| onbeforeformula?: Function | Intercept and modify a formula before execution.<br/>onbeforeformula(worksheet: Object, expression: String, x: Number, y: Number) => String  |\n\n\n### Methods\n\nAll methods related to formulas.\n\n| Method                    | Description                                                                                                     |\n| --------------------------|-----------------------------------------------------------------------------------------------------------------|\n| executeFormula?: Function | Execute a formula.<br/>executeFormula(expression: string, x?: number, y?: number, caching?: boolean) => String  |\n\n\n## Examples\n\n### Basic excel-like formulas usage\n\nA basic spreadsheet example using formulas, including currency, percentage and mask.\n\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            [ 'Crayons Crayola only (No Rose Art)', 2, 5.01, 0.01, '=B1*C1*(1-D1)' ],\n            [ 'Colored Pencils Crayola only', 2, 4.41, 0.02, '=B2*C2*(1-D2)' ],\n            [ 'Expo Dry-erase Markers Wide', 4, 3.00, 0.1, '=B3*C3*(1-D3)' ],\n            [ 'Index Cards Unlined', 3, 6.00, 0.03, '=B4*C4*(1-D4)' ],\n            [ 'Tissues', 10, 1.90, 0.01, '=B5*C5*(1-D5)' ],\n            [ 'Ziploc Sandwich-size Bags', 5, 1.00, 0.01, '=B6*C6*(1-D6)' ],\n            [ 'Thin Markers Crayola only', 2, 3.00, 0.02, '=B7*C7*(1-D7)' ],\n            [ 'Highlighter', 4, 1.20, 0.01, '=B8*C8*(1-D8)' ],\n            [ 'Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '', '=SUM(E1:E8)' ],\n        ],\n        columns: [\n            { type: 'text', title:'Product', width:'300' },\n            { type: 'text', title:'Qtd', width:'80', mask:'#.##0' },\n            { type: 'text', title:'Price', width:'100px', mask:'$ #.##0,00'  },\n            { type: 'text', title:'Discount', mask:'0.00%' },\n            {\n                type: 'number',\n                title: 'Total',\n                width: '100px',\n                format: 'US #.##0,00',\n            },\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Worksheet data\n    const data = [\n        [ 'Crayons Crayola only (No Rose Art)', 2, 5.01, 0.01, '=B1*C1*(1-D1)' ],\n        [ 'Colored Pencils Crayola only', 2, 4.41, 0.02, '=B2*C2*(1-D2)' ],\n        [ 'Expo Dry-erase Markers Wide', 4, 3.00, 0.1, '=B3*C3*(1-D3)' ],\n        [ 'Index Cards Unlined', 3, 6.00, 0.03, '=B4*C4*(1-D4)' ],\n        [ 'Tissues', 10, 1.90, 0.01, '=B5*C5*(1-D5)' ],\n        [ 'Ziploc Sandwich-size Bags', 5, 1.00, 0.01, '=B6*C6*(1-D6)' ],\n        [ 'Thin Markers Crayola only', 2, 3.00, 0.02, '=B7*C7*(1-D7)' ],\n        [ 'Highlighter', 4, 1.20, 0.01, '=B8*C8*(1-D8)' ],\n        [ 'Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '', '=SUM(E1:E8)' ],\n    ]\n\n    // Column definitions\n    const columns = [\n        { type: 'text', title:'Product', width:'300' },\n        { type: 'text', title:'Qtd', width:'80', mask:'#.##0' },\n        { type: 'text', title:'Price', width:'100px', mask:'$ #.##0,00'  },\n        { type: 'text', title:'Discount', mask:'0.00%' },\n        {\n            type: 'number',\n            title: 'Total',\n            width: '100px',\n            format: 'US #.##0,00',\n        },\n    ]\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        // Worksheet data\n        const data = ref([\n            ['Crayons Crayola only (No Rose Art)', 2, 5.01, 0.01, '=B1*C1*(1-D1)'],\n            ['Colored Pencils Crayola only', 2, 4.41, 0.02, '=B2*C2*(1-D2)'],\n            ['Expo Dry-erase Markers Wide', 4, 3.00, 0.1, '=B3*C3*(1-D3)'],\n            ['Index Cards Unlined', 3, 6.00, 0.03, '=B4*C4*(1-D4)'],\n            ['Tissues', 10, 1.90, 0.01, '=B5*C5*(1-D5)'],\n            ['Ziploc Sandwich-size Bags', 5, 1.00, 0.01, '=B6*C6*(1-D6)'],\n            ['Thin Markers Crayola only', 2, 3.00, 0.02, '=B7*C7*(1-D7)'],\n            ['Highlighter', 4, 1.20, 0.01, '=B8*C8*(1-D8)'],\n            ['Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '', '=SUM(E1:E8)'],\n        ]);\n\n        // Column definitions\n        const columns = ref([\n            { type: 'text', title: 'Product', width: '300' },\n            { type: 'text', title: 'Qtd', width: '80', mask: '#.##0' },\n            { type: 'text', title: 'Price', width: '100px', mask: '$ #.##0,00' },\n            { type: 'text', title: 'Discount', mask: '0.00%' },\n            {\n                type: 'number',\n                title: 'Total',\n                width: '100px',\n                format: 'US #.##0,00',\n            },\n        ]);\n\n        return {\n            data,\n            columns,\n        };\n    },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            style: ['background-color:orange; font-weight: bold;'],\n            worksheets: [{\n                data: [\n                    [ 'Crayons Crayola only (No Rose Art)', 2, 5.01, 0.01, '=B1*C1*(1-D1)' ],\n                    [ 'Colored Pencils Crayola only', 2, 4.41, 0.02, '=B2*C2*(1-D2)' ],\n                    [ 'Expo Dry-erase Markers Wide', 4, 3.00, 0.1, '=B3*C3*(1-D3)' ],\n                    [ 'Index Cards Unlined', 3, 6.00, 0.03, '=B4*C4*(1-D4)' ],\n                    [ 'Tissues', 10, 1.90, 0.01, '=B5*C5*(1-D5)' ],\n                    [ 'Ziploc Sandwich-size Bags', 5, 1.00, 0.01, '=B6*C6*(1-D6)' ],\n                    [ 'Thin Markers Crayola only', 2, 3.00, 0.02, '=B7*C7*(1-D7)' ],\n                    [ 'Highlighter', 4, 1.20, 0.01, '=B8*C8*(1-D8)' ],\n                    [ 'Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '', '=SUM(E1:E8)' ],\n                ],\n                columns: [\n                    { type: 'text', title:'Product', width:'300' },\n                    { type: 'text', title:'Qtd', width:'80', mask:'#.##0' },\n                    { type: 'text', title:'Price', width:'100px', mask:'$ #.##0,00'  },\n                    { type: 'text', title:'Discount', mask:'0.00%' },\n                    {\n                        type: 'number',\n                        title: 'Total',\n                        width: '100px',\n                        format: 'US #.##0,00',\n                    },\n                ]\n            }]\n        });\n    }\n}\n```\n\n\n## More Information\n\nExplore additional sections related to spreadsheet calculations.\n\n### Jspreadsheet CE\n\n- [Custom Excel-Like Formulas](/jspreadsheet/docs/custom-formulas)\n\n### Jspreadsheet PRO\n\n- [Custom Excel-Like Formulas](https://jspreadsheet.com/docs/custom-formulas)\n- [Formula Selector](https://jspreadsheet.com/docs/formula-picker)\n- [The Calculation Queue State](https://jspreadsheet.com/docs/calculations)\n- [Namespaces](https://jspreadsheet.com/docs/namespaces)\n- [The Formula Pro Extension](https://jspreadsheet.com/products/formulas)\n- [Defined Names](https://jspreadsheet.com/docs/defined-names)\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for 500+ Excel-Compatible Functions**\n>\n> Jspreadsheet CE provides basic formulas like SUM, AVERAGE, and arithmetic operations. **Jspreadsheet Pro** expands this with full Excel compatibility:\n>\n> - **Lookup Functions:** VLOOKUP, HLOOKUP, INDEX, MATCH, XLOOKUP\n> - **Conditional Formulas:** SUMIF, COUNTIF, AVERAGEIF, SUMIFS, COUNTIFS, IFS\n> - **Financial Functions:** NPV, IRR, PMT, FV, PV, RATE\n> - **Date/Time Functions:** Advanced date calculations, NETWORKDAYS, EDATE, EOMONTH\n> - **Text Functions:** String manipulation, CONCATENATE, TEXTJOIN, REGEX\n> - **Statistical Functions:** STDEV, VAR, PERCENTILE, QUARTILE\n> - **Cross-Worksheet Calculations:** Reference cells across multiple worksheets (e.g., Sheet2!A1:B10)\n> - **Dynamic Ranges:** Full column/row references (e.g., A:A, 1:1)\n> - **Array Formulas:** Matrix operations and bulk calculations\n>\n> Perfect for business applications requiring full Excel compatibility and advanced spreadsheet functionality.\n>\n> **[Explore Pro Formula Features →](https://jspreadsheet.com/docs/formulas)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n \n"
  },
  {
    "path": "docs/jspreadsheet/docs/freeze-columns.md",
    "content": "title: Jspreadsheet Freeze Columns\nkeywords: Jspreadsheet, freeze columns, data grid customization, JavaScript spreadsheets, Excel-like freeze functionality, floating columns, spreadsheet column freezing\ndescription: This section provides comprehensive information on freezing columns in Jspreadsheet, including programmatic settings, methods, and related events.\n\n# Freeze Columns\n\nFreezing columns in a spreadsheet keeps specific columns visible as you scroll. This section covers the settings, methods, and events for managing frozen columns. \n\n{.pro}\n> #### What you can find on the Pro Version\n> \n> The **Pro version** of Jspreadsheet provides programmatic methods, events, and manual controls for `freezing columns`, enabling users to adjust this setting directly through the interface.    \n> \\\n> [Learn more](https://jspreadsheet.com/docs/freeze-columns){.button}\n\n## Documentation\n\n### Initial Settings\n\nBelow are the initial settings related to freezing columns.\n\n| Property                   | Description                                       |\n|----------------------------|---------------------------------------------------|\n| **freezeColumns**: number  | Defines the columns to freeze on initialization.  |\n\n\n> #### Limitations\n> Freeze columns currently have limited functionality when used with filters and footers. These improvements are planned for the next release.\n\n## Examples\n\n### Basic Freeze Columns Example\n\nA straightforward example of how to use frozen columns in Jspreadsheet.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [50,10],\n        tableOverflow: true,\n        tableWidth: '800px',\n        freezeColumns: 1\n    }]\n});\n\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/style.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet minDimensions={[50,10]} tableOverflow={true} tableWidth={\"800px\"} freezeColumns={1} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :minDimensions=\"[50,10]\" :tableOverflow=\"true\" tableWidth=\"800px\" :freezeColumns=\"1\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    }\n};\n</script>\n\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport \"jsuites/dist/jsuites.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                minDimensions: [50,10],\n                tableOverflow: true,\n                tableWidth: '800px',\n                freezeColumns: 1\n            }]\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [Jspreadsheet Pro Freeze Columns](https://jspreadsheet.com/docs/freeze-columns) - Advanced freeze with user controls (Pro)\n- [Jspreadsheet Pro Freeze Rows](https://jspreadsheet.com/docs/freeze-rows) - Freeze header rows with interactive controls (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/getting-started.md",
    "content": "title: Getting Started with Jspreadsheet CE  \nkeywords: Jspreadsheet CE, Jexcel, JavaScript data grid, spreadsheets, JavaScript tables, Excel-like grids, web-based spreadsheets, data grid controls, interactive spreadsheet features  \ndescription: Learn how to create interactive data grids with powerful spreadsheet controls using Jspreadsheet CE.\n\n# Getting Started with Jspreadsheet CE v5\n\n## Overview\n\nJspreadsheet CE, formerly jExcel, is a free, lightweight JavaScript spreadsheet library to help developers bring Excel-like data grids and spreadsheet features to their applications. The library enables developers to build robust data management interfaces using React, Angular or pure JavaScript.\n\n### Why Choose Jspreadsheet CE?\n\n- Create rich, interactive data grid interfaces\n- Handle complex data inputs with Excel-like functionality\n- Direct Excel compatibility: Copy and paste using standard shortcuts\n- Proven success across thousands of implementations\n- Lightweight, fast, and intuitive\n- Easy integration with third-party plugins\n- Built for collaboration and sharing\n\n## Jspreadsheet Editions\n\n### Jspreadsheet CE - Community Edition\nThis page documents Jspreadsheet CE (v5), the free, open-source spreadsheet library:\n- Core spreadsheet functionality\n- Excel-like data grids\n- MIT license\n- Community support\n\n**Perfect for:**\n- Open source projects\n- Learning and prototyping\n- Basic spreadsheet needs\n- Budget-conscious projects\n\n### Upgrade to Jspreadsheet Pro\n[Jspreadsheet Pro](https://jspreadsheet.com/docs/getting-started) offers professional features not available in CE:\n- **Advanced Editors:** Dropdown with conditional logic, date/time, rich text, HTML, color pickers\n- **Enhanced Formulas:** 500+ Excel-compatible functions, cross-worksheet calculations\n- **Conditional Formatting:** Visual rules, data bars, color scales\n- **Data Validation:** Advanced rules, custom validation\n- **Multiple Worksheets:** Tabs, cross-sheet references\n- **Import/Export:** Full Excel compatibility, CSV, JSON\n- **Performance:** Optimized for large datasets\n- **Professional Support:** Dedicated support team, priority bug fixes\n- **Commercial License:** For commercial applications\n\n**[Explore Jspreadsheet Pro Features →](https://jspreadsheet.com/docs/getting-started)** | **[Compare Editions →](https://jspreadsheet.com/pricing)**\n\n### LemonadeJS Data Grid - Lightweight Alternative\nFor simple data display without Excel features, [LemonadeJS Data Grid](https://lemonadejs.net/docs/plugins/data-grid) offers:\n- Lightweight (5KB)\n- Reactive data binding\n- Basic search and pagination\n- Perfect for simple CRUD interfaces\n\n**[View LemonadeJS Data Grid →](https://lemonadejs.net/docs/plugins/data-grid)**\n\n---\n\n### Feature Comparison\n\n| Feature | CE | Pro | LemonadeJS Grid |\n|---------|----|----|-----------------|\n| Formulas | Basic | ✓✓ 500+ functions | ❌ |\n| Conditional Formatting | ❌ | ✓ | ❌ |\n| Advanced Editors | Basic | ✓✓ Full suite | ❌ |\n| Multiple Worksheets | ✓ | ✓ | ❌ |\n| Excel Import/Export | Basic | ✓✓ Full | ❌ |\n| Data Validation | Basic | ✓✓ Advanced | ❌ |\n| License | MIT | Commercial | MIT |\n| Support | Community | ✓ Dedicated | Community |\n| Size | Medium | Full-featured | 5KB |\n\n---\n\n\n## Installation\n\nChoose one of the following installation options:\n\n### NPM\n\nInstall jspreadsheet using NPM:\n\n```bash\nnpm install jspreadsheet-ce@5.0.0-beta.3\n```\n\n### CDN\n\nInclude jspreadsheet directly from JSDelivr CDN:\n\n{.ignore}\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5.0.0-beta.3/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5.0.0-beta.3/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css\" type=\"text/css\" />\n```\n\n### Download\n\nDownload and run jspreadsheet on your server or local machine:\n\nhttps://bossanova.uk/jspreadsheet/v5/jspreadsheet.zip\n\n\n## License\n\nThis software is distributed under the MIT license.\n\n\n## Documentation\n\n### Global methods and properties\n\n| Method Description                                                                                                                       |\n|------------------------------------------------------------------------------------------------------------------------------------------|\n| Create a new spreadsheet.<br/>`jspreadsheet(element: HTMLDivElement \\| HTMLTableElement, options: SpreadsheetOptions): WorksheetInstance[];`                                                     |\n| Destroy a given spreadsheet.<br/>`jspreadsheet.destroy(element: JspreadsheetInstanceElement, destroyEventHandlers?: boolean): void;`                                     |\n| Destroy all spreadsheets in all namespaces.<br/>`jspreadsheet.destroyAll(): void;`                                                              |\n| Translate Jspreadsheet components and extensions.<br/>`jspreadsheet.setDictionary(translations: Record<string, string>): void;`                                 |\n| Get a worksheet instance by name and namespace.<br/>`jspreadsheet.getWorksheetInstanceByName(worksheetName: string \\| null \\| undefined, namespace: string): WorksheetInstance \\| Record<string, WorksheetInstance>;` |\n\n\n## Examples\n\n### Create a new data grid\n\nYou can create a new data grid with spreadsheet-like controls from an HTML table element, a JS array, a CSV, a JSON file or an Excel XLSX file.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n    }]\n});\n</script>\n</html>\n```\n\n### Destroying The Data Grid\n\nThe following example shows how to dynamically destroy and recreate a new data grid. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"canonical\" href=\"https://bossanova.uk/jspreadsheet/docs/getting-started\" />\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='jspreadsheet'></div>\n\n<p><input type='button' value='Destroy' id=\"btn1\" /> <input type='button' value='Create' id=\"btn2\" /></p>\n\n<script>\n// Create a new spreadsheet\nlet create = function() {\n    if (document.getElementById('jspreadsheet').spreadsheet) {\n        destroy();\n    }\n\n    jspreadsheet(document.getElementById('jspreadsheet'), {\n    \tworksheets: [ { minDimensions:[5,5] } ]\n    });\n}\n\n// Destroy the spreadsheet\nlet destroy = function() {\n    jspreadsheet.destroy(document.getElementById('jspreadsheet'));\n}\n\n// Create the spreadsheet\njspreadsheet(document.getElementById('jspreadsheet'), {\n    worksheets: [{\n        minDimensions: [5,5],\n    }]\n});\n\ndocument.getElementById(\"btn1\").onclick = () => destroy()\ndocument.getElementById(\"btn2\").onclick = () => create()\n</script>\n</html>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/docs/headers.md",
    "content": "title: Spreadsheet Headers\nkeywords: Jspreadsheet, Jexcel, data grids, JavaScript data grid, Excel-like headers, column title, headers, data grid headers, column headers, customize headers, modify headers, spreadsheet customization, column title customization, data grid organization\ndescription: Customize and modify column headers in your data grids. Explore programmatic header updates and advanced options for organizing and managing your data grid.\n\n# Spreadsheet Headers\n\nThis section explains how to create and modify custom headers in Jspreadsheet. Users can change header titles via the context menu or by performing a long click on the header.\n\n## Documentation\n\n### Methods\n\nYou can update the headers programmatically using the following methods.\n\n| Method      | Description                                                                                                                                                                                                  |\n| ------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| getHeaders  | Get all header titles.<br/>@param `asArray` - If true, returns the items in an array, if false, returns them separated by \";\" within a single string.<br/>`worksheetInstance.getHeaders(asArray?: boolean): string \\| string[];`                           |\n| getHeader   | Get the column title.<br/>@param `column` - Column index.<br/>`worksheetInstance.getHeader(column: number): string;`                          |\n| setHeader   | Sets a custom header title for a specified column (starting at zero).<br/>@param `column` - column number starting on zero.<br/>@param `newValue` - New title. Empty string or undefined to reset the header title. <br/>`worksheetInstance.setHeader(column: number, newValue?: string): void;` |\n\n \n\n### Initial Settings\n\nTo customize headers, use the `title` and `tooltip` attributes in the column settings.\n\n\n{.ignore}\n```html\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        columns: [\n            {\n                type: 'text',\n                title: 'Country',\n                tooltip: 'This is the country',\n                width: '300px',\n            }\n        ]\n    }]\n});\n</script>\n```\n  \n\n### Available Events\n\n| Event          | Description                                                                                                                        |\n| ---------------|------------------------------------------------------------------------------------------------------------------------------------|\n| onchangeheader | Triggered when the header title is changed.<br/>`onchangeheader(instance: WorksheetInstance, colIndex: number, newValue: string, oldValue: string): void;` |\n\n \n\n## Examples\n\n### Updates to the Headers\n\nThe following example initializes the spreadsheet with basic headers and demonstrates how to update the titles programmatically.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n        columns: [\n            { title: 'Header 1' },\n            { title: 'Header 2' },\n            { title: 'Header 3' }\n        ],\n    }]\n});\n\n// Programmatically update the header titles\ntable[0].setHeader(0, 'New Header 1');\ntable[0].setHeader(1, 'New Header 2');\ntable[0].setHeader(2, 'New Header 3');\n</script>\n</html>\n```\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    const columns = [\n        { title: 'Header 1' },\n        { title: 'Header 2' },\n        { title: 'Header 3' }\n    ];\n\n    useEffect(() => {\n        if (spreadsheet.current) {\n            spreadsheet.current[0].setHeader(0, 'New Header 1');\n            spreadsheet.current[0].setHeader(1, 'New Header 2');\n            spreadsheet.current[0].setHeader(2, 'New Header 3');\n        }\n    }, [])\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet minDimensions={[6, 6]} columns={columns} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :minDimensions=\"[6,6]\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { ref, onMounted } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const spreadsheet = ref(null);\n\n        const columns = [\n            { title: \"Header 1\" },\n            { title: \"Header 2\" },\n            { title: \"Header 3\" },\n        ];\n\n        onMounted(() => {\n            if (spreadsheet.value) {\n                const instance = spreadsheet.value.current[0];\n                instance.setHeader(0, \"New Header 1\");\n                instance.setHeader(1, \"New Header 2\");\n                instance.setHeader(2, \"New Header 3\");\n            }\n        });\n\n        return {\n            spreadsheet,\n            columns,\n        };\n    },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            minDimensions: [6,6],\n            columns: [\n                { title: 'Header 1' },\n                { title: 'Header 2' },\n                { title: 'Header 3' }\n            ],\n        });\n\n        // Programmatically update the header titles\n        this.worksheets[0].setHeader(0, 'New Header 1');\n        this.worksheets[0].setHeader(1, 'New Header 2');\n        this.worksheets[0].setHeader(2, 'New Header 3');\n    }\n}\n```\n\n## Related Tools\n\n- [Nested Headers](/jspreadsheet/docs/nested-headers) - Create multi-level column headers\n- [Data Grid Footers](/jspreadsheet/docs/footers) - Add footer rows with formulas\n- [Jspreadsheet Pro Headers](https://jspreadsheet.com/docs/headers) - Advanced header customization (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/helpers.md",
    "content": "title: Common Spreadsheet Helper Methods\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like features, spreadsheet, data table, documentation, spreadsheet helpers, useful methods\ndescription: This section provides helper methods for everyday spreadsheet scripting, including formatting, transformations, and general spreadsheet utilities.\n\n# Spreadsheet Helpers\n\nThis section provides essential methods for common spreadsheet operations, such as handling cell names, ranges, formulas, and data parsing. Each method is documented for easy integration into your applications.\n\n\n## Documentation\n\n### getColumnName\n\n`jspreadsheet.helpers.getColumnName(i: number): string;`\n\n**Description:**  \nReturns the column letter corresponding to the given column index.  \nUseful for converting numeric indices (e.g., 0, 1, 2) into Excel-style letters (e.g., A, B, C).\n\n**Parameters:**\n- `columnNumber` (number): Zero-based column index.\n\n**Returns:**\n- `string`: Column letter (e.g., \"A\", \"B\", ...).\n\n\n{.break}\n---\n\n### getCellNameFromCoords\n\n`jspreadsheet.helpers.getCellNameFromCoords(x: Number, y: Number) => String`\n\n**Description:**  \nGenerates a cell name (e.g., A1) from specified zero-based x (column) and y (row) coordinates.\n\n**Parameters:**\n- `x` (Number): Column index (0-based).\n- `y` (Number): Row index (0-based).\n\n**Returns:**\n- `String`: Cell name (e.g., \"A1\", \"B2\").\n\n\n{.break}\n---\n\n\n### getCoordsFromCellName\n\n`jspreadsheet.helpers.getCoordsFromCellName(cellName: String) => [Number, Number]`\n\n**Description:**  \nConverts a cell name (e.g., A1) into zero-based coordinates.\n\n**Parameters:**\n- `cellName` (String): Cell name in Excel-style notation.\n\n**Returns:**\n- `[Number, Number]`: Array containing x (column) and y (row) indices.\n\n\n{.break}\n---\n\n\n### getCoordsFromRange\n\n`jspreadsheet.helpers.getCoordsFromRange(range: String, adjust?: boolean) => [Number, Number, Number, Number]`\n\n**Description:**  \nConverts a range string into its corresponding zero-based coordinates.\n\n**Parameters:**\n- `range` (String): Range string (e.g., \"A1:B2\").\n- `adjust` (boolean, optional): Whether to normalize coordinates (default: false).\n\n**Returns:**\n- `[Number, Number, Number, Number]`: Array containing x1, y1, x2, y2.\n\n\n{.break}\n---\n\n\n### createFromTable\n\n`jspreadsheet.helpers.createFromTable(element: HTMLElement, options: Object) => Object`\n\n**Description:**  \nCreates a new spreadsheet configuration based on an HTML table element.\n\n**Parameters:**\n- `element` (HTMLElement): Source HTML table.\n- `options` (Object): Configuration options.\n\n**Returns:**\n- `Object`: Spreadsheet configuration.\n\n\n{.break}\n---\n\n\n### parseCSV\n\n`jspreadsheet.helpers.parseCSV(str: string, delimiter?: string): string[][];`\n\n**Description:**  \nConverts a CSV string into a JavaScript array.\n\n**Parameters:**\n- `data` (string): CSV-formatted string.\n- `delimiter` (string): Delimiter character (e.g., \",\").\n\n**Returns:**\n- `string[][]`: Parsed array of rows.\n\n\n{.break}\n---\n\n\n### getTokensFromCoords\n\n`jspreadsheet.helpers.getTokensFromCoords(x1: Number, y1: Number, x2: Number, y2: Number, wsName?: String) => Array`\n\n**Description:**  \nGenerates cell tokens from a range of coordinates.\n\n**Parameters:**\n- `x1, y1, x2, y2` (Number): Range coordinates.\n- `wsName` (String, optional): Worksheet name.\n\n**Returns:**\n- `Array`: List of tokens (e.g., [\"A1\", \"A2\"]).\n\n\n{.break}\n---\n\n\n## Examples\n\n### Data Grid Helpers Example\n\n{.ignore}\n```javascript\n// Returns A1\njspreadsheet.helpers.getCellNameFromCoords(0,0);\n// Returns (4) [1, 0, 2, 3]\njspreadsheet.helpers.getCoordsFromRange('B1:C4');\n// Also works with the worksheet instance. Returns 1,1\njspreadsheet.helpers.getCoordsFromCellName('B2');\n```\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\"/>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\"/>\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><input type='button' value='Get Range' class=\"jss_object\" id='btn1' /> <div id=\"range\"></div></p>\n\n<script>\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n    }]\n});\n\nlet range = document.getElementById('range');\ndocument.getElementById('btn1').onclick = function() {\n    range.textContent = worksheets[0].selectedCell;\n}\n</script>\n</html>\n```"
  },
  {
    "path": "docs/jspreadsheet/docs/history.md",
    "content": "title: Jspreadsheet: Undo & Redo History\nkeywords: Jspreadsheet, Jexcel, JavaScript data grid, undo, redo, Ctrl+Z, Ctrl+Y, spreadsheet change tracking, action history, data revisions, Jspreadsheet history\ndescription: Discover how to manage action history in Jspreadsheet with undo and redo features, allowing easy tracking of data grid changes and control over modifications.\n\n# History Tracker\n\nThe Jspreadsheet History Tracker captures all changes within each spreadsheet, enabling users to undo (CTRL+Z) and redo (CTRL+Y) actions. In Jspreadsheet CE, the history tracker operates independently for each worksheet.\n\n{.pro}\n> #### What you can find on the Pro Version\n>\n> In the Pro version of Jspreadsheet, the unified history tracker supports cross-spreadsheet calculations and integration across all spreadsheets displayed on the same screen.\n> \n> \\\n> [Learn more](https://jspreadsheet.com/docs/history){.button}\n\n\n## Documentation\n\n### Methods\n\nThe undo and redo methods are normally invoked by the CTRL+Z, CTRL+Y keyboard shortcut. The following methods can be called programmatically, as follows:\n\n| Method   | Description                                                                     |\n|----------|---------------------------------------------------------------------------------|\n| `undo()`   | Undo the last worksheet change.<br/>`worksheet.undo() : void`                   |\n| `redo()`   | Redo the most recent worksheet change.<br/>`worksheet.redo() : void`            |\n\n### Events\n\nEvents related to the history changes tracker.\n\n| Event    | Description                                                                                                              |\n|----------|--------------------------------------------------------------------------------------------------------------------------|\n| `onredo` | `onredo(worksheet: Object, info: Object) : null`<br/>The info array contains all necessary information about the action. |\n| `onundo` | `onundo(worksheet: Object, info: Object) : null`<br/>The info array contains all necessary information about the action. |\n\n\n### History Tracker State\n\nUse the history tracker with caution. You can turn it off temporarily by setting `worksheet.ignoreHistory` to true.\n\n## Examples\n\n### Controlling the changes programmatically\n\nAs explained above, the history actions are available on the spreadsheet level. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<p><input type=\"button\" value=\"Undo\" id=\"btn1\" />\n<input type=\"button\" value=\"Redo\" id=\"btn2\" />\n\n<script>\n// Create the spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6, 6],\n    }],\n});\n\ndocument.getElementById(\"btn1\").onclick = () => spreadsheet[0].undo()\ndocument.getElementById(\"btn2\").onclick = () => spreadsheet[0].redo()\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet minDimensions={[5,5]} />\n            </Spreadsheet>\n            <button onClick={() => spreadsheet.current[0].undo()}>Undo</button>\n            <button onClick={() => spreadsheet.current[0].redo()}>Redo</button>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :min-dimensions=\"[8,8]\" />\n    </Spreadsheet>\n    <input type=\"button\" value=\"Undo\" @click=\"undo\" />\n    <input type=\"button\" value=\"Redo\" @click=\"redo\" />\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const spreadsheet = ref(null);\n\n        const undo = () => {\n            if (spreadsheet.value) {\n                spreadsheet.value.current[0].undo();\n            }\n        };\n\n        const redo = () => {\n            if (spreadsheet.value) {\n                spreadsheet.value.current[0].redo();\n            }\n        };\n\n        return {\n            spreadsheet,\n            undo,\n            redo,\n        };\n    },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <input type=\"button\" value=\"Undo\" (click)=\"this.worksheets[0].undo()\" />\n        <input type=\"button\" value=\"Redo\" (click)=\"this.worksheets[0].redo()\" />`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [8, 8],\n            }],\n        });\n    }\n}\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/images.md",
    "content": "title: Data Grid Cell Images\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, spreadsheet images, cell images, floating images, image handling, image customization, image placement, image integration\ndescription: Enhance data representation by embedding images directly in your data grid cells.\n\n# Spreadsheet Images\nThis section demonstrates how to embed images directly into data grid cells.  \n\n\n{.pro}\n> ### What you can find on the Pro Version\n> The Pro version of Jspreadsheet supports **floating images** compatible with Excel and Google Sheets, along with programmatic methods and events.\\\n> \\\n> [Learn more](https://jspreadsheet.com/docs/media){.button}\n\n\n## Examples\n\n\n### Column type image editor\n\nConfigure an image upload editor to insert an image in every cell across an entire column. \n\n{.small}\nDouble-click in the image cell to upload a new image.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Test Icon', 'data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjRweCIgZmlsbD0iIzVmNjM2OCI+PHBhdGggZD0iTTI4MC0yODBoMTYwdi0xNjBIMjgwdjE2MFptMjQwIDBoMTYwdi0xNjBINTIwdjE2MFpNMjgwLTUyMGgxNjB2LTE2MEgyODB2MTYwWm0yNDAgMGgxNjB2LTE2MEg1MjB2MTYwWk0yMDAtMTIwcS0zMyAwLTU2LjUtMjMuNVQxMjAtMjAwdi01NjBxMC0zMyAyMy41LTU2LjVUMjAwLTg0MGg1NjBxMzMgMCA1Ni41IDIzLjVUODQwLTc2MHY1NjBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBabTAtODBoNTYwdi01NjBIMjAwdjU2MFptMC01NjB2NTYwLTU2MFoiLz48L3N2Zz4='],\n        ],\n        minDimensions: [2,4],\n        columns: [\n            { type:'text', width:300, title:'Title' },\n            { type:'image', width:120, title:'Image' },\n        ],\n    }],\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    const spreadsheet = useRef();\n\n    const worksheets = [{\n        data: [\n            ['Test Icon', 'data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjRweCIgZmlsbD0iIzVmNjM2OCI+PHBhdGggZD0iTTI4MC0yODBoMTYwdi0xNjBIMjgwdjE2MFptMjQwIDBoMTYwdi0xNjBINTIwdjE2MFpNMjgwLTUyMGgxNjB2LTE2MEgyODB2MTYwWm0yNDAgMGgxNjB2LTE2MEg1MjB2MTYwWk0yMDAtMTIwcS0zMyAwLTU2LjUtMjMuNVQxMjAtMjAwdi01NjBxMC0zMyAyMy41LTU2LjVUMjAwLTg0MGg1NjBxMzMgMCA1Ni41IDIzLjVUODQwLTc2MHY1NjBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBabTAtODBoNTYwdi01NjBIMjAwdjU2MFptMC01NjB2NTYwLTU2MFoiLz48L3N2Zz4='],\n        ],\n        minDimensions: [2,4],\n        columns: [\n            { type:'text', width:300, title:'Title' },\n            { type:'image', width:120, title:'Image' },\n        ],\n    }]\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} worksheets={worksheets} />\n        </>\n    )\n}\n```\n```vue\n<template>\n  <div>\n    <Spreadsheet\n      ref=\"spreadsheet\"\n      :worksheets=\"worksheets\"\n    />\n  </div>\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet } from 'jspreadsheet-ce';\nimport 'jsuites/dist/jsuites.css';\nimport 'jspreadsheet-ce/dist/spreadsheet.css';\n\nexport default {\n  name: 'App',\n  components: {\n    Spreadsheet,\n  },\n  setup() {\n    const spreadsheet = ref(null);\n\n    const worksheets = ref([\n      {\n        data: [\n            ['Test Icon', 'data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjRweCIgZmlsbD0iIzVmNjM2OCI+PHBhdGggZD0iTTI4MC0yODBoMTYwdi0xNjBIMjgwdjE2MFptMjQwIDBoMTYwdi0xNjBINTIwdjE2MFpNMjgwLTUyMGgxNjB2LTE2MEgyODB2MTYwWm0yNDAgMGgxNjB2LTE2MEg1MjB2MTYwWk0yMDAtMTIwcS0zMyAwLTU2LjUtMjMuNVQxMjAtMjAwdi01NjBxMC0zMyAyMy41LTU2LjVUMjAwLTg0MGg1NjBxMzMgMCA1Ni41IDIzLjVUODQwLTc2MHY1NjBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBabTAtODBoNTYwdi01NjBIMjAwdjU2MFptMC01NjB2NTYwLTU2MFoiLz48L3N2Zz4='],\n        ],\n        minDimensions: [2,4],\n        columns: [\n            { type:'text', width:300, title:'Title' },\n            { type:'image', width:120, title:'Image' },\n        ],\n      },\n    ]);\n\n    return {\n      spreadsheet,\n      worksheets,\n    };\n  },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n              data: [\n                  ['Test Icon', 'data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjRweCIgZmlsbD0iIzVmNjM2OCI+PHBhdGggZD0iTTI4MC0yODBoMTYwdi0xNjBIMjgwdjE2MFptMjQwIDBoMTYwdi0xNjBINTIwdjE2MFpNMjgwLTUyMGgxNjB2LTE2MEgyODB2MTYwWm0yNDAgMGgxNjB2LTE2MEg1MjB2MTYwWk0yMDAtMTIwcS0zMyAwLTU2LjUtMjMuNVQxMjAtMjAwdi01NjBxMC0zMyAyMy41LTU2LjVUMjAwLTg0MGg1NjBxMzMgMCA1Ni41IDIzLjVUODQwLTc2MHY1NjBxMCAzMy0yMy41IDU2LjVUNzYwLTEyMEgyMDBabTAtODBoNTYwdi01NjBIMjAwdjU2MFptMC01NjB2NTYwLTU2MFoiLz48L3N2Zz4='],\n              ],\n              minDimensions: [2,4],\n              columns: [\n                  { type:'text', width:300, title:'Title' },\n                  { type:'image', width:120, title:'Image' },\n              ],\n            }],\n        });\n    }\n}\n```\n\n## Related Image Tools\n\n### Free Image Components\n- [JavaScript Image Cropper](https://jsuites.net/docs/image-cropper) - Crop, rotate, and edit images with brightness/contrast controls\n- [JavaScript Image Slider](https://jsuites.net/docs/image-slider) - Display image galleries and carousels\n- [LemonadeJS Image Cropper](https://lemonadejs.com/docs/plugins/image-cropper) - Reactive image cropper component\n\nThese free components complement Jspreadsheet CE for building complete image management solutions.\n"
  },
  {
    "path": "docs/jspreadsheet/docs/javascript-calendar.md",
    "content": "title: Javascript Calendar and Date Operations\nkeywords: Jspreadsheet, data grid, javascript, excel-like calendar, spreadsheet calendar, javascript calendar, data grid calendar, advanced calendar, calendar events, calendar settings, interactive data grid, customizable calendar, dynamic calendar, event-driven calendar, calendar integration\ndescription: Explore the data grid calendar input type, date operations, formatting, validations, events, and other related date operations and calendar features available in Jspreadsheet.\n\n# Date operations\n\nThis section explains how to handle dates in Jspreadsheet, focusing on the calendar input type and various date operations. You can work with dates by applying a mask to text input fields or using the dedicated calendar type.\n\n**Key Differences:**\n\n- **Text type with date mask**: This option supports date-related calculations, copy-paste functionality, and fill-handle operations similar to Excel. It allows dates to be treated as numerical values, making it ideal for performing operations.\n- **Calendar Type**: This option provides a calendar picker for selecting dates but stores the value as a string. It’s more focused on user-friendly input rather than complex date calculations.\n\nThis section covers the following topics: \n\n- **Configuring the calendar picker**: How to customize the calendar editor for user-friendly date selection.\n- **Validating date values**: Set rules to ensure date inputs are valid based on the values in other columns.\n- **Performing date calculations**: Use formulas to calculate date differences, add days, or perform other date-related operations.\n- **Formatting dates with new tokens**: Learn how to use tokens to format dates in different styles.\n- **Localization of date strings**: Translate date formats and related strings to match different locales and languages.\n\n## Documentation\n\n### Calendar editor\n\nThe [JavaScript calendar](https://jsuites.net/docs/javascript-calendar) from [jsuites.net](https://jsuites.net/docs) is a highly flexible and responsive plugin that offers numerous configurations to adapt to various application needs. For more information, refer to its [documentation](https://jsuites.net/docs/javascript-calendar).\n\n| Parameter                             | Description                                                                                       |\n|---------------------------------------|---------------------------------------------------------------------------------------------------|\n| type: `default \\| year-month-picker`  | Render type. `Default: default`                                                                   |\n| validRange: `[String, String]`        | Disables the dates out of the defined range. `[Initial date, Final date]`                         |\n| startingDay: `Number`                 | The day of the week the calendar starts on (0 for Sunday - 6 for Saturday). `Default: 0 (Sunday)` |\n| format: `String`                      | Date format. `Default: YYYY-MM-DD`                                                                |\n| readonly: `Boolean`                   | Calendar input is readonly. `Default: false`                                                      |\n| today: `Boolean`                      | Select today's date automatically when no date value is defined. `Default: true`                  |\n| time: `Boolean`                       | Show hour and minute dropdown. `Default: false`                                                   |\n| resetButton: `Boolean`                | Enabled reset button. `Default: true`                                                             |\n| placeholder: `String`                 | Default place holder for the calendar input.                                                      |\n| fullscreen: `Boolean`                 | Open in fullscreen mode.                                                                          |\n\n\n\n## Examples\n\n### Cells with Date Format\n\nThe example below demonstrates applying a date format to the results of a formula.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create worksheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n        data: [\n            [ '=NOW()' ]\n        ],\n        cells: {\n            A1: { format: 'dd/mm/yyyy' },\n        }\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [ '=NOW()', ]\n    ]\n    // Data grid cell definitions\n    const cells = {\n        A1: { format: 'dd/mm/yyyy' },\n    }\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} cells={cells} minDimensions={[4,4]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :cells=\"cells\" :minDimensions=\"[4,4]\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        // Data\n        const data = [\n            [ '=NOW()' ]\n        ]\n        // Data grid cell definitions\n        const cells = {\n            A1: { format: 'dd/mm/yyyy' },\n        }\n\n        return {\n            data,\n            cells\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [4,4],\n                data: [\n                    [ '=NOW()' ]\n                ],\n                cells: {\n                    A1: { format: 'dd/mm/yyyy' },\n                }\n            }]\n        });\n    }\n}\n```\n \n\n \n\n### Column Calendar Customization\n\nIn the example below, we configure the calendar column type as a year-month picker only. \n\n \n\n\n\n\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n        data: [\n            [ '2021-01-01', '', '', '' ]\n        ],\n        columns: [  \n            { type: 'calendar', options: { type: 'year-month-picker', format: 'Mon/YYYY' } },\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [ '2021-01-01', '', '', '' ]\n    ];\n    // Data grid cell definitions\n    const columns = [\n        { type: 'calendar', options: { type: 'year-month-picker', format: 'Mon/YYYY' } },\n    ];\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} minDimensions={[4,4]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :data=\"data\" :columns=\"columns\" :minDimensions=\"[4,4]\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n\n// Data\nconst data = ref([\n  [ '2021-01-01', '', '', '' ]\n]);\n\n// Data grid cell definitions\nconst columns = ref([\n  { type: 'calendar', options: { type: 'year-month-picker', format: 'Mon/YYYY' } },\n]);\n\n// Optional: spreadsheet ref to match template\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [4,4],\n                data: [\n                    [ '2021-01-01', '', '', '' ]\n                ],\n                columns: [\n                    { type: 'calendar', options: { type: 'year-month-picker', format: 'Mon/YYYY' } },\n                ]\n            }]\n        });\n    }\n}\n```\n\n\n### Calendar Date Validations\n\nIn the example below, `filterOptions` is used to overwrite the column configuration `validRange` just before the edit. The rule is that the last column cannot have a date after the previous column date. Additionally, the onbeforechange event behavior blocks the user from pasting or programmatically breaking this rule. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet filterOptions = function(worksheet, cell, x, y, value, config) {\n    // Get the value of the previous column\n    let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n    // Set a valid range to avoid past dates to be selected\n    config.options.validRange = [ previousColumnValue, null ];\n    // Customized options\n    return config;\n}\n\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Roger Taylor', '2019-01-01', '2019-03-01' ],\n            ['Bob Shiran', '2019-04-03', '2019-05-03'],\n            ['Daniel P.', '2018-12-03', '2018-12-03'],\n            ['Karen Roberts', '2018-12-03', '2019-01-03'],\n        ],\n        columns: [\n            {\n                type:'text',\n                title:'Name',\n                width:'300px',\n            },\n            {\n                type:'calendar',\n                title:'From',\n                options: { format:'DD/MM/YYYY' },\n                width:'150px',\n            },\n            {\n                type:'calendar',\n                title:'To',\n                options: { format:'DD/MM/YYYY' },\n                filterOptions: filterOptions,\n                width:'150px',\n            },\n        ],\n        worksheetName: 'Rules',\n    }],\n    onbeforechange: function(worksheet, cell, x, y, value) {\n        // Valid only for second column\n        if (x == 2 && value) {\n            // Get the value of the previous column\n            let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n            if (previousColumnValue > value) {\n                cell.style.border = '1px solid red';\n                // Return nothing\n                return '';\n            } else {\n                cell.style.border = '';\n            }\n        }\n        return value;\n    }\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Filter option will change the column settings just before the edition\nconst filterOptions = (worksheet, cell, x, y, value, config) => {\n    // Get the value of the previous column\n    let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n    // Set a valid range to avoid past dates to be selected\n    config.options.validRange = [ previousColumnValue, null ];\n    // Customized options\n    return config;\n}\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Roger Taylor', '2019-01-01', '2019-03-01' ],\n        ['Bob Shiran', '2019-04-03', '2019-05-03'],\n        ['Daniel P.', '2018-12-03', '2018-12-03'],\n        ['Karen Roberts', '2018-12-03', '2019-01-03'],\n    ];\n    // Data grid cell definitions\n    const columns = [\n        {\n            type:'text',\n            title:'Name',\n            width:'300px',\n        },\n        {\n            type:'calendar',\n            title:'From',\n            options: { format:'DD/MM/YYYY' },\n            width:'150px',\n        },\n        {\n            type:'calendar',\n            title:'To',\n            options: { format:'DD/MM/YYYY' },\n            filterOptions: filterOptions,\n            width:'150px',\n        },\n    ];\n    // Event\n    const onbeforechange = (worksheet, cell, x, y, value) => {\n        // Valid only for second column\n        if (x == 2 && value) {\n            // Get the value of the previous column\n            let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n            if (previousColumnValue > value) {\n                cell.style.border = '1px solid red';\n                // Return nothing\n                return '';\n            } else {\n                cell.style.border = '';\n            }\n        }\n        return value;\n    }\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet} onbeforechange={onbeforechange}>\n            <Worksheet worksheetName={\"Rules\"} data={data} columns={columns} minDimensions={[4,4]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <div>\n    <Spreadsheet\n      ref=\"spreadsheet\"\n      :onbeforechange=\"onBeforeChange\"\n    >\n      <Worksheet\n        :data=\"data\"\n        :columns=\"columns\"\n        :min-dimensions=\"[4, 4]\"\n      />\n    </Spreadsheet>\n    <div ref=\"log\"></div>\n    <button @click=\"setMerge('A3', 2, 3)\">setMerge('A3', 2, 3)</button>\n    <button @click=\"removeMerge('A3')\">removeMerge('A3')</button>\n    <button @click=\"getMergedCells\">Get all merged cells</button>\n    <button @click=\"destroyAllMergedCells\">Destroy all merged</button>\n  </div>\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from '@jspreadsheet-ce/vue';\nimport 'jsuites/dist/jsuites.css';\nimport 'jspreadsheet-ce/dist/jspreadsheet-ce.css';\n\nexport default {\n  name: 'App',\n  components: {\n    Spreadsheet,\n    Worksheet,\n  },\n  setup() {\n    const spreadsheet = ref(null);\n    const log = ref(null);\n\n    const data = ref([\n      ['Roger Taylor', '2019-01-01', '2019-03-01'],\n      ['Bob Shiran', '2019-04-03', '2019-05-03'],\n      ['Daniel P.', '2018-12-03', '2018-12-03'],\n      ['Karen Roberts', '2018-12-03', '2019-01-03'],\n    ]);\n\n    const columns = ref([\n      {\n        type: 'text',\n        title: 'Name',\n        width: '300px',\n      },\n      {\n        type: 'calendar',\n        title: 'From',\n        options: { format: 'DD/MM/YYYY' },\n        width: '150px',\n      },\n      {\n        type: 'calendar',\n        title: 'To',\n        options: { format: 'DD/MM/YYYY' },\n        filterOptions: (worksheet, cell, x, y, value, config) => {\n          let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n          config.options.validRange = [previousColumnValue, null];\n          return config;\n        },\n        width: '150px',\n      },\n    ]);\n\n    const setMerge = (cell, rows, cols) => {\n      spreadsheet.value[0].setMerge(cell, rows, cols);\n    };\n\n    const removeMerge = (cell) => {\n      spreadsheet.value[0].removeMerge(cell);\n    };\n\n    const getMergedCells = () => {\n      log.value.innerText = JSON.stringify(spreadsheet.value[0].getMerge());\n    };\n\n    const destroyAllMergedCells = () => {\n      spreadsheet.value[0].destroyMerge();\n    };\n\n    const onBeforeChange = (worksheet, cell, x, y, value) => {\n      if (x === 2 && value) {\n        let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n        if (previousColumnValue > value) {\n          cell.style.border = '1px solid red';\n          return '';\n        } else {\n          cell.style.border = '';\n        }\n      }\n      return value;\n    };\n\n    return {\n      spreadsheet,\n      log,\n      data,\n      columns,\n      setMerge,\n      removeMerge,\n      getMergedCells,\n      destroyAllMergedCells,\n      onBeforeChange,\n    };\n  },\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Filter the options in real time before opening the editor\nconst filterOptions = function(worksheet, cell, x, y, value, config) {\n    // Get the value of the previous column\n    let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n    // Set a valid range to avoid past dates to be selected\n    config.options.validRange = [ previousColumnValue, null ];\n    // Customized options\n    return config;\n}\n\n// Create component\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Roger Taylor', '2019-01-01', '2019-03-01' ],\n                    ['Bob Shiran', '2019-04-03', '2019-05-03'],\n                    ['Daniel P.', '2018-12-03', '2018-12-03'],\n                    ['Karen Roberts', '2018-12-03', '2019-01-03'],\n                ],\n                columns: [\n                    {\n                        type:'text',\n                        title:'Name',\n                        width:'300px',\n                    },\n                    {\n                        type:'calendar',\n                        title:'From',\n                        options: { format:'DD/MM/YYYY' },\n                        width:'150px',\n                    },\n                    {\n                        type:'calendar',\n                        title:'To',\n                        options: { format:'DD/MM/YYYY' },\n                        filterOptions: filterOptions,\n                        width:'150px',\n                    },\n                ],\n                worksheetName: 'Rules',\n            }],\n            onbeforechange: function(worksheet, cell, x, y, value) {\n                // Valid only for second column\n                if (x == 2 && value) {\n                    // Get the value of the previous column\n                    let previousColumnValue = worksheet.getValueFromCoords(x - 1, y);\n                    if (previousColumnValue > value) {\n                        cell.style.border = '1px solid red';\n                        // Return nothing\n                        return '';\n                    } else {\n                        cell.style.border = '';\n                    }\n                }\n                return value;\n            }\n        });\n    }\n}\n```\n \n\n \n\n### International Calendar Configurations\n\nTo translate the text in the calendar plugin, you can include the `setDictionary` method as below. \n\n```html\n<div id=\"spreadsheet\"></div>\n\n<script>\n//The dictionary can be defined as below\nlet dictionary = {\n    // Other entries (...)\n    // Calendar specific entries\n    'Jan': 'Jan',\n    'Feb': 'Fev',\n    'Mar': 'Mar',\n    'Apr': 'Abr',\n    'May': 'Mai',\n    'Jun': 'Jun',\n    'Jul': 'Jul',\n    'Aug': 'Ago',\n    'Sep': 'Set',\n    'Oct': 'Out',\n    'Nov': 'Nov',\n    'Dec': 'Dez',\n    'January': 'Janeiro',\n    'February': 'Fevereiro',\n    'March': 'Março',\n    'April': 'Abril',\n    'May': 'Maio',\n    'June': 'Junho',\n    'July': 'Julho',\n    'August': 'Agosto',\n    'September': 'Setembro',\n    'October': 'Outubro',\n    'November': 'Novembro',\n    'December': 'Dezembro',\n    'Sunday': 'Domingo',\n    'Monday': 'Segunda',\n    'Tuesday': 'Terca',\n    'Wednesday': 'Quarta',\n    'Thursday': 'Quinta',\n    'Friday': 'Sexta',\n    'Saturday': 'Sabado',\n    'Done': 'Feito',\n    'Reset': 'Apagar',\n    'Update': 'Atualizar',\n}\n\n// Send dictionary to the JSS scope\njspreadsheet.setDictionary(dictionary);\n\n// Create worksheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n        columns: [  \n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n        ]\n    }]\n});\n</script>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Send dictionary to the JSS scope\njspreadsheet.setDictionary({\n    // Other entries (...)\n    // Calendar specific entries\n    'Jan': 'Jan',\n    'Feb': 'Fev',\n    'Mar': 'Mar',\n    'Apr': 'Abr',\n    'May': 'Mai',\n    'Jun': 'Jun',\n    'Jul': 'Jul',\n    'Aug': 'Ago',\n    'Sep': 'Set',\n    'Oct': 'Out',\n    'Nov': 'Nov',\n    'Dec': 'Dez',\n    'January': 'Janeiro',\n    'February': 'Fevereiro',\n    'March': 'Março',\n    'April': 'Abril',\n    'May': 'Maio',\n    'June': 'Junho',\n    'July': 'Julho',\n    'August': 'Agosto',\n    'September': 'Setembro',\n    'October': 'Outubro',\n    'November': 'Novembro',\n    'December': 'Dezembro',\n    'Sunday': 'Domingo',\n    'Monday': 'Segunda',\n    'Tuesday': 'Terca',\n    'Wednesday': 'Quarta',\n    'Thursday': 'Quinta',\n    'Friday': 'Sexta',\n    'Saturday': 'Sabado',\n    'Done': 'Feito',\n    'Reset': 'Apagar',\n    'Update': 'Atualizar',\n});\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        { type: 'calendar', options: { startingDay: 1 } },\n        { type: 'calendar', options: { startingDay: 1 } },\n        { type: 'calendar', options: { startingDay: 1 } },\n        { type: 'calendar', options: { startingDay: 1 } },\n    ]\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} minDimensions={[4,4]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Send dictionary to the JSS scope\njspreadsheet.setDictionary({\n    // Other entries (...)\n    // Calendar specific entries\n    'Jan': 'Jan',\n    'Feb': 'Fev',\n    'Mar': 'Mar',\n    'Apr': 'Abr',\n    'May': 'Mai',\n    'Jun': 'Jun',\n    'Jul': 'Jul',\n    'Aug': 'Ago',\n    'Sep': 'Set',\n    'Oct': 'Out',\n    'Nov': 'Nov',\n    'Dec': 'Dez',\n    'January': 'Janeiro',\n    'February': 'Fevereiro',\n    'March': 'Março',\n    'April': 'Abril',\n    'May': 'Maio',\n    'June': 'Junho',\n    'July': 'Julho',\n    'August': 'Agosto',\n    'September': 'Setembro',\n    'October': 'Outubro',\n    'November': 'Novembro',\n    'December': 'Dezembro',\n    'Sunday': 'Domingo',\n    'Monday': 'Segunda',\n    'Tuesday': 'Terca',\n    'Wednesday': 'Quarta',\n    'Thursday': 'Quinta',\n    'Friday': 'Sexta',\n    'Saturday': 'Sabado',\n    'Done': 'Feito',\n    'Reset': 'Apagar',\n    'Update': 'Atualizar',\n});\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        // Data\n        const data = [\n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n            { type: 'calendar', options: { startingDay: 1 } },\n        ];\n\n        return {\n            data,\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n//The dictionary can be defined as below\nconst dictionary = {\n    // Other entries (...)\n    // Calendar specific entries\n    'Jan': 'Jan',\n    'Feb': 'Fev',\n    'Mar': 'Mar',\n    'Apr': 'Abr',\n    'May': 'Mai',\n    'Jun': 'Jun',\n    'Jul': 'Jul',\n    'Aug': 'Ago',\n    'Sep': 'Set',\n    'Oct': 'Out',\n    'Nov': 'Nov',\n    'Dec': 'Dez',\n    'January': 'Janeiro',\n    'February': 'Fevereiro',\n    'March': 'Março',\n    'April': 'Abril',\n    'May': 'Maio',\n    'June': 'Junho',\n    'July': 'Julho',\n    'August': 'Agosto',\n    'September': 'Setembro',\n    'October': 'Outubro',\n    'November': 'Novembro',\n    'December': 'Dezembro',\n    'Sunday': 'Domingo',\n    'Monday': 'Segunda',\n    'Tuesday': 'Terca',\n    'Wednesday': 'Quarta',\n    'Thursday': 'Quinta',\n    'Friday': 'Sexta',\n    'Saturday': 'Sabado',\n    'Done': 'Feito',\n    'Reset': 'Apagar',\n    'Update': 'Atualizar',\n}\n\n// Send dictionary to the JSS scope\njspreadsheet.setDictionary(dictionary);\n\n// Create component\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [4,4],\n                columns: [\n                    { type: 'calendar', options: { startingDay: 1 } },\n                    { type: 'calendar', options: { startingDay: 1 } },\n                    { type: 'calendar', options: { startingDay: 1 } },\n                    { type: 'calendar', options: { startingDay: 1 } },\n                ]\n            }]\n        });\n    }\n}\n```\n \n\n## Calendar Component Library\n\n### CalendarJS - Full Calendar & Scheduling Solution\n[CalendarJS](https://calendarjs.com/docs/calendar) is a complete calendar and scheduling platform (like Google Calendar) for building calendar applications outside the spreadsheet. Perfect for:\n- **Event scheduling systems** with drag & drop\n- **Appointment calendars** and booking systems\n- **Timeline displays** for project management\n- **Date pickers and filters** for your spreadsheet data\n- **Resource scheduling** and availability tracking\n\n**CalendarJS Includes:**\n- **Calendar** - Date picker component with range selection (3.18KB)\n- **Schedule** - Event scheduler with day/week views & drag-drop (4.2KB)\n- **Timeline** - Chronological event display\n- Advanced keyboard navigation with ARIA support\n- Excel-compatible date formats\n\n**[Explore CalendarJS →](https://calendarjs.com/docs/calendar)** | **[View Schedule Component →](https://calendarjs.com/docs/schedule)**\n\n---\n\n### LemonadeJS Calendar - Reactive Date Picker\nFor reactive applications, [LemonadeJS Calendar](https://lemonadejs.net/docs/plugins/calendar) provides two-way data binding with your spreadsheet state.\n\n**Use Cases:**\n- Reactive filters that automatically update the spreadsheet\n- Real-time date synchronization across components\n- Vue/React-like reactive patterns\n\n**[View LemonadeJS Calendar Documentation →](https://lemonadejs.net/docs/plugins/calendar)**\n\n---\n\n### Jspreadsheet Pro - Enhanced Calendar Features\nUpgrade to [Jspreadsheet Pro](https://jspreadsheet.com/docs/date) for enhanced calendar functionality including:\n- LemonadeJS Calendar integration\n- Improved accessibility and ARIA support\n- Additional date operations and formulas\n- Advanced customization options\n\n**[View Jspreadsheet Pro Date Documentation →](https://jspreadsheet.com/docs/date)**\n\n---\n\n### Comparison: Calendar Options for Jspreadsheet CE\n\n| Feature                    | Jspreadsheet CE Date | CalendarJS | LemonadeJS Calendar | Jspreadsheet Pro |\n|----------------------------|---------------------|------------|---------------------|------------------|\n| **In-cell date editing**   | ✓                   | -          | -                   | ✓                |\n| **External date picker**   | -                   | ✓          | ✓                   | ✓                |\n| **Reactive binding**       | -                   | -          | ✓                   | -                |\n| **Excel date format**      | ✓                   | ✓          | ✓                   | ✓                |\n| **ARIA support**           | Basic               | ✓✓         | ✓✓                  | ✓✓               |\n| **Range selection**        | ✓                   | ✓          | ✓                   | ✓                |\n| **Best for**               | CE grid cells       | Standalone | Reactive apps       | Pro grid cells   |\n\n---\n\n## Related content\n\n**Calendar Components:**\n- [CalendarJS](https://calendarjs.com/docs/calendar) - Standalone calendar and date picker\n- [LemonadeJS Calendar](https://lemonadejs.net/docs/plugins/calendar) - Reactive calendar component\n- [jSuites Calendar](https://jsuites.net/docs/javascript-calendar) - Calendar used by Jspreadsheet CE\n- [Jspreadsheet Pro Date](https://jspreadsheet.com/docs/date) - Enhanced calendar features\n\n**Related Jspreadsheet CE Features:**\n- [Cell Formatting](./format) - Format dates in cells\n- [Custom Formulas](./custom-formulas) - Create date calculations\n- [Headers](./headers) - Column headers and titles\n\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/javascript-dropdown.md",
    "content": "title: JavaScript Dropdown and Autocomplete Editors\nkeywords: Jspreadsheet, data grid, JavaScript dropdown, autocomplete, Excel-like dropdown, dynamic dropdown, dropdown customization, interactive grid\ndescription: Learn how to implement and configure dropdown and autocomplete editors in Jspreadsheet, including dynamic settings and conditional logic for enhanced data input.\n\n# JavaScript Dropdown\nJspreadsheet CE provides a versatile dropdown column type with features like:\n\n- Dropdowns from arrays, JSON, or key-value objects\n- Multiple selection and searchable options\n- Custom rendering styles, including icons and grouped options\n\n## Dropdown Component Library\n\n### jSuites Dropdown - CE Component\nJspreadsheet CE uses [jSuites Dropdown](https://jsuites.net/docs/dropdown) as the underlying dropdown component:\n- Lightweight vanilla JavaScript\n- Simple API\n- No framework dependencies\n- Perfect for basic dropdown needs\n\n**[View jSuites Dropdown Documentation →](https://jsuites.net/docs/dropdown)**\n\n### Upgrade to Jspreadsheet Pro\n[Jspreadsheet Pro](https://jspreadsheet.com/docs/dropdown-and-autocomplete) offers advanced dropdown features not available in CE:\n- **Conditional Dropdowns:** Options change based on other cell values\n- **Dynamic Ranges:** Link dropdowns to cell ranges (e.g., Sheet1!A1:A4)\n- **Remote Search:** Autocomplete from backend APIs with JWT support\n- **Enhanced Performance:** Uses LemonadeJS Dropdown (3x faster than jSuites)\n- **Better Accessibility:** Improved ARIA support and keyboard navigation\n- **Professional Support:** Commercial license with dedicated support\n\n**[Explore Jspreadsheet Pro Features →](https://jspreadsheet.com/docs/dropdown-and-autocomplete)**\n\n### LemonadeJS Dropdown - Standalone Component\nFor using dropdowns outside spreadsheets, [LemonadeJS Dropdown](https://lemonadejs.net/docs/plugins/dropdown) offers:\n- High-performance standalone dropdown\n- Framework integration (Vue, React, Angular)\n- This is what Jspreadsheet Pro uses internally\n- Perfect for forms and custom applications\n\n**[View LemonadeJS Dropdown →](https://lemonadejs.net/docs/plugins/dropdown)**\n\n---\n\n### Feature Comparison: CE vs Pro\n\n| Feature | CE (jSuites) | Pro (LemonadeJS) |\n|---------|--------------|------------------|\n| Basic Dropdowns | ✓ | ✓ |\n| Autocomplete | ✓ | ✓✓ Enhanced |\n| Multiple Selection | ✓ | ✓ |\n| Images/Icons | ✓ | ✓ |\n| **Conditional Dropdowns** | ❌ | ✓ Pro Only |\n| **Dynamic Ranges** | ❌ | ✓ Pro Only |\n| **Remote Search** | ❌ | ✓ Pro Only |\n| Performance | Good | ✓✓ 3x Faster |\n| Accessibility | Basic | ✓✓ ARIA Enhanced |\n| License | MIT | Commercial |\n| Support | Community | ✓ Dedicated |\n\n**[Upgrade to Jspreadsheet Pro →](https://jspreadsheet.com/docs/dropdown-and-autocomplete)**\n\n---\n\n\n## Documentation\n\n### Dropdown Settings\n\nThe Jspreadsheet CE supports various attributes for the dropdown column type.\n\n| Property                | Description                                                                    |\n|-------------------------|--------------------------------------------------------------------------------|\n| `source: Items[]`       | Array of items to populate the dropdown.                                       |\n| `url: String`           | Fetch dropdown data from a remote URL.                                         |\n| `multiple: Boolean`     | Enable selection of multiple options.                                          |\n| `delimiter: String`     | Define the delimiter for multiple selections. Default: `';'`.                 |\n| `autocomplete: Boolean` | Enable autocomplete for the dropdown.                                          |\n\n\n#### Extended Options\n\nExtended options can be defined using the `options` property within the column.\n\n| Property                 | Description                                                         |\n|--------------------------|---------------------------------------------------------------------|\n| `type: String`           | Render type: `default` \\| `picker` \\| `searchbar`.                 |\n| `placeholder: String`    | Placeholder text for instructions.                                 |\n\n\n### Properties of an Item\n\nAn object with the following attributes defines each option in the dropdown:\n\n| Property            | Description                             |\n|---------------------|-----------------------------------------|\n| `id: mixed`         | Key of the item.                        |\n| `value: string`     | Value of the item.                      |\n| `title: string`     | Description of the item.                |\n| `image: string`     | Icon for the item.                      |\n| `group: string`     | Name of the group the item belongs to.  |\n| `synonym: array`    | Keywords to help find the item.         |\n| `disabled: boolean` | Indicates if the item is disabled.      |\n| `color: number`     | Color associated with the item.         |\n| `icon: string`      | Material icon keyword.                  |\n| `tooltip: string`   | Instructions shown on mouse over.       |\n\n\n## Examples\n\n### Autocomplete and Multiple Options\n\nThe example below demonstrates the first column with autocomplete enabled and multiple options active.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n            ['CA;US;GB', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n            ['CA;BR', 'Grains', 'No', '2018-11-10'],\n            ['BR', 'Pasta', 'Yes', '2019-01-12'],\n        ],\n        columns: [\n            { type:'dropdown', width:'200px', title:'Product Origin', url:'/jspreadsheet/countries', autocomplete:true, multiple:true },\n            { type:'text', width:'100px', title:'Description' },\n            { type:'dropdown', width:'100px', title:'Stock', source:['No','Yes'] },\n            { type:'calendar', width:'100px', title:'Best before', format:'DD/MM/YYYY' },\n        ],\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n        ['CA;US;GB', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Grains', 'No', '2018-11-10'],\n        ['BR', 'Pasta', 'Yes', '2019-01-12'],\n    ];\n    // Columns\n    const columns = [\n        {\n            type:'dropdown',\n            width:'300px',\n            title: 'Product Origin',\n            url:'/jspreadsheet/countries',\n            autocomplete:true,\n            multiple:true\n        },\n        {\n            type:'text',\n            width:'200px',\n            title:'Description'\n        },\n        {\n            type:'dropdown',\n            width:'150px',\n            title:'Stock',\n            source:['No','Yes']\n        },\n        {\n            type:'calendar',\n            width:'150px',\n            title:'Best before',\n            format:'DD/MM/YYYY'\n        }\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :data=\"data\" :columns=\"columns\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Data\nconst data = ref([\n  ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n  ['CA;US;GB', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n  ['CA;BR', 'Grains', 'No', '2018-11-10'],\n  ['BR', 'Pasta', 'Yes', '2019-01-12'],\n]);\n\n// Columns\nconst columns = ref([\n  {\n      type:'dropdown',\n      width:'300px',\n      title: 'Product Origin',\n      url:'/jspreadsheet/countries',\n      autocomplete:true,\n      multiple:true\n  },\n  {\n      type:'text',\n      width:'200px',\n      title:'Description'\n  },\n  {\n      type:'dropdown',\n      width:'150px',\n      title:'Stock',\n      source:['No','Yes']\n  },\n  {\n      type:'calendar',\n      width:'150px',\n      title:'Best before',\n      format:'DD/MM/YYYY'\n  }\n]);\n\n// Optional: spreadsheet ref to match template\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n                    ['CA;US;GB', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n                    ['CA;BR', 'Grains', 'No', '2018-11-10'],\n                    ['BR', 'Pasta', 'Yes', '2019-01-12'],\n                ],\n                columns: [\n                    { type:'dropdown', width:'300px', title:'Product Origin', url:'/jspreadsheet/countries', autocomplete:true, multiple:true },\n                    { type:'text', width:'200px', title:'Description' },\n                    { type:'dropdown', width:'150px', title:'Stock', source:['No','Yes'] },\n                    { type:'calendar', width:'150px', title:'Best before', format:'DD/MM/YYYY' },\n                ],\n            }]\n        });\n    }\n}\n```\n\n### Group, Images, and Render Options\n\nEnhance the user experience with a responsive and visually enriched data picker.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create a new spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            [1, 'Morning'],\n            [2, 'Morning'],\n            [3, 'Afternoon'],\n            [3, 'Evening'],\n        ],\n        columns: [\n            {\n                type:'dropdown',\n                title:'Category',\n                width:'300',\n                source:[\n                    { id:'1', name:'Jorge', image:'img/2.jpg', title:'Admin', group:'Secretary' },\n                    { id:'2', name:'Cosme Sergio', image:'img/2.jpg', title:'Teacher', group:'Docent' },\n                    { id:'3', name:'Rose Mary', image:'img/3.png', title:'Teacher', group:'Docent' },\n                    { id:'4', name:'Fernanda', image:'img/3.png', title:'Admin', group:'Secretary' },\n                    { id:'5', name:'Roger', image:'img/3.png', title:'Teacher', group:'Docent' },\n                ]\n            },\n            {\n                type:'dropdown',\n                title:'Working hours',\n                width:'200',\n                source:['Morning','Afternoon','Evening'],\n                options: { type:'picker' },\n            },\n        ],\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [1, 'Morning'],\n        [2, 'Morning'],\n        [3, 'Afternoon'],\n        [3, 'Evening'],\n    ];\n    // Columns\n    const columns = [\n        {\n            type:'dropdown',\n            title:'Category',\n            width:'300',\n            source:[\n                { id:'1', name:'Jorge', image:'img/2.jpg', title:'Admin', group:'Secretary' },\n                { id:'2', name:'Cosme Sergio', image:'img/2.jpg', title:'Teacher', group:'Docent' },\n                { id:'3', name:'Rose Mary', image:'img/3.png', title:'Teacher', group:'Docent' },\n                { id:'4', name:'Fernanda', image:'img/3.png', title:'Admin', group:'Secretary' },\n                { id:'5', name:'Roger', image:'img/3.png', title:'Teacher', group:'Docent' },\n            ]\n        },\n        {\n            type:'dropdown',\n            title:'Working hours',\n            width:'200',\n            source:['Morning','Afternoon','Evening'],\n            options: { type:'picker' },\n        }\n    ];\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :data=\"data\" :columns=\"columns\" />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Data\nconst data = ref([\n  [1, 'Morning'],\n  [2, 'Morning'],\n  [3, 'Afternoon'],\n  [3, 'Evening'],\n]);\n\n// Columns\nconst columns = ref([\n  {\n      type:'dropdown',\n      title:'Category',\n      width:'300',\n      source:[\n          { id:'1', name:'Jorge', image:'img/2.jpg', title:'Admin', group:'Secretary' },\n          { id:'2', name:'Cosme Sergio', image:'img/2.jpg', title:'Teacher', group:'Docent' },\n          { id:'3', name:'Rose Mary', image:'img/3.png', title:'Teacher', group:'Docent' },\n          { id:'4', name:'Fernanda', image:'img/3.png', title:'Admin', group:'Secretary' },\n          { id:'5', name:'Roger', image:'img/3.png', title:'Teacher', group:'Docent' },\n      ]\n  },\n  {\n      type:'dropdown',\n      title:'Working hours',\n      width:'200',\n      source:['Morning','Afternoon','Evening'],\n      options: { type:'picker' },\n  }\n]);\n\n// Optional: spreadsheet ref to match template\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-cw\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-cw/dist/jspreadsheet.css\";\n\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    [1, 'Morning'],\n                    [2, 'Morning'],\n                    [3, 'Afternoon'],\n                    [3, 'Evening'],\n                ],\n                columns: [\n                    {\n                        type:'dropdown',\n                        title:'Category',\n                        width:'300',\n                        source:[\n                            { id:'1', name:'Jorge', image:'img/2.jpg', title:'Admin', group:'Secretary' },\n                            { id:'2', name:'Cosme Sergio', image:'img/2.jpg', title:'Teacher', group:'Docent' },\n                            { id:'3', name:'Rose Mary', image:'img/3.png', title:'Teacher', group:'Docent' },\n                            { id:'4', name:'Fernanda', image:'img/3.png', title:'Admin', group:'Secretary' },\n                            { id:'5', name:'Roger', image:'img/3.png', title:'Teacher', group:'Docent' },\n                        ]\n                    },\n                    {\n                        type:'dropdown',\n                        title:'Working hours',\n                        width:'200',\n                        source:['Morning','Afternoon','Evening'],\n                        options: { type:'picker' },\n                    },\n                ],\n            }]\n        });\n    }\n}\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/merged-cells.md",
    "content": "title: Spreadsheet Merged Cells\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, merged cells, react merged cells, excel-like merged cells, spreadsheet merged cells, merge cells functionality, data grid cell merging\ndescription: Merged cells in Jspreadsheet allow combining multiple cells into one. This section covers the settings, methods, and events related to merging spreadsheet cells.\n\n# Merged cells\n\nThis section covers how to create and manage merged cells in Jspreadsheet to combine adjacent cells into one. It includes details on settings, methods, and events for merging, unmerging, programmatically handling merged ranges, and managing alignment and formatting.\n\n## Documentation\n\n### Methods\n\nThe following methods allow for the programmatic management of merged cells.\n\n| Method                  | Description                                                                                                                                                                                                                                                                                                                                                                                          |\n|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `setMerge`              | Sets merged cells based on a specified number of columns and rows.<br/>@param `cellName` - Name of a cell. If it is a falsy value, this method merges the selected cells in the table and ignores all parameters of this method.<br/>@param `colspan` - Number of columns this merge occupies.<br/>@param `rowspan` - Number of rows this merge occupies.<br/>`worksheetInstance.setMerge(cellName?: string, colspan?: number, rowspan?: number,): null \\| undefined;`  |\n| `getMerge`              | Get information from one or all merged cells.<br/>@param `cellName` - Cell name. If it is a falsy value, it returns the information of all merges. If the given cell is not the anchor of a merge, it returns null.<br/>`worksheetInstance.getMerge(cellName?: string): Record<string, [number, number]> \\| [number, number] \\| null;`                                                                                          |\n| `removeMerge`           | Remove a merge.<br/>@param `cellName` - Merge anchor cell.<br/>@param `data` - Data to be placed in cells released from the merge.<br/>`worksheetInstance.removeMerge(cellName: string, data?: CellValue[]): void;`.                                                                                                                                      |\n| `destroyMerge`{.nowrap} | Removes all merged cells.<br/>`worksheetInstance.destroyMerge(): void;`                                                                                                                                                                                                                                                                                                                                                |\n\n \n\n### Events\n\nSpreadsheet merge cells related events.\n\n| Event     | Description                                                                                                                 |\n|-----------|-----------------------------------------------------------------------------------------------------------------------------|\n| `onmerge` | Occurs when a merge is created.<br/>`onmerge(instance: WorksheetInstance, merges: Record<string, [number, number]>): void;` |\n\n \n\n### Initial Settings\n\nInitial properties for merged cells in the spreadsheet.\n\n| Property                                      | Description                                                 |\n|-----------------------------------------------|-------------------------------------------------------------|\n| `mergeCells: Record<string, [number, number]>;` | Allow the user to define the initial default merged cells.  |\n\n\n## Examples\n\nA basic example illustrates how to initialize and programmatically modify merged cell definitions.\n\nOpen this [merged cells example](https://jsfiddle.net/spreadsheet/gLc0a1x2/) on JSFiddle.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\"/>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\"/>\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><div id=\"log\"></div></p>\n\n<input type=\"button\" value=\"setMerge('A3', 2, 3)\" id=\"btn1\" />\n<input type=\"button\" value=\"removeMerge('A3')\" id=\"btn2\" />\n<input type=\"button\" value=\"Get all merged cells\" id=\"btn3\" />\n<input type=\"button\" value=\"Destroy all merged\" id=\"btn4\" />\n\n<script>\n// Create the spreadsheet\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: true,\n    worksheets: [{\n        data: [\n            ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n            ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n            ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n            ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n        ],\n        columnDrag: true,\n        worksheetName: 'Merged Cells',\n        minDimensions: [50, 50],\n        tableOverflow: true,\n        tableWidth: '800px',\n        tableHeight: '300px',\n        columns: [\n            {\n                type: 'text',\n                width: '300px',\n                title: 'Model',\n            },\n            {\n                type: 'text',\n                width: '80px',\n                title: 'Year',\n            },\n            {\n                type: 'text',\n                width: '100px',\n                title: 'Price',\n            },\n            {\n                type: 'calendar',\n                width: '150px',\n                title: 'Date',\n                options: {\n                    format: 'DD/MM/YYYY HH24:MI',\n                    time: 1,\n                }\n            },\n        ],\n        mergeCells: {\n            A1: [2, 2]\n        }\n    }]\n});\n\ndocument.getElementById(\"btn1\").onclick = () => table[0].setMerge('A3', 2, 3);\ndocument.getElementById(\"btn2\").onclick = () => table[0].removeMerge('A3');\ndocument.getElementById(\"btn3\").onclick = () => {\n    document.getElementById(\"log\").innerHTML = JSON.stringify(table[0].getMerge());\n}\ndocument.getElementById(\"btn4\").onclick = () => table[0].destroyMerge();\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    const spreadsheet = useRef();\n    const log = useRef();\n\n    const worksheets = [{\n        data: [\n            ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n            ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n            ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n            ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n        ],\n        columnDrag: true,\n        worksheetName: 'Merged Cells',\n        minDimensions: [50, 50],\n        tableOverflow: true,\n        tableWidth: '800px',\n        tableHeight: '300px',\n        columns: [\n            {\n                type: 'text',\n                width: '300px',\n                title: 'Model',\n            },\n            {\n                type: 'text',\n                width: '80px',\n                title: 'Year',\n            },\n            {\n                type: 'text',\n                width: '100px',\n                title: 'Price',\n            },\n            {\n                type: 'calendar',\n                width: '150px',\n                title: 'Date',\n                options: {\n                    format: 'DD/MM/YYYY HH24:MI',\n                    time: 1,\n                }\n            },\n        ],\n        mergeCells: {\n            A1: [2,2]\n        }\n    }]\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} worksheets={worksheets} />\n            <div ref={log}></div>\n            <input type=\"text\" value=\"setMerge('A3', 2, 3)\" onclick=\"spreadsheet.current[0].setMerge('A3', 2, 3);\" />\n            <input type=\"text\" value=\"removeMerge('A3')\" onclick=\"spreadsheet.current[0].removeMerge('A3');\" />\n            <input type=\"text\" value=\"Get all merged cells\"\n                onclick=\"log.current.value = JSON.stringify(spreadsheet.current[0].getMerge());\" />\n            <input type=\"text\" value=\"Destroy all merged\" onclick=\"spreadsheet.current[0].destroyMerge();\" />\n        </>\n    )\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\" :worksheets=\"worksheets\"/>\n  <div ref=\"log\"></div>\n  <input type=\"text\" value=\"setMerge('A3', 2, 3)\" @click=\"setMerge('A3', 2, 3)\" />\n  <input type=\"text\" value=\"removeMerge('A3')\" @click=\"removeMerge('A3')\" />\n  <input type=\"text\" value=\"Get all merged cells\" @click=\"getAllMergedCells\" />\n  <input type=\"text\" value=\"Destroy all merged\" @click=\"destroyAllMerges\" />\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet } from \"@jspreadsheet-ce/vue\";\nimport formula from \"@jspreadsheet/formula\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Refs\nconst spreadsheet = ref(null);\nconst log = ref(null);\n\n// Worksheets configuration\nconst worksheets = ref([{\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n    ],\n    columnDrag: true,\n    worksheetName: 'Merged Cells',\n    minDimensions: [50, 50],\n    tableOverflow: true,\n    tableWidth: '800px',\n    tableHeight: '300px',\n    columns: [\n        {\n            type: 'text',\n            width: '300px',\n            title: 'Model',\n        },\n        {\n            type: 'text',\n            width: '80px',\n            title: 'Year',\n        },\n        {\n            type: 'text',\n            width: '100px',\n            title: 'Price',\n        },\n        {\n            type: 'calendar',\n            width: '150px',\n            title: 'Date',\n            options: {\n                format: 'DD/MM/YYYY HH24:MI',\n                time: 1,\n            }\n        },\n    ],\n    mergeCells: {\n        A1: [2,2]\n    }\n}]);\n\n// Methods for merge cell interactions\nconst setMerge = (cell, rowspan, colspan) => {\n    if (spreadsheet.value && spreadsheet.value.current[0]) {\n        spreadsheet.value.current[0].setMerge(cell, rowspan, colspan);\n    }\n};\n\nconst removeMerge = (cell) => {\n    if (spreadsheet.value && spreadsheet.value.current[0]) {\n        spreadsheet.value.current[0].removeMerge(cell);\n    }\n};\n\nconst getAllMergedCells = () => {\n    if (spreadsheet.value && spreadsheet.value.current[0] && log.value) {\n        log.value.value = JSON.stringify(spreadsheet.value.current[0].getMerge());\n    }\n};\n\nconst destroyAllMerges = () => {\n    if (spreadsheet.value && spreadsheet.value.current[0]) {\n        spreadsheet.value.current[0].destroyMerge();\n    }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <div #log></div>\n        <input type=\"button\" value=\"setMerge('A3', 2, 3)\" (click)=\"this.worksheets[0].setMerge('A3', 2, 3);\" />\n        <input type=\"button\" value=\"removeMerge('A3')\" (click)=\"this.worksheets[0].removeMerge('A3');\" />\n        <input type=\"button\" value=\"Get all merged cells\" (click)=\"getMerge()\" />\n        <input type=\"button\" value=\"Destroy all merged\" (click)=\"this.worksheets[0].destroyMerge();\" />`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    @ViewChild(\"log\") log: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            toolbar: true,\n            worksheets: [{\n                data: [\n                    ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                    ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                    ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                    ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                ],\n                columnDrag: true,\n                worksheetName: 'Merged Cells',\n                minDimensions: [50, 5000],\n                tableOverflow: true,\n                tableWidth: '800px',\n                tableHeight: '300px',\n                columns: [\n                    {\n                        type: 'text',\n                        width: '300px',\n                        title: 'Model',\n                    },\n                    {\n                        type: 'text',\n                        width: '80px',\n                        title: 'Year',\n                    },\n                    {\n                        type: 'text',\n                        width: '100px',\n                        title: 'Price',\n                    },\n                    {\n                        type: 'calendar',\n                        width: '150px',\n                        title: 'Date',\n                        options: {\n                            format: 'DD/MM/YYYY HH24:MI',\n                            time: 1,\n                        }\n                    },\n                ],\n                mergeCells: {\n                    A1: [2, 2]\n                }\n            }]\n        });\n    }\n\n    getMerge() {\n        this.log.nativeElement.innerHTML = JSON.stringify(this.worksheets[0].getMerge());\n    }\n}\n```\n\n## Related Tools\n\n- [Jspreadsheet Pro Merged Cells](https://jspreadsheet.com/docs/merged-cells) - Advanced merge operations with batch methods (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/meta-information.md",
    "content": "title: Data Grid Cell Meta Information\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, excel-like features, spreadsheet meta information, cell meta information, hidden metadata in cells, hidden data in Jspreadsheet, data grid meta information, cell metadata management, managing hidden metadata, storing additional information in cells, Jspreadsheet cell properties, cell meta information methods, data grid customization\ndescription: Jspreadsheet allows you to store hidden metadata in cells, enabling advanced customization and control over data grid behavior.\n\n# Data Grid Cell Meta Information\n\nThe cell meta information feature allows you to store hidden metadata in cells, which is invisible to users. This feature helps track additional information or manage custom states. This guide explains how to set, retrieve, and reset metadata in Jspreadsheet.\n\n> Meta information is managed programmatically and does not have a visible interface.\n\n## Documentation\n\n### Methods\n\nHere are the main methods for managing cell meta information:\n\n| Method      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getMeta`   | Get meta information from one or all cells.<br/>@param `cell` - Cell name. If it is a falsy value, the metadata of all cells is returned.<br/>`worksheetInstance.getMeta(cell?: string): any;`                                                                                                                                                                                                                                                                               |\n| `setMeta`   | Set a property on a cell's meta information.<br/>@param `cellName` - Cell name.<br/>@param `key` - Property name.<br/>@param `value` - Property value.<br/>`worksheetInstance.setMeta(cellName: string, key: string, value: string): void;`<br/><br/>Remove current and define new meta information for one or more cells.<br/>@param `newMeta` - Object with the new meta information.<br/>`worksheetInstance.setMeta(newMeta: Record<string, Record<string, any>>): void;` |\n\n### Events\n\nJspreadsheet emits an event when cell meta information is changed. This event allows developers to track or react to metadata changes.\n\n| Event           | Description                                                                       |\n|-----------------|-----------------------------------------------------------------------------------|\n| `onchangemeta`  | `onchangemeta(instance: WorksheetInstance, cellName: Record<string, any>): void;` |\n\n### Initial Settings\n\nYou can initialize Jspreadsheet with predefined meta information for cells. The `meta` property allows setting this information during grid initialization.\n\n| Property                                    | Description                                 |\n|---------------------------------------------|---------------------------------------------|\n| `meta: Record<string, Record<string, any>>` | Defines initial meta information for cells. |\n\n### Use Cases for Meta Information\n\n- **Tracking User Actions**: Use meta information to store data about user actions, such as edit history or validation status for each cell.\n- **Custom Data**: Store custom information such as IDs, statuses, or other data that doesn't need to be visible to users but is essential for backend processing.\n\n## Examples\n\nHere are examples of how to use the `getMeta`, `setMeta`, and `resetMeta` methods to manage metadata in Jspreadsheet.\n\n### Basic Meta Information Example\n\nThis example demonstrates how to set and get meta information programmatically:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Initialize the spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Apple', 'Banana'],\n            ['Orange', 'Pineapple']\n        ],\n        columns: [\n            { width: 100 },\n            { width: 100 }\n        ],\n        // Initial meta information\n        meta: {\n            A1: { category: 'Fruit', id: '123' },\n            B1: { category: 'Fruit', id: '124' }\n        }\n    }]\n});\n// Set meta information for B2\nspreadsheet[0].setMeta('B2', 'category', 'Citrus');\n// Get meta information for A1\nconsole.log(spreadsheet[0].getMeta('A1'));\n</script>\n</html>\n```\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    const spreadsheet = useRef();\n\n    const data = [\n        ['Apple', 'Banana'],\n        ['Orange', 'Pineapple']\n    ]\n\n    const columns = [\n        { width: 100 },\n        { width: 100 }\n    ]\n\n    const meta = {\n        A1: { category: 'Fruit', id: '123' },\n        B1: { category: 'Fruit', id: '124' }\n    }\n\n    useEffect(() => {\n        if (spreadsheet.current) {\n            spreadsheet.current[0].setMeta('B2', 'category', 'Citrus');\n\n            console.log(spreadsheet.current[0].getMeta('A1'));\n        }\n    }, [])\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} meta={meta} />\n            </Spreadsheet>\n        </>\n    )\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet \n          :data=\"data\" \n          :columns=\"columns\" \n          :meta=\"meta\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref, onMounted } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheet = ref(null);\n\n// Data\nconst data = ref([\n  ['Apple', 'Banana'],\n  ['Orange', 'Pineapple']\n]);\n\n// Columns configuration\nconst columns = ref([\n  { width: 100 },\n  { width: 100 }\n]);\n\nconst meta = ref({\n  A1: { category: 'Fruit', id: '123' },\n  B1: { category: 'Fruit', id: '124' }\n});\n\n// Lifecycle hook equivalent to useEffect\nonMounted(() => {\n  if (spreadsheet.value && spreadsheet.value[0]) {\n      spreadsheet.value[0].setMeta('B2', 'category', 'Citrus');\n\n      console.log(spreadsheet.value.current[0].getMeta('A1'));\n  }\n});\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Apple', 'Banana'],\n                    ['Orange', 'Pineapple']\n                ],\n                columns: [\n                    { width: 100 },\n                    { width: 100 }\n                ],\n                // Initial meta information\n                meta: {\n                    A1: { category: 'Fruit', id: '123' },\n                    B1: { category: 'Fruit', id: '124' }\n                }\n            }],\n        });\n\n        // Set meta information for B2\n        this.worksheets[0].setMeta('B2', 'category', 'Citrus');\n        // Get meta information for A1\n        console.log(this.worksheets[0].getMeta('A1'));\n    }\n}\n```\n\n### Interacting with Meta Information Programmatically\n\nYou can interact with cell meta information at any point during runtime, either to store or retrieve hidden data that can be used for various features:\n\n#### Set meta information for multiple cells\n\n{.ignore}\n```javascript\nspreadsheet[0].setMeta({\n    A1: { category: 'Fruit', id: '123' },\n    B2: { category: 'Citrus', id: '125' }\n});\n```\n\n#### Get all meta information\n\n{.ignore}\n```javascript\nlet allMeta = spreadsheet[0].getMeta(null);\nconsole.log(allMeta);\n```\n\n### Batch Meta Information Reset\n\nYou can reset the meta information for multiple cells or for all cells in a spreadsheet. This example demonstrates how to reset metadata for specific cells:\n\n{.ignore}\n```javascript\nspreadsheet[0].resetMeta(['A1', 'B2', 'C2']);\n```\n\n### Working Example\n\nFor a working example of how to interact with meta information in a Jspreadsheet grid, check out this [Data Grid Meta Information](https://jsfiddle.net/spreadsheet/vauo24ws/) example on JSFiddle.\n"
  },
  {
    "path": "docs/jspreadsheet/docs/nested-headers.md",
    "content": "title: Data Grid Nested Headers\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, spreadsheet, data tables, nested headers, hierarchical column headers, nested header customization, header hierarchy\ndescription: Learn to enhance Jspreadsheet data grids with hierarchical column headers.\n\n# Data Grid Nested Headers\n\nThis section covers creating spreadsheets with nested headers.\n\n\n## Documentation\n\n### Initial Settings\n\nLearn how to generate a new spreadsheet containing nested headers.\n\n| Property                                                                               | Description                          |\n|----------------------------------------------------------------------------------------|--------------------------------------|\n| `nestedHeaders: { id?: string, colspan?: number; title?: string; align?: string;}[][]` | Worksheet nested header definitions. |\n\n\n## Examples\n\n### Nested Header Example\n\nThe example below demonstrates a basic configuration for nested headers in a JSS spreadsheet.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['BR', 'Cheese', 1],\n            ['CA', 'Apples', 0],\n            ['US', 'Carrots', 1],\n            ['GB', 'Oranges', 0],\n        ],\n        columns: [\n            {\n                type: 'autocomplete',\n                title: 'Country',\n                width: '200px'\n            },\n            {\n                type: 'dropdown',\n                title: 'Food',\n                width: '100px',\n                source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n            },\n            {\n                type: 'checkbox',\n                title: 'Stock',\n                width: '100px'\n            },\n            {\n                type: 'number',\n                title: 'Price',\n                width: '100px'\n            },\n        ],\n        minDimensions: [6,4],\n        nestedHeaders:[\n            [\n                {\n                    title: 'Supermarket information',\n                    colspan: '6',\n                },\n            ],\n            [\n                {\n                    title: 'Location',\n                    colspan: '1',\n                },\n                {\n                    title: ' Other Information',\n                    colspan: '2'\n                },\n                {\n                    title: ' Costs',\n                    colspan: '3'\n                }\n            ],\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ];\n    // Columns\n    const columns = [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '200px'\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '100px',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width: '100px'\n        },\n        {\n            type: 'number',\n            title: 'Price',\n            width: '100px'\n        },\n    ];\n    // Nested headers\n    const nestedHeaders = [\n        [\n            {\n                title: 'Supermarket information',\n                colspan: '8',\n            },\n        ],\n        [\n            {\n                title: 'Location',\n                colspan: '1',\n            },\n            {\n                title: ' Other Information',\n                colspan: '2'\n            },\n            {\n                title: ' Costs',\n                colspan: '5'\n            }\n        ],\n    ];\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} nestedHeaders={nestedHeaders} minDimensions={[8,4]} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet \n          :data=\"data\" \n          :columns=\"columns\" \n          :nestedHeaders=\"nestedHeaders\" \n          :minDimensions=\"[8,4]\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Data\nconst data = ref([\n  ['BR', 'Cheese', 1],\n  ['CA', 'Apples', 0],\n  ['US', 'Carrots', 1],\n  ['GB', 'Oranges', 0],\n]);\n\n// Columns\nconst columns = ref([\n  {\n      type: 'autocomplete',\n      title: 'Country',\n      width: '200px'\n  },\n  {\n      type: 'dropdown',\n      title: 'Food',\n      width: '100px',\n      source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n  },\n  {\n      type: 'checkbox',\n      title: 'Stock',\n      width: '100px'\n  },\n  {\n      type: 'number',\n      title: 'Price',\n      width: '100px'\n  },\n]);\n\n// Nested headers\nconst nestedHeaders = ref([\n  [\n      {\n          title: 'Supermarket information',\n          colspan: '8',\n      },\n  ],\n  [\n      {\n          title: 'Location',\n          colspan: '1',\n      },\n      {\n          title: ' Other Information',\n          colspan: '2'\n      },\n      {\n          title: ' Costs',\n          colspan: '5'\n      }\n  ],\n]);\n\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['BR', 'Cheese', 1],\n                    ['CA', 'Apples', 0],\n                    ['US', 'Carrots', 1],\n                    ['GB', 'Oranges', 0],\n                ],\n                columns: [\n                    {\n                        type: 'autocomplete',\n                        title: 'Country',\n                        width: '200px'\n                    },\n                    {\n                        type: 'dropdown',\n                        title: 'Food',\n                        width: '100px',\n                        source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n                    },\n                    {\n                        type: 'checkbox',\n                        title: 'Stock',\n                        width: '100px'\n                    },\n                    {\n                        type: 'number',\n                        title: 'Price',\n                        width: '100px'\n                    },\n                ],\n                minDimensions: [8,4],\n                nestedHeaders:[\n                    [\n                        {\n                            title: 'Supermarket information',\n                            colspan: '8',\n                        },\n                    ],\n                    [\n                        {\n                            title: 'Location',\n                            colspan: '1',\n                        },\n                        {\n                            title: ' Other Information',\n                            colspan: '2'\n                        },\n                        {\n                            title: ' Costs',\n                            colspan: '5'\n                        }\n                    ],\n                ]\n            }]\n        });\n    }\n}\n```\n\n### More Examples\n\nExplore a working example of a JSS [spreadsheet with nested headers](https://jsfiddle.net/spreadsheet/0nwh5u71/) that updates programmatically on JSFiddle.\n\n## Related Tools\n\n- [Spreadsheet Headers](/jspreadsheet/docs/headers) - Basic column header configuration\n- [Data Grid Footers](/jspreadsheet/docs/footers) - Add footer rows with calculations\n- [Jspreadsheet Pro Headers](https://jspreadsheet.com/docs/headers) - Advanced header features with styling (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/pagination.md",
    "content": "title: Spreadsheet Pagination\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, data grid pagination, large data visualization, efficient data grid, high-performance data grid, paginated data grid, paginated data visualization\ndescription: The spreadsheet pagination feature enables efficient handling and visualization of large datasets.\n\n# Spreadsheet Pagination\n\nThe spreadsheet pagination feature can manage large datasets by rendering a specified number of rows per page and offering a navigation index for quick access to different sections. This section details the settings, methods, and events related to pagination.\n\n## Documentation\n\n### Methods\n\nThe following methods are related to pagination.\n\n| Method           | Description                                                                                                                                           |\n|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `page`           | Navigate to the specified page number.<br/>@param `pageNumber` - Page number (starting at 0).<br/>`worksheetInstance.page(pageNumber: number): void;` |\n| `whichPage`      | Get the page index of a row.<br/>@param `cell` - Row index.<br/>`worksheetInstance.whichPage(cell: number): number;`                                  |\n| `quantiyOfPages` | `worksheet.quantiyOfPages() : number`<br/>Get the total number of pages available.                                                                    |\n\n\n### Events\n\nThis event is triggered when the user changes the page.\n\n| Event                   | Description                                                                                                                                                                                                |\n|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onchangepage`{.nowrap} | After the page has changed.<br/>@param `instance` - Instance of the worksheet where the change occurred.<br/>@param `newPageNumber` - Page the worksheet is on.<br/>@param `oldPageNumber` - Page the worksheet was on.<br/>@param `quantityPerPage` - Maximum number of lines on pages.<br/>`onchangepage(instance: WorksheetInstance, newPageNumber: number, oldPageNumber: number, quantityPerPage: number): void;`                                                                          |\n\n\n### Initial Settings\n\nInitial configuration related to the pagination of your data grid.\n\n| Property                      | Description                                                        |\n|-------------------------------|--------------------------------------------------------------------|\n| `pagination: number`          | The number of items per page                                       |\n| `paginationOptions: number[]` | The options for the user to select the number of results per page. |\n\n \n\n## Examples\n\n### Data Grid Search and Pagination\n\nEnabling search and pagination features during the spreadsheet initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><input type='button' value='Search for APP' id=\"btn1\"/>\n<input type='button' value='Go to the second page' id=\"btn2\"/></p>\n\n<script>\n// Create the spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        csv: '/tests/demo.csv',\n        csvHeaders: true,\n        search: true,\n        pagination: 10,\n        paginationOptions: [10,25,50,100],\n        columns: [\n            { type:'text', width:80 },\n            { type:'text', width:200 },\n            { type:'text', width:100 },\n            { type:'text', width:200 },\n            { type:'text', width:100 },\n        ],\n    }],\n    onchangepage: function(el, pageNumber, oldPage) {\n        console.log('New page: ' + pageNumber);\n    }\n});\n\ndocument.getElementById(\"btn1\").onclick = () => spreadsheet[0].search('app');\ndocument.getElementById(\"btn2\").onclick = () => spreadsheet[0].page(1);\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Columns\n    const columns = [\n        { type:'text', width:80 },\n        { type:'text', width:200 },\n        { type:'text', width:100 },\n        { type:'text', width:200 },\n        { type:'text', width:100 },\n    ];\n    // Event\n    const onchangepage = (el, pageNumber, oldPage) => {\n        console.log('New page: ' + pageNumber);\n    }\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} onchangepage={onchangepage}>\n            <Worksheet columns={columns}\n                csv=\"/tests/demo.csv\"\n                csvHeaders\n                search\n                pagination=\"10\"\n                paginationOptions={[10,25,50,100]}\n            />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n\n\n<template>\n  <Spreadsheet ref=\"spreadsheet\" :onchangepage=\"onchangepage\">\n      <Worksheet \n          :columns=\"columns\"\n          csv=\"/tests/demo.csv\"\n          csvHeaders\n          search\n          pagination=\"10\"\n          :paginationOptions=\"[10,25,50,100]\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheet = ref(null);\n\n// Columns\nconst columns = ref([\n  { type:'text', width:80 },\n  { type:'text', width:200 },\n  { type:'text', width:100 },\n  { type:'text', width:200 },\n  { type:'text', width:100 },\n]);\n\n// Page change event handler\nconst onchangepage = (el, pageNumber, oldPage) => {\n  console.log('New page: ' + pageNumber);\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n    <input type='button' value='Search for APP' (click)=\"this.worksheets[0].search('app')\" />\n    <input type='button' value='Go to the second page' (click)=\"this.worksheets[0].page(1)\" />`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                csv: '/tests/demo.csv',\n                csvHeaders: true,\n                search: true,\n                pagination: 10,\n                paginationOptions: [10,25,50,100],\n                columns: [\n                    { type:'text', width:80 },\n                    { type:'text', width:200 },\n                    { type:'text', width:100 },\n                    { type:'text', width:200 },\n                    { type:'text', width:100 },\n                ],\n            }],\n            onchangepage: function(el, pageNumber, oldPage) {\n                console.log('New page: ' + pageNumber);\n            }\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [Data Grid Search](/jspreadsheet/docs/search) - Search functionality for data grids\n- [Data Grid Filters](/jspreadsheet/docs/filters) - Filter data before pagination\n- [Jspreadsheet Pro Pagination](https://jspreadsheet.com/docs/pagination) - Advanced pagination with custom controls (Pro)\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/plugins.md",
    "content": "title: Jspreadsheet CE Plugins\nkeywords: Jspreadsheet, spreadsheets, plugins, add-ons, feature extensions, customization, free plugins, premium plugins, custom plugins\ndescription: Develop and distribute plugins for Jspreadsheet CE to encapsulate advanced features, enhance integration, and extend the core functionality of your spreadsheets.\n\n# Plugins\n\nPlugins help developers integrate multiple components with Jspreadsheet core features, such as the toolbar, context menu, event handling, etc. Their modular design simplifies development, making distribution and reuse more efficient within Jspreadsheet.  \n\n## Documentation\n\n### Methods\n\nCustomize Jspreadsheet by overriding these methods to add or enhance features like the toolbar, context menu, event handling, or server-side data persistence.\n\n| Method        | Description                                                                                                                          |\n|---------------|--------------------------------------------------------------------------------------------------------------------------------------|\n| `beforeinit`  | Before adding a new worksheet.<br/>`beforeinit(worksheet: Object, config: Object): void \\| object`                                   |\n| `init`        | When a new worksheet is added.<br/>`init(worksheet: Object): void`                                                                   |\n| `onevent`     | Called for every spreadsheet event.<br/>`onevent(event: String, a?: any, b?: any, c?: any, d?: any): void`                           |\n| `persistence` | Handles server-side data persistence.<br/>`persistence(worksheet: Object, method: String, args: Array): void`                        |\n| `contextMenu` | When the context menu opens.<br/>`contextMenu(worksheet: Object, x: Number, y: Number, e: Object, items: [], section: String): void` |\n| `toolbar`     | When the toolbar is created or clicked.<br/>`toolbar(worksheet: Object, items: Array): void`                                         |\n\n \n### Worksheet Options\n\nYou can define custom options for each worksheet using the `pluginOptions` property.\n\n\n### Basic Implementation\n\nBelow is a basic implementation example that can be used as a reference for defining custom worksheet options.\n\n{.ignore}\n```javascript\nconst newPlugin = (function() {\n    // Plugin object\n    let plugin = {};\n\n    /**\n     * It will be executed for every new worksheet\n     */\n    plugin.init = function(worksheet) {\n    }\n\n    /**\n     * Jspreadsheet events\n     */\n    plugin.onevent = function() {\n        // It would be executed in every single event and can be used to customize actions\n    }\n\n    /**\n     * It would be call every single time persistence is required\n     * @param {object} worksheet - worksheet\n     * @param {string} method - action executed\n     * @param {object} args - depending on the action.\n     */\n    plugin.persistence = function(worksheet, method, args) {\n        // Different options are used depending on the action performed.\n    }\n\n    /**\n     * Run on the context menu\n     * @param instance Jexcel Spreadsheet Instance\n     * @param x coordinates from the clicked cell\n     * @param y coordinates from the clicked cell\n     * @param e click object\n     * @param items current items in the contextMenu\n     */\n    plugin.contextMenu = function(instance, x, y, e, items) {\n        // Can be used to overwrite the contextMenu\n\n        return items;\n    }\n\n    /**\n     * Run on toolbar\n     * @param instance Jexcel Spreadsheet Instance\n     * @param items current items in the toolbar \n     */\n    plugin.toolbar = function(instance, items) {\n        // Can be used to overwrite the toolbar\n\n        return items;\n    }\n\n    // Any startup configuration goes here\n    // (...)\n\n    // Return the object\n    return plugin;\n});\n```\n\n## Examples\n\nThe following code is a working example of a plugin in action.  \n\n### Spreadsheet properties update\n\nThe properties plugin allow the user to change some of the spreadsheet settings, through a new option included in the context menu. \n\n{.small}\nRight-click in any cell and choose the last option in the context menu.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<script src=\"https://bossanova.uk/jspreadsheet/v5/plugins/properties.js\"></script>\n\n<div id='spreadsheet'></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        { minDimensions: [6, 6] },\n        { minDimensions: [6, 6] },\n    ],\n    plugins: { properties },\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Installation npm install @jspreadsheet/properties\nimport properties from \"@jspreadsheet/properties\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render data grid component\n    return (\n        <Spreadsheet ref={spreadsheet} plugins={{ properties }}>\n            <Worksheet minDimension={[6, 6]}/>\n            <Worksheet minDimension={[6, 6]}/>\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" :plugins=\"properties\">\n        <Worksheet :minDimensions=\"[10,10]\" />\n        <Worksheet :minDimensions=\"[10,10]\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\n// Installation npm install @jspreadsheet/properties\nimport properties from \"@jspreadsheet/properties\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        return {\n            properties: { properties }\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n// Installation: npm install @jspreadsheet/properties\nimport properties from \"@jspreadsheet/properties\";\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                { minDimensions: [6, 6] }, // Worksheet 1\n                { minDimensions: [6, 6] }, // Worksheet 2\n            ],\n            plugins: { properties }\n        });\n    }\n}\n```\n"
  },
  {
    "path": "docs/jspreadsheet/docs/react/tests.md",
    "content": "title: Unit Tests for Jspreadsheet in React\nkeywords: Jspreadsheet, Jexcel, JavaScript, Web-based Applications, Web-based Spreadsheet, Unit Tests, React, Next, NextJS, ReactJS\ndescription: Enhance your application quality by creating unit tests for Jspreadsheet inside your React application.\n\n# Testing Jspreadsheet in React with Jest\n\n## Introduction\n\nIn modern React applications, unit testing is essential to ensure components work correctly and prevent regressions. Jspreadsheet integrates easily into React, enabling you to add spreadsheet functionality to your web applications. This guide will walk you through setting up and running tests for Jspreadsheet in a React project using Jest as the testing framework.\n\nIn this section, we’ll guide you through setting up a **React environment** specifically for testing **Jspreadsheet** using **Jest** and **JSDOM**. This will help you create a solid foundation for **running unit tests** for your Jspreadsheet instances.\n\n## Environment Setup\n\nWe’ll set up a React environment for testing Jspreadsheet using Jest and JSDOM. Follow these steps to prepare your project.\n\n### Step 1: Clone or Create a Project\n\nYou can either clone an existing project or create a new React project using `create-next-app`:\n\n```bash\nnpx create-next-app jspreadsheet-react-testing\ncd jspreadsheet-react-testing\n```\n\nAlternatively, clone our setup from [GitHub](??).\n\n### Step 2: Install Dependencies\n\nNext, install the necessary dependencies, including `jspreadsheet`, Jest, and `jest-environment-jsdom`:\n\n```bash\nnpm install jspreadsheet-ce@5.0.0-beta.3\nnpm install --save-dev jest@29.7.0 jest-environment-jsdom@29.7.0\n```\n\n### Step 3: Configure Jest for Jspreadsheet\n\nTo integrate Jspreadsheet properly in a Jest testing environment, you'll need to set up JSDOM. First, create a `jest.setup.js` file in the root of your project:\n\n```javascript\n// jest.setup.js\n\nconst jspreadsheet = require('jspreadsheet-ce');\n\n  // Code that runs between each test\nbeforeEach(() => {\n    if (typeof document !== 'undefined') {\n        jspreadsheet.destroyAll();\n\n        if (!global.jspreadsheet && !global.root) {\n            global.jspreadsheet = jspreadsheet;\n\n            global.root = document.createElement('div');\n            global.root.style.width = '100%';\n            global.root.style.height = '100%';\n\n            document.body.appendChild(global.root);\n        }\n    }\n});\n```\n\nNext, configure Jest to use this setup by adding the following entry to your `package.json`:\n\n```json\n{\n  \"jest\": {\n    \"setupFilesAfterEnv\": [\"<rootDir>/jest.setup.js\"],\n  }\n}\n```\n\nThis configuration ensures JSDOM will emulate the DOM environment required to run Jspreadsheet within Jest.\n\n### Step 4: Create a Test\n\nCreate a folder inside your project if it doesn't exist, then inside this folder create a file named `jspreadsheet.test.js`.\n\n```javascript\n// */tests/jspreadsheet.test.js\n\n/**\n * @jest-environment jsdom\n */\n\ntest(\"Testing data\", () => {\n    let instance = jspreadsheet(root, {\n        worksheets: [\n            {\n                data: [\n                    [\"Mazda\", 2001, 2000],\n                    [\"Peugeot\", 2010, 5000],\n                    [\"Honda Fit\", 2009, 3000],\n                    [\"Honda CRV\", 2010, 6000],\n                ],\n                minDimensions: [4, 4],\n            },\n        ],\n    });\n\n    expect(instance[0].getValue(\"A1\", true)).toEqual(\"Mazda\");\n    expect(instance[0].getValue(\"A2\", true)).toEqual(\"Peugeot\");\n    expect(instance[0].getValue(\"B1\", true)).toEqual(\"2001\");\n});\n```\n\nThis test verifies that a basic Jspreadsheet instance is created and that the data values are correctly placed. You can modify it to check whatever you want to test.\n\n## Running the Tests\n\nEnsure you add the following line to the `scripts` section of your `package.json`:\n\n```json\n\"test\": \"jest\"\n```\n\nAfter creating your tests and updating `package.json`, you can run them using the following command:\n\n```bash\nnpm test\n```\n\nJest will run all the tests in your project and display the results in the console. If everything is configured correctly, your tests should pass.\n"
  },
  {
    "path": "docs/jspreadsheet/docs/react.md",
    "content": "title: React Spreadsheet\nkeywords: Jspreadsheet, Jexcel, javascript, React, data grid, spreadsheet-like controls, React data grid, Jspreadsheet integration, React integration, JavaScript data grid, spreadsheet controls in React\ndescription: Description: How to integrate spreadsheet-like features into your React applications with Jspreadsheet.\n\n# React Spreadsheet\n\nJspreadsheet CE is a lightweight JavaScript data grid library with spreadsheet controls. It integrates seamlessly with React, enabling developers to create highly interactive and customizable spreadsheet-like application components. This guide walks you through the process of integrating Jspreadsheet CE into a React project.\n\n## Install\n\n### Install the Package\n\nInstall Jspreadsheet React Data Grid wrapper using NPM.\n\n```bash\nnpm install @jspreadsheet-ce/react@5.0.0-beta.3\n```\n\n### Import Required Styles\n\nImport the necessary Spreadsheet CSS style to your project.\n\n{.ignore}\n```javascript\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n```\n\nTo ensure icons display correctly, include Material Icons in your application. Add the following code to your main HTML file:\n\n{.ignore}\n```html\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n```\n\n## Example\n\n### React Wrapper\n\nCreate your first React grid with spreadsheet controls using the Jspreadsheet React wrapper.\n\n{.ignore}\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport \"jsuites/dist/jsuites.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} tabs={true} toolbar={true}>\n            <Worksheet minDimensions={[6, 6]} />\n        </Spreadsheet>\n    );\n}\n```\n\n### React Component\n\nYou can create your React component using the library directly for better control.\n\n{.ignore}\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport jspreadsheet from \"@jspreadsheet-ce/react\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport \"jsuites/dist/jsuites.css\";\n\nexport default function App() {\n    const jssRef = useRef(null);\n\n    useEffect(() => {\n        // Create the spreadsheet only once\n        if (!jssRef.current.jspreadsheet) {\n            jspreadsheet(jssRef.current, {\n                worksheets: [{\n                    minDimensions: [10, 10]\n                }],\n            });\n        }\n    }, null);\n\n    return (<div ref={jssRef} />);\n}\n```\n\n## Overview\n\nJspreadsheet operates with simple objects, including big datasets. It manages object references internally to optimize performance, minimize overhead, and maintain efficient data handling. For this reason, proprietary methods and events are provided to interact with its internal states.\n\n### Integration with React\n\n#### States\n\nDue to its architecture, Jspreadsheet does not work directly with React States. To integrate, you must declare Jspreadsheet events and use them to synchronize with React.\n\n#### Events\n\nEvents can be declared at the spreadsheet level. Refer to the [events documentation](/jspreadsheet/docs/events) for available events and usage examples.\n\n{.ignore}\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport \"jsuites/dist/jsuites.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    const afterchanges = function() {\n        // There were changes on the data\n    }\n    \n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} onafterchanges={afterchanges}>\n            <Worksheet minDimensions={[6, 6]} />\n        </Spreadsheet>\n    );\n}\n```\n\n\n## Jspreadsheet Pro with React\n\n### Real-Time React Spreadsheet\n\nCreate a Real-time JavaScript Spreadsheet with React and TypeScript using Jspreadsheet.\n\n* [Real-Time Collaboration](https://github.com/jspreadsheet/spreadsheet-react-server)\n\n#### More React Spreadsheet Examples\n\n* [React Spreadsheet](https://codesandbox.io/p/sandbox/react-components-on-jspreadsheet-zx9zxr)\nBasic react spreadsheet with translations. \n* [React Spreadsheet Cell Editors](https://codesandbox.io/s/react-spreadsheet-with-a-custom-editor-ic6h3l)\nHow to create a custom data grid cell editor with React. \n* [Custom React Components](https://codesandbox.io/s/react-components-on-jspreadsheet-k7wc4c)\nIntegrate a custom React component (Recharts) with Jspreadsheet. \n* [React Spreadsheet as React Classes](https://codesandbox.io/p/sandbox/react-spreadsheet-kkz3s8)\nCreate a basic react spreadsheet using React classes \n* [React Data Grid Validations](https://codesandbox.io/s/online-spreadsheet-with-validations-with-jspreadsheetxy777)\nHow to crate a data grid with cell validations\n* [MUI React as a Custom Editor](https://codesandbox.io/p/sandbox/custom-editors-with-react-mui-y4v8lj)\nReact Calendar with Antd\n* [Antd React Calendar Cell Editor](https://stackblitz.com/edit/vitejs-vite-kwqcwy)\nReact Calendar with MUI\n* [MUI React Calendar as a Custom Editor](https://codesandbox.io/p/sandbox/custom-editors-with-react-mui-forked-6hw4vz)\n\n\n#### NextJS integration\n\n* [Online XLSX NextJS reader](https://codesandbox.io/s/jspreadsheet-and-nextjs-6fhsz)\nCreating an online XLSX reader with NextJS and Jspreadsheet\n* [Import a Excel file to NextJS](https://codesandbox.io/s/nextjs-spreadsheet-52mr2z)\nHow to import an Excel file in NextJS using Jspreadsheet.\n\n{.pro}\n> **Jspreadsheet Pro for React - Professional Spreadsheet Components**\n>\n> While Jspreadsheet CE provides core spreadsheet functionality for React, **Jspreadsheet Pro** offers enhanced React integration and enterprise features:\n>\n> **Enhanced React Integration:**\n> - **TypeScript Support:** Full TypeScript definitions for type-safe development\n> - **React Hooks:** Custom hooks for common spreadsheet operations (useSpreadsheet, useWorksheet)\n> - **State Management:** Built-in Redux/MobX integration for state management\n> - **React Context:** Context API integration for global spreadsheet state\n> - **Server-Side Rendering:** Full SSR/SSG support for Next.js and Gatsby\n> - **React 18 Features:** Concurrent rendering, automatic batching support\n>\n> **Professional Components:**\n> - **Advanced Editors:** Conditional dropdowns, rich text, HTML editors with React integration\n> - **Formula System:** 500+ Excel functions with React bindings\n> - **Conditional Formatting:** Visual rules, data bars, color scales, icon sets\n> - **Data Validation:** Real-time validation with custom error components\n> - **Import/Export:** Full Excel (.xlsx) import/export with formatting preservation\n> - **Charts & Graphs:** Built-in charting components for data visualization\n>\n> **Performance & Scale:**\n> - **Virtual Scrolling:** Handle 100K+ rows with smooth scrolling\n> - **Lazy Loading:** Load data on-demand for optimal performance\n> - **Optimized Rendering:** React-optimized rendering for large datasets\n> - **Web Workers:** Background processing for heavy calculations\n> - **Memory Management:** Efficient memory usage for large spreadsheets\n>\n> **Developer Experience:**\n> - **Comprehensive Documentation:** React-specific examples and best practices\n> - **Professional Support:** Priority email and chat support for integration issues\n> - **Regular Updates:** Continuous improvements and React version compatibility\n> - **Migration Tools:** Easy migration from CE to Pro with code guides\n>\n> **React-Specific Pro Features:**\n> - **Custom React Components:** Use React components as cell editors\n> - **Event Integration:** Seamless integration with React event system\n> - **Component Lifecycle:** Hooks into React component lifecycle\n> - **Prop Validation:** PropTypes and TypeScript validation\n> - **Testing Support:** Jest/React Testing Library integration examples\n>\n> Perfect for React applications requiring enterprise-grade spreadsheet functionality with professional support.\n>\n> **[Explore Jspreadsheet Pro React →](https://jspreadsheet.com/docs/react)** | **[Compare CE vs Pro →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)** \n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/readonly.md",
    "content": "title: Data Grid Read-only Cells\nkeywords: Jspreadsheet, JavaScript, plugins, spreadsheet, read-only cells, data grid, data protection, cell permissions, non-editable cells\ndescription: Learn to configure read-only columns or cells in Jspreadsheet, protecting data and restricting user edits for specific cells in your data grid.\n\n# Read Only Cells\n\n## Documentation\n\n### Methods\n\nThe following methods allow programmatic updates to read-only states in your spreadsheets.\n\n| Method                                 | Description                                                                                             |\n|----------------------------------------|---------------------------------------------------------------------------------------------------------|\n| `setReadOnly(object\\|string, boolean)` | Sets the read-only state of a cell.<br/>`setReadOnly(ident: HTMLElement\\|string, state: boolean): void` |\n| `isReadOnly(number, number)`           | Checks if a cell is read-only.<br/>`isReadOnly(x: number, y: number): boolean`                          |\n\n## Examples\n\n### Readonly\n\nA basic spreadsheet example with a read-only column and an additional read-only cell.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Mazda', 2001, 2000, 1],\n            ['GM', 2010, 5000, 1],\n            ['Honda Fit', 2009, 3000, 1],\n            ['Honda CRV', 2010, 6000, 0],\n        ],\n        columns: [\n            {\n                type: 'text',\n                title:'Description',\n                width:'200px',\n                readOnly: true,\n            },\n            {\n                type: 'text',\n                title: 'Year',\n                width: '200px'\n            },\n            {\n                type: 'text',\n                title: 'Price',\n                width: '100px',\n                mask: '#.##',\n                render: function(td, value, x, y) {\n                    if (y === 2) {\n                        td.classList.add('readonly');\n                    }\n                }\n            },\n            {\n                type: 'checkbox',\n                title:'Automatic',\n                width:'100px'\n            },\n        ],\n        updateTable: function(el, cell, x, y, source, value, id) {\n            if (x == 2 && y == 2) {\n                cell.classList.add('readonly');\n            }\n        } \n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Mazda', 2001, 2000, 1],\n        ['Peugeot', 2010, 5000, 1],\n        ['Honda Fit', 2009, 3000, 1],\n        ['Honda CRV', 2010, 6000, 0],\n    ];\n\n    const columns = [\n        {\n            type: 'text',\n            title:'Description',\n            width:'200px',\n            readOnly:true,\n        },\n        {\n            type: 'text',\n            title:'Year',\n            width:'200px'\n        },\n        {\n            type: 'text',\n            title:'Price',\n            width:'100px',\n            mask:'#.##',\n        },\n        {\n            type: 'checkbox',\n            title:'Automatic',\n            width:'100px'\n        },\n    ];\n\n    const updateTable = function(el, cell, x, y, source, value, id) {\n        if (x == 2 && y == 2) {\n            cell.classList.add('readonly');\n        }\n    }\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} updateTable={updateTable} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet \n          :data=\"data\" \n          :columns=\"columns\" \n          :updateTable=\"updateTable\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheet = ref(null);\n\n// Data\nconst data = ref([\n  ['Mazda', 2001, 2000, 1],\n  ['Peugeot', 2010, 5000, 1],\n  ['Honda Fit', 2009, 3000, 1],\n  ['Honda CRV', 2010, 6000, 0],\n]);\n\n// Columns configuration\nconst columns = ref([\n  {\n      type: 'text',\n      title:'Description',\n      width:'200px',\n      readOnly:true,\n  },\n  {\n      type: 'text',\n      title:'Year',\n      width:'200px'\n  },\n  {\n      type: 'text',\n      title:'Price',\n      width:'100px',\n      mask:'#.##',\n  },\n  {\n      type: 'checkbox',\n      title:'Automatic',\n      width:'100px'\n  },\n]);\n\n// Update table method\nconst updateTable = (el, cell, x, y, source, value, id) => {\n  if (x === 2 && y === 2) {\n      cell.classList.add('readonly');\n  }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <ol class='example' style='cursor:pointer;'>\n            <li><a (click)=\"this.worksheets[0].insertRow()\">\n                Click to insert a new blank row at the end of the spreadsheet\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].insertRow(2, 0, true)\">\n                Click to insert two new blank rows at the beginning of the spreadsheet\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].deleteRow(-1)\">\n                Click to delete the last row\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].moveRow(0, 2)\">\n                Click to move the first row to the third position\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].hideRow(0)\">\n                Hide the first row\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].showRow(0)\">\n                Show the first row\n            </a></li>\n        </ol>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            data: [\n                ['Mazda', 2001, 2000, 1],\n                ['GM', 2010, 5000, 1],\n                ['Honda Fit', 2009, 3000, 1],\n                ['Honda CRV', 2010, 6000, 0],\n            ],\n            columns: [\n                {\n                    type: 'text',\n                    title:'Description',\n                    width:'200px',\n                    readOnly: true,\n                },\n                {\n                    type: 'text',\n                    title: 'Year',\n                    width: '200px'\n                },\n                {\n                    type: 'text',\n                    title: 'Price',\n                    width: '100px',\n                    mask: '#.##',\n                    render: function(td, value, x, y) {\n                        if (y === 2) {\n                            td.classList.add('readonly');\n                        }\n                    }\n                },\n                {\n                    type: 'checkbox',\n                    title:'Automatic',\n                    width:'100px'\n                },\n            ],\n            updateTable: function(el, cell, x, y, source, value, id) {\n                if (x == 2 && y == 2) {\n                    cell.classList.add('readonly');\n                }\n            }\n        });\n    }\n}\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/rows.md",
    "content": "title: Managing the Data Grid Rows\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like rows, spreadsheet rows, data table rows, row management, add rows, delete rows, move rows, row manipulation, row events, row settings, row documentation\ndescription: Learn to manage rows in Jspreadsheet, including adding, deleting, and moving them. Explore methods, events, and settings for customizing row behaviour in dynamic and integrated data grid applications.\n\n# Spreadsheet Rows\n\nRow settings in Jspreadsheet define behaviours and attributes, such as unique identifiers, row height, styling, and cell properties like read-only status. This section covers the methods, events, and settings for managing rows in a data grid.\n\n## Documentation\n\n### Methods\n\nYou can manage rows programmatically using one of the following methods.\n\n| Method      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n|-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getHeight` | Retrieves the height of all rows.<br/>`worksheetInstance.getHeight(row?: undefined): string[];`<br/><br/>Retrieves the height of a row.<br/>`worksheetInstance.getHeight(row: number): string;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\n| `setHeight` | Sets the height of a row.<br/>@param `row` - Row index.<br/>@param `height` - New height. An integer greater than zero.<br/>`worksheetInstance.setHeight(row: number, height: number): void;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\n| `moveRow`   | Moves a row to a new position.<br/>@param `rowNumber` - Row index.<br/>@param `newPositionNumber` - New row index.<br/>`worksheetInstance.moveRow(rowNumber: number, newPositionNumber: number): false \\| undefined;`                                                                                                                                                                                                                                                                                                                                                                           |\n| `insertRow` | Inserts one or more new rows.<br/>@param `mixed` - Number of rows to insert. It can also be an array of values, but in this case, only one row is inserted, whose data is based on the array items. Default: 1.<br/>@param `rowNumber` - Index of the row used as reference for the insertion. Default: last row.<br/>@param `insertBefore` - Insert new rows before or after the reference row. Default: false.<br/>`worksheetInstance.insertRow(mixed?: number \\| CellValue[],rowNumber?: number,insertBefore?: number): false \\| undefined;` |\n| `deleteRow` | Deletes one or more rows.<br/>@param `rowNumber` - Row index from which removal starts.<br/>@param `numOfRows` - Number of rows to be removed.<br/>`worksheetInstance.deleteRow(rowNumber?: number, numOfRows?: number): false \\| undefined;`                                                                                                                                                                                                                                                                                             |\n\n\n### Events\n\nThe following events are related to rows in your spreadsheet.\n\n| Event               | Description                                                                                                                                                     |\n|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onbeforeinsertrow` | Triggered before a new row is inserted. Return `false` to cancel the action.<br/>`onbeforeinsertrow(worksheet: Object, rows: Object[]) => Boolean \\| Object[] \\| void` |\n| `oninsertrow`       | Triggered after a new row is inserted.<br/>`oninsertrow(worksheet: Object, rows: Object[]) => void`                                                              |\n| `onbeforedeleterow` | Triggered before a row is deleted. Return `false` to cancel the action.<br/>`onbeforedeleterow(worksheet: Object, rows: Number[]) => Number[] \\| Boolean \\| void` |\n| `ondeleterow`       | Triggered after a row is deleted.<br/>`ondeleterow(worksheet: Object, rows: Number[]) => void`                                                                   |\n| `onmoverow`         | Triggered after a row is moved to a new position.<br/>`onmoverow(worksheet: Object, origin: Number, destination: Number) => void`                                 |\n| `onresizerow`       | Triggered after the height of one or more rows is changed.<br/>`onresizerow(worksheet: Object, row: Mixed, height: Mixed, oldHeight: Mixed) => void`             |\n\n \n\n### Initial Settings\n\nThe following row-related properties are available during spreadsheet initialization.\n\n| Property                        | Description                                                                                  |\n|---------------------------------|----------------------------------------------------------------------------------------------|\n| `allowInsertRow: boolean`       | Enables the user to insert new rows. `Default: true`                                         |\n| `allowManualInsertRow: boolean` | Automatically inserts a new row when the user presses Enter on the last row. `Default: true` |\n| `allowDeleteRow: boolean`       | Allows the user to delete rows. `Default: true`                                              |\n| `rowDrag: boolean`              | Enables the user to change the position of a row by dragging and dropping. `Default: true`   |\n| `rowResize: boolean`            | Allows the user to resize rows. `Default: true`                                              |\n| `defaultRowHeight: number`      | Sets the default row height.                                                                 |\n| `minSpareRows: number`          | Specifies the number of mandatory blank rows at the end of the spreadsheet. `Default: none.` |\n\n\n\n### Available properties\n\nYou can initialize the spreadsheet with custom `id`, `text`, and `height` using the following properties:\n\n| Property          | Description                                                                                  |\n|-------------------|----------------------------------------------------------------------------------------------|\n| `id: number`      | Unique identifier for the row, which can be used to synchronize the content with a database. |\n| `height: number`  | The row height in pixels.                                                                    |\n| `title: string`   | The title or name of the row.                                                                |\n\n\n## Examples\n\nA basic spreadsheet with a few programmatic methods available. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div><br/><br/>\n\n<input type=\"button\" value=\"Click to insert a new blank row at the end of the spreadsheet\" id=\"btn1\"><br/><br/>\n<input type=\"button\" value=\"Click to insert two new blank rows at the beginning of the spreadsheet\" id=\"btn2\"><br/><br/>\n<input type=\"button\" value=\"Click to delete the last row\" id=\"btn4\"><br/><br/>\n<input type=\"button\" value=\"Click to move the first row to the third position\" id=\"btn5\"><br/><br/>\n<input type=\"button\" value=\"Hide the first row\" id=\"btn6\"><br/><br/>\n<input type=\"button\" value=\"Show the first row\" id=\"btn7\"><br/><br/>\n\n<script>\n\n\n// Create the spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['US', 'Cheese', 1000 ],\n            ['CA', 'Apples', 1200 ],\n            ['CA', 'Carrots', 2000 ],\n            ['BR', 'Oranges', 3800 ],\n        ],\n        worksheetName: 'Row management',\n    }],\n});\n\ndocument.getElementById(\"btn1\").onclick = () => spreadsheet[0].insertRow();\ndocument.getElementById(\"btn2\").onclick = () => spreadsheet[0].insertRow(2, 0, 1);\ndocument.getElementById(\"btn4\").onclick = () => spreadsheet[0].deleteRow();\ndocument.getElementById(\"btn5\").onclick = () => spreadsheet[0].moveRow(0, 2);\ndocument.getElementById(\"btn6\").onclick = () => spreadsheet[0].hideRow(0);\ndocument.getElementById(\"btn7\").onclick = () => spreadsheet[0].showRow(0);\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['US', 'Cheese', 1000 ],\n        ['CA', 'Apples', 1200 ],\n        ['CA', 'Carrots', 2000 ],\n        ['BR', 'Oranges', 3800 ],\n    ];\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet minDimensions={[6,6]} worksheetName={\"Row management\"} />\n            </Spreadsheet>\n            <button onClick={() => spreadsheet.current[0].insertRow()}>\n                Click to insert a new blank row at the end of the spreadsheet\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].insertRow(2, 0, 1)}>\n                Click to insert two new blank rows at the beginning of the spreadsheet\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].insertRow([{ data: [ '0.99', '1.22', '3.11', '2.21' ]}])}>\n                Click to insert a new row with pre-populated values at the end of the spreadsheet\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].deleteRow()}>\n                Click to delete the last row\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].moveRow(0, 2)}>\n                Click to move the first row to the third position\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].hideRow(0)}>\n                Hide the first row\n            </button><br/>\n            <button onClick={() => spreadsheet.current[0].showRow(0)}>\n                Show the first row\n            </button>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet :data=\"data\" />\n  </Spreadsheet>\n  <ol class='example' style='cursor:pointer;'>\n      <li><a @click=\"insertRowAtEnd()\">\n          Click to insert a new blank row at the end of the spreadsheet\n      </a></li>\n      <li><a @click=\"insertRowsAtBeginning()\">\n          Click to insert two new blank rows at the beginning of the spreadsheet\n      </a></li>\n      <li><a @click=\"deleteLastRow()\">\n          Click to delete the last row\n      </a></li>\n      <li><a @click=\"moveFirstRowToThird()\">\n          Click to move the first row to the third position\n      </a></li>\n      <li><a @click=\"hideFirstRow()\">\n          Hide the first row\n      </a></li>\n      <li><a @click=\"showFirstRow()\">\n          Show the first row\n      </a></li>\n  </ol>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheet = ref(null);\n\n// Data\nconst data = ref([\n  ['US', 'Cheese', 1000 ],\n  ['CA', 'Apples', 1200 ],\n  ['CA', 'Carrots', 2000 ],\n  ['BR', 'Oranges', 3800 ],\n]);\n\n// Row operation methods\nconst insertRowAtEnd = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].insertRow();\n  }\n};\n\nconst insertRowsAtBeginning = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].insertRow(2, 0, 1);\n  }\n};\n\nconst deleteLastRow = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].deleteRow();\n  }\n};\n\nconst moveFirstRowToThird = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].moveRow(0, 2);\n  }\n};\n\nconst hideFirstRow = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].hideRow(0);\n  }\n};\n\nconst showFirstRow = () => {\n  if (spreadsheet.value && spreadsheet.value.current[0]) {\n      spreadsheet.value.current[0].showRow(0);\n  }\n};\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <ol class='example' style='cursor:pointer;'>\n            <li><a (click)=\"this.worksheets[0].insertRow()\">\n                Click to insert a new blank row at the end of the spreadsheet\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].insertRow(2, 0, true)\">\n                Click to insert two new blank rows at the beginning of the spreadsheet\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].deleteRow(-1)\">\n                Click to delete the last row\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].moveRow(0, 2)\">\n                Click to move the first row to the third position\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].hideRow(0)\">\n                Hide the first row\n            </a></li>\n            <li><a (click)=\"this.worksheets[0].showRow(0)\">\n                Show the first row\n            </a></li>\n        </ol>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            data: [\n                ['US', 'Cheese', 1000 ],\n                ['CA', 'Apples', 1200 ],\n                ['CA', 'Carrots', 2000 ],\n                ['BR', 'Oranges', 3800 ],\n            ],\n            worksheetName: 'Row management',\n        });\n    }\n}\n```\n "
  },
  {
    "path": "docs/jspreadsheet/docs/search.md",
    "content": "title: Data Grid Search\nkeywords: Jspreadsheet, data grid search, JavaScript, Excel-like search functionality, spreadsheet search, search customization, search event handling, row filtering\ndescription: Explore search functionality in Jspreadsheet, including methods and events to filter rows and customize search behaviour for specific application requirements.\n\n# Data Grid Search\n\nThe Jspreadsheet's search functionality filters rows using keyword matching, offering flexibility to modify or terminate operations based on application needs. This section details the technical methods and events for customizing search behaviour.\n\n\n{.pro}\n> #### Differences in the Pro Version\n> Jspreadsheet Pro allows complete customization of search operations, including events to highlight results, customize search criteria, or integrate with the backend.\\\n> \\\n> [Learn more](https://jspreadsheet.com/docs/search){.button}\n\n## Documentation\n\n### Methods\n\nThe following methods are used to implement and manage search functionality in the spreadsheet:\n\n| Method        | Description                                                                                                                                       |\n|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------|\n| `search`      | Searches for rows containing the specified terms.<br/>@param `query` - Text to be searched.<br/>`worksheetInstance.search(query: string): void;`  |\n| `resetSearch` | Resets search terms and displays all rows.<br/>`worksheetInstance.resetSearch(): void;`                                                           |  \n\n### Initial Settings\n\nThe following property is available to configure the search functionality during spreadsheet initialization:\n\n| Property          | Description                             |\n|-------------------|-----------------------------------------|\n| `search: boolean` | Enables or disables the search feature. |\n\n\n## Examples\n\n### Data Grid with Search and Pagination\n\nThe example below demonstrates a data grid configured with search functionality and pagination support.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><input type='button' value='Search for APP' id=\"btn1\" /></p>\n\n<script>\n// Create the spreadsheet\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        csv: '/tests/demo.csv',\n        csvHeaders: true,\n        search: true,\n        pagination: 10,\n        paginationOptions: [10,25,50,100],\n        columns: [\n            { type:'text', width:80 },\n            { type:'text', width:100 },\n            { type:'text', width:100 },\n            { type:'text', width:200 },\n            { type:'text', width:100 },\n        ],\n    }]\n});\n\ndocument.getElementById(\"btn1\").onclick = () => worksheets[0].search('app');\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Columns\n    const columns = [\n        { type:'text', width:80 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n        { type:'text', width:200 },\n        { type:'text', width:100 },\n    ]\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet\n                    columns={columns}\n                    csv=\"/tests/demo.csv\"\n                    csvHeaders\n                    search\n                    pagination=\"10\"\n                    paginationOptions={[10,25,50,100]} />\n            </Spreadsheet>\n            <button onClick={() => spreadsheet.current[0].search('app')}>Search for APP</button>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet\n          :columns=\"columns\"\n          :csv=\"/tests/demo.csv\"\n          csvHeaders\n          search\n          :pagination=\"10\"\n          :paginationOptions=\"[10,25,50,100]\"\n      />\n  </Spreadsheet>\n  <input type='button' value='Search for APP' @click=\"search\" />\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\nexport default {\n  components: {\n      Spreadsheet,\n      Worksheet,\n  },\n  setup() {\n      const spreadsheet = ref(null);\n\n      const columns = [\n          { type:'text', width:80 },\n          { type:'text', width:100 },\n          { type:'text', width:100 },\n          { type:'text', width:200 },\n          { type:'text', width:100 },\n      ];\n\n      const search = () => {\n          spreadsheet.value.current[0].search('app');\n      };\n\n      return {\n          spreadsheet,\n          columns,\n          search\n      };\n  }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n        <input type='button' value='Search for APP' (click)=\"this.worksheets[0].search('app')\" />`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            csv: '/tests/demo.csv',\n            csvHeaders: true,\n            search: true,\n            pagination: 10,\n            paginationOptions: [10,25,50,100],\n            columns: [\n                { type:'text', width:80 },\n                { type:'text', width:100 },\n                { type:'text', width:100 },\n                { type:'text', width:200 },\n                { type:'text', width:100 },\n            ],\n        });\n    }\n}\n```\n \n## Related Tools\n\n- [Data Grid Filters](/jspreadsheet/docs/filters) - Filter data grid columns with custom criteria\n- [Data Grid Pagination](/jspreadsheet/docs/pagination) - Paginate large datasets\n- [Jspreadsheet Pro Search](https://jspreadsheet.com/docs/search) - Enhanced search with regex and highlighting (Pro)\n- [Jspreadsheet Pro Filters](https://jspreadsheet.com/docs/filters) - Advanced filtering capabilities (Pro)\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/selection.md",
    "content": "title: Spreadsheet Selection\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, excel-like selection, spreadsheet selection, cell selection, data grid selection, selection methods, range selection, multiple cell selection\ndescription: This section covers the properties, events, and methods for managing data grid selections, including range and multiple cell selection.\n\n# Spreadsheet Selection\n\nThis section covers the technical aspects of handling spreadsheet selections in Jspreadsheet, detailing the key properties, events, and methods for managing selection behaviour.\n\n{.pro}\n> #### What You Can Find in the Pro Version\n> The Pro version of Jspreadsheet enhances selection capabilities, offering features commonly found in advanced spreadsheet software:\n> - **Non-consecutive selection**: Users can hold `Ctrl` to select multiple ranges at once;\n> - **Custom borders**: Supports different border colours, which are helpful in real-time collaboration for identifying different users or creating custom features;\\\n>\n>\\\n> [Learn more](https://jspreadsheet.com/docs/selection){.button}\n\n## Documentation\n\n### Methods\n\nThe following methods manage selections in the Jspreadsheet data grid.\n\n#### Main Selection\n\n| Method                               | Description                                                                                                                                                                                                                        |\n|--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getSelection`                       | Get the coordinates of the main selection.<br/>`getSelection(preserveOrder: Boolean) : Array \\| null`                                                                                                                              |\n| `getHighlighted`                     | Get the coordinates of the highlighted selections.<br/>`getHighlighted() : Array \\| null`                                                                                                                                          |\n| `getRange`                           | Get the range description of the highlighted cells.<br/>`getRange() : String \\| null`                                                                                                                                              |\n| `getSelectedColumns`                 | Get the selected columns.<br/>@param `visibleOnly` - If true, the method returns only visible columns.<br/>`worksheetInstance.getSelectedColumns(visibleOnly?: boolean): number[];`                                                                                                                                                  |\n| `getSelectedRows`                    | Get the selected rows.<br/>@param `visibleOnly` - If true, the method returns only visible rows.<br/>`worksheetInstance.getSelectedRows(visibleOnly?: boolean): number[];`                                                                                                                                                        |\n| `getSelected`                        | Get the worksheet selected cell names or objects. <br/>@param {Boolean?} columnNameOnly: To get only the cell names as string (true). Get the cell coordinates as an object (false).  `worksheetInstance.getSelected(columnNameOnly: Boolean) => []` |\n| `isSelected`                         | Verify if the coordinates given are included in the current selection.<br/>`isSelected(x: Number, y: Number) : Boolean`                                                                                                            |\n| `selectAll`                          | Select all cells available in the data grid.<br/>`worksheetInstance.selectAll(): void;`                                                                                                                                                              |\n| `updateSelectionFromCoords`{.nowrap} | Select cells based on the given coordinates.<br/>@param `x1` - Column index of the first cell of the selection. If omitted or null, rows \"y1\" through \"y2\" are selected.<br/>@param `y1` - Row index of the first cell of the selection. If omitted or null, columns \"x1\" through \"x2\" are selected.<br/>@param `x2` - Column index of the last cell of the selection. Default: Parameter \"x1\".<br/>@param `y2` - Row index of the last cell of the selection. Default: Parameter \"y1\".<br/>`worksheetInstance.updateSelectionFromCoords: (x1: number \\| null, y1: number \\| null, x2?: number \\| null, y2?: number \\| null): void;`                                                                                            |\n| `resetSelection`                     | Remove the selection.<br/>@returns If there were highlighted cells, it returns 1, otherwise it returns 0.<br/>`worksheetInstance.resetSelection(): 0 \\| 1;`                                                                                                                                                                                |\n\n\n ### Selection Events\n\n| Event                        | Description                                                                                                                                                                         |\n|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onblur`                     | Occurs when the table is blurred.<br/>`onblur(instance: WorksheetInstance): void;`                                                                                                  |\n| `onfocus`                    | Occurs when the table is focused.<br/>`onfocus(instance: WorksheetInstance): void;`                                                                                                 |\n| `onbeforeselection`{.nowrap} | `onbeforeselection(worksheet: Object, x1: Number, y1: Number, x2: Number, y2: Number, e: MouseEvent) : void`                                                                        |\n| `onselection`                | `onselection(instance: WorksheetInstance, borderLeftIndex: number, borderTopIndex: number, borderRightIndex: number, borderBottomIndex: number, origin: Event \\| undefined): void;` |\n\n \n### Initial Settings\n\n| Property                | Description                  |\n|-------------------------|------------------------------|\n| `selectionCopy: boolean`  | Disable the clone selection. |\n\n\n## Examples\n\n### Programmatically Data Grid Selection\n\nDemonstrates how to select all cells within a worksheet in the grid programmatically.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p>\n<input type=\"button\" value=\"Select all\" id=\"btn1\" />\n<input type=\"button\" value=\"updateSelectionFromCoords(2,2,3,3)\" id=\"btn2\" /></p>\n\n<script>\n// Create the spreadsheet\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,6],\n    }],\n});\n\ndocument.getElementById(\"btn1\").onclick = () => table[0].selectAll();\ndocument.getElementById(\"btn2\").onclick = () => table[0].updateSelectionFromCoords(2,2,3,3);\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet minDimensions={[6,6]} />\n            </Spreadsheet>\n            <button onClick={() => spreadsheet.current[0].selectAll()}>Select all</button>\n            <button onClick={() => spreadsheet.current[0].updateSelectionFromCoords(2,2,3,3)}>Select coords (2,2,3,3)</button>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :minDimensions=\"[6,6]\" />\n    </Spreadsheet>\n    <input type=\"button\" value=\"Select all\" @click=\"selectAll\" />\n    <input type=\"button\" value=\"Select coords (2,2,3,3)\" @click=\"updateSelectionFromCoords(2,2,3,3)\" />\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const spreadsheet = ref(null);\n\n        const updateSelectionFromCoords = (...args) => {\n            spreadsheet.value.current[0].updateSelectionFromCoords(...args);\n        };\n\n        const selectAll = () => {\n            spreadsheet.value.current[0].selectAll();\n        };\n\n        return {\n            spreadsheet,\n            updateSelectionFromCoords,\n            selectAll\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `\n        <div #spreadsheet></div>\n        <input type=\"button\" value=\"Select all\" (click)=\"this.worksheets[0].selectAll()\" />\n        <input type=\"button\" value=\"Select coords (2,2,3,3)\" (click)=\"this.worksheets[0].updateSelectionFromCoords(2,2,3,3)\" />`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                {\n                    minDimensions: [6,6],\n                }\n            ]\n        });\n    }\n}\n```\n"
  },
  {
    "path": "docs/jspreadsheet/docs/sorting.md",
    "content": "title: Data Sorting\nkeywords: Jspreadsheet, spreadsheet, javascript, javascript table sorting, spreadsheet sorting, data grid sorting, custom sorting, data organization, sortable data grid, javascript data grid, column sorting, row sorting, excel-like sorting, Jexcel sorting, spreadsheet controls, data grid controls, dynamic data sorting\ndescription: Explore comprehensive information on Jspreadsheet data sorting, including sorting events, programmatic sorting, customization methods, and initial settings.\n\n# Data Sorting\n\nJspreadsheet CE enables developers to create custom sorting handlers to override the default data grid sorting behaviour. Rows can be sorted through the context menu, double-clicking column headers, or programmatically using the orderBy method. \n\n## Documentation\n\n### Methods\n\nThe following method can be invoked to execute sorting programmatically:\n\n| Method  | Description                                                                                                                                                                                              |\n| --------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| orderBy | Sort the data grid. <br/>@param {number} columnNumber - Sort by column number <br/>@param {boolean} direction: false (asc), true (desc) <br/>`orderBy(columnNumber: Number, direction: Boolean) : void` |\n \n\n### Events\n\n| Event        | Description                                                                                   |\n| -------------|-----------------------------------------------------------------------------------------------|\n| onsort       | `onsort(worksheet: Object, column: Number, direction: Number, newValue: Array) : void`        |\n\n \n### Initial Settings\n\nYou can define the sorting behaviour of your spreadsheet using the following properties:\n\n| Property                         | Description                                                                                                                       |\n|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|\n| sorting: function                | Defines a custom sorting handler. Use this to implement your specific sorting logic.<br/>`sorting(direction: Boolean) : function` |\n| columnSorting: boolean           | Enables or disables column-based sorting for the spreadsheet.`Default: true`                                                      |\n\n\n## Examples\n\n### Basic Sorting\n\nThe example below demonstrates sorting behaviour across various column types.\n\n{.small}\nDouble-click on any `data grid` column header below to observe sorting functionality.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><select id='btn2'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n<input type='button' value='Sort column' id=\"btn1\" /></p>\n\n<script>\n// Create the spreadsheet\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n        ]\n    }]\n});\ndocument.getElementById(\"btn1\").onclick = (e) => table[0].orderBy(e.target.previousElementSibling.value);\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    const select = useRef();\n    // Data\n    const data = [\n        ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n        ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n        ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n        ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n    ];\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} />\n            </Spreadsheet>\n            <select ref={select}>\n            <option value='0'>Column 1</option>\n            <option value='1'>Column 2</option>\n            <option value='2'>Column 3</option>\n            <option value='3'>Column 4</option>\n            </select>\n            <input type='button' value='Sort column'\n                onClick={() => spreadsheet.current[0].orderBy(select.current.value)} />\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" />\n    </Spreadsheet>\n    <select ref=\"select\">\n        <option value=\"0\">Column 1</option>\n        <option value=\"1\">Column 2</option>\n        <option value=\"2\">Column 3</option>\n        <option value=\"3\">Column 4</option>\n    </select>\n    <input type=\"button\" value=\"Sort column\" @click=\"sortColumn\" />\n</template>\n\n<script>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const spreadsheet = ref(null);\n        const select = ref(null);\n\n        const data = [\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n        ];\n\n        const sortColumn = () => {\n            const columnIndex = select.value.value;\n            spreadsheet.value.current[0].orderBy(columnIndex);\n        };\n\n        return {\n            spreadsheet,\n            select,\n            data,\n            sortColumn\n        };\n    }\n}\n</script>\n\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `\n        <div #spreadsheet></div>\n        <select id='columnNumber'>\n        <option value='0'>Column 1</option>\n        <option value='1'>Column 2</option>\n        <option value='2'>Column 3</option>\n        <option value='3'>Column 4</option>\n        </select>\n        <input type='button' value='Sort column'\n            (click)=\"this.worksheets[0].orderBy(this.select.nativeElement.value)\">`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    @ViewChild(\"select\") select: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n                    ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n                    ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n                    ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n                ]\n            }]\n        });\n    }\n}\n```\n\n### Custom Sorting Handler\n\nThe example below demonstrates how to customize spreadsheet sorting behaviour using the `sorting` property.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Spreadsheets', 1],\n            ['Grids', 2],\n            ['Tables', 3],\n            ['Plugins', 4],\n            ['', ''],\n            ['', ''],\n            ['', ''],\n            ['', ''],\n        ],\n        columns: [\n            { type: 'text', width:200 },\n            { type: 'text', width:400 },\n        ],\n    }],\n    sorting: function(direction, column) {\n        return function(a, b) {\n            let valueA = a[1];\n            let valueB = b[1];\n\n            // Consider blank rows in the sorting\n            if (! direction) {\n                return (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;\n            } else {\n                return (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;\n            }\n        }\n    }\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Spreadsheets', 1],\n        ['Grids', 2],\n        ['Tables', 3],\n        ['Plugins', 4],\n        ['', ''],\n        ['', ''],\n        ['', ''],\n        ['', ''],\n    ];\n    // Columns\n    const columns = [\n        { type: 'text', width:200 },\n        { type: 'text', width:400 },\n    ];\n    // Sorting handler\n    const sorting = (direction, column) => {\n        return (a, b) => {\n            let valueA = a[1];\n            let valueB = b[1];\n\n            // Consider blank rows in the sorting\n            if (! direction) {\n                return (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;\n            } else {\n                return (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;\n            }\n        }\n    }\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} sorting={sorting}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" :sorting=\"sorting\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Create the data grid component\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    methods: {\n        // Sorting handler\n        sorting(direction, column) {\n            return (a, b) => {\n                let valueA = a[1];\n                let valueB = b[1];\n\n                // Consider blank rows in the sorting\n                if (! direction) {\n                    return (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;\n                } else {\n                    return (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;\n                }\n            }\n        }\n    },\n    data() {\n        // Data\n        const data = [\n            ['Spreadsheets', 1],\n            ['Grids', 2],\n            ['Tables', 3],\n            ['Plugins', 4],\n            ['', ''],\n            ['', ''],\n            ['', ''],\n            ['', ''],\n        ];\n        // Columns\n        const columns = [\n            { type: 'text', width:200 },\n            { type: 'text', width:400 },\n        ];\n\n        return {\n            data,\n            columns,\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Spreadsheets', 1],\n                    ['Grids', 2],\n                    ['Tables', 3],\n                    ['Plugins', 4],\n                    ['', ''],\n                    ['', ''],\n                    ['', ''],\n                    ['', ''],\n                ],\n                columns: [\n                    { type: 'text', width:200 },\n                    { type: 'text', width:400 },\n                ],\n            }],\n            sorting: function(direction, column) {\n                return function(a, b) {\n                    let valueA = a[1];\n                    let valueB = b[1];\n\n                    // Consider blank rows in the sorting\n                    if (! direction) {\n                        return (valueA > valueB) ? 1 : (valueA < valueB) ? -1 : 0;\n                    } else {\n                        return (valueA > valueB) ? -1 : (valueA < valueB) ? 1 : 0;\n                    }\n                }\n            }\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [Data Grid Filters](/jspreadsheet/docs/filters) - Filter data before sorting\n- [Jspreadsheet Pro Sorting](https://jspreadsheet.com/docs/sorting) - Advanced sorting with multi-column support (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/style.md",
    "content": "title: Spreadsheet Style\nkeywords: Jspreadsheet, Jexcel, data grid, JavaScript, Excel-like style, spreadsheet cell style, table style, style, themes, style methods, style events, cell formatting, spreadsheet customization, grid style customization, data grid aesthetics, CSS for data grid, style settings, Jspreadsheet design, dynamic styling\ndescription: Learn how to apply CSS styles to cells in Jspreadsheet, including settings, events, and programmatic styling methods.\n\n# Spreadsheet Style\n\nJspreadsheet allows flexible styling directly on spreadsheet cells using CSS, thanks to its DOM-based structure. While you can apply external CSS styles to customize the appearance, these styles don’t automatically track changes or save to Jspreadsheet’s internal history. For a more dynamic approach, Jspreadsheet provides built-in settings and methods that let you apply, update, and manage cell styles programmatically.  \n\n{.pro}\n> #### What You Can Find in the Pro Version\n>\n> The **Pro version** of Jspreadsheet offers enhanced performance and includes:\n> - A global style property at the spreadsheet level, allowing styles to be reused across different worksheets.\n> - Support row or column styling using Excel-like syntax, such as `A:A` for columns or `1:1` for rows.\n>\n>\n> \\\n> [Learn more](https://jspreadsheet.com/docs/style){.button}\n\n## Documentation\n\n### Methods\n\nYou can manage the spreadsheet cell styles using the following methods:\n\n| Method                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\n|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `getStyle`            | Get styles from one or all cells.<br/>@param `cell` - Name or coordinate of a cell. If omitted, returns styles for all cells.<br/>@param `key` - Style property. if specified, returns only that property. Otherwise, it returns all the cell's style properties.<br/>`worksheetInstance.getStyle(cell?: string \\| [number, number], key?: string): string \\| Record<string, string>;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\n| `setStyle`            | Change a single style of one or more cells.<br/>@param `cells` - Name of a cell.<br/>@param `k` - property to be changed.<br/>@param `v` - New property value. If equal to the property's current value and the \"force\" parameter is false, removes that property from the style.<br/>@param `force` - If true, changes the value of the property even if the cell is read-only. Also, if true, even if the new value of the property is the same as the current one, the property is not removed.<br>`worksheetInstance.setStyle(cells: string, k: string, v: string, force?: boolean): void;`<br/><br/>Change cell styles.<br/>@param `o` - Object where each key is the name of a cell and each value is the style changes for that cell. Each value can be a string with css styles separated by semicolons or an array where each item is a string with a css style.<br/>@param `k` - It is not used.<br/>@param `v` - It is not used.<br/>@param `force` - If true, changes the value of the property even if the cell is read-only. Also, if true, even if the new value of the property is the same as the current one, the property is not removed.<br>`worksheetInstance.setStyle(cells: Record<string, string \\| string[]>, k?: null \\| undefined, v?: null \\| undefined, force?: boolean): void;` |\n| `resetStyle`{.nowrap} | Reset styles of one or more cells.<br/>@param `cells` - Object whose keys are the names of the cells that must have their styles reset.<br/>@param `ignoreHistoryAndEvents` - If true, do not add this action to history.<br/>`worksheetInstance.resetStyle(cells: Record<string, any>, ignoreHistoryAndEvents?: boolean): void;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n\n\n### Events\n\nEvents related to spreadsheet styling.\n\n| Event            | Description                                                                                                                                                                            |\n|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onchangestyle`  | Triggered when a style change occurs, returning an object with affected cells and properties.<br/>`onchangestyle(instance: WorksheetInstance, changes: Record<string, string>): void;` |\n\n\n### Initial Settings\n\nThe following property is available during the initialization of the online spreadsheet:\n\n| Property                                 | Description                                                                                                                         |\n|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------|\n| `style: Record<string, string>`{.nowrap} | Defines initial styles for cells. Each object property corresponds to a cell name or range, with values representing a CSS string.  |\n\n\n## Examples\n\n### Style at the Worksheet Level\n\nApply style definitions directly at the worksheet level.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [6,4],\n        style: {\n            'E1': 'background-color: #ccffff;',\n        },\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Style\n    const style = {\n        'E1': 'background-color: #ccffff;',\n    }\n\n    // Render component\n    return (<Spreadsheet ref={spreadsheet}>\n        <Worksheet style={style} minDimensions={[6,4]} />\n    </Spreadsheet>);\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :style=\"style\" :minDimensions=\"[6,4]\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        // Style\n        const style = {\n            'E1': 'background-color: #ccffff;',\n        }\n        // Return object\n        return {\n            style\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                {\n                    minDimensions: [6,4],\n                    style: {\n                        'E1': 'background-color: #ccffff;',\n                    },\n                }\n            ]\n        });\n    }\n}\n```\n\n### Programmatic Updates\n\nDefine cell styles during initialization and modify them using `getStyle` and `setStyle` methods after initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\nlet w = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['US', 'Cheese', 'Yes', '2019-02-12'],\n            ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n            ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n            ['BR', 'Oranges', 'Yes', '2019-01-12'],\n        ],\n        columns: [\n            {\n                type: 'dropdown',\n                title: 'Product Origin',\n                width: 300,\n                url: '/jspreadsheet/countries', // Remote source for your dropdown\n                autocomplete: true,\n                multiple: true\n            },\n            {\n                type: 'text',\n                title: 'Description',\n                width: 200\n            },\n            {\n                type: 'dropdown',\n                title: 'Stock',\n                width: 100 ,\n                source: ['No','Yes']\n            },\n            {\n                type: 'calendar',\n                title: 'Best before',\n                width: 100\n            },\n        ],\n        style: {\n            A1:'background-color: orange;',\n            B1:'background-color: orange;',\n        },\n    }]\n});\n\ndocument.getElementById(\"btn1\").onclick = () => w[0].setStyle('A2', 'background-color', 'yellow');\ndocument.getElementById(\"btn2\").onclick = () => w[0].setStyle({ A3:'font-weight: bold;', B3:'background-color: yellow;' });\ndocument.getElementById(\"btn3\").onclick = () => document.getElementById('console').innerHTML = w[0].getStyle('A1');\ndocument.getElementById(\"btn4\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(w[0].getStyle());\n</script>\n\n<p><textarea id='console' style=\"width:100%;max-width:600px;height:100px;\"></textarea></p>\n\n<input type=\"button\" value=\"Set A2 background\" id=\"btn1\">\n<input type=\"button\" value=\"Change A3, B3 style\" id=\"btn2\">\n<input type=\"button\" value=\"Get A1 style\" id=\"btn3\">\n<input type=\"button\" value=\"Get the table style\" id=\"btn4\">\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    const console = useRef();\n    const data = [\n        ['US', 'Cheese', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n        ['BR', 'Oranges', 'Yes', '2019-01-12'],\n    ];\n    const columns = [\n        {\n            type: 'dropdown',\n            title: 'Product Origin',\n            width: 300,\n            url: '/jspreadsheet/countries', // Remote source for your dropdown\n            autocomplete: true,\n            multiple: true\n        },\n        {\n            type: 'text',\n            title: 'Description',\n            width: 200\n        },\n        {\n            type: 'dropdown',\n            title: 'Stock',\n            width: 100 ,\n            source: ['No','Yes']\n        },\n        {\n            type: 'calendar',\n            title: 'Best before',\n            width: 100\n        },\n    ];\n    const style = {\n        A1:'background-color: orange;',\n        B1:'background-color: orange;',\n    };\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} style={style} />\n            </Spreadsheet>\n            <textarea ref={console} style={{ width: '100%', maxWidth: '600px', height: '100px'}}></textarea>\n            <input type=\"button\" value=\"Set A2 background\"\n                onClick={() => spreadsheet.current[0].setStyle('A2', 'background-color', 'yellow')} />\n            <input type=\"button\" value=\"Change A3, B3 style\"\n                onClick={() => spreadsheet.current[0].setStyle({ A3:'font-weight: bold;', B3:'background-color: yellow;' })} />\n            <input type=\"button\" value=\"Get A1 style\"\n                onClick={() => console.current.innerHTML = spreadsheet.current[0].getStyle('A1')} />\n            <input type=\"button\" value=\"Get the table style\"\n                onClick={() => console.current.innerHTML = JSON.stringify(spreadsheet.current[0].getStyle())} />\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" :style=\"style\" />\n    </Spreadsheet>\n    <textarea ref='console' style='width:100%;max-width:600px;height:100px;'></textarea>\n    <input type=\"button\" value=\"Set A2 background\"\n        @click=\"setStyle('A2', 'background-color', 'yellow')\" />\n    <input type=\"button\" value=\"Change A3, B3 style\"\n        @click=\"setStyle({ A3:'font-weight: bold;', B3:'background-color: yellow;' })\" />\n    <input type=\"button\" value=\"Get A1 style\"\n        @click=\"console.value.innerHTML = getStyle('A1')\" />\n    <input type=\"button\" value=\"Get the table style\"\n        @click=\"console.value.innerHTML = JSON.stringify(getStyle())\" />\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        const data = [\n            ['US', 'Cheese', 'Yes', '2019-02-12'],\n            ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n            ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n            ['BR', 'Oranges', 'Yes', '2019-01-12'],\n        ];\n        const columns = [\n            {\n                type: 'dropdown',\n                title: 'Product Origin',\n                width: 300,\n                url: '/jspreadsheet/countries', // Remote source for your dropdown\n                autocomplete: true,\n                multiple: true\n            },\n            {\n                type: 'text',\n                title: 'Description',\n                width: 200\n            },\n            {\n                type: 'dropdown',\n                title: 'Stock',\n                width: 100 ,\n                source: ['No','Yes']\n            },\n            {\n                type: 'calendar',\n                title: 'Best before',\n                width: 100\n            },\n        ];\n        const style = {\n            A1:'background-color: orange;',\n            B1:'background-color: orange;',\n        };\n\n        // Return object\n        return {\n            data,\n            columns,\n            style,\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `\n        <div #spreadsheet></div>\n        <textarea ref='console' style='width:100%;max-width:600px;height:100px;'></textarea>\n        <input type=\"button\" value=\"Set A2 background\"\n            (click)=\"this.worksheets[0].setStyle('A2', 'background-color', 'yellow')} />\n        <input type=\"button\" value=\"Change A3, B3 style\"\n            (click)=\"this.worksheets[0].setStyle({ A3:'font-weight: bold;', B3:'background-color: yellow;' })} />\n        <input type=\"button\" value=\"Get A1 style\"\n            (click)=\"console.current.innerHTML = this.worksheets[0].getStyle('A1')} />\n        <input type=\"button\" value=\"Get the table style\"\n            (click)=\"console.current.innerHTML = JSON.stringify(this.worksheets[0].getStyle())} />\n    `\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            data: [\n                ['US', 'Cheese', 'Yes', '2019-02-12'],\n                ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n                ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n                ['BR', 'Oranges', 'Yes', '2019-01-12'],\n            ],\n            columns: [\n                {\n                    type: 'dropdown',\n                    title: 'Product Origin',\n                    width: 300,\n                    url: '/jspreadsheet/countries', // Remote source for your dropdown\n                    autocomplete: true,\n                    multiple: true\n                },\n                {\n                    type: 'text',\n                    title: 'Description',\n                    width: 200\n                },\n                {\n                    type: 'dropdown',\n                    title: 'Stock',\n                    width: 100 ,\n                    source: ['No','Yes']\n                },\n                {\n                    type: 'calendar',\n                    title: 'Best before',\n                    width: 100\n                },\n            ],\n            style: {\n                A1:'background-color: orange;',\n                B1:'background-color: orange;',\n            },\n        });\n    }\n}\n```\n\n{.pro}\n> **Upgrade to Jspreadsheet Pro for Advanced Styling**\n>\n> Jspreadsheet CE provides basic programmatic cell styling. **Jspreadsheet Pro** adds professional styling capabilities:\n>\n> **Enhanced Styling API:**\n> - **Global Styles:** Define styles at spreadsheet level, reuse across worksheets\n> - **Row/Column Styling:** Style entire rows/columns with `A:A` or `1:1` syntax\n> - **Range Styling:** Apply styles to cell ranges (e.g., `A1:D10`) in one command\n> - **Style Inheritance:** Cascade styles from worksheet → column → cell\n> - **Style Templates:** Reusable named style templates for consistent branding\n> - **Batch Styling:** Style thousands of cells efficiently with optimized API\n>\n> **Conditional Styling:**\n> - **Value-Based Styles:** Auto-apply styles based on cell values (>100 = green)\n> - **Formula-Based Styles:** Apply styles using conditional formulas\n> - **Data Bars:** In-cell horizontal bars showing relative values\n> - **Color Scales:** 2-color and 3-color gradients based on value ranges\n> - **Icon Sets:** Visual icons (arrows, flags, traffic lights) based on values\n> - **Duplicate Highlighting:** Auto-highlight duplicate values\n>\n> **Rich Text & Advanced Formatting:**\n> - **Rich Text in Cells:** Bold, italic, underline, colors within single cell\n> - **Cell-Level Fonts:** Different fonts, sizes, colors per cell\n> - **Advanced Borders:** All Excel border styles (thick, thin, dashed, double, custom colors)\n> - **Border Styles:** Top, bottom, left, right, diagonal borders per cell\n> - **Gradient Fills:** Linear and radial gradients for cell backgrounds\n> - **Pattern Fills:** Dots, stripes, crosshatch patterns\n>\n> **Professional Features:**\n> - **Style Import/Export:** Import/export Excel styles with full fidelity\n> - **Theme Support:** Pre-built professional themes (Material, Corporate, Dark mode)\n> - **Style Events:** Track style changes with advanced event system\n> - **Performance:** Optimized for styling large datasets (100K+ cells)\n> - **Responsive Styles:** Different styles for mobile/tablet/desktop\n> - **Print Styles:** Specific styles for printing/PDF export\n>\n> Perfect for dashboards, reports, and applications requiring professional visual presentation.\n>\n> **[Explore Pro Styling →](https://jspreadsheet.com/docs/style)** | **[Compare Editions →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n## Related Tools\n\n- [CE Themes](/jspreadsheet/docs/themes) - Pre-built themes for Jspreadsheet CE\n- [Jspreadsheet Pro Appearance](https://jspreadsheet.com/docs/appearance) - Visual attributes and theming (Pro)\n- [Conditional Formatting](https://jspreadsheet.com/docs/conditional-formatting) - Dynamic styling based on values (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/tests.md",
    "content": "title: Unit Tests for Jspreadsheet\nkeywords: Jspreadsheet, Jexcel, JavaScript, Web-based Applications, Web-based Spreadsheet, Unit Tests\ndescription: Enhance your application quality by creating unit tests for Jspreadsheet.\n\n# Testing\n\n## Introduction\n\nUnit testing ensures robust Jspreadsheet integrations by validating functionality and catching regressions early. This guide uses Mocha, Chai, and JSDOM to test data grid behaviour in a simulated browser environment.\n\n## Environment Setup\n\nSet up a basic environment to implement Jspreadsheet testing. Follow these steps or clone our GitHub example project.\n\n### Step 1: Clone the Project\n\nClone a setup with Jspreadsheet pre-installed and a test file:\n\n```bash\ngit clone https://github.com/jspreadsheet/tests.git\ncd tests\n```\n\n### Step 2: Install the Dependencies\n\nInstall required libraries using npm:\n\n```bash\nnpm install jspreadsheet-ce@5.0.0-beta.3\n```\n\n### Step 3: Configure Mocha\n\nCreate or edit the `mocha.config.js` file in the project root to configure Mocha and emulate the browser environment using JSDOM.\n\n{.ignore}\n```javascript\n#! /usr/bin/env node\n\nrequire('jsdom-global')(undefined, { url: 'https://localhost' });\nconst jspreadsheet = require('jspreadsheet');\n\nglobal.jspreadsheet = jspreadsheet;\n\nglobal.root = document.createElement('div');\nglobal.root.style.width = '100%';\nglobal.root.style.height = '100%';\ndocument.body.appendChild(global.root);\n\nexports.mochaHooks = {\n    afterEach(done) {\n        jspreadsheet.destroyAll();\n        done();\n    },\n};\n```\n\n### Step 4: Create your first test\n\nInside the `test` folder, create a file named `data.js`:\n\n{.ignore}\n```javascript\nconst { expect } = require('chai');\n\ndescribe(\"Data\", () => {\n  it(\"Testing data\", () => {\n    let test = jspreadsheet(root, {\n      worksheets: [\n        {\n          data: [\n            [\"Mazda\", 2001, 2000],\n            [\"Peugeot\", 2010, 5000],\n            [\"Honda Fit\", 2009, 3000],\n            [\"Honda CRV\", 2010, 6000],\n          ],\n          minDimensions: [4, 4]\n        },\n      ],\n    });\n\n    expect(test[0].getValue(\"A1\", true)).to.equal(\"Mazda\");\n    expect(test[0].getValue(\"A2\", true)).to.equal(\"Peugeot\");\n    expect(test[0].getValue(\"B1\", true)).to.equal(\"2001\");\n  });\n});\n```\n\n### Step 5: Running the Tests\n\nAfter writing your tests, you can run them with the following command:\n\n```bash\nnpm run test\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/themes.md",
    "content": "title: Jspreadsheet CE Theme Editor - Customize Your Data Grid Styles\nkeywords: Jspreadsheet, data grid customization, JavaScript spreadsheet, Excel-like applications, theme editor, visual styling, colour customization, dynamic themes, interactive spreadsheets, web-based spreadsheet tool\ndescription: Customize your Jspreadsheet data grid with the Theme Editor for unique, visually appealing designs, including colour and dynamic theme options that align with your application’s style.\ncanonical: https://bossanova.uk/jspreadsheet/docs/themes\n\n# Data Grid Theme Editor\n\nThe Data Grid Theme Editor is an online tool for customizing spreadsheet colours in Jspreadsheet. To enable this feature, add the CSS file jspreadsheet.themes.css to your project. Then, configure the CSS variables, as shown in the example below, to implement your custom theme.\n\n{.green}\n> To use themes in Jspreadsheet, include `jspreadsheet.themes.css` in your project and add the CSS variables as shown in the example below.\n\n<lm-themes></lm-themes>\n\n{.ignore-execution}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.themes.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        {\n            minDimensions: [6,10],\n        }\n    ]\n});\n</script>\n```\n```jsx\nimport React, { useRef, useState } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet/react\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport 'jspreadsheet/dist/jspreadsheet.themes.css';\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet>\n        <Worksheet />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\nimport 'jspreadsheet/dist/jspreadsheet.themes.css';\n\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet\n    },\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\nimport 'jspreadsheet/dist/jspreadsheet.themes.css';\n\n\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`;\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                minDimensions: [10,10],\n            }]\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [CE Style Guide](/jspreadsheet/docs/style) - Custom cell styling in Jspreadsheet CE\n- [Jspreadsheet Pro Appearance](https://jspreadsheet.com/docs/appearance) - Advanced theming and visual customization (Pro)\n- [Jspreadsheet Pro Themes](https://jspreadsheet.com/docs/themes) - Professional pre-built themes (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/toolbars.md",
    "content": "title: Data Grid Toolbars\nkeywords: Jspreadsheet, Jexcel, data grid, javascript, excel-like toolbar, spreadsheet toolbar, table toolbar, toolbars, data grid toolbars, customizable toolbars, toolbar customization, toolbar integration, interactive data grid, JavaScript data grid, dynamic toolbar, toolbar controls.\ndescription: Learn how to create and customize toolbar controls in Jspreadsheet. This guide covers adding custom buttons, configuring built-in tools, and extending the toolbar to fit your application's needs.\n\n# Data Grid Toolbars\n\nThis section covers all methods, settings, and events for the data grid toolbar, including advanced customization options, control integration, and key configuration settings. \n\n{.green}\n> **Icons**\n> \n> The use of Material icons style sheet is required for utilizing the toolbar.\n> \n> ```<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />```\n\n## Documentation\n\n### Methods\n\nThe following methods manage the visibility state of the toolbar:\n\n| Method         | Description                                                        |\n|----------------|--------------------------------------------------------------------|\n| `showToolbar`  | Displays the toolbar.<br/>`worksheet.parent.showToolbar() : void`  |\n| `hideToolbar`  | Hides the toolbar.<br/>`worksheet.parent.hideToolbar() : void`     |\n\n \n\n### Settings\n\nCustomize toolbar items during initialization using the following property:\n\n| Property                                                                                  | Description                                                                                                                                            |\n|-------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `toolbar: boolean \\| ToolbarItem[] \\| ((defaultToolbar: ToolbarItem[]) => ToolbarItem[])` | Enables toolbar customization. Options include: `true` for the default toolbar, a function for dynamic setup, or an array for specific configurations. |\n\n\n### Toolbar Object\n\nCustomize toolbar items during initialization using the properties below.\n\n#### Toolbar General Properties\n\n| Property     | Description                                     |\n|--------------|-------------------------------------------------|\n| `container`  | Displays a border around the toolbar container. |\n| `badge`      | Adds a badge for each toolbar element.          |\n| `title`      | Shows a title below each icon.                  |\n| `responsive` | Enables a responsive toolbar. `Default: false`  |\n| `items`      | Array of items to display in the toolbar.       |\n\n#### Toolbar Item Properties\n\n| Property             | Description                                                             |\n|----------------------|-------------------------------------------------------------------------|\n| `type: string`       | Specifies the element type: `icon` \\| `divisor` \\| `label` \\| `select`. |\n| `content: string`    | Defines the content of the toolbar element.                             |\n| `title: boolean`     | Sets the tooltip for the toolbar element.                               |\n| `width: number`      | Width of the toolbar element.                                           |\n| `active: boolean`    | Initial active state for the toolbar element.                           |\n| `class: string`      | CSS class for each toolbar element.                                     |\n| `value: number`      | Initially selected option for `select` type.                            |\n| `render: function`   | Render function for dropdown elements when `type: select`.              |\n| `onclick: function`  | Callback for when an item is clicked.                                   |\n| `onchange: function` | Callback for when a new item is selected (`select` type only).          |\n\n\n## Examples\n\n### Toolbar visibility\n\nThe example below shows how to enable the default data grid toolbar and programmatically control its visibility after initialization. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><input type='button' value='Show Toolbar' id=\"btn1\">\n<input type='button' value='Hide Toolbar' id=\"btn2\"></p>\n\n<script>\n// Create the spreadsheet\nlet grid = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        { minDimensions: [6,6] },\n    ],\n    toolbar: true,\n});\n\ndocument.getElementById(\"btn1\").onclick = () => grid[0].parent.showToolbar();\ndocument.getElementById(\"btn2\").onclick = () => grid[0].parent.hideToolbar();\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} toolbar>\n                <Worksheet minDimensions={[6,6]} />\n            </Spreadsheet>\n            <input type='button' value='Show Toolbar' onClick={() => spreadsheet.current[0].parent.showToolbar()} />\n            <input type='button' value='Hide Toolbar' onClick={() => spreadsheet.current[0].parent.hideToolbar()} />\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" toolbar>\n        <Worksheet :minDimensions=\"[6,6]\" />\n    </Spreadsheet>\n    <input type=\"button\" value=\"Show Toolbar\" @click=\"showToolbar\" />\n    <input type=\"button\" value=\"Hide Toolbar\" @click=\"hideToolbar\" />\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    methods: {\n        showToolbar() {\n            this.$refs.spreadsheet.current[0].showToolbar();\n        },\n        hideToolbar() {\n            this.$refs.spreadsheet.current[0].hideToolbar();\n        },\n    },\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `\n        <div #spreadsheet></div>\n        <input type=\"button\" value=\"Show Toolbar\" (click)=\"this.worksheets[0].showToolbar();\" />\n        <input type=\"button\" value=\"Hide Toolbar\" (click)=\"this.worksheets[0].hideToolbar();\" />`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                { minDimensions: [6,6] },\n            ],\n            toolbar: true\n        });\n    }\n}\n```\n\n### Toolbar Custom Handler\n\nThe toolbar property can be a function that allows you to add custom items to the default toolbar without rebuilding it from scratch. See the example below. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: function(toolbar) {\n        // Add a new custom item in the end of my toolbar\n        toolbar.items.push({\n            tooltip: 'My custom item',\n            content: 'share',\n            onclick: function() {\n                alert('Custom click');\n            }\n        });\n\n        return toolbar;\n    },\n    worksheets: [{\n        minDimensions: [6,6],\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Toolbar handler\n    const toolbar = (toolbar) => {\n        // Add a new custom item in the end of my toolbar\n        toolbar.items.push({\n            tooltip: 'My custom item',\n            content: 'share',\n            onclick: function() {\n                alert('Custom click');\n            }\n        });\n        return toolbar;\n    }\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} toolbar={toolbar}>\n            <Worksheet minDimensions={[6,6]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" toolbar>\n        <Worksheet :minDimensions=\"[6,6]\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    methods: {\n        // Toolbar handler\n        toolbar(toolbar) {\n            // Add a new custom item in the end of my toolbar\n            toolbar.items.push({\n                tooltip: 'My custom item',\n                content: 'share',\n                onclick: function() {\n                    alert('Custom click');\n                }\n            });\n            return toolbar;\n        }\n    },\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            toolbar: function(toolbar) {\n                // Add a new custom item in the end of my toolbar\n                toolbar.items.push({\n                    tooltip: 'My custom item',\n                    content: 'share',\n                    onclick: function() {\n                        alert('Custom click');\n                    }\n                });\n\n                return toolbar;\n            },\n            worksheets: [{\n                minDimensions: [6,6],\n            }]\n        });\n    }\n}\n```\n\n### Custom Toolbar Object\n\nThe toolbar property allows you to customize the items in the spreadsheet toolbar fully.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet customToolbar = {\n    items: [\n        {\n            content: 'save',\n            onclick: function () {\n                jspreadsheet.current.download();\n            }\n        },\n        {\n            type:'divisor',\n        },\n        {\n            type:'select',\n            width: '160px',\n            options: [ 'Verdana', 'Arial', 'Courier New' ],\n            render: function(e) {\n                return '<span style=\"font-family:' + e + '\">' + e + '</span>';\n            },\n            onchange: function(a,b,c,d) {\n                jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-family', d);\n            }\n        },\n        {\n            type: 'i',\n            content: 'format_bold',\n            onclick: function(a,b,c) {\n                jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-weight', 'bold');\n            }\n        },\n        {\n            type: 'i',\n            content: 'format_italic',\n            onclick: function(a,b,c) {\n                jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-style', 'italic');\n            }\n        },\n        {\n            content: 'search',\n            onclick: function(a,b,c) {\n                if (c.children[0].innerText == 'search') {\n                    jspreadsheet.current.showSearch();\n                    c.children[0].innerText = 'search_off';\n                } else {\n                    jspreadsheet.current.hideSearch();\n                    c.children[0].innerText = 'search';\n                }\n            },\n            tooltip: 'Toggle Search',\n            updateState: function(a,b,c,worksheet) {\n                // Call this one when the worksheet is opened and on the selection of any cells\n                if (worksheet.options.search == true) {\n                    c.children[0].innerText = 'search_off';\n                } else {\n                    c.children[0].innerText = 'search';\n                }\n            }\n        }\n    ]\n}\n\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        worksheetName: 'Toolbar example',\n        minDimensions: [6,6],\n    }],\n    toolbar: customToolbar\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Toolbar handler\n    const toolbar = {\n        items: [{\n                content: 'save',\n                onclick: function () {\n                    spreadsheet.current.download();\n                }\n            },\n            {\n                type:'divisor',\n            },\n            {\n                type:'select',\n                width: '160px',\n                options: [ 'Verdana', 'Arial', 'Courier New' ],\n                render: function(e) {\n                    return '<span style=\"font-family:' + e + '\">' + e + '</span>';\n                },\n                onchange: function(a,b,c,d) {\n                    spreadsheet.current.setStyle(spreadsheet.current.getSelected(), 'font-family', d);\n                }\n            },\n            {\n                type: 'i',\n                content: 'format_bold',\n                onclick: function(a,b,c) {\n                    spreadsheet.current.setStyle(spreadsheet.current.getSelected(), 'font-weight', 'bold');\n                }\n            },\n            {\n                type: 'i',\n                content: 'format_italic',\n                onclick: function(a,b,c) {\n                    spreadsheet.current.setStyle(spreadsheet.current.getSelected(), 'font-style', 'italic');\n                }\n            },\n            {\n                content: 'search',\n                onclick: function(a,b,c) {\n                    if (c.children[0].innerText == 'search') {\n                        spreadsheet.current.showSearch();\n                        c.children[0].innerText = 'search_off';\n                    } else {\n                        spreadsheet.current.hideSearch();\n                        c.children[0].innerText = 'search';\n                    }\n                },\n                tooltip: 'Toggle Search',\n                updateState: function(a,b,c,worksheet) {\n                    // Call this one when the worksheet is opened and on the selection of any cells\n                    if (worksheet.options.search == true) {\n                        c.children[0].innerText = 'search_off';\n                    } else {\n                        c.children[0].innerText = 'search';\n                    }\n                }\n            }\n        ]\n    }\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} toolbar={toolbar}>\n            <Worksheet minDimensions={[6,6]} worksheetName={\"Toolbar example\"} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" toolbar>\n        <Worksheet :minDimensions=\"[6,6]\" worksheetName=\"Toolbar example\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        // Toolbar handler\n        const toolbar = {\n            items: [{\n                    content: 'save',\n                    onclick: function () {\n                        jspreadsheet.current.download();\n                    }\n                },\n                {\n                    type:'divisor',\n                },\n                {\n                    type:'select',\n                    width: '160px',\n                    options: [ 'Verdana', 'Arial', 'Courier New' ],\n                    render: function(e) {\n                        return '<span style=\"font-family:' + e + '\">' + e + '</span>';\n                    },\n                    onchange: function(a,b,c,d) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-family', d);\n                    }\n                },\n                {\n                    type: 'i',\n                    content: 'format_bold',\n                    onclick: function(a,b,c) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-weight', 'bold');\n                    }\n                },\n                {\n                    type: 'i',\n                    content: 'format_italic',\n                    onclick: function(a,b,c) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-style', 'italic');\n                    }\n                },\n                {\n                    content: 'search',\n                    onclick: function(a,b,c) {\n                        if (c.children[0].innerText == 'search') {\n                            jspreadsheet.current.showSearch();\n                            c.children[0].innerText = 'search_off';\n                        } else {\n                            jspreadsheet.current.hideSearch();\n                            c.children[0].innerText = 'search';\n                        }\n                    },\n                    tooltip: 'Toggle Search',\n                    updateState: function(a,b,c,worksheet) {\n                        // Call this one when the worksheet is opened and on the selection of any cells\n                        if (worksheet.options.search == true) {\n                            c.children[0].innerText = 'search_off';\n                        } else {\n                            c.children[0].innerText = 'search';\n                        }\n                    }\n                }\n            ]\n        }\n\n        return {\n            toolbar,\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Custom toolbar definitions\n        let customToolbar = {\n            items: [\n                {\n                    content: 'save',\n                    onclick: function () {\n                        jspreadsheet.current.download();\n                    }\n                },\n                {\n                    type: 'divisor',\n                },\n                {\n                    type: 'select',\n                    width: '160px',\n                    options: ['Verdana', 'Arial', 'Courier New'],\n                    render: function (e) {\n                        return '<span style=\"font-family:' + e + '\">' + e + '</span>';\n                    },\n                    onchange: function (a, b, c, d) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-family', d);\n                    }\n                },\n                {\n                    type: 'i',\n                    content: 'format_bold',\n                    onclick: function (a, b, c) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-weight', 'bold');\n                    }\n                },\n                {\n                    type: 'i',\n                    content: 'format_italic',\n                    onclick: function (a, b, c) {\n                        jspreadsheet.current.setStyle(jspreadsheet.current.getSelected(), 'font-style', 'italic');\n                    }\n                },\n                {\n                    content: 'search',\n                    onclick: function (a, b, c) {\n                        if (c.children[0].innerText == 'search') {\n                            jspreadsheet.current.showSearch();\n                            c.children[0].innerText = 'search_off';\n                        } else {\n                            jspreadsheet.current.hideSearch();\n                            c.children[0].innerText = 'search';\n                        }\n                    },\n                    tooltip: 'Toggle Search',\n                    updateState: function (a, b, c, worksheet) {\n                        // Call this one when the worksheet is opened and on the selection of any cells\n                        if (worksheet.options.search == true) {\n                            c.children[0].innerText = 'search_off';\n                        } else {\n                            c.children[0].innerText = 'search';\n                        }\n                    }\n                }\n            ]\n        }\n\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                worksheetName: 'Toolbar example',\n                minDimensions: [6, 6],\n            }],\n            toolbar: customToolbar\n        });\n    }\n}\n```\n\n## Related Tools\n\n- [JavaScript Toolbar](https://jsuites.net/docs/toolbar) - Standalone toolbar component with customizable items\n- [Jspreadsheet Pro Toolbar](https://jspreadsheet.com/docs/toolbar) - Advanced toolbar customization with plugins (Pro)\n"
  },
  {
    "path": "docs/jspreadsheet/docs/upgrade-from-v4-to-v5.md",
    "content": "title: Jspreadsheet Upgrade Guide: v4 to v5\nkeywords: Jspreadsheet upgrade, version 4 to 5, Jspreadsheet CE migration, breaking changes, Jspreadsheet compatibility, JavaScript data grid, spreadsheet customization, migration guide\ndescription: Upgrade guide from Jspreadsheet CE v4 to v5. Learn about breaking changes and updated features for a smooth code transition.\n\n# Jspreadsheet Upgrade from v4 to v5\n\n## Overview\n\nThe latest update to Jspreadsheet CE introduces significant enhancements, simplifying customization and improving compatibility across all distributions. Key updates include structural refinements, increased extensibility, and standardized functionality. Upgrading from version 4 to version 5 includes breaking changes due to updates in properties, methods, and events. This document outlines the critical adjustments required to transition your existing code to the new version.\n\n>  Be aware that upgrading from version 4 to version 5 introduces breaking changes resulting from updates to properties, methods, and events.\n\n### Spreadsheet vs. Worksheets\n\nJspreadsheet CE introduces two distinct levels: the spreadsheet level and the worksheet level. This separation avoids redundancy by centralizing elements that should not be duplicated across each worksheet, such as toolbars or event declarations.\n\n#### Version 4\n\nIn version 4, all properties were defined within a single object.\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: true,\n    minDimensions: [4,4],\n    onchange: function() {\n        // something\n    }\n});\n```\n\n#### Version 5\n\nIn version 5, the worksheets attribute allows you to declare multiple worksheets, each with specific properties, while maintaining centralized common configurations at the spreadsheet level.\n\n{.ignore}\n```javascript\n// Create your spreadsheets\njspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: true,\n    worksheets: [\n        {\n            minDimensions: [4,4],\n        },\n        // More worksheets\n    ],\n    onchange: function() {\n        // something\n    }\n});\n```\n\n### Instances\n\nWhen creating a new **spreadsheet**, Jspreadsheet returns an array of **worksheet** instances. Each worksheet object includes a `parent` property, allowing access to spreadsheet-level features.\n\n{.ignore}\n```javascript\n// Create your spreadsheets\nlet worksheets = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [\n        {\n            minDimensions: [4,4],\n        },\n    ],\n});\n\n// Get the spreadsheet data\nworksheets[0].getData();\n// Show toolbar\nworksheets[0].parent.showToolbar();\n```\n\n### Translations\n\nFrom version 5, translations can be managed globally using `jspreadsheet.setDictionary`. This method accepts an object where the keys are the original English texts and the values are their translations.\n\n#### Version 4\n\nIn version 4, translations were defined directly within the spreadsheet instance:\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    minDimensions: [4,4],\n    text:{\n        noRecordsFound: 'Nenhum registro encontrado',\n        show: 'Show',\n        // many other translations\n    }\n});\n```\n\n#### Version 5\n\nIn version 5, you define translations globally using setDictionary, making it easier to manage and apply them across all instances:\n\n{.ignore}\n```javascript\n// Translate all application\njspreadsheet.setDictionary({\n    'No records found': 'Nenhum registro encontrado',\n    'Show': 'Exibir',\n    //...\n});\n\n// Create your spreadsheets\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n    }],\n});\n```\n\n### Plugin and Editor Support\n\nJspreadsheet CE now includes plugin support, offering developers enhanced flexibility and the ability to extend functionality. Editors align more closely with the Pro version, ensuring more across the different distributions.\n\n{.ignore}\n```javascript\n// Create your spreadsheets\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        minDimensions: [4,4],\n        columns: [{ type: myEditor }], // Custom editors\n    }],\n    plugins: { myPlugin }, // Plugin declaration\n});\n```\n\n## Library Level\n\n### Library Level Property Updates  \n\n| Status                                  | Property                              | Description                                                                                                              |\n|-----------------------------------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                 | `destroyAll`                          | Destroys all spreadsheets across all namespaces.                                                                         |\n| *New*{.info-label .new}                 | `getWorksheetInstanceByName`{.nowrap} | Retrieves a worksheet by its name and namespace. Can also return a namespace depending on the first argument.            |\n| *New*{.info-label .new}                 | `setDictionary`                       | Adds a helper for defining new translations.                                                                             |\n| *Removed*{.info-label .removed}         | `tabs`                                | Removed as it is no longer necessary since CE always creates a spreadsheet by default.                                   |\n| *Removed*{.info-label .removed}         | `createTabs`                          | Removed as it is no longer necessary since CE now always creates a spreadsheet by default.                               |\n| *Removed*{.info-label .removed .nowrap} | `getColumnName`                       | Removed because this functionality already exists in the helpers.                                                        |\n\n### Helpers\n\n| Status                           | Method                    | Description                                                                                           |\n|----------------------------------|---------------------------|-------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}          | `getCoordsFromRange`      | Retrieves coordinates from a specified range.                                                         |\n| *Updated*{.info-label .updated}  | `createFromTable`         | Now functions like the previous library-level method with the same name.                              |\n| *Updated*{.info-label .updated}  | `getColumnNameFromCoords` | Renamed to `getCellNameFromCoords`.                                                                   |\n| *Updated*{.info-label .updated}  | `getCoordsFromColumnName` | Renamed to `getCoordsFromCellName`.                                                                   |\n| *Removed*{.info-label .removed}  | `injectArray`             | Removed as it was not documented.                                                                     |\n\n## Spreadsheet Level  \n\n### Settings  \n\n| Status                  | Property        | Description                                                                                              |\n|-------------------------|-----------------|----------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new} | `parseHTML`     | Similar functionality to the previous `stripHTML` property.                                              |\n| *New*{.info-label .new} | `debugFormulas` | Enables formula debugging. Debugging was previously always enabled by default but is now disabled.       |\n| *New*{.info-label .new} | `fullscreen`    | Defines a spreadsheet as fullscreen.                                                                     |\n\n### Toolbars  \n\n| Status                   | Method        | Description                                      |\n|--------------------------|---------------|--------------------------------------------------|\n| *New*{.info-label .new}  | `hideToolbar` | Hides the toolbar.                               |\n| *New*{.info-label .new}  | `showToolbar` | Displays the toolbar using the current settings. |\n\n### Events\n\nAll events are now defined at the spreadsheet level. Except for the `onload`, `onbeforesave`, and `onsave` events, all other events now receive the worksheet instance as their first argument. Additional changes to method arguments have also been introduced.\n\n| Status                                  | Event                           | Description                                                                                                                             |\n|-----------------------------------------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                 | `onbeforeformula`               | Intercepts formulas before they are calculated.                                                                                         |\n| *New*{.info-label .new}                 | `onbeforeselection`             | Intercepts cell selection and cancels it if `false` is returned.                                                                        |\n| *New*{.info-label .new}                 | `oncreatecell`                  | Triggered when a cell is created.                                                                                                       |\n| *New*{.info-label .new}                 | `oncreateworksheet`             | Triggered when a worksheet is created.                                                                                                  |\n| *New*{.info-label .new}                 | `ondeleteworksheet`             | Triggered when a worksheet is deleted.                                                                                                  |\n| *Updated*{.info-label .updated}         | `onafterchanges`                | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onbeforedeletecolumn`{.nowrap} | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onbeforedeleterow`             | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onbeforeinsertcolumn`          | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onbeforeinsertrow`             | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onbeforepaste`                 | The second argument is a 2D array of objects with a \"value\" property representing the values to be pasted.                              |\n| *Updated*{.info-label .updated}         | `onbeforesave`                  | The first argument is now the spreadsheet instance.                                                                                     |\n| *Updated*{.info-label .updated}         | `onchangeheader`                | The third and fourth arguments for this event have been swapped.                                                                        |\n| *Updated*{.info-label .updated}         | `onchangemeta`                  | The second argument is now always an object containing the changes. The third and fourth arguments have been removed.                   |\n| *Updated*{.info-label .updated}         | `onchangepage`                  | This event now includes a fourth argument for the number of items per page.                                                             |\n| *Updated*{.info-label .updated}         | `onchangestyle`                 | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `oncopy`                        | Can cancel the copy action or override the copied value.                                                                                |\n| *Updated*{.info-label .updated}         | `oncreateeditor`                | The fifth argument is always `null`, and a new fifth argument has been added for the column configuration.                              |\n| *Updated*{.info-label .updated}         | `ondeletecolumn`                | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `ondeleterow`                   | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `oninsertcolumn`                | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `oninsertrow`                   | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onload`                        | The first argument is now the spreadsheet instance.                                                                                     |\n| *Updated*{.info-label .updated}         | `onmerge`                       | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onmovecolumn`                  | This event now includes a fourth argument indicating the number of columns moved.                                                       |\n| *Updated*{.info-label .updated}         | `onmoverow`                     | This event now includes a fourth argument indicating the number of rows moved.                                                          |\n| *Updated*{.info-label .updated}         | `onpaste`                       | All arguments for this event have been updated.                                                                                         |\n| *Updated*{.info-label .updated}         | `onredo`                        | The second argument has been updated based on the method the event refers to.                                                           |\n| *Updated*{.info-label .updated}         | `onsave`                        | The first argument is now the spreadsheet instance.                                                                                     |\n| *Updated*{.info-label .updated}         | `onsort`                        | Now includes a fourth argument, an array representing the new order of rows.                                                            |\n| *Updated*{.info-label .updated .nowrap} | `onundo`                        | The second argument has been updated based on the method the event refers to.                                                           |\n\n\n## Worksheets Updates  \n\n### Attributes\n\n| Status                                  | Property                        | Description                                                                                                                                                         |\n|-----------------------------------------|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| *Updated*{.info-label .updated}         | `highlighted`                   | Now stores records instead of just cell tags.                                                                                                                       |\n| *Updated*{.info-label .updated}         | `csvHeaders`                    | The default value for this property is now `false`.                                                                                                                 |\n| *Updated*{.info-label .updated}         | `nestedHeaders`                 | Now requires a two-dimensional array.                                                                                                                               |\n| *Updated*{.info-label .updated}         | `ignoreEvents`                  | Now operates at the spreadsheet level.                                                                                                                              |\n| *Updated*{.info-label .updated}         | `options`                       | Updated to no longer set default values for several properties.                                                                                                     |\n| *Updated*{.info-label .updated}         | `records`                       | Now a 2D array containing objects with cell coordinates and corresponding HTML elements.                                                                            |\n| *Updated*{.info-label .updated}         | `rows`                          | Now an array of objects with the HTML element for each row and index.                                                                                               |\n| *Updated*{.info-label .updated}         | `persistance`                   | Renamed to `persistence`.                                                                                                                                           |\n| *Updated*{.info-label .updated}         | `rowResize`                     | The default value for this property is now `true`.                                                                                                                  |\n| *Updated*{.info-label .updated}         | `tableHeight`                   | Now also accepts a value of type `number`.                                                                                                                          |\n| *Updated*{.info-label .updated}         | `tableWidth`                    | Now also accepts a value of type `number`.                                                                                                                          |\n| *Updated*{.info-label .updated}         | `toolbar`                       | Now supports being a function, boolean, or a configuration object for the jSuites toolbar. Array configurations were also updated, and it is now spreadsheet-level. |\n| *Removed*{.info-label .removed}         | `contextMenu`                   | Now operates at the spreadsheet level and includes four new arguments.                                                                                              |\n| *Removed*{.info-label .removed}         | `fullscreen`                    | Now operates at the spreadsheet level.                                                                                                                              |\n| *Removed*{.info-label .removed}         | `colgroup`                      | Replaced by the new `cols` property, which functions similarly.                                                                                                     |\n| *Removed*{.info-label .removed}         | `el`                            | Replaced by the new `element` property.                                                                                                                             |\n| *Removed*{.info-label .removed}         | `contextMenu`                   | Now operates at the spreadsheet level.                                                                                                                              |\n| *Removed*{.info-label .removed}         | `copyCompatibility`             | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `detachForUpdates`              | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `imageOptions`                  | Use the column's configuration object instead.                                                                                                                      |\n| *Removed*{.info-label .removed}         | `includeHeadersOnCopy`{.nowrap} | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `loadingSpin`                   | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `method`                        | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `requestVariables`              | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `stripHTML`                     | Use the `parseHTML` property instead.                                                                                                                               |\n| *Removed*{.info-label .removed}         | `stripHTMLOnCopy`               | This property has been removed.                                                                                                                                     |\n| *Removed*{.info-label .removed}         | `text`                          | Removed as JSS CE now uses `jSuites.translate`.                                                                                                                     |\n| *Removed*{.info-label .removed .nowrap} | `updateTable`                   | This property has been removed.                                                                                                                                     |\n\n### Comments  \n\n| Status                                  | Property                 | Description                                                                                                         |\n|-----------------------------------------|--------------------------|---------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                 | `comments`               | Represents comments associated with the worksheet.                                                                  |\n| *Updated*{.info-label .updated}         | `allowComments`{.nowrap} | The default value of this property is now `true`.                                                                   |\n| *Updated*{.info-label .updated}         | `getComments`            | The undocumented second argument has been removed. Additionally, the first argument can no longer be an array.      |\n| *Updated*{.info-label .updated .nowrap} | `setComments`            | The first argument of this method has been updated. Additionally, the undocumented third argument has been removed. |\n\n### Columns\n\n| Status                                   | Property                      | Description                                                                                                                                                                                       |\n|------------------------------------------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                  | `columns.render`              | Intercepts and modifies visible data before inserting into a grid cell.                                                                                                                           |\n| *Updated*{.info-label .updated}          | `columnDrag`                  | The default value for this property is now `true`.                                                                                                                                                |\n| *Updated*{.info-label .updated}          | `columns.type`                | Now accepts an object to create custom editors, replacing the old `columns.editor`. Note: \"autocomplete\" and \"readonly\" have been removed; use `autocomplete` and `readOnly` properties instead.  |\n| *Removed*{.info-label .removed}          | `columns.editor`              | This property has been removed. Its functionality for creating custom editors has been moved to the `columns.type` property.                                                                      |\n| *Removed*{.info-label .removed}          | `columns.stripHTML`{.nowrap}  | This property has been removed.                                                                                                                                                                   |\n| *Removed*{.info-label .removed}          | `colAlignments`               | Use the `align` property in the `columns` array items instead.                                                                                                                                    |\n| *Removed*{.info-label .removed}          | `colHeaders`                  | Use the `title` property in the `columns` array items instead.                                                                                                                                    |\n| *Removed*{.info-label .removed .nowrap}  | `colWidths`                   | Use the `width` property in the `columns` array items instead.                                                                                                                                    |\n| *Updated*{.info-label .updated}          | `hideColumn`                  | The first argument for this method can now also be an array.                                                                                                                                      |\n| *Updated*{.info-label .updated}          | `showColumn`                  | The first argument for this method can now also be an array.                                                                                                                                      |\n| *Updated*{.info-label .updated}          | `setWidth`                    | Previously undocumented, this method had a third argument, which has now been removed.                                                                                                            |\n\n### Data\n\n| Status                                  | Method                      | Description                                                                                                                              |\n|-----------------------------------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                 | `getDataFromRange`{.nowrap} | Retrieves data from a specified range.                                                                                                   |\n| *Updated*{.info-label .updated}         | `getData`                   | This method now accepts two additional arguments.                                                                                        |\n| *Updated*{.info-label .updated}         | `setData`                   | No longer emits the `onload` event, and its first argument can no longer be a JSON object.                                               |\n| *Updated*{.info-label .updated}         | `getValue`                  | The first argument must now be a string.                                                                                                 |\n| *Updated*{.info-label .updated}         | `setValue`                  | The object array in the first argument no longer uses the `newValue` property; use the `value` property instead.                         |\n| *Updated*{.info-label .updated}         | `getColumnData`             | This method now includes a second argument.                                                                                              |\n| *Updated*{.info-label .updated}         | `setColumnData`             | This method now includes a third argument.                                                                                               |\n| *Updated*{.info-label .updated}         | `getRowData`                | This method now includes a second argument.                                                                                              |\n| *Updated*{.info-label .updated}         | `setRowData`                | This method now includes a third argument.                                                                                               |\n| *Updated*{.info-label .updated}         | `download`                  | This method now accepts a second argument.                                                                                               |\n| *Updated*{.info-label .updated}         | `getLabel`                  | The first argument can no longer be an array with two numbers. Instead, the method now accepts two numeric arguments.                    |\n| *Removed*{.info-label .removed}         | `parseCSV`                  | Use the helper methods instead.                                                                                                          |\n| *Removed*{.info-label .removed .nowrap} | `getJson`                   | Use the `getData` method instead.                                                                                                        |\n\n### Selection\n\n| Status                                  | Method                               | Description                                                                                                                                     |\n|-----------------------------------------|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|\n| *New*{.info-label .new}                 | `getHighlighted`                     | Retrieves the coordinates of the highlighted selections.                                                                                        |\n| *New*{.info-label .new}                 | `getSelected`                        | Retrieves information about selected cells in the worksheet.                                                                                    |\n| *New*{.info-label .new}                 | `getSelection`                       | Retrieves the coordinates of the selected range in the worksheet.                                                                               |\n| *New*{.info-label .new}                 | `isSelected`                         | Checks if a cell is part of the current selection.                                                                                              |\n| *Updated*{.info-label .updated}         | `updateSelectionFromCoords`{.nowrap} | Previously undocumented, this method had a fifth argument, which has now been removed. It can now return `false` if the selection is cancelled. |\n| *Updated*{.info-label .updated}         | `getSelectedColumns`                 | This method now accepts an argument.                                                                                                            |\n| *Updated*{.info-label .updated}         | `getSelectedRows`                    | The first argument for this method has been updated.                                                                                            |\n| *Updated*{.info-label .updated}         | `resetSelection`                     | Previously undocumented, this method had an argument, which has now been removed.                                                               |\n| *Removed*{.info-label .removed .nowrap} | `updateSelection`                    | Use the `updateSelectionFromCoords` method instead.                                                                                             |\n\n\n### Editors\n\n#### Calendar Editor Type\n\n| Status                             | Property         | Description                                                                               |\n|------------------------------------|------------------|-------------------------------------------------------------------------------------------|\n| *Updated*{.info-label .updated}    | `format`         | The default value for this property is now `\"YYYY-MM-DD\"`.                                |\n| *Removed*{.info-label .removed}    | `value`          | This property has been removed.                                                           |\n| *Deprecated*{.info-label .removed} | `months`         | Still available but should be replaced with the `setDictionary` method.                   |\n| *Deprecated*{.info-label .removed} | `weekdays`       | Still available but should be replaced with the `setDictionary` method.                   |\n| *Deprecated*{.info-label .removed} | `weekdays_short` | Still available but should be replaced with the `setDictionary` method.                   |\n\n\n### Removed\n\nThe following methods and attributes have been removed from the public scope or the library:\n\n#### Methods\n\n- `col`  \n- `conditionalSelectionUpdate`  \n- `copyData`  \n- `createCell`  \n- `createCellHeader`  \n- `createNestedHeader`  \n- `createRow`  \n- `createTable`  \n- `createToolbar`  \n- `getColumnOptions`  \n- `getDropDownValue`  \n- `getFreezeWidth`  \n- `getJsonRow`  \n- `getLabelFromCoords`  \n- `hash`  \n- `historyProcessColumn`  \n- `historyProcessRow`  \n- `init`  \n- `isColMerged`  \n- `isRowMerged`  \n- `loadDown`  \n- `loadPage`  \n- `loadUp`  \n- `loadValidation`  \n- `onafterchanges`  \n- `parseNumber`  \n- `parseValue`  \n- `prepareJson`  \n- `prepareTable`  \n- `refresh`  \n- `refreshSelection`  \n- `removeCopySelection`  \n- `removeCopyingSelection`  \n- `row`  \n- `save`  \n- `scrollControls`  \n- `setCheckRadioValue`  \n- `setFooter`  \n- `setHistory`  \n- `updateCell`  \n- `updateCopySelection`  \n- `updateCornerPosition`  \n- `updateFormula`  \n- `updateFormulaChain`  \n- `updateFormulas`  \n- `updateFreezePosition`  \n- `updateMeta`  \n- `updateNestedHeader`  \n- `updateOrder`  \n- `updateOrderArrow`  \n- `updatePagination`  \n- `updateResult`  \n- `updateScroll`  \n- `updateTable`  \n- `updateTableReferences`  \n- `wheelControls`  \n\n#### Attributes\n\n- `setExtensions`;\n- `formula`;\n- `build`;\n- `keyDownControls`;\n- `mouseDownControls`;\n- `mouseUpControls`;\n- `mouseMoveControls`;\n- `mouseOverControls`;\n- `doubleClickControls`;\n- `copyControls`;\n- `cutControls`;\n- `pasteControls`;\n- `contextMenuControls`;\n- `touchStartControls`;\n- `touchEndControls`;\n- `fromSpreadsheet`;\n- `validLetter`;\n- `injectArray`;\n- `getIdFromColumnName`;\n- `getColumnNameFromId`;\n- `getElement`;\n- `doubleDigitFormat`;\n- `createFromTable`;\n\n#### Elements\n\nChanges in HTML element properties:\n\n| Status                           | Property                  | Description                                                        |\n|----------------------------------|---------------------------|--------------------------------------------------------------------|\n| *Removed*{.info-label .removed}  | `worksheetElement.jexcel` | Use the `worksheetElement.jspreadsheet` property instead.          |\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/vue/tests.md",
    "content": "title: Unit Tests for Jspreadsheet in Vue.js\nkeywords: Jspreadsheet, Jexcel, JavaScript, Web-based Applications, Web-based Spreadsheet, Unit Tests, React, Vue, Vite, Vue.js, VueJS, Vue testing\ndescription: Enhance your application quality by creating unit tests for Jspreadsheet inside your Vue.js application.\n\n# Testing Jspreadsheet in Vue.js with Jest: A Comprehensive Guide\n\n## Introduction to Vue.js Unit Testing with Jest\n\nUnit testing plays a vital role in modern **Vue.js applications** to ensure that components function as expected and to prevent any potential regressions. When working with **Jspreadsheet** and integrating it into **Vue.js**, it's essential to test this functionality thoroughly. This guide will walk you through the process of **setting up unit tests for Jspreadsheet in Vue.js** using **Jest**.\n\nBy following this guide, you'll learn how to efficiently integrate and test Jspreadsheet in your **Vue.js project**, ensuring the **reliability** and **performance** of your components.\n\n## Vue.js Testing Environment Setup\n\nIn this section, we’ll guide you through setting up a **Vue.js environment** specifically for testing **Jspreadsheet** using **Jest** and **JSDOM**. This will help you create a solid foundation for **running unit tests** for your Jspreadsheet instances.\n\n### Step 1: Clone or Create a Vue.js Project\n\nTo begin, you can either clone an existing **Vue.js project** or create a new one using `@vue/cli`. Here's how to get started:\n\n```bash\nvue create jspreadsheet-vue-testing\ncd jspreadsheet-vue-testing\n```\n\nAlternatively, clone our setup from [GitHub](??).\n\n### Step 2: Install Dependencies\n\nNext, install the necessary dependencies, including `jspreadsheet`, Jest, and `jest-environment-jsdom`:\n\n```bash\nnpm install jspreadsheet-ce@5.0.0-beta.3\nnpm install --save-dev jest@29.7.0 jest-environment-jsdom@29.7.0\n```\n\n### Step 3: Configure Jest for Jspreadsheet\n\nTo integrate Jspreadsheet properly in a Jest testing environment, you'll need to set up JSDOM. First, create a `jest.setup.js` file in the root of your project:\n\n```javascript\n// jest.setup.js\n\nconst jspreadsheet = require('jspreadsheet-ce');\n\n  // Code that runs before each test\nbeforeEach(() => {\n    if (typeof document !== 'undefined') {\n        jspreadsheet.destroyAll();\n\n        if (!global.jspreadsheet && !global.root) {\n            global.jspreadsheet = jspreadsheet;\n\n            global.root = document.createElement('div');\n            global.root.style.width = '100%';\n            global.root.style.height = '100%';\n\n            document.body.appendChild(global.root);\n        }\n    }\n});\n```\n\nNext, configure Jest to use this setup by adding the following entry to your `package.json`:\n\n```json\n{\n  \"jest\": {\n    \"setupFilesAfterEnv\": [\"<rootDir>/jest.setup.js\"],\n  }\n}\n```\n\nThis configuration ensures JSDOM will emulate the DOM environment required to run Jspreadsheet within Jest.\n\n### Step 4: Create a Test\n\nCreate a folder inside your project if it doesn't exist, then inside this folder create a file named `jspreadsheet.test.js`.\n\n```javascript\n// */tests/jspreadsheet.test.js\n\n/**\n * @jest-environment jsdom\n */\n\ntest(\"Testing data\", () => {\n    let instance = jspreadsheet(root, {\n        worksheets: [\n            {\n                data: [\n                    [\"Mazda\", 2001, 2000],\n                    [\"Peugeot\", 2010, 5000],\n                    [\"Honda Fit\", 2009, 3000],\n                    [\"Honda CRV\", 2010, 6000],\n                ],\n                minDimensions: [4, 4],\n            },\n        ],\n    });\n\n    expect(instance[0].getValue(\"A1\", true)).toEqual(\"Mazda\");\n    expect(instance[0].getValue(\"A2\", true)).toEqual(\"Peugeot\");\n    expect(instance[0].getValue(\"B1\", true)).toEqual(\"2001\");\n});\n```\n\nThis test verifies that a basic Jspreadsheet instance is created and that the data values are correctly placed. You can modify it to check for more specific scenarios as needed.\n\n## Running the Tests\n\nEnsure you add the following line to the `scripts` section of your `package.json`:\n\n```json\n\"test\": \"jest\"\n```\n\nAfter creating your tests and updating `package.json`, you can run them using the following command:\n\n```bash\nnpm test\n```\n\nJest will run all the tests in your project and display the results in the console. If everything is configured correctly, your tests should pass.\n"
  },
  {
    "path": "docs/jspreadsheet/docs/vue.md",
    "content": "title: VueJS Spreadsheet\nkeywords: Jspreadsheet, Jexcel, JavaScript, Vue.js, Vue data grid, spreadsheet-like controls, Excel-like data grid, Vue.js spreadsheet, Jspreadsheet Vue wrapper, Vue integration, JavaScript spreadsheet, spreadsheet controls\ndescription: Build advanced data grid applications with intuitive spreadsheet-like controls using Jspreadsheet Vue.js wrapper\n\n# Vue Spreadsheet\n\nJspreadsheet CE version 5 introduces a VueJS wrapper, enabling developers to create advanced data grid solutions with intuitive spreadsheet-like controls seamlessly integrated with VueJS version 3.\n\n## Documentation\n\n### Using the Vue Data Grid Wrapper\n\nTo start using the Jspreadsheet Vue wrapper, first install the package:\n\n```bash\nnpm install @jspreadsheet-ce/vue@5.0.0-beta.3\n```\n\n### Adding CSS to Your Project\n\nTo apply styles, import the necessary CSS files:\n\n{.ignore}\n```javascript\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n```\n\nTo ensure icons display correctly, include Material Icons in your application. Add the following code to your main HTML file:\n\n{.ignore}\n```html\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n```\n\n#### Creating Your First Jspreadsheet Vue Data Grid\n\nLearn how to build a web-based spreadsheet using the Jspreadsheet Vue wrapper, seamlessly integrating advanced data grid functionality into your Vue.js applications.\n\n{.ignore}\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" />\n    </Spreadsheet>\n    <input type=\"button\" value=\"getData\" @click=\"getData\" />\n</template>\n\n<script>\nimport { ref } from \"vue\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    setup() {\n        // Worksheet data\n        const data = ref([\n            [\"US\", \"Cheese\", \"2019-02-12\"],\n            [\"CA\", \"Apples\", \"2019-03-01\"],\n            [\"CA\", \"Carrots\", \"2018-11-10\"],\n            [\"BR\", \"Oranges\", \"2019-01-12\"],\n        ]);\n\n        // Columns\n        const columns = ref([\n            { width: \"300px\" },\n            { width: \"200px\" },\n            { width: \"200px\" },\n        ]);\n\n        // Reference for the spreadsheet\n        const spreadsheet = ref(null);\n\n        // Method to get data\n        const getData = () => {\n            console.log(spreadsheet.value.current[0].getData());\n        };\n\n        return {\n            data,\n            columns,\n            spreadsheet,\n            getData,\n        };\n    },\n};\n</script>\n```\n \n\n## Customizations\n\nYou can also create your wrapper for Jspreadsheet. This approach offers developers enhanced flexibility and control over the implementation, allowing for tailored integration and custom behaviours.\n\n### Wrapper for VueJS 3\n\nTo integrate Jspreadsheet directly with Vue 3, initialize the library within a Vue component using the mounted lifecycle hook. This method allows you to harness the power of Jspreadsheet while maintaining control over its behaviour and styling.\n\n{.ignore}\n```vue\n<template>\n  <Jspreadsheet :options=\"Options\" />\n</template>\n\n<script>\nimport Jspreadsheet from \"./components/Jspreadsheet\";\n\nexport default {\n  components: {\n    Jspreadsheet,\n  },\n  setup() {\n    const Options = {\n      worksheets: [\n        {\n          search: true,\n          data: [\n            [42, 42, 42, 42],\n            [42, 42, 42, 42],\n          ],\n          columns: [\n            { title: \"First Column\", width: 100 },\n            { title: \"Second Column\", width: 150 },\n            { title: \"Third Column\", width: 200 },\n            { title: \"Fourth Column\", width: 250 },\n          ],\n        },\n      ]\n    };\n    return { Options };\n  },\n};\n</script>\n```\n  \n\n### Integration with Vue2\n\n\n{.ignore}\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<br>\n\n<input type=\"button\" value=\"Add new row\" onclick=\"vm.insertRow()\" />\n\n<script>\nlet options = {\n    worksheets: [{\n        data:[[]],\n        minDimensions:[8,10],\n    }],\n}\n\nlet vm = new Vue({\n    el: '#spreadsheet',\n    mounted: function() {\n        let spreadsheet = jspreadsheet(this.$el, options);\n        Object.assign(this, spreadsheet);\n    }\n}); \n</script>\n```\n \n## Jspreadsheet Pro\n\n### More examples\n\nIf you are interested in using Jspreadsheet Pro, explore the following resources for advanced features and integrations. These examples demonstrate how to leverage Jspreadsheet Pro for creating advanced and feature-rich spreadsheets in Vue.js applications.\n\n* [Vue Spreadsheet Pro](https://jspreadsheet.com/docs/vue)\n* [Vue Spreadsheet with actions](https://codesandbox.io/s/vue3-spreadsheet-with-actions-chx7dw)\n* [Vue Spreadsheet Editor](https://codesandbox.io/s/vue-spreadsheet-zpmf2)\n\n{.pro}\n> **Jspreadsheet Pro for Vue - Professional Spreadsheet Components**\n>\n> While Jspreadsheet CE provides core spreadsheet functionality for Vue, **Jspreadsheet Pro** offers enhanced Vue integration and enterprise features:\n>\n> **Enhanced Vue Integration:**\n> - **TypeScript Support:** Full TypeScript definitions for Vue 3 with type-safe development\n> - **Composition API:** Native Composition API support with custom composables\n> - **Vue 3 Optimized:** Built specifically for Vue 3 with full reactivity system support\n> - **Pinia Integration:** Built-in Pinia store integration for state management\n> - **Nuxt Support:** Full SSR/SSG support for Nuxt 3 applications\n> - **Vue DevTools:** Enhanced debugging with Vue DevTools integration\n>\n> **Professional Components:**\n> - **Advanced Editors:** Conditional dropdowns, rich text, HTML editors with Vue reactivity\n> - **Formula System:** 500+ Excel functions with Vue reactive bindings\n> - **Conditional Formatting:** Visual rules, data bars, color scales, icon sets\n> - **Data Validation:** Real-time validation with custom Vue components\n> - **Import/Export:** Full Excel (.xlsx) import/export with formatting preservation\n> - **Charts & Graphs:** Built-in Vue chart components for data visualization\n>\n> **Vue Reactivity Features:**\n> - **Reactive Data Binding:** Two-way binding with Vue ref/reactive objects\n> - **Computed Properties:** Use computed values for cell formulas\n> - **Watchers:** Automatic updates when source data changes\n> - **V-Model Support:** Full v-model binding for spreadsheet data\n> - **Teleport Support:** Use Vue Teleport for dialogs and overlays\n> - **Suspense Ready:** Async component loading with Suspense\n>\n> **Performance & Scale:**\n> - **Virtual Scrolling:** Handle 100K+ rows with smooth scrolling\n> - **Lazy Loading:** Load data on-demand for optimal performance\n> - **Vue 3 Performance:** Optimized for Vue 3's improved rendering engine\n> - **Web Workers:** Background processing for heavy calculations\n> - **Memory Efficiency:** Optimized memory usage for large datasets\n>\n> **Developer Experience:**\n> - **Vue-Specific Documentation:** Examples with Options API and Composition API\n> - **Professional Support:** Priority support for Vue integration issues\n> - **Regular Updates:** Continuous Vue 3 compatibility improvements\n> - **Migration Tools:** Easy migration from CE to Pro with Vue examples\n>\n> **Vue-Specific Pro Features:**\n> - **Custom Vue Components:** Use Vue components as cell editors and renderers\n> - **Scoped Slots:** Advanced customization with scoped slots\n> - **Event Integration:** Seamless integration with Vue event system\n> - **Provide/Inject:** Share spreadsheet context across component tree\n> - **Testing Support:** Vitest/Vue Test Utils integration examples\n>\n> Perfect for Vue applications requiring enterprise-grade spreadsheet functionality with professional support.\n>\n> **[Explore Jspreadsheet Pro Vue →](https://jspreadsheet.com/docs/vue)** | **[Compare CE vs Pro →](https://jspreadsheet.com/docs/getting-started)** | **[View Pricing →](https://jspreadsheet.com/pricing)**\n\n"
  },
  {
    "path": "docs/jspreadsheet/docs/worksheets.md",
    "content": "title: Worksheets Settings, Methods, and Related Events\nkeywords: Jspreadsheet, data grid, javascript, excel-like worksheets, spreadsheet, data tables, grid, worksheet events, worksheet support, Jspreadsheet worksheets, worksheet settings, worksheet methods, worksheet events, create worksheets, customize worksheets, track changes in worksheets, worksheet control, dynamic data grids, JavaScript data tables, worksheet customization, worksheet organization, interactive worksheets, data management\ndescription: Learn how to set up and manage worksheets in Jspreadsheet programmatically. Explore various worksheet properties, settings, and efficient handling and customization techniques.\n\n# Worksheets\n\nJspreadsheet offers robust tools for managing spreadsheets with multiple worksheets. It enables precise control over user interactions and provides programmatic controls and event listeners to customize the behaviour and monitor changes within the spreadsheet. \n\n## Documentation\n\n### Methods\n\nExplore the available methods to programmatically interact with and manage worksheets in Jspreadsheet.\n\n| Property                          | Description                                                                                                                                                          |\n|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `openWorksheet(number)`           | Opens a worksheet by its index.<br/>`openWorksheet(position?: Number) => void`                                                                                          |\n| `createWorksheet(object, number)` | Adds a new worksheet based on a configuration object, optionally at a specific position.<br/>`createWorksheet(worksheetOptions: Object, position?: Number) => worksheetInstance` |\n| `deleteWorksheet(number)`         | Removes an existing worksheet by its index.<br/>`deleteWorksheet(position?: Number) => void`                                                                              |\n\n### Events\n\nExplore the available events to monitor and respond to worksheet interactions in Jspreadsheet.\n\n| Event                     | Description                                                                                                                                                               |\n|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `onopenworksheet`         | `onopenworksheet(worksheet: Object, worksheetNumber: Number) : void`                                                                                                      |\n| `onbeforecreateworksheet` | Intercept worksheet creation to cancel or configure the new worksheet.<br/>`onbeforecreateworksheet(worksheet: Object, options: Object, worksheetNumber: Number) : mixed` |\n| `oncreateworksheet`       | `oncreateworksheet(worksheet: Object, worksheetOptions: Object, worksheetNumber: Number) : void`                                                                          |\n| `ondeleteworksheet`       | `ondeleteworksheet(worksheet: Object, oldWorksheetNumber: Number) : void`                                                                                                 |\n\n\n### Configuration\n\nYou can customize spreadsheet and worksheet behaviour using the following settings.\n\n#### Worksheet Settings\n\n| Property                 | Description                          |\n|--------------------------|--------------------------------------|\n| `worksheetId: string`    | Unique identifier for the worksheet. |\n| `worksheetName: string`  | Title of the worksheet.              |\n\n#### Spreadsheet Properties\n\n| Property                         | Description                                                                                        |\n|----------------------------------|----------------------------------------------------------------------------------------------------|\n| `tabs: boolean\\|object`          | Enables tabs for worksheet navigation and allows users to create new worksheets. Default: `false`. |\n| `allowDeleteWorksheet: boolean`  | Enables the delete worksheet option in the context menu. Default: `true`.                          |\n| `allowRenameWorksheet: boolean`  | Enables the rename worksheet option in the context menu. Default: `true`.                          |\n| `allowMoveWorksheet: boolean`    | Enables drag-and-drop functionality for rearranging worksheets. Default: `true`.                   |\n\n\n{.green}\n> ##### Tabs\n> The `tabs` object uses the same properties as the `jSuites.tabs` plugin, allowing you to customize the position and behaviour of the worksheet tabs. For more details, visit the: [Tabs Plugin](https://jsuites.net/docs/javascript-tabs)\n\n \n#### Quick example\n\n{.ignore-execution}\n```html\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: {\n        allowCreate: true,\n        allowChangePosition: true,\n        animation: true,\n        position: \"bottom\",\n    },\n    worksheets: [{\n        minDimensions: [8,8],\n    }],\n});\n</script>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Tabs\n    const tabs = {\n        allowCreate: true,\n        allowChangePosition: true,\n        animation: true,\n        position: \"bottom\",\n    };\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} tabs={tabs}>\n            <Worksheet minDimensions={[6,6]} />\n            <Worksheet minDimensions={[6,6]} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" :tabs=\"tabs\">\n        <Worksheet :minDimensions=\"[8,8]\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    data() {\n        return {\n            // Tabs customizations\n            tabs: {\n                allowCreate: true,\n                allowChangePosition: true,\n                animation: true,\n                position: \"bottom\",\n            }\n        };\n    }\n}\n</script>\n```\n```angularjs\njspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: {\n        allowCreate: true,\n        allowChangePosition: true,\n        animation: true,\n        position: \"bottom\",\n    },\n    worksheets: [{\n        minDimensions: [8,8],\n    }],\n});\n```\n\n## Examples\n\n### Worksheet events\n\nCreate a new worksheet and explore the related events. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// Create the spreadsheet\njspreadsheet(document.getElementById('spreadsheet'), {\n    // Allow to create a new tab button\n    tabs: true,\n    // Initial worksheet\n    worksheets: [\n        {\n            data: [\n                [\"1\",\"DIVINELY UNINSPIRED TO A HELLISH EXTENT\",\"LEWIS CAPALDI\"],\n                [\"2\",\"NO 6 COLLABORATIONS PROJECT\",\"ED SHEERAN\"],\n                [\"3\",\"THE GREATEST SHOWMAN\",\"MOTION PICTURE CAST RECORDING\"],\n                [\"4\",\"WHEN WE ALL FALL ASLEEP WHERE DO WE GO\",\"BILLIE EILISH\"]\n            ],\n            columns: [\n                { type: 'autonumber', title: 'Id' },\n                { type: 'text', width: '350px', title: 'Title' },\n                { type: 'text', width: '250px', title: 'Artist' },\n            ],\n            // Name of the worksheet\n            worksheetName: 'Albums',\n        }\n    ],\n    // Intercept the new worksheet and define the options for the new worksheet\n    onbeforecreateworksheet: function(config, index) {\n        return {\n            minDimensions: [5, 5],\n            worksheetName: 'Albums ' + index\n        }\n    },\n    // When you open the worksheet\n    onopenworksheet: function(element, instance, worksheetNumber) {\n        console.log(element, instance, worksheetNumber);\n    },\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        [\"1\",\"DIVINELY UNINSPIRED TO A HELLISH EXTENT\",\"LEWIS CAPALDI\"],\n        [\"2\",\"NO 6 COLLABORATIONS PROJECT\",\"ED SHEERAN\"],\n        [\"3\",\"THE GREATEST SHOWMAN\",\"MOTION PICTURE CAST RECORDING\"],\n        [\"4\",\"WHEN WE ALL FALL ASLEEP WHERE DO WE GO\",\"BILLIE EILISH\"]\n    ]\n    // Columns\n    const columns = [\n        { type: 'autonumber', title: 'Id' },\n        { type: 'text', width: '350px', title: 'Title' },\n        { type: 'text', width: '250px', title: 'Artist' },\n    ]\n\n    // Intercept the action to define the default configuration for each new worksheet\n    const onbeforecreateworksheet = () => {\n        return {\n            minDimensions: [5,5],\n        }\n    }\n    // When a new worksheet is opened\n    const onopenworksheet = (element, instance, worksheetNumber) => {\n        console.log(element, instance, worksheetNumber);\n    }\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet} tabs onbeforecreateworksheet={onbeforecreateworksheet} onopenworksheet={onopenworksheet}>\n                <Worksheet data={data} columns={columns} worksheetName={\"Albums\"} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" tabs\n            :onbeforecreateworksheet=\"onbeforecreateworksheet\" :onopenworksheet=\"onopenworksheet\">\n        <Worksheet :data=\"data\" :columns=\"columns\" worksheetName=\"Albums\" />\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"jspreadsheet-ce/dist/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    methods: {\n        // Intercept the action to define the default configuration for each new worksheet\n        onbeforecreateworksheet() {\n            return {\n                minDimensions: [5,5],\n            }\n        },\n        // When a new worksheet is opened\n        onopenworksheet(element, instance, worksheetNumber) {\n            console.log(element, instance, worksheetNumber);\n        }\n    },\n    data() {\n        return {\n            // Data\n            data: [\n                [\"1\",\"DIVINELY UNINSPIRED TO A HELLISH EXTENT\",\"LEWIS CAPALDI\"],\n                [\"2\",\"NO 6 COLLABORATIONS PROJECT\",\"ED SHEERAN\"],\n                [\"3\",\"THE GREATEST SHOWMAN\",\"MOTION PICTURE CAST RECORDING\"],\n                [\"4\",\"WHEN WE ALL FALL ASLEEP WHERE DO WE GO\",\"BILLIE EILISH\"]\n            ],\n            // Columns\n            columns: [\n                { type: 'autonumber', title: 'Id' },\n                { type: 'text', width: '350px', title: 'Title' },\n                { type: 'text', width: '250px', title: 'Artist' },\n            ],\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            // Allow create a new tab button\n            tabs: true,\n            // Initial worksheet\n            worksheets: [\n                {\n                    data: [\n                        [\"1\",\"DIVINELY UNINSPIRED TO A HELLISH EXTENT\",\"LEWIS CAPALDI\"],\n                        [\"2\",\"NO 6 COLLABORATIONS PROJECT\",\"ED SHEERAN\"],\n                        [\"3\",\"THE GREATEST SHOWMAN\",\"MOTION PICTURE CAST RECORDING\"],\n                        [\"4\",\"WHEN WE ALL FALL ASLEEP WHERE DO WE GO\",\"BILLIE EILISH\"]\n                    ],\n                    columns: [\n                        { type: 'autonumber', title: 'Id' },\n                        { type: 'text', width: 350, title: 'Title' },\n                        { type: 'text', width: 250, title: 'Artist' },\n                    ],\n                    // Name of the worksheet\n                    worksheetName: 'Albums',\n                }\n            ],\n            // Intercept the new worksheet and define the options for the new worksheet\n            onbeforecreateworksheet: function(config, index) {\n                let options = {\n                    minDimensions: [5,5],\n                    worksheetName: 'Albums ' + index\n                }\n                return options;\n            },\n            // When you open the worksheet\n            onopenworksheet: function(element, instance, worksheetNumber) {\n                console.log(element, instance, worksheetNumber);\n            },\n        });\n    }\n}\n```\n\n### Programmatic operations\n\nCreate a new worksheet programmatically. \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><input type=\"button\" value=\"Create a new worksheet\" id=\"btn1\" /></p>\n\n<script>\n// Create the spreadsheet\nlet spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: true,\n    worksheets: [\n        {\n            minDimensions: [5,5],\n            worksheetName: 'Example2',\n        }\n    ]\n});\n\ndocument.getElementById(\"btn1\").onclick = () => spreadsheet[0].createWorksheet({ minDimensions: [5,5] });\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} tabs>\n                <Worksheet minDimensions={[5,5]} worksheetName={\"Example2\"} />\n            </Spreadsheet>\n            <input type={\"button\"} value={\"Create a new worksheet\"}\n                onClick={() => spreadsheet.current[0].createWorksheet({ minDimensions: [5,5] })} />\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\">\n        <Worksheet :minDimensions=\"[5,5]\" worksheetName=\"Example2\" />\n    </Spreadsheet>\n    <input type=\"button\" value=\"Create a new worksheet\" @click=\"createWorksheet\" />\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n    methods: {\n        createWorksheet() {\n            this.$refs.spreadsheet.current[0].createWorksheet({ minDimensions: [5,5] });\n        }\n    },\n    data() {\n        return {\n        };\n    }\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>\n    <input type=\"button\" value=\"Create a new worksheet\" (click)=\"this.worksheets[0].createWorksheet({ minDimensions: [5,5] })\">`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [5,5],\n                    worksheetName: 'Example2',\n                }\n            ]\n        });\n    }\n}\n```\n"
  },
  {
    "path": "docs/jspreadsheet/docs.md",
    "content": "title: The JavasScript Spreadsheet Documentation\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table, data grid\ndescription: Jspreadsheet CE is an extensible JavaScript component for building sophisticated data grid interfaces with Excel-like controls.\ncanonical: https://bossanova.uk/jspreadsheet/docs\n\n# Jspreadsheet v5: The JavaScript Spreadsheet\n\n**Jexcel** has been renamed to **Jspreadsheet**.\n\n\n## Jspreadsheet CE Use Cases\n\nJspreadsheet CE is an extensible framework for building sophisticated data-oriented interfaces with Excel-like controls. By bringing familiar spreadsheet features to your application, you can drastically reduce development time while delivering an interface that users already know how to use, leading to faster adoption and increased productivity. You can use Jspreadsheet in many different applications, such as:\n\n- An editable data grid-based interface to simplify inventory management and production planning in a manufacturing company's ERP system.\n- At an educational institution, Jspreadsheet powers grade management systems where teachers can efficiently import and modify student data.\n- A logistics company uses Jspreadsheet to create dynamic delivery route planning tables with real-time updates.\n- In a research laboratory, scientists use Jspreadsheet to collect and analyze experimental data with custom validation rules.\n- At a retail chain, managers use Spreadsheet-based tools to coordinate staff schedules across multiple locations.\n\n\n## Installation\n\n\n### From the NPM\n\n```bash\nnpm install jspreadsheet-ce@5\n```\n\n### From a CDN\n\n{.ignore}\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/index.min.js\"></script>\n```\n\n\n## Data Grid with Spreadsheets Controls\n\nHow to embed a simple javascript spreadsheet in your application. Find more [examples](/jspreadsheet/docs/examples) here.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    tabs: true,\n    toolbar: true,\n    worksheets: [{\n        minDimensions: [6,6],\n    }],\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet, jspreadsheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} tabs={true} toolbar={true}>\n                <Worksheet minDimensions={[6, 6]} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheet\" :tabs=\"true\" :toolbar=\"true\">\n        <Worksheet :minDimensions=\"[6, 6]\"/>\n    </Spreadsheet>\n</template>\n\n<script>\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default {\n    components: {\n        Spreadsheet,\n        Worksheet,\n    },\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new JavaScript data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            tabs: true,\n            toolbar: true,\n            worksheets: [{\n                minDimensions: [6,6],\n            }],\n        });\n    }\n}\n```\n\n## Copyright and License\n\nJspreadsheet CE is released under the MIT license.\n\n## Jspreadsheet Changelog\n\n### Jspreadsheet 5.0.0\n- Separation of spreadsheet and worksheets;\n- New worksheet methods and events;\n- Dedicated wrappers for React and Vue for better framework integration;\n- Modern development environment powered by Webpack;\n- Updated architecture aligned with other distributions;\n\n[More information](/jspreadsheet/docs/upgrade-from-v4-to-v5)\n\n### Jspreadsheet 4.6.0\n- Jexcel renamed to Jspreadsheet.\n- Integration with Jsuites v4.\n\n### Jspreadsheet 4.2.3\n- The spreadsheet plugin is now compatible with Jsuites v3.\n- New flags and security implementations.\n- New DOM element references in the toolbar.\n- Worksheet events are now tabbed.\n\n### Jspreadsheet 4.0.0\nSpecial thanks to [FDL - Fonds de Dotation du Libre](https://www.fdl-lef.org/) for their support and sponsorship, which made the new version possible with many exciting features.\n\n- Workbook/tab support for spreadsheets.\n- Create dynamic spreadsheets from static HTML elements.\n- Highlight selected cells in the spreadsheet after CTRL+C.\n- Footer with formula support.\n- Multiple column resizing.\n- JSON update support (helpers to update a remote server).\n- Centralized event dispatch method for all spreadsheet events.\n- Custom helpers: `=PROGRESS` (progress bar), `=RATING` (5-star rating).\n- Custom formula helpers: `=COLUMN`, `=ROW`, `=CELL`, `=TABLE`, `=VALUE`.\n- Dynamic nested header updates.\n- New HTML editing column type.\n- New flags: `includeHeadersOnCopy`, `persistence`, `filters`, `autoCasting`, `freezeColumns`.\n- New events: `onevent`, `onchangepage`, `onbeforesave`, `onsave`.\n- More examples and documentation.\n\n### Jspreadsheet 3.9.0\n- New methods.\n- General fixes.\n\n### Jspreadsheet 3.6.0\n- Improved spreadsheet formula parsing.\n- New spreadsheet events.\n- New initialization options.\n- General fixes.\n\n### Jspreadsheet 3.2.3\n- `getMeta`, `setMeta` methods.\n- NPM package with jSuites.\n- General fixes.\n\n### Jspreadsheet 3.0.1\nJspreadsheet v3 is a complete rebuild of the JavaScript spreadsheet (previously a jQuery plugin). Due to the changes, full compatibility could not be ensured. If upgrading, your code may require some updates. For more information, refer to the article on upgrading from Jspreadsheet v2 or Handsontable.\n\n**New features in Jspreadsheet v3:**\n- Drag and drop columns.\n- Resizable rows.\n- Merge columns.\n- Search functionality.\n- Pagination.\n- Lazy loading.\n- Full-screen mode.\n- Image upload.\n- Native color picker.\n- Better mobile compatibility.\n- Enhanced nested headers support.\n- Advanced keyboard navigation.\n- Better hidden column management.\n- Data picker enhancements: dropdown, autocomplete, multiple selection, group options, and icons.\n- Import from XLSX (experimental).\n\n**Major improvements:**\n- A new formula engine with faster results and no external dependencies.\n- No use of selectors, leading to faster performance.\n- New native column types.\n- No jQuery required.\n- Examples for React, Vue, and Angular.\n- XLSX support via a custom SheetJS integration (experimental).\n\n### Jspreadsheet 2.1.0\n- Mobile touch improvements.\n- Paste fixes and a new CSV parser.\n\n### Jspreadsheet 2.0.0\n- New radio column type.\n- New dropdown with autocomplete and multiple selection options.\n- Header/body separation for better scroll and column resize behavior.\n- Text-wrap improvements, including Excel-compatible `alt+enter`.\n- New `set/get` meta information.\n- New `set/get` configuration parameters.\n- Programmatic `set/get` cell styles.\n- `set/get` cell comments.\n- Custom toolbar for tables.\n- Responsive calendar picker.\n\n### Jspreadsheet 1.5.7\n- Improvements to checkbox column type.\n- Updates to table destruction in jQuery.\n\n### Jspreadsheet 1.5.1\n- Spreadsheet data overflow and fixed headers.\n- Navigation improvements.\n\n### Jspreadsheet 1.5.0\n- Relative `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`.\n- Redo and undo support for `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`, `moveRow`.\n- New formula column recursive chain.\n- New alternative design option (Bootstrap-like).\n- `updateSettings` improvements.\n"
  },
  {
    "path": "docs/jspreadsheet/download.md",
    "content": "title: Download Jspreadsheet\nkeywords: Download Jspreadsheet\ndescription: Download Jspreadsheet for your projects.\ncanonical: https://bossanova.uk/jspreadsheet/download\n\n# Jspreadsheet Download\n\nJspreadsheet is available through multiple distribution channels, making it easy to integrate into your project regardless of your preferred setup.\n\n## Download Options\n\n### Direct Download\n\n[Click here to download](https://bossanova.uk/jspreadsheet/v5/jspreadsheet.zip)\n\n### CDN\n\nYou can include Jspreadsheet directly via CDN for quick setup:  \n\n{.ignore}\n```html\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/jspreadsheet.css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/jspreadsheet.js\"></script>\n```  \n\n### NPM\n\nFor modern web development, install Jspreadsheet via NPM:\n\n```bash\nnpm install jspreadsheet-ce\n```\n\n### GitHub Repository\n\nAccess the source code and contribute to the development of Jspreadsheet on [GitHub](https://github.com/jspreadsheet/jspreadsheet).\n\n## Documentation\n\nFor more information on integrating Jspreadsheet into your project, visit the [official documentation](https://bossanova.uk/jspreadsheet/docs).  \n"
  },
  {
    "path": "docs/jspreadsheet/sponsors.md",
    "content": "title: Sponsors  \nkeywords: Jspreadsheet sponsors, open-source sponsorship, FDL sponsors, ETH Scientific IT Services, Initiative Tree, Market Control  \ndescription: Learn about the most recent sponsors supporting Jspreadsheet, including FDL - Libre Endowment Fund, ETH Scientific IT Services, Initiative Tree, and Market Control.\ncanonical: https://bossanova.uk/jspreadsheet/sponsors\n\n# Sponsors\n\nThe most recent sponsors:\n\n![](https://www.fdl-lef.org/FDL-Logo.Colorised.Forest?format=png)  \n\n**FDL - Fonds de Dotation du Libre - Libre Endowment Fund**\n\nFDL - Libre Endowment Fund is a French endowment fund that supports developers and publishers of Free and Open Source software projects. FDL accepts tax-deductible donations to fund specific feature developments for software deemed of general interest.\n\nFDL works in synergy with its sister non-profit organization, \"AWL\" (Action pour un Web Libre). FDL focuses on long-term projects, while AWL sponsors short-term initiatives. Both maintain a repository of Free and Open Source software publishers at [afs.one](https://afs.one). Learn more about FDL and AWL at [www.fdl-lef.org](https://www.fdl-lef.org).\n\n**ETH Scientific IT Services**\n\n**Initiative Tree**\n\n**Market Control**  "
  },
  {
    "path": "docs/jspreadsheet/v2/docs/events.md",
    "content": "title: Handling events on Jspreadsheet\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table, grid, events\ndescription: Learn how to handle events on Jspreadsheet\n\n[Back to Documentation](/jspreadsheet/v2/docs)\n\n# Events on your online spreadsheets\n\n## Update Settings on the Go\n\nThe JavaScript spreadsheet plugin offers a native feature to customize your table on the go. You can define the method updateSettings to create a parser and customize the data should be shown to the user, as the example below.\n\n```javascript\n// Live update of the settings\n$('#my').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        if (col == 2 || col == 3) {\n            // Get text\n            txt = $(cell).text();\n            // Format text\n            txt = numeral(txt).format('0,0.00');\n            // Update cell value\n            $(cell).html(' $ ' + txt);\n        }\n    }\n});\n```\n\n[Basic example updating the table based on the user entry](/jspreadsheet/v2/examples/currency-and-masking-numbers)\n\n## Basic events\n\nThe JavaScript spreadsheet basic events available in this current version.\n\n| Event| description  |\n| ---|---  |\n| **onload** | This method is called when the method setData  |\n| **onbeforechange** | Before a column value is changed.  |\n| **onchange** | After a column value is changed.  |\n| **onafterchange** | After all change events is performed.  |\n| **oninsertrow** | After a new row is inserted.  |\n| **ondeleterow** | After a row is excluded.  |\n| **oninsertcolumn** | After a new column is inserted.  |\n| **ondeletecolumn** | After a column is excluded.  |\n| **onselection** | On the selection is changed.  |\n| **onsort** | After a colum is sorted.  |\n| **onresize** | After a colum is resized.  |\n| **onmoverow** | After a row is moved to a new position.  |\n| **onfocus** | On table focus  |\n| **onblur** | On table blur  |\n  \n[Advanced example logging all events in a tableexample](/jspreadsheet/v2/examples/tracking-changes-on-the-spreadsheet)  \n  \n\n## Basic Example:\n\n{.ignore}\n```javascript\nhandler = function(obj, cell, val) {\n    console.log('My table id: ' + $(obj).prop('id'));\n    console.log('Cell changed: ' + $(cell).prop('id'));\n    console.log('Value: ' + val);\n};\n\ninsertrow = function(obj) {\n    alert('new row added on table: ' + $(obj).prop('id'));\n}\n\ndeleterow = function(obj) {\n    alert('row excluded on table: ' + $(obj).prop('id'));\n}\n\ndata = [\n    ['Mazda', 2001, 2000, '2006-01-01 00:00:00'],\n    ['Peugeot', 2010, 5000, '2005-01-01 00:00:00'],\n    ['Honda Fit', 2009, 3000, '2004-01-01 00:00:00'],\n    ['Honda CRV', 2010, 6000, '2003-01-01 00:00:00'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Date', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    onchange:handler,\n    oninsertrow:insertrow,\n    ondeleterow:deleterow,\n    columns: [\n        { type: 'text' },\n        { type: 'numeric' },\n        { type: 'numeric' },\n        { type: 'calendar', options: { format:'DD/MM/YYYY HH24:MI', time:1 } },\n    ]\n});\n```\n\nSee this working example on jsfiddle:\n\n[Basic example on handling events on jsFiddle](https://jsfiddle.net/spreadsheet/5n21ep6s/)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/docs/programmatically-changes.md",
    "content": "title: Programmatical Changes\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jexcel.\n\n[Back to Documentation](/jspreadsheet/v2/docs)\n\n# Programmatically changes available in the plugin\n\nUsing Jspreadsheet will have a comprehensive range of native commands to interact with the users of your javascript spreadsheet.\n\n## General Methods\n\n| Description| Example|\n| ---|---|\n| **getData: get the full or partial table data in json** <br>@Param boolan onlyHighlighedCells - Get only highlighted cells  | $('#my').jexcel('getData', false); |  \n| **getRowData: get the data from the a row** <br>@Param integer rowNumber - Row number starting from zero.  | $('#my').jexcel('getRowData', 1);  |\n| **getColumnData: get the data from the a column**  <br>@Param integer columnNumber - Column number starting from zero.  | $('#my').jexcel('getColumnData', 2);  |\n| **setData: Update the table data**  <br>@Param json newData - New data, null will reload what is in memory.<br>@Param boolean ignoreSpare - Ignore configuration of min spare column and rows. | $('#my').jexcel('setData', [data], false); |  \n| **insertColumn: add a new column** <br>@Param mixed [array or integer] - Integer as the number of columns to be added. Or array to define the data you would like to insert.<br>@Param json - Properties of the new columns, null to set default.<br>@Param integer columnNumber - Column reference, null to add the new column after the last column. | $('#my').jexcel('insertColumn', 1, null, 3); |  \n| **deleteColumn: remove column by number**  <br>@Param integer columnNumber - Which column should be excluded starting on zero <br>@Param integer numOfColumns - How many columns should be deleted  | $('#my').jexcel('deleteColumn', 1); |  \n| **insertRow: add a new row**  <br>@Param mixed - Array with the new data or integer with the number of rows should be added<br>@Param rowNumber - Reference where to add the new row.  | $('#my').jexcel('insertRow', 1); |  \n| **deleteRow: remove row by number**  <br>@Param integer rowNumber - Which row should be excluded starting on zero<br>@Param integer numberOfRows - How many rows should be excluded | $('#my').jexcel('deleteRow', 1); |  \n| **getHeader: get the current header by column number**  <br>@Param integer columnNumber - Column number starting on zero | $('#my').jexcel('getHeader', 2); |  \n| **setHeader: change header by column**  <br>@Param integer columnNumber - column number starting on zero<br>@Param string columnTitle - New header title | $('#my').jexcel('setHeader', 1, 'Title'); |  \n| **getWidth: get the current column width**  <br>@Param integer columnNumber - column number starting on zero | $('#my').jexcel('getWidth', 2); |  \n| **setWidth: change column width**  <br>@Param integer columnNumber - column number starting on zero<br>@Param string newColumnWidth - New column width | $('#my').jexcel('setWidth', 1, 100); |  \n| **orderBy: will reorder a column asc or desc**  <br>@Param integer columnNumber - column number starting on zero<br>@Param smallint sortType - Zero will toggle current option, one for desc, two for asc | $('#my').jexcel('orderBy', 2); |  \n| **getValue: get current cell value**  <br>@Param mixed cellIdent - str compatible with excel, or as object. | $('#my').jexcel('getValue', 'A1'); |  \n| **setValue: change the cell value**  <br>@Param mixed cellIdent - str compatible with excel, or as object.<br>@Param string Value - new value for the cell | $('#my').jexcel('setValue', 'A1'); |  \n| **updateSelection: select cells**  <br>@Param object startCell - cell object<br>@Param object endCell - cell object | $('#my').jexcel('updateSelection', [cell], [cell]); |  \n| **download: get the current data as a CSV file.**  <br>@Param none | $('#my').jexcel('download'); |  \n| **destroy: remove the table and all references and events attached.** <br>@Param none | $('#my').jexcel('destroy'); |  \n| **getCell: get the cell object based on a string**  <br>@Param styring cellIdent - str compatible with excel, or as object. | $('#my').jexcel('getCell', 'B1'); |\n| **getSelectedCells: get all selected cells**  <br>@Param none | $('#my').jexcel('getSelectedCells'); |\n| **undo: undo an action**  <br>@Param none | $('#my').jexcel('undo'); |\n| **redo: redo an action**  <br>@Param none | $('#my').jexcel('redo'); |\n| **moveRow: move an row to another position**  <br>@Param from - from position y0<br>@Param to - to position y1 | $('#my').jexcel('moveRow', 1, 2); |\n| **getStyle: get table or cell style**  <br>@Param mixed - cell identification or null for the whole table. | $('#my').jexcel('getStyle', 'A1');  |\n| **setStyle: set cell(s) CSS style**  <br>@Param mixed - json with whole table style information or just one cell identification. Ex. A1.<br>@Param k [optional]- CSS key<br>@Param v [optional]- CSS value | $('#my').jexcel('setSyle', [ { A1:'background-color:red' }, { B1: 'color:red'} ]); |\n| **getComments: get cell comments**  <br>@Param mixed - cell identification or null for the whole table. | $('#my').jexcel('getComments', 'A1'); |\n| **setComments: set cell comments**  <br>@Param cell - cell identification<br>@Param text - comments | $('#my').jexcel('setComments', 'A1', 'My cell comments!'); |\n| **getMeta: get the table or cell meta information**  <br>@Param mixed - cell identification or null for the whole table. | $('#my').jexcel('getMeta', 'A1');  |\n| **setMeta: set the table or cell meta information** <br>@Param mixed - json with whole table meta information. | $('#my').jexcel('setMeta', [ A1: { info1:'test' }, { B1: { info2:'test2', info3:'test3'} } ]); |\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/docs/quick-reference.md",
    "content": "title: Quick Reference\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Quick Reference for Jexcel spreadsheet properties\n\n[Back to Documentation](/jspreadsheet/v2/docs \"Back to the documentation section\")\n\n# The javascript spreadsheet quick reference\n\n## Methods\n\n| Method                                                                                                                                                                                                     | Description                                                                                     |\n|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|\n| **getData: Get the full or partial table data**  <br>@Param boolan onlyHighlighedCells - Get only highlighted cells                                                                                        | $('#my').jexcel('getData', false);                                                              |\n| **setData: Update the table data**  <br>@Param json newData - New json data, null will reload what is in memory.<br>@Param boolean ignoreSpare - ignore configuration of minimal spareColumn/spareRows     | $('#my').jexcel('setData', [json], false);                                                      |\n| **insertColumn: add a new column**  <br>@Param integer numberOfColumns - Number of columns should be added<br>@Param string headerTitle - Header title                                                     | $('#my').jexcel('insertColumn', 1, { header:'Title' });                                         |\n| **deleteColumn: remove column by number**  <br>@Param integer columnNumber - Which column should be excluded starting on zero                                                                              | $('#my').jexcel('deleteColumn', 1);                                                             |                                                            \n| **insertRow: add a new row**  <br>@Param integer numberOfRows - Number of rows should be added                                                                                                             | $('#my').jexcel('insertRow', 1);                                                                |                                                           \n| **deleteRow: remove row by number**  <br>@Param integer rowNumber - Which row should be excluded starting on zero                                                                                          | $('#my').jexcel('deleteRow', 1);                                                                |\n| **getHeader: get the current header by column number**  <br>@Param integer columnNumber - Column number starting on zero                                                                                   | $('#my').jexcel('getHeader', 2);                                                                |\n| **setHeader: change header by column**  <br>@Param integer columnNumber - column number starting on zero<br>@Param string columnTitle - New header title                                                   | $('#my').jexcel('setHeader', 1, 'Title');                                                       |\n| **getWidth: get the current column width**  <br>@Param integer columnNumber - column number starting on zero                                                                                               | $('#my').jexcel('getWidth', 2);                                                                 |\n| **setWidth: change column width**  <br>@Param integer columnNumber - column number starting on zero<br>@Param string newColumnWidth - New column width                                                     | $('#my').jexcel('setWidth', 1, 100);                                                            |\n| **orderBy: will reorder a column asc or desc**  <br>@Param integer columnNumber - column number starting on zero<br>@Param smallint sortType - Zero will toggle current option, one for desc, two for asc  | $('#my').jexcel('orderBy', 2);                                                                  |\n| **getValue: get current cell value**  <br>@Param mixed cellIdent - str compatible with excel, or as object.                                                                                                | $('#my').jexcel('getValue', 'A1');                                                              |\n| **setValue: change the cell value**  <br>@Param mixed cellIdent - str compatible with excel, or as object.<br>@Param string Value - new value for the cell                                                 | $('#my').jexcel('setValue', 'A1');                                                              | \n| **updateSelection: select cells**  <br>@Param object startCell - cell object<br>@Param object endCell - cell object<br>@Param boolean ignoreEvents - ignore onselection event                              | $('#my').jexcel('updateSelection', [cell], [cell], true);                                       |\n| **download: get the current data as a CSV file.**  <br>@Param none                                                                                                                                         | $('#my').jexcel('download');                                                                    |\n| **getConfig: get the current value of one configuration by key** <br>@Param string configuration key                                                                                                       | $('#my').jexcel('getConfig', 'allowInsertColumn');                                              |\n| **setConfig: set the value of one configuration by key**  <br>@Param string configuration key<br>@Param mixed configuration value                                                                          | $('#my').jexcel('setConfig', 'allowInsertColumn', true);                                        |\n| **download: get the current data as a CSV file.**  <br>@Param none                                                                                                                                         | $('#my').jexcel('download');                                                                    |\n| **getStyle: get table or cell style**  <br>@Param mixed - cell identification or null for the whole table.                                                                                                 | $('#my').jexcel('getStyle', 'A1');                                                              |\n| **setStyle: set cell(s) CSS style**  <br>@Param mixed - json with whole table style information or just one cell identification. Ex. A1.<br>@Param k [optional]- CSS key<br>@Param v [optional]- CSS value | $('#my').jexcel('setSyle', [ { A1:'background-color:red' }, { B1: 'color:red'} ]);              |\n| **getComments: get cell comments**  <br>@Param mixed - cell identification or null for the whole table.                                                                                                    | $('#my').jexcel('getComments', 'A1');                                                           |\n| **setComments: set cell comments**  <br>@Param cell - cell identification<br>@Param text - comments                                                                                                        | $('#my').jexcel('setComments', 'A1', 'My cell comments!');                                      |\n| **getMeta: get the table or cell meta information**  <br>@Param mixed - cell identification or null for the whole table.                                                                                   | $('#my').jexcel('getMeta', 'A1');                                                               |\n| **setMeta: set the table or cell meta information**  <br>@Param mixed - json with whole table meta information.                                                                                            | $('#my').jexcel('setMeta', [ A1: { info1:'test' }, { B1: { info2:'test2', info3:'test3'} } ]);  |\n  \n[Working example](/jspreadsheet/v2/docs/programmatically-changes)\n\n  \n\n## Events\n\n| Event| description  |\n| ---|---  |\n| **onload** | This method is called when the method setData  |\n| **onbeforechange** | Before a column value is changed.  |\n| **onchange** | After a column value is changed.  |\n| **onafterchange** | After all change events is performed.  |\n| **oninsertrow** | After a new row is inserted.  |\n| **ondeleterow** | After a row is excluded.  |\n| **oninsertcolumn** | After a new column is inserted.  |\n| **ondeletecolumn** | After a column is excluded.  |\n| **onselection** | On the selection is changed.  |\n| **onsort** | After a colum is sorted.  |\n| **onresize** | After a colum is resized.  |\n| **onmoverow** | After a row is moved to a new position.  |\n| **onfocus** | On table focus  |\n| **onblur** | On table blur  |\n  \n[Example on handling events on your javascript spreadsheet](/jspreadsheet/v2/examples/tracking-changes-on-the-spreadsheet)\n\n## Initialization parameters\n\n| Parameter| description  |\n| ---|---  |\n| **columns** | Column type, dropdown options, text wrapping,marking, etc. |\n| **colHeaders** | Column header titles  |\n| **colWidths** | Column widths: width in px.  |\n| **colAlignments** | Column alignments: left, right, center.  |\n| **colHeaderClasses** | Column custom CSS classes  |\n| **defaultColWidth** | Default width for a new column  |\n| **minSpareRows** | Minimum number of spare rows  |\n| **minSpareCols** | Minimum number of spare cols  |\n| **minDimensions** | Minimum table dimensions: [cols,rows]  |\n| **contextMenu** | Context menu content: function() { return customMenu }  |\n| **columnSorting** | Allow column sorting: bool  |\n| **columnResize** | Allow column resizing: bool  |\n| **rowDrag** | Allow row dragging: bool  |\n| **editable** | Allow table edition: bool  |\n| **allowInsertRow** | Allow insert a new row: bool  |\n| **allowManualInsertRow** | Allow user to insert a new row: bool  |\n| **allowInsertColumn** | Allow insert a new column: bool  |\n| **allowManualInsertColumn** | Allow user to create a new column: bool |\n| **allowDeleteRow** | Allow delete a row: bool  |\n| **allowDeleteColumn** | Allow delete a column: bool  |\n| **wordWrap** | Global text wrapping: bool  |\n| **csvFileName** | CSV default file name: string  |\n| **selectionCopy** | Allow selection copy: bool  |\n| **tableOverflow** | Allow table overflow: bool  |\n| **tableHeight** | Force the max height of the table  |\n| **tableWidth** | Force the max width of the table  |\n| **allowComments** | Allow comments over the cells  |\n| **toolbar** | Add custom toolbars |\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/docs.md",
    "content": "title: Jspreadsheet | Documentation\nkeywords: Jspreadsheet, grid, data, table, datatable, json, excel, excel-like, jquery, javascript, spreadsheet\ndescription: Introduction, basic methods, event handles and all information you need about this jquery plugin.\n\n## Column types\n\nJspreadsheet brings some native columns in addition to the default input text. It means you can get alternative ways to entry data in your spreadsheet. From advanced numeric inputs, dropdowns to calendar picks and a very easy way to have your custom integrations, makes the jExcel plugin a very flexible tool to enhance the user experience when using your applications.\n\nIn the example below, you will have text, numeric inputs and a calendar picker. But, other native options will be available such as: _text, numeric, calendar, checkbox, dropdown, autocomplete._\n\n```javascript\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Date', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'numeric' },\n        { type: 'numeric' },\n        { type: 'calendar', options: { format:'DD/MM/YYYY' } },\n    ]\n});\n```\n\n### Custom columns\n\nTo make a flexible tools, it is possible to extend the plugin to create your custom entries. The following example will show you how to integrate a third party plugin to create your custom columns.\n\n[http://bossanova.uk/jspreadsheet/v2/examles/integrating-a-third-party-plugin-into-your-spreadsheet](/jspreadsheet/v2/examples/integrating-a-third-party-plugin-into-your-spreadsheet)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/a-custom-table-design.md",
    "content": "title: Jspreadsheet | Examples | Bootstrap and custom table design\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid\ndescription: Create a custom table design. For example a bootstrap-like spreadsheet table.\ncanonical: https://bossanova.uk/jspreadsheet/v2/examples/a-custom-table-design\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Create custom CSS for your javascript spreadsheet\n\nThe following example shows a CSS addon to change the core layout of your jquery tables.\n\n  \n\n## Green borders and corners jquery spreadsheet\n\n[Bootstrap-like jquery spreadsheet example](/jspreadsheet/v2/examples/a-custom-table-design)\n\n## Bootstrap-like jquery spreadsheet.\n\n[Green borders and corners jquery spreadsheet example](/jspreadsheet/v2/examples/a-custom-table-design?layout=green)\n\nYour jquery table can be customized by including an additional addon CSS. If you have created a nice design, please share with us.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<!-- Optional CSS addon -->\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.green.min.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.bootstrap.min.css\" type=\"text/css\" />\n\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['BR', 'Cheese', 1, 1900.01],\n    ['CA', 'Apples', 0, 1200.21],\n    ['US', 'Carrots', 1, 400.34],\n    ['GB', 'Oranges', 0, 900.43],\n];\n\n$('#my').jexcel({\n    data: data,\n    colHeaders: ['Country', 'Food', 'Stock', 'Price'],\n    colWidths: [ 300, 100, 100, 100 ],\n    colAlignments: [ 'left', 'left', 'left', 'left' ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'autocomplete', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n        { type: 'number' },\n    ]\n});\n</script>\n</html>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/autocomplete.md",
    "content": "title: Autocomplete Column\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, autocomplete, customization, column\ndescription: Customize your column type to autocomplete\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Autocomplete column\n\nIt is natively presented as single dropdown with text filter enabled and it is based on a pre-defined options defined in the instance declaration or in a dynamic remote JSON source.\n\n[Json source demo](/jspreadsheet/countries)\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['BR', 'Cheese', 1],\n    ['CA', 'Apples', 0],\n    ['US', 'Carrots', 1],\n    ['GB', 'Oranges', 0],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock'],\n    colWidths: [ 300, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'autocomplete', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/comments.md",
    "content": "title: Jspreadsheet | Examples | Add comments in your jquery table\nkeywords: Jexcel, jquery, javascript, cell comments, jquery table\ndescription: Manage a table cell comments\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Manage cell comments in your jquery table\n\nThe most recent version the spreadsheet plugin brings the possibility to manage custom comments for each individual cells. By allowComments in the initialization, the user will be able to add comments in the rigth click contextMenu.\n\nIt is also possible to manage your cell comments programmatically by the use of commands such as setComments or getComments, for example:\n\n| Method                                                                  | Description        |\n|-------------------------------------------------------------------------|--------------------|\n| $('#my1').jexcel('setComments', 'A1', 'This is the comments from A1');  | Set A1 comments    |\n| $('#my1').jexcel('getComments', 'A1');                                  | Get A1 comments    |\n| $('#my1').jexcel('setComments', 'A1', '');                              | Reset A1 comments  |\n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jcalendar.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jcalendar.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\ndata1 = [\n    ['US', 'Cheese', '2019-02-12'],\n    ['CA', 'Apples', '2019-03-01'],\n    ['CA', 'Carrots', '2018-11-10'],\n    ['BR', 'Oranges', '2019-01-12'],\n];\n\n$('#my1').jexcel({\n    data:data1,\n    colHeaders: [ 'Product Origin','Description','Best before' ],\n    colWidths: [ 300, 200, 100 ],\n    columns: [\n        { type: 'dropdown', url:'/jspreadsheet/countries' },\n        { type: 'text' },\n        { type: 'calendar' },\n    ],\n    allowComments:true,\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/create-from-a-existing-html-table.md",
    "content": "title: Create From HTML Table\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, table\ndescription: Create Spreadsheet based on HTML Table\n\n```html\n<table id='spreadsheet'>\n<thead><tr><th>Creado</th><th>Creado por</th><th>N</th><th>Nombre</th><th>Nombre</th><th>Tipo</th><th>Calle</th><th>Poblacin</th><th>Nombre SAP</th><th>Cdigo Producto</th><th>Respuesta</th></tr></thead><tbody><tr><td>2019-04-02</td><td>ocastellanos</td><td>30616</td><td>GRUPO UVESCO, S.A. &quot;NETTO&quot;</td><td>GRANDE</td><td>Supermercado grande</td><td>CTRA. S-20. BARRIO MONTE, S/N</td><td>SANTANDER</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30616</td><td>GRUPO UVESCO, S.A. &quot;NETTO&quot;</td><td>GRANDE</td><td>Supermercado grande</td><td>CTRA. S-20. BARRIO MONTE, S/N</td><td>SANTANDER</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30616</td><td>GRUPO UVESCO, S.A. &quot;NETTO&quot;</td><td>GRANDE</td><td>Supermercado grande</td><td>CTRA. S-20. BARRIO MONTE, S/N</td><td>SANTANDER</td><td>NUGGETS</td><td>206776</td><td>ROTURA</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30616</td><td>GRUPO UVESCO, S.A. &quot;NETTO&quot;</td><td>GRANDE</td><td>Supermercado grande</td><td>CTRA. S-20. BARRIO MONTE, S/N</td><td>SANTANDER</td><td>ESCALOPA</td><td>206777</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30716</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>JUAN XXIII, 12</td><td>TORRELAVEGA</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30716</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>JUAN XXIII, 12</td><td>TORRELAVEGA</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30716</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>JUAN XXIII, 12</td><td>TORRELAVEGA</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-02</td><td>ocastellanos</td><td>30716</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>JUAN XXIII, 12</td><td>TORRELAVEGA</td><td>NUGGETS</td><td>206776</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-03</td><td>ocastellanos</td><td>46825</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>Barrio la Cruz S/N</td><td>Liencres</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-03</td><td>ocastellanos</td><td>46825</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>Barrio la Cruz S/N</td><td>Liencres</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-03</td><td>ocastellanos</td><td>46825</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>Barrio la Cruz S/N</td><td>Liencres</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-03</td><td>ocastellanos</td><td>46825</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado medianos</td><td>Barrio la Cruz S/N</td><td>Liencres</td><td>NUGGETS</td><td>206776</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>NUGGETS</td><td>206776</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>NUGGETS</td><td>206776</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-05</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>ESCALOPA</td><td>206777</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>ESCALOPA</td><td>206777</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30296</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>1</td><td>Supermercado grande</td><td>LEONARDO RUCABADO, 26</td><td>CASTRO URDIALES</td><td>NUGGETS</td><td>206776</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-12</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-12</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-12</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>NUGGETS</td><td>206776</td><td>ROTURA</td></tr>\n<tr><td>2019-04-12</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>NUGGETS</td><td>206776</td><td>ROTURA</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30596</td><td>GRUPO UVESCO, S.A. &quot;BM SUPERMERCADOS&quot;</td><td>PEQUEO</td><td>Supermercado grande</td><td>RUBEN DARIO, 2</td><td>SANTANDER</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>ESCALOPA</td><td>206777</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>NUGGETS</td><td>206776</td><td>PRESENTE</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>HAMBURGUESA BARBACOA</td><td>206775</td><td>NO EST EN SURTIDO</td></tr>\n<tr><td>2019-04-15</td><td>ocastellanos</td><td>30676</td><td>GRUPO UVESCO, S.A. &quot;NORCASH&quot;</td><td>PEQUEO</td><td>Supermercado pequeo</td><td>P ALISAS, S/N</td><td>SOLARES</td><td>HAMBURGUESA NATURAL</td><td>206774</td><td>NO EST EN SURTIDO</td></tr>\n</tbody></table>\n\n<script>\n$('#my').jexcel($.fn.jexcel('parseTable', document.getElementById('spreadsheet'), true));\n</script>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/creating-a-table-from-an-external-csv-file.md",
    "content": "title: Jspreadsheet | Examples | Creating a web spreadsheet based on an external CSV\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, CSV, table, grid\ndescription: How to load the data from an external CSV file into a Jspreadsheet grid or table.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Creating a javascript spreadsheet based on an external CSV\n\nThe example below helps you to create a javascript spreadsheet table based on a remote CSV file, including the headers. The examples also requires a third party jquery CSV parser plugin (100% IETF RFC 4180).\n\nOriginal file: [/jspreadsheet/demo.csv](/jspreadsheet/demo.csv).\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<p><button id='download'>Export my spreadsheet as CSV</button></p>\n\n<script>\n$('#my').jexcel({\n    // Full CSV URL\n    csv:'/jspreadsheet/demo1.csv',\n    // Use the first row of your CSV as the headers\n    csvHeaders:true,\n    tableOverflow:true,\n    tableHeight:'300px',\n    // Headers\n    colWidths: [70, 200, 300],\n});\n\n$('#download').on('click', function () {\n    $('#my').jexcel('download');\n});\n</script>\n</html>\n```  \n\n## Online demo on jsFiddle\n\n  \n  \n  \n\n# Creating a javascript spreadsheet based on an external JSON file\n\nIn a similar way, you can create a jquery table based on an external JSON file format by using the _url: directive_ as below.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\n$('#my1').jexcel({\n    // The URL from your data table file in JSON format.\n    url:'/jspreadsheet/json'\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/currency-and-masking-numbers.md",
    "content": "title: Jspreadsheet | Examples | Using currency column type and how to mask numbers\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, masking, current, numbers\ndescription: Handling currency types and masking numbers.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Currency and masking numbers\n\nThe next example will show how to mask the column and automatic change colors based on the column values.\n \n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery.mask/1.14.3/jquery.mask.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['3D Systems ', 'DDD', 15.08, 15.1, 0.01, 0.07],\n    ['3M', 'MMM', 178.34, 178.65, 0.49, 0.28],\n    ['58.com', 'ADR WUBA', 30.01, 29.1, -0.76, -2.55],\n    ['500.com', 'ADR WBAI', 13.48, 13.56, 0.1, 0.74],\n];\n\n// Create the spreadsheet based on data\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Name', 'Symbol', 'Open', 'Close', '% Net', '% Change'],\n    colWidths: [ 300, 80, 100, 100, 100, 100 ],\n    columns: [\n              { type: 'text' },\n              { type: 'text' },\n              { type: 'text', mask:'#.###.00', options:{ reverse: true } },\n              { type: 'text', mask:'#.###.00', options:{ reverse: true } },\n              { type: 'text' },\n              { type: 'text' },\n              ]\n});\n\n// Live update of the settings\n$('#my').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        if (col == 4 || col == 5) {\n            if (val < 0) {\n                $(cell).css('color', '#ff0000');\n            } else {\n                $(cell).css('color', '#249D7F');\n            }\n        }\n    }\n});\n</script>\n</html>\n```  \n\n# Formatting a column value using an external javascript plugin\n\nThis example shows an alternative way to format numbers in your table. It integrates the numeraljs javascript plugin to format the column.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\nlet data1 = [\n    ['Apple', 'Kg', 3.99],\n    ['Orange', 'Kg', 2.99],\n    ['Carrots', 'Units', 0.39],\n];\n\n$('#my1').jexcel({\n    data:data1,\n    colHeaders: ['Product', 'Unit', 'Price' ],\n    colWidths: [ 300, 80, 100],\n    columns:[\n        { type:'text' },\n        { type:'text' },\n        { type:'number' },\n    ]\n});\n\n$('#my1').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        if (col == 2) {\n            txt = numeral(val).format('0,0.00');\n            $(cell).html(' ' + txt);\n        }\n    }\n});\n</script>\n</html>\n```  \n\n**NOTE** The Jspreadsheet uses the [jQuery Mask Plugin](https://github.com/igorescobar/jQuery-Mask-Plugin) to perform the masking. But, the example above shows that it is possible to integrate any external plugin for masking or to visual adjust the data automatically.\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/getting-data-from-table.md",
    "content": "title: Jspreadsheet | Examples | Extract Data from Spreadsheet\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, extract, get, data\ndescription: Get data from inside the datagrid to javascript\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Extract the data from your Jspreadsheet jquery plugin\n\nThe following example shows how to extract the data from your jquery table in CSV or JSON formats.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<p>\n<button id='upload'>Convert to CSV</button>\n<button id='json'>Convert to JSON</button>\n</p>\n\n<textarea id='txt' style='width:400px;height:120px'></textarea>\n\n<script>\n$('#my').jexcel({\n    csv:'https://bossanova.uk/components/bossanova-ui/demo/demo1.csv',\n    csvHeaders:true,\n    colWidths: [70, 200, 300],\n});\n\n$('#upload').on('click', function () {\n  var data = $('#my').jexcel('getData');\n\t$('#txt').val($.csv.fromArrays(data));\n});\n\n$('#json').on('click', function () {\n  var data = $('#my').jexcel('getData');\n\t$('#txt').val(JSON.stringify(data));\n});\n</script>\n</html>\n```  \n\n## Online demo on jsFiddle\n\n[How to extract data example from your jquery table on jsFiddle](https://jsfiddle.net/spreadsheet/tzy1h6rg/)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/headers.md",
    "content": "title: Jspreadsheet | Examples | Nested Headers\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, nested headers\ndescription: Creating a Jspreadsheet table with nested headers.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Nested Headers\n\nThe most recent version of the jquery plugin Jspreadsheet implements nested headers natively. A few examples on how to add nested headers your jquery table:\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['BR', 'Cheese', 1],\n    ['CA', 'Apples', 0],\n    ['US', 'Carrots', 1],\n    ['GB', 'Oranges', 0],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock'],\n    colWidths: [ 300, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'dropdown', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n    ],\n    nestedHeaders:[\n        [\n            { title:'Supermarket information', colspan:'3' },\n        ],\n        [\n            { title:'Location', colspan:'1' },\n            { title:' Other Information', colspan:'2' }\n        ],\n    ],\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/images.md",
    "content": "title: Jspreadsheet | Examples | Images on your images\nkeywords: Jexcel, jquery, javascript, spreadsheet, table, jquery plugin, images\ndescription: Add images on your spreadsheet cells\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Embed images on your cells\n\nThe following example shows how to render images inside your spreadsheet cells\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nvar data = [\n    ['https://marketplace.canva.com/MACcZp2p4po/2/0/thumbnail_large/canva-black-white-acoustic-album-cover-MACcZp2p4po.jpg', 'Paul Parker'],\n    ['https://marketplace.canva.com/MACcY55adP4/1/0/thumbnail_large/canva-black-and-white-masculine-acoustic-modern-album-cover-MACcY55adP4.jpg','Mark Ellen']\n]\n\n$('#my').jexcel({\n    data:data,\n    colWidths: [300, 200],\n});\n\n$('#my').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        if (col == 0) {\n            $(cell).html('<input type=\"hidden\" value=\"'+ val +'\"><img src=\"' + val + '\" style=\"width:100px;height:100px\">');\n        }\n    }\n});\n</script>\n</html>\n```\n\n[Edit this example on jsFiddle](https://jsfiddle.net/spreadsheet/9296zmpf/)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/including-formulas-on-your-spreadsheet.md",
    "content": "title: Jspreadsheet | Examples | Formulas on Spreadsheet\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, autocomplete, customization, column\ndescription: Using Functions inside your spreadsheet\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Including formulas on your javascript spreadsheet plugin\n\nThe example below shows how to use Excel like formulas on your jquery spreadsheet.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n<script src=\"http://cdn.bossanova.uk/js/excel-formula.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['Furnace',1,10000,'=B1*C1','=IF(C1 > 5000, true, false)'],\n    ['Tower',2,6000,'=B2*C2','=IF(C2 > 5000, true, false)'],\n    ['Drum',3,5000,'=B3*C3','=IF(C3 > 5000, true, false)'],\n    ['Pump',4,4000,'=B4*C4','=IF(C4 > 5000, true, false)'],\n    ['Total','=SUM(B1:B4)','=(C1+C2+C3+C4)','=SUM(D1:D4)','']\n]\n\n$('#my').jexcel({\n    data:data,\n    columns: [\n        { type:'text' },\n        { type:'numeric' },\n        { type:'numeric' },\n        { type:'numeric' },\n    ],\n    colHeaders: ['Equipment','Quantity', 'Price', 'Total', '>5000?'],\n    colWidths: [ 200, 80, 100, 100, 100 ]\n});\n\n$('#my').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        // Format numbers\n        if (col == 2 || col == 3) {\n            // Get text\n            txt = $(cell).text();\n            // Format text\n            txt = numeral(txt).format('0,0.00');\n            // Update cell value\n            $(cell).html(' $ ' + txt);\n        }\n\n        // Bold the total row\n        if ($(cell).text() == 'Total') {\n            $('.r' + row).css('font-weight', 'bold');\n            $('.r' + row).css('background-color', '#fffaa3');\n        }\n    }\n});\n</script>\n</html>\n```  \n\n# Third party formula implementations\n\nYou can use a third party libraries to execute various Excel like formulas.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.0/underscore-min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/underscore.string/3.3.4/underscore.string.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jstat/1.7.1/jstat.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeric/1.2.6/numeric.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<script src=\"https://bossanova.uk/components/jexcel/dist/js/excel-formula.min.js\"></script>\n<script src=\"https://bossanova.uk/components/jexcel/dist/js/formulas.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\nlet data1 = [\n    ['Furnace', 10, 'AVERAGE', '=AVERAGE(B1:B4)'],\n    ['Tower', 9, 'STDEVA', '=STDEVA(B1:B4)'],\n    ['Drum', 44, 'MAX', '=MAX(B1,B2,B3,B4)'],\n    ['Pump', 12, 'COUNT', '=COUNT(B1:B4)'],\n];\n\n$('#my1').jexcel({\n    data:data1,\n    colWidths: [ 400, 80, 100, 200 ],\n});\n</script>\n</html>\n```  \n  \n\n# Custom javascript formulas\n\nThe example below shows how to create your own method in javascript to apply as a Excel like formula\n\n## Source code\n\n```html\n<html>\n<script src=\"http://cdn.bossanova.uk/js/excel-formula.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my2\"></div>\n\n<script>\nlet data2 = [\n    ['Furnace','=CUSTOM(A1)', 10],\n    ['Tower','=CONSOLE(A2)', 9],\n    ['Drum','=CONCAT_COLUMNS(A1,A3)', 3],\n    ['Pump','=CONSOLE(A4)', 7],\n    ['Total','', '=AVG(C1:C4)'],\n]\n\n$('#my2').jexcel({\n    data:data1,\n    colWidths: [ 400, 200 ],\n});\n\nfunction CUSTOM(cell) {\n    return 'alert: ' + cell;\n}\n\nfunction CONSOLE(cell) {\n    console.log(cell);\n    return cell;\n}\n\nfunction CONCAT_COLUMNS(a, b) {\n    return a + ', ' + b;\n}\n\nfunction AVG(v)\n{\n    var sum = v.reduce(function(a, b) { return a + b; });\n    var avg = sum / v.length;\n\n    return avg;\n}\n\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/integrating-a-third-party-plugin-into-your-spreadsheet.md",
    "content": "title: Jspreadsheet | Examples | Custom column and integrating plugins on your table\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: How to create custom column types based on a third party jquery plugin\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Create a custom column based on a third party jquery plugin\n\nThis particular example shows how to create a custom color picker column type using the [Spectrum Jquery Plugin](https://bgrins.github.io/spectrum/). This example illustrates how to create your own custom columns based on any third party jquery plugin.\n\n[See this example on jsFiddle](https://jsfiddle.net/spreadsheet/rp0876b1/)\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nvar spectrumEditor = {\n    // Methods\n    closeEditor : function(cell, save) {\n        // Get value\n        var value = $(cell).find('.editor').spectrum('get').toHexString();\n\n        // Set visual value\n        $(cell).html(value);\n        $(cell).css('color', value);\n\n        // Close edition\n        $(cell).removeClass('edition');\n\n        // Save history\n        return value;\n    },\n    openEditor : function(cell) {\n        // Get current content\n        var html = $(cell).html();\n\n        // Create the editor\n        var editor = document.createElement('div');\n        $(cell).html(editor);\n        $(editor).prop('class', 'editor');\n\n        // Create the instance of the plugin\n        $(editor).spectrum({ color:html, preferredFormat:'hex', hide: function(color) {\n            // Close editor\n            $('#' + $.fn.jexcel.current).jexcel('closeEditor', $(cell), true);\n        }});\n\n        // Run\n        $(editor).spectrum('show');\n    },\n    getValue : function(cell) {\n        return $(cell).html();\n    },\n    setValue : function(cell, value) {\n        $(cell).html(value);\n        $(cell).css('color', value);\n\n        return true;\n    }\n}\n\nlet data = [\n    ['Google', '#542727'],\n    ['Yahoo', '#724f4f'],\n    ['Bing', '#b43131'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: [ 'Name', 'Custom color' ],\n    colWidths: [ 300, 200 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text', editor:spectrumEditor },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/jquery-table-with-toolbars.md",
    "content": "title: Jspreadsheet | Examples | Your jquery table with toolbars\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, jquery table, toolbars\ndescription: Full examples how to load a autocomplete dropdown.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Add a custom toolbar in your jquery tables\n\nThe following example will show how to integrate a custom native toolbar in your spreadsheet plugin.\n\n## Instructions\n\nThe toolbar can be customized with a few parameters. \n\n| | |\n| ---|---  |\n| _**type**_ |  could be **i** for icon, **select** for a dropdown, **spectrum** or a colorpicker.  |\n| _**content**_ |  defines the icon (from material icons) when you use type:i; [click here for all possible keys](https://material.io/tools/icons/) |  \n| _**k**_ |  means the style should be apply to the cell;  |\n| _**v**_ |  means the value of the style should be apply to the cell; When type:select, you can define an array of possibles values;  |\n| _**method**_ |  can be used together with type:i to implement any custom method. The method will receive the spreadsheet instance and all marked cells by default.  |\n  \n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.0/spectrum.min.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['BR', 'Cheese', 1],\n    ['CA', 'Apples', 0],\n    ['US', 'Carrots', 1],\n    ['GB', 'Oranges', 0],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock'],\n    colWidths: [ 300, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'dropdown', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n    ],\n    toolbar:[\n        { type:'i', content:'undo', method:undo },\n        { type:'i', content:'redo', method:redo },\n        { type:'i', content:'save', method:function (instance, selectedCell) { $(instance).jexcel('download'); } },\n        { type:'select', k:'font-family', v:['Arial','Verdana'] },\n        { type:'select', k:'font-size', v:['9px','10px','11px','12px','13px','14px'] },\n        { type:'i', content:'format_align_left', k:'text-align', v:'left' },\n        { type:'i', content:'format_align_center', k:'text-align', v:'center' },\n        { type:'i', content:'format_align_right', k:'text-align', v:'right' },\n        { type:'i', content:'format_bold', k:'font-weight', v:'bold' },\n        { type:'spectrum', content:'format_color_text', k:'color' },\n        { type:'spectrum', content:'format_color_fill', k:'background-color' },\n    ],\n});\n\nfunction undo(instance, selectedCell)\n{\n    $(instance).jexcel('undo');\n}\n\nfunction redo(instance, selectedCell)\n{\n    $(instance).jexcel('redo');\n}\n</script>\n</html>\n```\n\n**NOTE:** It is important to have google material fonts loaded.\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/meta-information.md",
    "content": "title: Jspreadsheet | Examples | Add meta information in your cells\nkeywords: Jexcel, jquery, javascript, jquery table, meta information\ndescription: Manage the table meta information\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Add meta information in your spreadsheet spreadsheet plugin\n\nYou can keep meta information bind to cells but not visible to users. This can be useful for integrating Jspreadsheet with third party software.\n\nBut, after the initialization is still possible to manage the cell meta information programmatically using the method getMeta and setMeta, as follow.\n\n|   |   |\n| ---|--- |  \n| $('#my1').jexcel('setMeta', [{ A1: { newInfo:'newInfo' } }, { B1: { info1:'test1', info2:'test2'} }, { C2: { metaTest:'test3' } }]); | Set meta data for the table in a batch  |\n| $('#my1').jexcel('setMeta', 'A2', 'myKeyMeta', 'myValueMeta'); | Set a meta information for A2  |\n| $('#my1').jexcel('getMeta', 'A1'); | Get the meta information for A1  |\n| $('#my1').jexcel('getMeta'); | Get all meta information  |\n  \n  \n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\ndata1 = [\n    ['US', 'Cheese', 'Yes', '2019-02-12'],\n    ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n    ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n    ['BR', 'Oranges', 'Yes', '2019-01-12'],\n];\n\n$('#my1').jexcel({\n    data:data1,\n    colHeaders: [ 'Product Origin','Description', 'Stock', 'Best before' ],\n    colWidths: [ 300, 200, 100, 100 ],\n    columns: [\n        { type: 'dropdown', url:'/jspreadsheet/countries', autocomplete:true, multiple:true }, // Remote source for your dropdown\n        { type: 'text' },\n        { type: 'dropdown', source:['No','Yes'] },\n        { type: 'calendar' },\n    ],\n    meta:[\n        { A1: { myMeta:'this is just a test', otherMetaInformation:'other test'} },\n    ],\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/mobile.md",
    "content": "title: Jspreadsheet | Examples | Mobile Layout\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, mobile\ndescription: Responsive Layout\n\n{.ignore}\n```html\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['BR', 'Classe A', 1, 1, '2017-01-12'],\n    ['CA', 'Classe B', 1, 1, '2017-01-12'],\n    ['US', 'Classe A', 2, 1, '2017-01-12'],\n    ['US', 'Classe C', 3, 0, '2017-01-12'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders:  [ 'Country', 'Description', 'Type', 'Stock', 'Next purchase' ],\n    colWidths: [ 250, 80, 120, 100, 120 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'text' },\n        { type: 'dropdown', source:[ {'id':'1', 'name':'Fruits'}, {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type: 'checkbox' },\n        { type: 'calendar' },\n    ]\n});\n</script>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/multiple-spreadsheets-in-the-same-page.md",
    "content": "title: Jspreadsheet | Examples | Create multiple instances in the same page\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: How to create multiple table instances in the same page\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Multiple spreadsheets in the same page\n\nHow to embed multiple spreadsheets in the same page.\n\n## Source code\n\n{.ignore}\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div><br>\n<div id=\"my2\"></div>\n\n<script>\nvar data1 = $.fn.jexcel('helper', { action:'createEmptyData', cols:6, rows:10 });\nvar data2 = $.fn.jexcel('helper', { action:'createEmptyData', cols:6, rows:10 });\n\n$('#my1').jexcel({\n    data:data1\n});\n$('#my2').jexcel({\n    data:data2\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/readonly-options.md",
    "content": "title: Jspreadsheet | Examples | Handling readonly column and cells on your spreadsheet\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, events\ndescription: Understanding how to set a readonly column or multiple custom cells\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Readonly columns and cells.\n\n## Source code\n\n```html\n<html>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n    \n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n    \n    <div id=\"my\"></div>\n    \n    <script>\n    let data = [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ];\n    \n    $('#my').jexcel({\n        data:data,\n        colHeaders: ['Description','Year','Price', 'Buy'],\n        colWidths: [ 200, 300, 100, 100 ],\n        columns: [\n            { type: 'text', readOnly:true },\n            { type: 'text', },\n            { type: 'text', },\n            { type: 'checkbox' },\n        ]\n    });\n    \n    //Set readonly a specific column\n    $('#my').jexcel('updateSettings', {\n        cells: function (cell, col, row) {\n            // If the column is number 4 or 5\n            if (row == 2 && col == 2) {\n                $(cell).addClass('readonly');\n            }\n        }\n    });\n    </script>\n</html>\n```  \n\n## Online demo on jsFiddle\n\n\n<iframe width=\"100%\" height=\"300\" src=\"//jsfiddle.net/hodware/LL0sexgt/embedded/js,html,result/\" allowfullscreen=\"allowfullscreen\" frameborder=\"0\"></iframe>\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/responsive-columns.md",
    "content": "title:  Jspreadsheet | Examples | Responsive Layout\nkeywords: Jexcel, jquery, javascript, bootstrap, table design, spreadsheet, CSV, table, grid, autocomplete, customization, column\ndescription: Creating a more responsive layout on jexcel\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Responsive columns\n\nThe Jspreadsheet jquery plugin brings a more responsive column types.\n\n![](img/iphone.png)\n\n[Open this table only in a new window](/jspreadsheet/v2/examples/mobile)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/sorting-data.md",
    "content": "title: Jspreadsheet | Examples | Sorting your grid\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: Sorting your Jspreadsheet spreadsheet\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Sorting your table\n\nYou can reorder your jquery table by double clicking in the header or by selecting the desired option in the context menu. The example shows how to change the order of your jquery table programmatically.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['Mazda', 2001, 2000, '2006-01-01'],\n    ['Peugeot', 2010, 5000, '2005-01-01'],\n    ['Honda Fit', 2009, 3000, '2004-01-01'],\n    ['Honda CRV', 2010, 6000, '2003-01-01'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Year', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'text' },\n    ]\n});\n</script>\n\n<p>\n<input type='button' value='Order by' onclick=\"$('#my').jexcel('orderBy', $(this).next().val())\">\n<select>\n<option value='0'>Column 1</option>\n<option value='1'>Column 2</option>\n<option value='2'>Column 3</option>\n<option value='3'>Column 4</option>\n</select>\n</p>\n\n</html>\n```\n\n### Programmatically sorting\n\n{.ignore}\n```html\n<script>\n    $('#my').jexcel('orderBy', 1); // Order by the second column (The column number starts on zero)\n</script>\n```  \n\n### Disable the table sorting\n\nThe ordering is a native enabled feature. To disable that feature please use the columnSorting:false, directive in the initialization.\n\n{.ignore}\n```html\n<script>\n    let data = [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ];\n    \n    $('#my').jexcel({\n        data:data,\n        colHeaders: ['Model', 'Year', 'Price', 'Date'],\n        colWidths: [ 300, 80, 100, 100 ],\n        columns: [\n            { type: 'text' },\n            { type: 'text' },\n            { type: 'text' },\n            { type: 'text' },\n        ],\n        columnSorting:false,\n    });\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/table-styling.md",
    "content": "title: Jspreadsheet | Examples | Changing the table style\nkeywords: Jexcel, jquery, javascript, table design, spreadsheet, table, grid, colours\ndescription: Table styling\n\n[Back to Examples](/v2/examples#more)\n\n# How to change the spreadsheet style.\n\nYou can define the CSS for specific columns during the initialization. The following example uses the directive style:[] to bring populate the style attribute for each column.\n\nBut, after the initialization is still possible to manage the cell style programmatically using the method getStyle or setStyle.\n\n|    |     |\n| ---|---  |\n| $('#my1').jexcel('setStyle', 'A1', 'font-weight', 'bold'); | Change A1 fontWeight  |\n| $('#my1').jexcel('setStyle', 'A2', 'background-color', 'yellow'); | Change A2 backgroundColor  |\n| $('#my1').jexcel('setStyle', [ { A1: 'font-weight: bold; color:red;' }, { B2: 'background-color: yellow;' }, { C1: 'text-decoration: underline;' }, { D2: 'text-align:left;' } ]); | Change the table in one batch  |\n| $('#my1').jexcel('getStyle', 'A1');| Get A1 style attributes  |\n| $('#my1').jexcel('getStyle');| Get the whole table attributes  |\n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/excel-formula.min.js\"></script>\n\n<div id=\"my1\"></div>\n\n<script>\nlet data1 = [\n    ['US', 'Cheese', 'Yes', '2019-02-12'],\n    ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n    ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n    ['BR', 'Oranges', 'Yes', '2019-01-12'],\n];\n\n$('#my1').jexcel({\n    data:data1,\n    colHeaders: [ 'Product Origin','Description', 'Stock', 'Best before' ],\n    colWidths: [ 300, 200, 100, 100 ],\n    columns: [\n        { type: 'dropdown', url:'/jspreadsheet/countries', autocomplete:true, multiple:true }, // Remote source for your dropdown\n        { type: 'text' },\n        { type: 'dropdown', source:['No','Yes'] },\n        { type: 'calendar' },\n    ],\n    style:[\n        { A1: 'background-color: orange; ' },\n        { B1: 'background-color: orange; ' },\n        { C1: 'background-color: orange; ' },\n        { D1: 'background-color: orange; ' },\n    ],\n});\n</script>\n</html>\n```  \n  \n\n## How to set global CSS rules for your jquery tables\n\nGive your clients a nice look table with colours and styling.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/excel-formula.min.js\"></script>\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['BR', 'Cheese', 1, 3.99],\n    ['CA', 'Apples', 0, 1.00],\n    ['US', 'Carrots', 1, 0.90],\n    ['GB', 'Oranges', 0, 1.20],\n    ['CH', 'Chocolats', 1, 0.40],\n    ['AR', 'Apples', 1, 1.10],\n    ['AR', 'Bananas', 1, 0.30],\n    ['BR', 'Oranges', 1, 0.95],\n    ['BR', 'Pears', 1, 0.90],\n    ['', '', '', '=ROUND(SUM(D1:D8), 2)'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock', 'Price'],\n    colWidths: [ 300, 100, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'autocomplete', source:['Apples','Bananas','Carrots','Oranges','Cheese','Kiwi','Chocolats','Pears'] },\n        { type: 'checkbox' },\n        { type: 'number' },\n    ]\n});\n\n$('#my').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        // Number formating\n        if (col == 3) {\n            // Get text\n            txt = $(cell).text();\n            // Format text\n            txt = numeral(txt).format('0,0.00');\n            // Update cell value\n            $(cell).html(' $ ' + txt);\n        }\n\n        // Odd row colours\n        if (row % 2) {\n            $(cell).css('background-color', '#edf3ff');\n        }\n\n        // Remove controls for the last row\n        if (row == 9) {\n            if (col < 3) {\n                $(cell).html('');\n            } \n\n            if (col == 2) {\n                $(cell).html(' **Total** ');\n            }\n\n            $(cell).css('background-color', '#f46e42');\n            $(cell).css('color', '#fff');\n        }\n    }\n});\n\n// This is a custom method for rounding\nfunction ROUND(v, f)\n{\n    v = v.toFixed(f);\n    v = numeral(v).format('0,0.00');\n\n    return '$ ' + v;\n}\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/table-with-fixed-headers.md",
    "content": "title: Jspreadsheet | Examples | Data table with fixed headers and scrolling\nkeywords: Jexcel, jquery, javascript, fixed headers, scrolling, data tables\ndescription: Overflow table and scrolling\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Spreadsheet with fixed headers and scrolling\n\nCreate an instance of your jquery plugin to allow a table overflow and scrolling.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\n$('#my1').jexcel({\n    csv:'/jspreadsheet/demo1.csv',\n    csvHeaders:true,\n    tableOverflow:true,\n    tableHeight:'300px',\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/text-wrapping.md",
    "content": "title: Jspreadsheet | Examples | Text wrap\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, text wrapping\ndescription: How to change the text wrap behavior in a Jspreadsheet column\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Text wrapping\n\nThe javascript spreadsheet default configuration does not wrap the text. But, you can change this behavior by using the wordWrap option in the jquery plugin initialization. In the most recent version of Jspreadsheet, alt+enter will allow the user to add a new line when editing the text when the wrap is enabled\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n        ['Spyro Trilogy Reignited (PS4)', '29.99', 'Edition: Standard Edition\\nSpyro`s back and he`s all scaled up!\\nThe original roast master is back! Same sick burns, same smoldering attitude, now all scaled up in stunning HD. Spyro is bringing the heat like never before in the SpyroTM Reignited Trilogy game collection\\nAll 3 original Spyro games fully remastered in HD\\nIncludes Spyro the Dragon, Spyro 2: Ripto`s Rage! and Spyro: Year of the Dragon\\n\\n100+ levels, remastered with breathtaking graphical updates and improved gameplay controls'],\n        ['Call of Duty: Black Ops 4 (PS4)', '49.99', 'Forget what you know\\nTune in to the call of duty: Black ops four community reveal event: May 17, 2018'],\n];\n    \n$('#my').jexcel({\n    data:data,\n    colHeaders: [ 'Game', 'Price', 'Description' ],\n    colWidths: [200, 100, 400],\n    colAlignments: [ 'center', 'center', 'left' ],\n    columns: [\n        { type:'text' },\n        { type:'text' },\n        { type:'text', wordWrap:true },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/tracking-changes-on-the-spreadsheet.md",
    "content": "title: Jspreadsheet | Examples | Events\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, events\ndescription: Handling events on your spreadsheet\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Handling events\n\nTracking changes on your spreadsheet.\n\n  \n[See this example on jsFiddle](https://jsfiddle.net/spreadsheet/0dyms2b9/)  \n\n## Advanced Example\n\nUpdate the chart on every change in your spreadsheet, using the onchange handler\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\nlet data = [\n    ['Tokyo', 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6],\n    ['New York', -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5],\n    ['Berlin', -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0],\n    ['London', 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8],\n];\n\nlet update = function (obj, cel, val) {\n    // Get the cell position x, y\n    let id = $(cel).prop('id').split('-');\n    // If the related series does not exists create a new one\n    if (! chart.series[id[1]]) {\n        // Create a new series row\n        let row = [];\n        for (i = 1; i < data[id[1]].length; i++) {\n            row.push(parseFloat(data[id[1]][i]));\n        }\n        // Append new series to the chart\n        chart.addSeries({ name:data[id[1]][0], data:row });\n    } else {\n        // Update the value from the chart\n        chart.series[id[1]].data[id[0]-1].update({y:parseFloat(val)});\n    }\n}\n\n$('#my').jexcel({\n    data:data,\n    onchange:update,\n    colHeaders: [ 'Country' ],\n    colWidths: [ 300 ]\n});\n\n// Kepp it global\nlet chart = null;\n\n$(function () {\n    chart = Highcharts.chart('container', {\n        title: {\n            text: 'Monthly Average Temperature',\n            x: -20 //center\n        },\n        subtitle: {\n            text: 'Source: WorldClimate.com',\n            x: -20\n        },\n        xAxis: {\n            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n                'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n        },\n        yAxis: {\n            title: {\n                text: 'Temperature (°C)'\n            },\n            plotLines: [{\n                value: 0,\n                width: 1,\n                color: '#808080'\n            }]\n        },\n        tooltip: {\n            valueSuffix: '°C'\n        },\n        legend: {\n            layout: 'vertical',\n            align: 'right',\n            verticalAlign: 'middle',\n            borderWidth: 0\n        },\n        series: [{\n            name: 'Tokyo',\n            data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n        }, {\n            name: 'New York',\n            data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n        }, {\n            name: 'Berlin',\n            data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n        }, {\n            name: 'London',\n            data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n        }]\n    });\n});\n</script>\n</html>\n```  \n\n## Online demo on jsFiddle\n\n<iframe width=\"100%\" height=\"700\" src=\"//jsfiddle.net/zmtsbnng/embedded/js,html,result/\" allowfullscreen=\"allowfullscreen\" frameborder=\"0\"></iframe>\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/using-a-calendar-column-type.md",
    "content": "title: Jspreadsheet | Examples | Calendar column type with date and datetime picker\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, date, datetime picker\ndescription: How to handle a calendar with date and datetime picker.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Calendar column type\n\nThe example below shows how to use and customize special calendar column type.\n\nSee a live example of calendar usage on [jsfiddle](https://jsfiddle.net/spreadsheet/ajv413cb/).\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jcalendar.js\"></script>\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jcalendar.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['Mazda', 2001, 2000, '2006-01-01'],\n    ['Peugeot', 2010, 5000, '2005-01-01'],\n    ['Honda Fit', 2009, 3000, '2004-01-01'],\n    ['Honda CRV', 2010, 6000, '2003-01-01'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Date', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'numeric' },\n        { type: 'numeric' },\n        { type: 'calendar', options: { format:'DD/MM/YYYY' } },\n    ]\n});\n</script>\n</html>\n```  \n\n## Date column customization\n\nCustomize the format and the behavior of your column through the initialization options, as follow:\n\n### Calendar initialization options\n\n{.ignore}\n```javascript\n{\n    options: {\n        format:'DD/MM/YYYY', // Date format\n        readonly:0, // Input as readonly (true or false)\n        today:0, // Input with at today as default (true or false)\n        time:0, // Show time picker\n        clear:1, // Show clear button\n        mask:1, // Mask the input\n        months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // Translations can be done here\n        weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], // Translations can be done here\n        weekdays_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'] // Translations can be done here\n    };\n}\n```\n\nConsidering the example above, you can create a calendar including a time picker by simple send the option **time:1** as the following example.\n\n{.ignore}\n```html\n<div id=\"my2\"></div>\n\n<script>\n$('#my2').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Year', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 120 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'calendar', options: { format:'DD/MM/YYYY HH24:MI', time:1 } },\n    ]\n});\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/working-with-dropdowns.md",
    "content": "title:  Jspreadsheet | Examples | Advanced dropdown column type\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: Full examples on how to handle simple, advanced, autocomplete and conditional dropdowns.\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Working with dropdowns\n\nJspreadsheet brings some new very flexible dropdown options that enables you to delivery great user experience though applications. The new dropdown column options include the autocomplete, multiple options, data picker, different template types and much more advantages, such as:\n\n  * Create a simple dropdown from array\n  * Value or key-value select is available\n  * Populate a dropdown from a external JSON request\n  * Dynamic autocomplete search based on another column value\n  * Conditional dropdowns: options from a dropdown based on a method return\n  * Multiple and autocomplete\n  * Responsive data picker\n\n## Multiple and autocomplete options\n\nThe highlight features introduced in the most version of the jquery table is definitely the autocomplete and multiple options. The following example shows the column Product Origin with the autocomplete and multiple directives initiated as true.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\n$('#my1').jexcel({\n    data: [\n        ['US', 'Cheese', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n        ['BR', 'Oranges', 'Yes', '2019-01-12'],\n    ],\n    colHeaders: [ 'Product Origin','Description', 'Stock', 'Best before' ],\n    colWidths: [ 300, 200, 100, 100 ],\n    columns: [\n        { type: 'dropdown', source: ['BR', 'US', 'UK', 'CA'], autocomplete:true, multiple:true },\n        { type: 'text' },\n        { type: 'dropdown', source:['No','Yes'] },\n        { type: 'calendar' },\n    ]\n});\n</script>\n</html>\n```  \n  \n\n## Conditional dropdown\n\nJspreadsheet dropdown column can show different options based on the value of other columns. To use that function you should use a method defined by the filter parameter in the initialization. The following example shows the product column options based on the value selected on the column Category.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\nlet dropdown = function(instance, cell, c, r, source) {\n    // Get a value from the same row but previous column\n    var value = $(instance).jexcel('getValue', c-1 + '-' + r);\n\n    // Return the values will be part in your current column\n    if (value == 1) {\n        return ['Apples','Bananas','Oranges'];\n    } else if (value == 2) {\n        return ['Carrots'];\n    } else {\n        return source;\n    }\n}\n\n$('#spreadsheet2').jexcel({\n    data: [\n        [3, 'Cheese', 0],\n        [1, 'Apples', 1],\n        [2, 'Carrots', 0],\n        [1, 'Oranges', 0],\n    ],\n    colHeaders: ['Category','Product', 'Buy later'],\n    colWidths: [ 300, 200, 100 ],\n    columns: [\n        { type: 'dropdown', source: [ {'id':'1', 'name':'Fruits'},  {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type: 'dropdown', source: [ 'Apples', 'Bananas', 'Carrots', 'Oranges', 'Cheese' ], filter:dropdown },\n        { type: 'checkbox' },\n    ]\n});\n</script>\n</html>\n```  \n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples/working-with-the-data.md",
    "content": "title: Jspreadsheet | Examples | Working with the data\nkeywords: Jexcel, jquery, javascript, insert, remove and move columns and rows, spreadsheet, CSV, table, grid\ndescription: Insert, remove and move columns and rows\n\n[Back to Examples](/jspreadsheet/v2/examples)\n\n# Programmatically insert, remove and move columns and rows\n\nThe following example shows how to manage data programmatically in your javascript spreadsheet.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/excel-formula.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n\n<div id=\"my1\"></div>\n\n<script>\n$('#my1').jexcel({\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    colHeaders: [ 'Product', 'Quantity', 'Price', 'Total' ],\n    colWidths: [ 300, 100, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ] },\n        { type: 'number' },\n        { type: 'number' },\n        { type: 'number' },\n    ],\n});\n\n$('#my1').jexcel('updateSettings', {\n    table: function (instance, cell, col, row, val, id) {\n        if (col == 2 || col == 3) {\n            // Get text\n            txt = $(cell).text();\n            // Format text\n            txt = numeral(txt).format('0,0.00');\n            // Update cell value\n            $(cell).html(' $ ' + txt);\n        }\n    },\n});\n\ndocument.getElementById(\"btn1\").onclick = () => $('#my1').jexcel('insertColumn');\ndocument.getElementById(\"btn2\").onclick = () => $('#my1').jexcel('insertColumn', 5, null, 0);\ndocument.getElementById(\"btn3\").onclick = () => $('#my1').jexcel('insertColumn', [ '0.99', '1.22', '3.11', '2.21' ]);\ndocument.getElementById(\"btn4\").onclick = () => $('#my1').jexcel('insertRow'); event.preventDefault();\ndocument.getElementById(\"btn5\").onclick = () => $('#my1').jexcel('insertRow', [ 'Pears', 10, 0.59, '=B2*C2' ], 1);\ndocument.getElementById(\"btn6\").onclick = () => $('#my1').jexcel('insertRow', 10);\ndocument.getElementById(\"btn7\").onclick = () => $('#my1').jexcel('moveRow', 3, 0);\n</script>\n\n<button id=\"btn1\">Insert a new blank column at the end of the table</button>\n<button id=\"btn2\">Insert five new blank columns at the beginning of the table</button>\n<button id=\"btn3\">Insert a new column with pre-populated values at the end of the table</button>\n<button id=\"btn4\">Insert a new blank row at the end of the table</button>\n<button id=\"btn5\">Insert a new pre-populated row just after the second row.</button>\n<button id=\"btn6\">Create ten rows at the end of the table</button>\n<button id=\"btn7\">Move the forth row to the first position</button>\n\n\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v2/examples.md",
    "content": "title: Examples\nkeywords: Jexcel, javascript, examples\ndescription: Examples how to create web based spreadsheets using Jexcel.\n\n# Examples\n\nThe Spreadsheet minimalist jQuery Plugin examples shows the usage with various column types such as text, dropdown, autocomplete, checkbox and date.\n\n```html\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['BR', 'Classe A', 1, 1, '2017-01-12'],\n    ['CA', 'Classe B', 1, 1, '2017-01-12'],\n    ['US', 'Classe A', 2, 1, '2017-01-12'],\n    ['US', 'Classe C', 3, 0, '2017-01-12'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders:  [ 'Country', 'Description', 'Type', 'Stock', 'Next purchase' ],\n    colWidths: [ 250, 80, 120, 100, 120 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'text' },\n        { type: 'dropdown', source:[ {'id':'1', 'name':'Fruits'}, {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type: 'checkbox' },\n        { type: 'calendar' },\n    ]\n});\n</script>\n```\n\n[See this example on jsfiddle](https://jsfiddle.net/spreadsheet/kgmcav01/)\n\n## Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js\"></script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jexcel.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jdropdown.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/js/jquery.jcalendar.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jexcel.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jdropdown.min.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.1.0/css/jquery.jcalendar.min.css\" type=\"text/css\" />\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    [3, 'Classe A', 'Cheese', 1, '2017-01-12'],\n    [1, 'Classe B', 'Apples', 1, '2017-01-12'],\n    [2, 'Classe A', 'Carrots', 1, '2017-01-12'],\n    [1, 'Classe C', 'Oranges', 0, '2017-01-12'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders:  [ 'Country', 'Description', 'Type', 'Stock', 'Next purchase' ],\n    colWidths: [ 300, 80, 100, 60, 120 ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'text' },\n        { type: 'dropdown', source:[ {'id':'1', 'name':'Fruits'}, {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type: 'checkbox' },\n        { type: 'calendar' },\n    ]\n});\n</script>\n</html>\n```\n\n## More examples\n\n* [Advanced dropdowns](/jspreadsheet/v2/examples/working-with-dropdowns \"Advanced dropdown column type\")  \n  Full examples on how to handle simple, advanced, autocomplete and conditional dropdowns.\n\n* [Programmaticaly changes](/jspreadsheet/v2/examples/working-with-the-data \"Jspreadsheet | Examples | Working with the data\")  \n  Insert, remove and move columns and rows\n\n* [Fixed headers](/jspreadsheet/v2/examples/table-with-fixed-headers \"Jspreadsheet | Examples | Data table with fixed headers and scrolling\")  \n  Overflow table and scrolling\n\n* [Custom table design](/jspreadsheet/v2/examples/a-custom-table-design \"Jspreadsheet | Examples | Bootstrap and custom table design\")  \n  Create a custom table design. For example a bootstrap-like spreadsheet table.\n\n* [Table styling](/jspreadsheet/v2/examples/table-styling \"Jspreadsheet | Examples | Changing the table style\")  \n  Table styling\n\n* [Manage cell comments](/jspreadsheet/v2/examples/comments \"Jspreadsheet | Examples | Add comments in your jquery table\")  \n  Manage a table cell comments\n\n* [Custom meta information](/jspreadsheet/v2/examples/meta-information \"Jspreadsheet | Examples | Add meta information in your cells\")  \n  Manage the table meta information\n\n* [Custom toolbars](/jspreadsheet/v2/examples/jquery-table-with-toolbars \"Jspreadsheet | Examples | Your jquery table with toolbars\")  \n  Full examples how to load a autocomplete dropdown.\n\n* [Loading remote data](/jspreadsheet/v2/examples/creating-a-table-from-an-external-csv-file \"Jspreadsheet | Examples | Creating a web spreadsheet based on an external CSV\")  \n  How to load the data from an external CSV file into a Jspreadsheet grid or table.\n\n* [Masking and formating](/jspreadsheet/v2/examples/currency-and-masking-numbers \"Jspreadsheet | Examples | Using currency column type and how to masking numbers\")  \n  Handling currency types and masking numbers.\n\n* [Calendar picker](/jspreadsheet/v2/examples/using-a-calendar-column-type \"Jspreadsheet | Examples | Calendar column type with date and datetime picker\")  \n  How to handle a calendar with date and datetime picker.\n\n* [Sorting](/jspreadsheet/v2/examples/sorting-data \"Jspreadsheet | Examples | Sorting your grid\")  \n  Sorting your Jspreadsheet spreadsheet\n\n* [Create custom cells](/jspreadsheet/v2/examples/integrating-a-third-party-plugin-into-your-spreadsheet \"Jspreadsheet | Examples | Custom column and integrating plugins on your table\")  \n  How to create custom column types based on a third party jquery plugin\n\n* [Handling events](/jspreadsheet/v2/examples/tracking-changes-on-the-spreadsheet \"Jspreadsheet | Examples | Events\")  \n  Handling events on your spreadsheet\n\n* [Readonly options](/jspreadsheet/v2/examples/readonly-options \"Jspreadsheet | Examples | Handling readonly column and cells on your spreadsheet\")  \n  Understanding how to set a readonly column or multiple custom cells\n\n* [Multiple instances](/jspreadsheet/v2/examples/multiple-spreadsheets-in-the-same-page \"Jspreadsheet | Examples | Create multiple instances in the same page\")  \n  How to create multiple table instances in the same page\n\n* [Text wrap](/jspreadsheet/v2/examples/text-wrapping \"Jspreadsheet | Examples | Text wrap\")  \n  How to change the text wrap behavior in a Jspreadsheet column\n\n* [Nested headers](/jspreadsheet/v2/examples/headers \"Jspreadsheet | Examples | Nested Headers\")  \n  Creating a Jspreadsheet table with nested headers.\n\n* [Add images on your cells](/jspreadsheet/v2/examples/images \"Jspreadsheet | Examples | Images on your images\")  \n  Add images on your spreasheet cells\n"
  },
  {
    "path": "docs/jspreadsheet/v2/getting-started.md",
    "content": "title: Getting Started with jExcel\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with jExcel.\n\n[Back to Documentation](/jspreadsheet/v2/docs)\n\n# Getting started with the jquery plugin\n\n## Initialization\n\nJspreadsheet can receive data from a JS array, CSV or a JSON format, following the examples below:\n\nThere are three ways to load data into your spreadsheet. Using the parameter _data:_ passing a reference of a javascript array. The second method is to load the data straight from a remove CSV file using the _csv:_ parameter. The third is to pass the URL from a json content using the _url:_ parameter as show in the following examples.\n\n### Loading from a javascript array\n\n```html\n<div id='my-spreadsheet'></div>\n    \n<script>\nlet data = [\n    ['Mazda', 2001, 2000],\n    ['Peugeot', 2010, 5000],\n    ['Honda Fit', 2009, 3000],\n    ['Honda CRV', 2010, 6000],\n];\n\n$('#my-spreadsheet').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Price', 'Price' ],\n    colWidths: [ 300, 80, 100 ]\n});\n</script>\n```\n\n### Loading from a JSON file\n\n{.ignore}\n```html\n<div id='my-spreadsheet'></div>\n\n<script>\n$('#my-spreadsheet').jexcel({\n    url:'/jspreadsheet/json',\n    colHeaders: ['Model', 'Price', 'Price' ],\n    colWidths: [ 300, 80, 100 ]\n});\n</script>\n```\n\n### Loading from a CSV file\n\n{.ignore}\n```html\n<div id='my-spreadsheet'></div>\n\n<script>\n$('#my-spreadsheet').jexcel({\n    // URL from the CSV file\n    csv:'/jspreadsheet/csv',\n    // Get the first of the CSV file and consider the headers\n    csvHeaders: true,\n    // Default column widths\n    colWidths: [ 300, 80, 100 ]\n});\n</script>\n```\n\n[See a working example](/jspreadsheet/v2/examples/creating-a-table-from-an-external-csv-file)\n\n  \n\n## Destroying a table\n\nYou can destroy the table, all data and events related to an existing table by using the method _destroy_ as shown below.\n\n{.ignore}\n```html\n<script>\n// Create the table\n$('#my').jexcel({\n    csv:'/jspreadsheet/csv',\n    csvHeaders:true,\n    colWidths: [70, 200, 300],\n});\n\n// Destroy the table\n$('#my').jexcel('destroy');\n</script>\n```\n\n## Header titles\n\nIf you do not define the column title, the default will be the use of a letter, just as any other spreadsheet software. But, if you would like to have custom column names you can use the directive colHeaders:\n\n{.ignore}\n```javascript\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Price', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n});\n```\n\n**Note** : If you are loading your data from a CSV file, you can define the csvHeader:true, so the first row will be used as your column names.\n\n## Column width\n\nThe parameter _colWidths_ can be used to define your column widths.\n\n## Column types\n\nThe javascript spreadsheet has available some extra native column types in addition to the default input text. It means you can get alternative ways to enter data in your spreadsheet. Advanced numeric inputs, dropdowns to calendar picks and a very easy way to have your custom integrations, makes the spreadsheet plugin a very flexible tool to enhance the user experience when using your applications.\n\nIn the example below, you will have text, numeric inputs and a calendar picker. But, other native options will be available such as: _**text, numeric, hidden, dropdown, autocomplete, checkbox, calendar.**_\n\n{.ignore}\n```javascript\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model', 'Date', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'numeric' },\n        { type: 'numeric' },\n        { type: 'calendar', options: { format:'DD/MM/YYYY' } },\n    ]\n});\n```\n\n### Calendar type\n\nWhen using the calendar column, you can change the behavior behavior of your calendar by sending some extra options as example above. The possible values are:\n\n{.ignore}\n```javascript\nlet defaults = {\n    format:'DD/MM/YYYY', // Date format\n    readonly:0,          // Readonly input\n    today:0,             // Default as today\n    time:0,              // Show time picker\n    clear:1,             // Clear buttom\n    mask:1,              // Mask calendar\n};\n```\n\n[See a working example](/jspreadsheet/v2/examples/using-a-calendar-column-type)  \n\n### Dropdown and autocomplete type\n\nThere are different ways to work with dropdowns in Jspreadsheet. It is possible to define the parameter _source_ as a simple or key-value array. Additionally, is possible to use the paramter _url_ to populate your dropdown from an external json format source.\n\nThe autocomplete drowndown has the same configuration inputs, and both can be used as follow:\n\n{.ignore}\n```javascript\nlet data = [\n    ['Honda', 1, 'Civic', '4'],\n    ['Peugeot', 3,'1007', '2'],\n    ['Smart', 3,'Cabrio', '4;5'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Model','Color', 'Description'],\n    colWidths: [ 300, 80, 100 ],\n    columns: [\n        { type: 'dropdown', source:['Seat', 'Renault', 'Peugeot'] },\n        { type: 'dropdown', source:[{'id':1,'name':'Yellow'}, {'id':2,'name':'Black'}, {'id':3,'name':'Green'}] },\n        { type: 'dropdown', url:'/jspreadsheet/test' },\n        { type: 'dropdown', url:'/jspreadsheet/countries' autocomplete:true, multiple:true },\n    ]\n});\n```\n\n[See a working example](/jspreadsheet/v2/examples/working-with-dropdowns)\n\n### Custom type\n\nJspreadsheet makes possible to extend third party jquery plugins to create your custom columns. Basically to use this feature, you should implement some basic methods such as: openEditor, closeEditor, getValue, setValue as\nfollowing.\n\n{.ignore}\n```javascript\nlet customEditor = {\n    // Methods\n    closeEditor : function(cell, save) {\n        // Get value\n        var value = $(cell).find('.editor').spectrum('get').toHexString();\n\n        // Set visual value\n        $(cell).html(value);\n        $(cell).css('color', value);\n\n        // Close edition\n        $(cell).removeClass('edition');\n    },\n    openEditor : function(cell) {\n        var main = this;\n        // Get current content\n        var html = $(cell).html();\n\n        // Basic editor\n        var editor = document.createElement('div');\n        $(cell).html(editor);\n        $(editor).prop('class', 'editor');\n        $(editor).spectrum({ color:html, preferredFormat:'hex', hide: function(color) {\n            main.closeEditor($(cell), true);\n        }});\n        $(editor).spectrum('show');\n    },\n    getValue : function(cell) {\n        return $(cell).html();\n    },\n    setValue : function(cell, value) {\n        $(cell).html(value);\n        $(cell).css('color', value);\n        return true;\n    }\n}\n\nlet data = [\n    ['Google', '#542727'],\n    ['Yahoo', '#724f4f'],\n    ['Bing', '#b43131'],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: [ 'Name', 'Custom color' ],\n    colWidths: [ 300, 200 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text', editor:customEditor },\n    ]\n});\n```\n\n[See a working example](/jspreadsheet/v2/examples/integrating-a-third-party-plugin-into-your-spreadsheet)\n\n  \n  \n\n## Define a minimum table dimension size.\n\nThe follow example will create a data table with a minimum number of ten columns and five rows:\n\n{.ignore}\n```javascript\ndata3 = [\n    ['Mazda', 2001, 2000],\n    ['Peugeot', 2010, 5000],\n    ['Honda Fit', 2009, 3000],\n    ['Honda CRV', 2010, 6000],\n];\n\n$('#minExample').jexcel({\n    data:data3,\n    minDimensions:[10,5],\n    colHeaders: ['Model', 'Year', 'Price' ],\n    colWidths: [ 300, 80, 100 ]\n});\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/docs/events.md",
    "content": "title: Spreadsheet Events: Integration and Customization\nkeywords: Jspreadsheet, data grid, JavaScript, Excel-like features, spreadsheet events, event handling, customized actions, JavaScript integration, interactive spreadsheets, feature customization, event-driven programming, data grid events\ndescription: Learn more about Jspreadsheet’s comprehensive event system for advanced customization and integration.\n\n[Back to Documentation](/jspreadsheet/v3/docs)\n\n# Events on the online spreadsheet\n\n## Custom table scripting after changes\n\nJspreadsheet offers a native feature to customize your table on the fly. You can define the method updateTable to create rules to customize the data should be shown to the user, as the example below.\n\n[See an example in action](/jspreadsheet/v3/examples/table-scripting)\n\n## Events\n\nJspreadsheet available events in this version.\n\n[Example on handling events on your spreasheet](/jspreadsheet/v3/examples/events)\n\n| Event | description  | \n| ---|---  |\n| **onload** | This method is called when the method setData  |\n| **onbeforechange** | Before a column value is changed. NOTE: It is possible to overwrite the original value, by return a new value on this method. v3.4.0+  |\n| **onchange** | After a column value is changed.  |\n| **onafterchanges** | After all changes are applied in the table.  |\n| **onpaste** | After a paste action is performed in the javascript table.  |\n| **onbeforepaste** | Before the paste action is performed. Used to parse any input data, should return the data.  |\n| **oninsertrow** | After a new row is inserted. |  \n| **onbeforeinsertrow** | Before a new row is inserted. You can cancel the insert event by returning false.  |\n| **ondeleterow** | After a row is excluded.  |\n| **onbeforedeleterow** | Before a row is deleted. You can cancel the delete event by returning false.  |\n| **oninsertcolumn** | After a new column is inserted. | \n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false.  |\n| **ondeletecolumn** | After a column is excluded. |  \n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false.  |\n| **onmoverow** | After a row is moved to a new position.  |\n| **onmovecolumn** | After a column is moved to a new position.  |\n| **onresizerow** | After a change in row height.  |\n| **onresizecolumn** | After a change in column width.  |\n| **onselection** | On the selection is changed.  |\n| **onsort** | After a colum is sorted.  |\n| **onfocus** | On table focus  |\n| **onblur** | On table blur  |\n| **onmerge** | On column merge |  \n| **onchangeheader** | On header change  |\n| **onundo** | On undo is applied  |\n| **onredo** | On redo is applied  |\n| **oneditionstart** | When a openEditor is called.  |\n| **oneditionend** | When a closeEditor is called.  |\n| **onchangestyle** | When a setStyle is called.  |\n| **onchangemeta** | When a setMeta is called. |\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/docs/programmatically-changes.md",
    "content": "title: Programmatically Changes\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jspreadsheet CE.\n\n[Back to Documentation](/jspreadsheet/v3/docs \"Back to the documentation section\")\n\n# Programmatically changes\n\nJspreadsheet has a comprehensive number of native methods to programmatically interact with your javascript spreadsheet and its data.\n\n[Go to a working example](/jspreadsheet/v3/examples/programmatically-updates)\n\n  \n\n## General Methods\n\n| Method                                                                                                                                                                                                                                                               | Example                                                                            |\n|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|\n| **getData:** Get the full or partial table data<br>@Param boolan onlyHighlighedCells - Get only highlighted cells                                                                                                                                                    | spreadsheet.getData([bool]);                                                       |\n| **getJson:** Get the full or partial table data in JSON format<br>@Param boolan onlyHighlighedCells - Get only highlighted cells                                                                                                                                     | spreadsheet.getData([bool]);                                                       |\n| **getRowData:** Get the data from one row by number<br>@Param integer rowNumber - Row number                                                                                                                                                                         | spreadsheet.getRowData([int]);                                                     |\n| **setRowData:** Set the data from one row by number<br>@Param integer rowNumber - Row number<br>@param array rowData - Row data                                                                                                                                      | spreadsheet.setRowData([int], [array]);                                            |\n| **getColumnData:** Get the data from one column by number  <br>@Param integer columnNumber - Column number                                                                                                                                                           | spreadsheet.getColumnData([int]);                                                  |\n| **setColumnData:** Set the data from one column by number<br>@Param integer columnNumber - Column number<br>@Param array colData - Column data                                                                                                                       | spreadsheet.setColumnData([int], [array]);                                         |\n| **setData:** Set the table data<br>@Param json newData - New json data, null will reload what is in memory.                                                                                                                                                          | spreadsheet.setData([json]);                                                       |\n| **setMerge:** Merge cells<br>@Param string columnName - Column name, such as A1.<br>@Param integer colspan - Number of columns<br>@Param integer rowspan - Number of rows                                                                                            | spreadsheet.setMerge([string], [int], [int]);                                      \n| **getMerge:** Get merged cells properties<br>@Param string columnName - Column name, such as A1.                                                                                                                                                                     | spreadsheet.getMerge([string]);                                                    |\n| **removeMerge:** Destroy merged by column name<br>@Param string columnName - Column name, such as A1.                                                                                                                                                                | spreadsheet.removeMerge([string]);                                                 |\n| **destroyMerged:** Destroy all merged cells                                                                                                                                                                                                                          | spreadsheet.destroyMerge();                                                        |  \n| **getCell** : get current cell DOM<br>@Param string columnName - str compatible with excel, or as object.                                                                                                                                                            | spreadsheet.getCell([string]);                                                     |\n| **getLabel** : get current cell DOM innerHTML<br>@Param string columnName - str compatible with excel, or as object.                                                                                                                                                 | spreadsheet.getLabel([string]);                                                    |\n| **getValue:** get current cell value<br>@Param mixed cellIdent - str compatible with excel, or as object.                                                                                                                                                            | spreadsheet.getValue([string]);                                                    |\n| **getValueFromCoords:** get value from coords<br>@Param integer x<br>@Param integer y                                                                                                                                                                                | spreadsheet.getValueFromCoords([integer], [integer]);                              \n| **setValue:** change the cell value<br>@Param mixed cellIdent - str compatible with excel, or as object.<br>@Param string Value - new value for the cell<br>@Param bool force - update readonly columns                                                              | spreadsheet.setValue([string], [string], [bool]);                                  |\n| **setValueFromCoords:** get value from coords<br>@Param integer x<br>@Param integer y<br>@Param string Value - new value for the cell<br>@Param bool force - update readonly columns                                                                                 | spreadsheet.getValueFromCoords([integer], [integer], [string], [bool]);            |\n| **resetSelection:** Reset the table selection<br>@Param boolean executeBlur - execute the blur from the table                                                                                                                                                        | spreadsheet.resetSelection([bool]);                                                |\n| **updateSelection:** select cells<br>@Param object startCell - cell object<br>@Param object endCell - cell object<br>@Param boolean ignoreEvents - ignore onselection event                                                                                          | spreadsheet.updateSelection([cell], [cell], true);                                 |\n| **updateSelectionFromCoords:** select cells<br>@Param integer x1<br>@Param integer y1<br>@Param integer x2<br>@Param integer y2                                                                                                                                      | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |\n| **getWidth:** get the current column width<br>@Param integer columnNumber - column number starting on zero                                                                                                                                                           | spreadsheet.getWidth([integer]);                                                   |\n| **setWidth:** change column width<br>@Param integer columnNumber - column number starting on zero<br>@Param string newColumnWidth - New column width                                                                                                                 | spreadsheet.setWidth([integer], [integer]);                                        |\n| **getHeight:** get the current row height<br>@Param integer rowNumber - row number starting on zero                                                                                                                                                                  | spreadsheet.getHeight([integer]);                                                  |\n| **setHeight:** change row height<br>@Param integer rowNumber - row number starting on zero<br>@Param string newRowHeight- New row height                                                                                                                             | spreadsheet.setHeight([integer], [integer]);                                       |\n| **getHeader:** get the current header by column number<br>@Param integer columnNumber - Column number starting on zero                                                                                                                                               | spreadsheet.getHeader([integer]);                                                  |\n| **getHeaders:** get all header titles                                                                                                                                                                                                                                | spreadsheet.getHeaders();                                                          |\n| **setHeader:** change header by column<br>@Param integer columnNumber - column number starting on zero<br>@Param string columnTitle - New header title                                                                                                               | spreadsheet.setHeader([integer], [string]);                                        |\n| **getStyle:** get table or cell style<br>@Param mixed - cell identification or null for the whole table.                                                                                                                                                             | spreadsheet.getStyle([string]);                                                    | \n| **setStyle:** set cell(s) CSS style<br>@Param mixed - json with whole table style information or just one cell identification. Ex. A1.<br>@Param k [optional]- CSS key<br>@Param v [optional]- CSS value                                                             | spreadsheet.setSyle([object], [string], [string]);                                 |\n| **resetStyle:** remove all style from a cell<br>@Param string columnName - Column name, example: A1, B3, etc                                                                                                                                                         | spreadsheet.resetStyle([string]);                                                  |\n| **getComments:** get cell comments<br>@Param mixed - cell identification or null for the whole table.                                                                                                                                                                | spreadsheet.getComments([string]);                                                 |\n| **setComments:** set cell comments<br>@Param cell - cell identification<br>@Param text - comments                                                                                                                                                                    | spreadsheet.setComments([string], [string]);                                       |\n| **orderBy:** reorder a column asc or desc<br>@Param integer columnNumber - column number starting on zero<br>@Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order                                        | spreadsheet.orderBy([integer], [boolean]);                                         |\n| **getConfig:** get table definitions                                                                                                                                                                                                                                 | spreadsheet.getConfig();                                                           |\n| **insertColumn:** add a new column<br>@Param mixed - num of columns to be added or data to be added in one single column<br>@Param int columnNumber - number of columns to be created<br>@Param boolean insertBefore<br>@Param object properties - column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]);                 |\n| **deleteColumn:** remove column by number<br>@Param integer columnNumber - Which column should be excluded starting on zero<br>@Param integer numOfColumns - number of columns to be excluded from the reference column                                              | spreadsheet.deleteColumn([integer], [integer]);                                    |\n| **moveColumn:** change the column position<br>@Param integer columnPosition<br>@Param integer newColumnPosition                                                                                                                                                      | spreadsheet.moveColumn([integer], [integer]);                                      |\n| **insertRow:** add a new row<br>@Param mixed - number of blank lines to be insert or a single array with the data of the new row<br>@Param integer rowNumber - reference row number<br>@param boolean insertBefore                                                   | spreadsheet.insertRow([mixed], [integer], [boolean]);                              |\n| **deleteRow:** remove row by number<br>@Param integer rowNumber - Which row should be excluded starting on zero<br>@Param integer numOfRows - number of lines to be excluded                                                                                         | spreadsheet.deleteRow([integer], [integer]);                                       |\n| **moveRow:** change the row position<br>@Param integer rowPosition<br>@Param integer newRowPosition                                                                                                                                                                  | >spreadsheet.moveRow([integer], [integer]);                                        |\n| **download:** get the current data as a CSV file<br>@Param bool - true to download parsed formulas.                                                                                                                                                                  | spreadsheet.download([bool]);                                                      |\n| **getMeta:** get the table or cell meta information<br>@Param mixed - cell identification or null for the whole table.                                                                                                                                               | spreadsheet.getMeta([string]);                                                     |\n| **setMeta:** set the table or cell meta information<br>@Param mixed - json with whole table meta information.                                                                                                                                                        | spreadsheet.setMeta([mixed]);                                                      |\n| **fullscreen:** Toogle table fullscreen mode<br>@Param boolan fullscreen - define fullscreen status as true or false                                                                                                                                                 | spreadsheet.fullscreen([bool]);                                                    |\n| **getSelectedRows:** Get the selected rows<br>@Param boolean asIds - Get the rowNumbers or row DOM elements                                                                                                                                                          | spreadsheet.getSelectedRows([bool]);                                               |\n| **getSelectedColumns:** Get the selected columns<br>@Param boolan asIds - Get the colNumbers or row DOM elements                                                                                                                                                     | spreadsheet.getSelectedColumns([bool]);                                            |\n| **showIndex:** show column of index numbers                                                                                                                                                                                                                          | spreadsheet.showIndex();                                                           |\n| **hideIndex:** hide column of index numbers                                                                                                                                                                                                                          | spreadsheet.hideIndex();                                                           |\n| **search:** search in the table, only if directive is enabled during inialization.<br>@Param string - Search for word                                                                                                                                                | spreadsheet.search([string]);                                                      |\n| **resetSearch:** reset search table                                                                                                                                                                                                                                  | spreadsheet.resetSearch();                                                         |\n| **whichPage:** Which page showing on jspreadsheet - Valid only when pagination is true.                                                                                                                                                                              | spreadsheet.whichPage();                                                           |\n| **page:** Go to page number- Valid only when pagination is true.<br>@Param integer - Go to page number                                                                                                                                                               | spreadsheet.page([integer]);                                                       |\n| **undo:** Undo last changes                                                                                                                                                                                                                                          | spreadsheet.undo();                                                                |\n| **redo:** Redo changes                                                                                                                                                                                                                                               | spreadsheet.redo();                                                                |\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/docs/quick-reference.md",
    "content": "title: Quick Reference\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Quick Reference of the jspreadsheet properties\n\n[Back to Documentation](/jspreadsheet/v3/docs)\n\n# The online spreadsheet quick reference\n\n## Methods\n\n| Method | Example |  \n| --- | --- |  \n| **getData:** Get the full or partial table data<br/>@Param boolean onlyHighlightedCells - Get only highlighted cells | spreadsheet.getData([bool]); |  \n| **getJson:** Get the full or partial table data in JSON format<br/>@Param boolean onlyHighlightedCells - Get only highlighted cells | spreadsheet.getJson([bool]); |  \n| **getRowData:** Get the data from one row by number<br/>@Param integer rowNumber - Row number | spreadsheet.getRowData([int]); |  \n| **setRowData:** Set the data from one row by number<br/>@Param integer rowNumber - Row number<br/>@Param array rowData - Row data | spreadsheet.setRowData([int], [array]); |  \n| **getColumnData:** Get the data from one column by number<br/>@Param integer columnNumber - Column number | spreadsheet.getColumnData([int]); |  \n| **setColumnData:** Set the data from one column by number<br/>@Param integer columnNumber - Column number<br/>@Param array colData - Column data | spreadsheet.setColumnData([int], [array]); |  \n| **setData:** Set the table data<br/>@Param json newData - New json data, null will reload what is in memory. | spreadsheet.setData([json]); |  \n| **setMerge:** Merge cells<br/>@Param string columnName - Column name, such as A1.<br/>@Param integer colspan - Number of columns<br/>@Param integer rowspan - Number of rows | spreadsheet.setMerge([string], [int], [int]); |  \n| **getMerge:** Get merged cells properties<br/>@Param string columnName - Column name, such as A1. | spreadsheet.getMerge([string]); |  \n| **removeMerge:** Destroy merged by column name<br/>@Param string columnName - Column name, such as A1. | spreadsheet.removeMerge([string]); |  \n| **destroyMerged:** Destroy all merged cells | spreadsheet.destroyMerge(); |  \n| **getCell** : get current cell DOM<br/>@Param string columnName - str compatible with excel, or as object. | spreadsheet.getCell([string]); |  \n| **getLabel** : get current cell DOM innerHTML<br/>@Param string columnName - str compatible with excel, or as object. | spreadsheet.getLabel([string]); |  \n| **getValue:** get current cell value<br/>@Param mixed cellIdent - str compatible with excel, or as object. | spreadsheet.getValue([string]); |  \n| **getValueFromCoords:** get value from coords<br/>@Param integer x<br/>@Param integer y | spreadsheet.getValueFromCoords([integer], [integer]); |  \n| **setValue:** change the cell value<br/>@Param mixed cellIdent - str compatible with excel, or as object.<br/>@Param string Value - new value for the cell<br/>@Param bool force - update readonly columns | spreadsheet.setValue([string], [string], [bool]); |  \n| **setValueFromCoords:** get value from coords<br/>@Param integer x<br/>@Param integer y<br/>@Param string Value - new value for the cell<br/>@Param bool force - update readonly columns | spreadsheet.setValueFromCoords([integer], [integer], [string], [bool]); |  \n| **resetSelection:** Reset the table selection<br/>@Param boolean executeBlur - execute the blur from the table | spreadsheet.resetSelection([bool]); |  \n| **updateSelection:** select cells<br/>@Param object startCell - cell object<br/>@Param object endCell - cell object<br/>@Param boolean ignoreEvents - ignore onselection event | spreadsheet.updateSelection([cell], [cell], true); |  \n| **updateSelectionFromCoords:** select cells<br/>@Param integer x1<br/>@Param integer y1<br/>@Param integer x2<br/>@Param integer y2 | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |  \n| **getWidth:** get the current column width<br/>@Param integer columnNumber - column number starting on zero | spreadsheet.getWidth([integer]); |  \n| **setWidth:** change column width<br/>@Param integer columnNumber - column number starting on zero<br/>@Param string newColumnWidth - New column width | spreadsheet.setWidth([integer], [integer]); |  \n| **getHeight:** get the current row height<br/>@Param integer rowNumber - row number starting on zero | spreadsheet.getHeight([integer]); |  \n| **setHeight:** change row height<br/>@Param integer rowNumber - row number starting on zero<br/>@Param string newRowHeight- New row height | spreadsheet.setHeight([integer], [integer]); |  \n| **getHeader:** get the current header by column number<br/>@Param integer columnNumber - Column number starting on zero | spreadsheet.getHeader([integer]); |  \n| **getHeaders:** get all header titles | spreadsheet.getHeaders(); |  \n| **setHeader:** change header by column<br/>@Param integer columnNumber - column number starting on zero<br/>@Param string columnTitle - New header title | spreadsheet.setHeader([integer], [string]); |  \n| **getStyle:** get table or cell style<br/>@Param mixed - cell identification or null for the whole table. | spreadsheet.getStyle([string]); |  \n| **setStyle:** set cell(s) CSS style<br/>@Param mixed - json with whole table style information or just one cell identification. Ex. A1.<br/>@Param k [optional]- CSS key<br/>@Param v [optional]- CSS value | spreadsheet.setStyle([object], [string], [string]); |  \n| **resetStyle:** remove all style from a cell<br/>@Param string columnName - Column name, example: A1, B3, etc | spreadsheet.resetStyle([string]); |  \n| **getComments:** get cell comments<br/>@Param mixed - cell identification or null for the whole table. | spreadsheet.getComments([string]); |  \n| **setComments:** set cell comments<br/>@Param cell - cell identification<br/>@Param text - comments | spreadsheet.setComments([string], [string]); |  \n| **orderBy:** reorder a column asc or desc<br/>@Param integer columnNumber - column number starting on zero<br/>@Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order | spreadsheet.orderBy([integer], [boolean]); |  \n| **getConfig:** get table definitions | spreadsheet.getConfig(); |  \n| **insertColumn:** add a new column<br/>@Param mixed - num of columns to be added or data to be added in one single column<br/>@Param int columnNumber - number of columns to be created<br/>@Param boolean insertBefore<br/>@Param object properties - column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]); |  \n| **deleteColumn:** remove column by number<br/>@Param integer columnNumber - Which column should be excluded starting on zero<br/>@Param integer numOfColumns - number of columns to be excluded from the reference column | spreadsheet.deleteColumn([integer], [integer]); |  \n| **moveColumn:** change the column position<br/>@Param integer columnPosition<br/>@Param integer newColumnPosition | spreadsheet.moveColumn([integer], [integer]); |  \n| **insertRow:** add a new row<br/>@Param mixed - number of blank lines to be insert or a single array with the data of the new row<br/>@Param integer rowNumber - reference row number<br/>@Param boolean insertBefore | spreadsheet.insertRow([mixed], [integer], [boolean]); |  \n| **deleteRow:** remove row by number<br/>@Param integer rowNumber - Which row should be excluded starting on zero<br/>@Param integer numOfRows - number of lines to be excluded | spreadsheet.deleteRow([integer], [integer]); |  \n| **moveRow:** change the row position<br/>@Param integer rowPosition<br/>@Param integer newRowPosition | spreadsheet.moveRow([integer], [integer]); |  \n| **download:** get the current data as a CSV file<br/>@Param bool - true to download parsed formulas. | spreadsheet.download([bool]); |  \n| **getMeta:** get the table or cell meta information<br/>@Param mixed - cell identification or null for the whole table. | spreadsheet.getMeta([string]); |  \n| **setMeta:** set the table or cell meta information<br/>@Param mixed - json with whole table meta information. | spreadsheet.setMeta([mixed]); |  \n| **fullscreen:** Toggle table fullscreen mode<br/>@Param boolean fullscreen - define fullscreen status as true or false | spreadsheet.fullscreen([bool]); |  \n| **getSelectedRows:** Get the selected rows<br/>@Param boolean asIds - Get the rowNumbers or row DOM elements | spreadsheet.getSelectedRows([bool]); |  \n| **getSelectedColumns:** Get the selected columns<br/>@Param boolean asIds - Get the colNumbers or row DOM elements | spreadsheet.getSelectedColumns([bool]); |  \n| **showColumn:** show column by number | spreadsheet.showIndex([int]); |  \n| **hideColumn:** hide column by number | spreadsheet.hideColumn([int]); |  \n| **showIndex:** show column of index numbers | spreadsheet.showIndex(); |  \n| **hideIndex:** hide column of index numbers | spreadsheet.hideIndex(); |  \n|\n\n\n  \n[Working example](/jspreadsheet/v3/examples/programmatically-changes)\n\n## Events\n\n| Event | Description |  \n| --- | --- |  \n| **onload** | This method is called when the method setData |  \n| **onbeforechange** | Before a column value is changed. NOTE: It is possible to overwrite the original value by returning a new value on this method. v3.4.0+ |  \n| **onchange** | After a column value is changed. |  \n| **onafterchanges** | After all changes are applied in the table. |  \n| **onpaste** | After a paste action is performed in the JavaScript table. |  \n| **onbeforepaste** | Before the paste action is performed. Used to parse any input data, should return the data. |  \n| **oninsertrow** | After a new row is inserted. |  \n| **onbeforeinsertrow** | Before a new row is inserted. You can cancel the insert event by returning false. |  \n| **ondeleterow** | After a row is excluded. |  \n| **onbeforedeleterow** | Before a row is deleted. You can cancel the delete event by returning false. |  \n| **oninsertcolumn** | After a new column is inserted. |  \n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false. |  \n| **ondeletecolumn** | After a column is excluded. |  \n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false. |  \n| **onmoverow** | After a row is moved to a new position. |  \n| **onmovecolumn** | After a column is moved to a new position. |  \n| **onresizerow** | After a change in row height. |  \n| **onresizecolumn** | After a change in column width. |  \n| **onselection** | When the selection is changed. |  \n| **onsort** | After a column is sorted. |  \n| **onfocus** | On table focus |  \n| **onblur** | On table blur |  \n| **onmerge** | On column merge |  \n| **onchangeheader** | On header change |  \n| **onundo** | When undo is applied |  \n| **onredo** | When redo is applied |  \n| **oneditionstart** | When openEditor is called. |  \n| **oneditionend** | When closeEditor is called. |  \n| **onchangestyle** | When setStyle is called. |  \n| **onchangemeta** | When setMeta is called. |  \n| **onchangepage** | When the page is changed. |\n\n  \n[Example on handling events on Jspreadsheet](/jspreadsheet/v3/examples/events)\n\n## Initialization\n\n| Parameter | Description |  \n| --- | --- |  \n| **url** | Load an external JSON file from this URL: string |  \n| **data** | Load this data into the JavaScript table: array |  \n| **copyCompatibility** | When true, copy and export will bring formula results; if false, will bring formulas: boolean |  \n| **rows** | Row properties: height.: object |  \n| **columns** | Column type, title, width, align, dropdown options, text wrapping, mask, etc.: object |  \n| **defaultColWidth** | Default width for a new column: integer |  \n| **defaultColAlign** | Default align for a new column: [center, left, right] |  \n| **minSpareRows** | Minimum number of spare rows: [integer] |  \n| **minSpareCols** | Minimum number of spare cols: [integer] |  \n| **minDimensions** | Minimum table dimensions: [cols, rows] |  \n| **allowExport** | Allow table export: bool |  \n| **includeHeadersOnDownload** | Include header titles on download: bool |  \n| **columnSorting** | Allow column sorting: bool |  \n| **columnDrag** | Allow column dragging: bool |  \n| **columnResize** | Allow column resizing: bool |  \n| **rowResize** | Allow row resizing: bool |  \n| **rowDrag** | Allow row dragging: bool |  \n| **editable** | Allow table edition: bool |  \n| **allowInsertRow** | Allow insert a new row: bool |  \n| **allowManualInsertRow** | Allow user to insert a new row: bool |  \n| **allowInsertColumn** | Allow insert a new column: bool |  \n| **allowManualInsertColumn** | Allow user to create a new column: bool |  \n| **allowDeleteRow** | Allow delete a row: bool |  \n| **allowDeleteColumn** | Allow delete a column: bool |  \n| **allowRenameColumn** | Allow rename a column: bool |  \n| **allowComments** | Allow comments over the cells: bool |  \n| **wordWrap** | Global text wrapping: bool |  \n| **csv** | Load an external CSV file from this URL: string |  \n| **csvFileName** | Default filename for a download method: string |  \n| **csvHeaders** | Load header titles from the CSV file: bool |  \n| **csvDelimiter** | Default delimiter for the CSV file: string |  \n| **selectionCopy** | Allow selection copy: bool |  \n| **mergeCells** | Cells to be merged in the table initialization: object |  \n| **toolbar** | Add custom toolbars: object |  \n| **search** | Allow search in the table: bool |  \n| **pagination** | Break the table by pages: integer |  \n| **paginationOptions** | Number of records per page: 25, 50, 75, 100 for example: [array of numbers] |  \n| **fullscreen** | Fullscreen mode: bool |  \n| **lazyLoading** | Activate the table lazy loading: bool |  \n| **loadingSpin** | Activate the loading spin: bool |  \n| **tableOverflow** | Allow table overflow: bool |  \n| **tableHeight** | Force the max height of the table: CSS String |  \n| **tableWidth** | Force the max width of the table: CSS String |  \n| **meta** | Meta information: object |  \n| **style** | Cells style in the table initialization: object |  \n| **parseFormulas** | Enable execution of formulas inside the table: bool |  \n| **autoIncrement** | Auto increment actions when using the dragging corner: bool |  \n| **updateTable** | Method to config custom script execution. NOTE: This does not work with lazyLoading, Pagination or Search options. |  \n| **nestedHeaders** | Define the nested headers, including title, colspan, etc.: object |  \n| **contextMenu** | Context menu content: function() { return customMenu } |  \n| **text** | All messages to be customized: object |\n\n  \n\n## Translations\n\n| Key | Default value |  \n| --- | --- |  \n| **noRecordsFound** | No records found |  \n| **showingPage** | Showing page {0} of {1} entries |  \n| **show** | Show |  \n| **entries** | entries |  \n| **insertANewColumnBefore** | Insert a new column before |  \n| **insertANewColumnAfter** | Insert a new column after |  \n| **deleteSelectedColumns** | Delete selected columns |  \n| **renameThisColumn** | Rename this column |  \n| **orderAscending** | Order ascending |  \n| **orderDescending** | Order descending |  \n| **insertANewRowBefore** | Insert a new row before |  \n| **insertANewRowAfter** | Insert a new row after |  \n| **deleteSelectedRows** | Delete selected rows |  \n| **editComments** | Edit comments |  \n| **addComments** | Add comments |  \n| **comments** | Comments |  \n| **clearComments** | Clear comments |  \n| **copy** | Copy... |  \n| **paste** | Paste... |  \n| **saveAs** | Save as... |  \n| **about** | About |  \n| **areYouSureToDeleteTheSelectedRows** | Are you sure to delete the selected rows? |  \n| **areYouSureToDeleteTheSelectedColumns** | Are you sure to delete the selected columns? |  \n| **thisActionWillDestroyAnyExistingMergedCellsAreYouSure** | This action will destroy any existing merged cells. Are you sure? |  \n| **thisActionWillClearYourSearchResultsAreYouSure** | This action will clear your search results. Are you sure? |  \n| **thereIsAConflictWithAnotherMergedCell** | There is a conflict with another merged cell |  \n| **invalidMergeProperties** | Invalid merged properties |  \n| **cellAlreadyMerged** | Cell already merged |  \n| **noCellsSelected** | No cells selected |\n\n  \n[Working example](/jspreadsheet/v3/examples/translations)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/docs.md",
    "content": "title: The javascript spreadsheet documentation\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table, grid\ndescription: Jspreadsheet CE official documentation, the javascript spreadsheet\n\nJspreadsheet v3 Documentation\n=============================\n\nThe Jspreadsheet is a lightweight javascript spreadsheet component to help with data management in web applications.\n\n* [Getting started](/jspreadsheet/v3/docs/getting-started \"Getting started with Jspreadsheet\")  \n    Getting started with Jspreadsheet - the online spreadsheet. Learning the basics, create instances, and more about the general spreadsheet configuration.\n  \n* [Programmatically changes](/jspreadsheet/v3/docs/programmatically-changes \"Programmatically changes\")  \n    How to interact with your Jspreadsheet - the online spreadsheet and tables through javascript.\n  \n* [Handling events](/jspreadsheet/v3/docs/events \"Handling events on Jspreadsheet\")  \n    How to handle events on your javascript spreadsheet.\n  \n* [Quick reference](/jspreadsheet/v3/docs/quick-reference \"Jspreadsheet method and events\")  \n    Quick reference of all methods, configuration variables, events and language."
  },
  {
    "path": "docs/jspreadsheet/v3/examples/angular.md",
    "content": "title: Jexcel with Angular\nkeywords: Jexcel, javascript, using jspreadsheet and angular\ndescription: A full example on how to integrate Jspreadsheet with Angular\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# The Javascript spreadsheet with Angular\n\n[Click here to see the project running on codesandbox.](https://codesandbox.io/s/jexcel-and-angular-ej4u2)\n\n### Source code\n\n{.ignore}\n```javascript\nimport { Component } from \"@angular/core\";\nimport * as jexcel from \"jexcel\";\n\nrequire(\"jexcel/dist/jexcel.css\");\n\n@Component({\n  selector: \"app-root\",\n  templateUrl: \"./app.component.html\",\n  styleUrls: [\"./app.component.css\"]\n})\nexport class AppComponent {\n  title = \"CodeSandbox\";\n\n  ngAfterViewInit() {\n    jexcel(document.getElementById(\"spreadsheet\"), {\n      data: [[]],\n      columns: [\n        { type: \"dropdown\", width: \"100px\", source: [\"Y\", \"N\"] },\n        { type: \"color\", width: \"100px\", render: \"square\" }\n      ],\n      minDimensions: [10, 10]\n    });\n  }\n}\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/column-types.md",
    "content": "title: Column types\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Learn more about the powerful column types. This example brings all native column types and how to create your own custom type.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Column types\n\nThe native available types in jspreadsheet javascript spreadsheet are the following:\n\n  * text\n  * numeric\n  * hidden\n  * dropdown\n  * autocomplete\n  * checkbox\n  * radio\n  * calendar\n  * image\n  * color\n\n## Native column types\n\nThere are several other properties to change the behavior of those columns, please check the dropdown, calendar examples to get more advanced examples.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet data = [\n    ['Jazz', 'Honda', '2019-02-12', '', true, '$ 2.000,00', '#777700'],\n    ['Civic', 'Honda', '2018-07-11', '', true, '$ 4.000,01', '#007777'],\n];\n\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { type: 'text', title:'Car', width:100 },\n        { type: 'dropdown', title:'Make', width:100, source:[ \"Alfa Romeo\", \"Audi\", \"Bmw\" ] },\n        { type: 'calendar', title:'Available', width:100 },\n        { type: 'image', title:'Photo', width:120 },\n        { type: 'checkbox', title:'Stock', width:80 },\n        { type: 'numeric', title:'Price', width:100, mask:'$ #.##,00', decimal:',' },\n        { type: 'color', width:100, render:'square', }\n     ]\n});\n</script>\n</html>\n```  \n\n## Custom column type\n\nJspreadsheet is very powerful and flexible, and you can create custom column type based on any external plugins.\n\nA time custom column based on the [clockpicker plugin](https://weareoutman.github.io/clockpicker/) by weareoutman.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"http://weareoutman.github.io/clockpicker/dist/jquery-clockpicker.min.css\" />\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n<script src=\"http://weareoutman.github.io/clockpicker/dist/jquery-clockpicker.min.js\"></script>\n\n<div id=\"custom\"></div>\n\n<script>\nlet data2 = [\n    ['PHP', '14:00'],\n    ['Javascript', '16:30'],\n];\n\nlet customColumn = {\n    // Methods\n    closeEditor : function(cell, save) {\n        let value = cell.children[0].value;\n        cell.innerHTML = value;\n        return value;\n    },\n    openEditor : function(cell) {\n        // Create input\n        let element = document.createElement('input');\n        element.value = cell.innerHTML;\n        // Update cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(element);\n        $(element).clockpicker({\n            afterHide:function() {\n                setTimeout(function() {\n                    // To avoid double call\n                    if (cell.children[0]) {\n                        spreadsheet.closeEditor(cell, true);\n                    }\n                });\n            }\n        });\n        // Focus on the element\n        element.focus();\n    },\n    getValue : function(cell) {\n        return cell.innerHTML;\n    },\n    setValue : function(cell, value) {\n        cell.innerHTML = value;\n    }\n}\n\nspreadsheet = jexcel(document.getElementById('custom'), {\n    data:data2,\n    columns: [\n        { type: 'text', title:'Course Title', width:300 },\n        { type: 'text', title:'Time', width:100, editor:customColumn },\n     ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/comments.md",
    "content": "title: Allow comments in your javascript table\nkeywords: Jexcel, spreadsheet, javascript, cell comments, javascript table\ndescription: Allow comments in your table spreadsheet.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Comments on your javascript spreadsheet\n\nThe javascript vanilla spreadsheet plugin allows the user to set custom comments for each individual cells. By allowComments in the initialization, the user will be able to add comments in the rigth click contextMenu.\n\n## Manage cell comments programmatically\n\nTo apply comments via javascript, you can use the methods setComments or getComments, as follow:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div><br/>\n\n<input type=\"button\" id=\"setComments\" value=\"Set A1 comments\" />\n<input type=\"button\" id=\"getComments\" value=\"Get A1 comments\" />\n<input type=\"button\" id=\"resetComments\" value=\"Reset A1 comments\" />\n\n<script>\nlet table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ],\n    columns: [\n        {\n            type: 'dropdown',\n            source: ['BR', 'US', 'CA'],\n            width:200,\n        },\n        {\n            type: 'text',\n            width:200,\n        },\n        {\n            type: 'calendar',\n            width:200,\n        }\n     ],\n     allowComments:true,\n});\n\ndocument.getElementById(\"setComments\").onclick = () => table.setComments('A1', 'This is the comments from A1');\ndocument.getElementById(\"getComments\").onclick = () => alert(table.getComments('A1'));\ndocument.getElementById(\"resetComments\").onclick = () => table.setComments('A1', '');\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/contextmenu.md",
    "content": "title: Custom contextmenu\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: How to customize Jspreadsheet contextmenu\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Custom contextmenu\n\nThe following example remove the copy and paste from the contextmenu in order to create a custom contextMenu\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nmySpreadsheet = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ],\n    columns: [\n        { type: 'dropdown', source: ['BR', 'CA', 'US', 'UK'], width:200, },\n        { type: 'text', width:200, },\n        { type: 'calendar', width:100, }\n     ],\n     allowComments: true,\n     contextMenu: function(obj, x, y, e) {\n         var items = [];\n\n         if (y == null) {\n             // Insert a new column\n             if (obj.options.allowInsertColumn == true) {\n                 items.push({\n                     title:obj.options.text.insertANewColumnBefore,\n                     onclick:function() {\n                         obj.insertColumn(1, parseInt(x), 1);\n                     }\n                 });\n             }\n\n             if (obj.options.allowInsertColumn == true) {\n                 items.push({\n                     title:obj.options.text.insertANewColumnAfter,\n                     onclick:function() {\n                         obj.insertColumn(1, parseInt(x), 0);\n                     }\n                 });\n             }\n\n             // Delete a column\n             if (obj.options.allowDeleteColumn == true) {\n                 items.push({\n                     title:obj.options.text.deleteSelectedColumns,\n                     onclick:function() {\n                         obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x));\n                     }\n                 });\n             }\n\n             // Rename column\n             if (obj.options.allowRenameColumn == true) {\n                 items.push({\n                     title:obj.options.text.renameThisColumn,\n                     onclick:function() {\n                         obj.setHeader(x);\n                     }\n                 });\n             }\n\n             // Sorting\n             if (obj.options.columnSorting == true) {\n                 // Line\n                 items.push({ type:'line' });\n\n                 items.push({\n                     title:obj.options.text.orderAscending,\n                     onclick:function() {\n                         obj.orderBy(x, 0);\n                     }\n                 });\n                 items.push({\n                     title:obj.options.text.orderDescending,\n                     onclick:function() {\n                         obj.orderBy(x, 1);\n                     }\n                 });\n             }\n         } else {\n             // Insert new row\n             if (obj.options.allowInsertRow == true) {\n                 items.push({\n                     title:obj.options.text.insertANewRowBefore,\n                     onclick:function() {\n                         obj.insertRow(1, parseInt(y), 1);\n                     }\n                 });\n                 \n                 items.push({\n                     title:obj.options.text.insertANewRowAfter,\n                     onclick:function() {\n                         obj.insertRow(1, parseInt(y));\n                     }\n                 });\n             }\n\n             if (obj.options.allowDeleteRow == true) {\n                 items.push({\n                     title:obj.options.text.deleteSelectedRows,\n                     onclick:function() {\n                         obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y));\n                     }\n                 });\n             }\n\n             if (x) {\n                 if (obj.options.allowComments == true) {\n                     items.push({ type:'line' });\n\n                     var title = obj.records[y][x].getAttribute('title') || '';\n\n                     items.push({\n                         title: title ? obj.options.text.editComments : obj.options.text.addComments,\n                         onclick:function() {\n                             obj.setComments([ x, y ], prompt(obj.options.text.comments, title));\n                         }\n                     });\n\n                     if (title) {\n                         items.push({\n                             title:obj.options.text.clearComments,\n                             onclick:function() {\n                                 obj.setComments([ x, y ], '');\n                             }\n                         });\n                     }\n                 }\n             }\n         }\n\n         // Line\n         items.push({ type:'line' });\n\n         // Save\n         if (obj.options.allowExport) {\n             items.push({\n                 title: obj.options.text.saveAs,\n                 shortcut: 'Ctrl + S',\n                 onclick: function () {\n                     obj.download();\n                 }\n             });\n         }\n\n         // About\n         if (obj.options.about) {\n             items.push({\n                 title:obj.options.text.about,\n                 onclick:function() {\n                     alert(obj.options.about);\n                 }\n             });\n         }\n\n         return items;\n     }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/custom-table-design.md",
    "content": "title: Custom Table Design\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Customized CSS for your datagrid\n\n[Back to Examples](jspreadsheet/v3/examples)\n\n# Create custom CSS for your javascript spreadsheet\n\nThe following example shows a CSS addon to change the core layout of your jquery tables.\n\n  \n\n## Green borders and corners jquery spreadsheet\n\nYour jquery table can be customized by including an additional addon CSS. If you have created a nice design, please share with us.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<!-- Optional CSS addon -->\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.0.0/css/jquery.jexcel.green.min.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jexcel/2.0.0/css/jquery.jexcel.bootstrap.min.css\" type=\"text/css\" />\n\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['BR', 'Cheese', 1, 1900.01],\n    ['CA', 'Apples', 0, 1200.21],\n    ['US', 'Carrots', 1, 400.34],\n    ['GB', 'Oranges', 0, 900.43],\n];\n\n$('#my').jexcel({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock', 'Price'],\n    colWidths: [ 300, 100, 100, 100 ],\n    colAlignments: [ 'left', 'left', 'left', 'left' ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'autocomplete', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n        { type: 'number' },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/datatables.md",
    "content": "title: Searchable Datatables\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Full spreadsheet example with search and pagination to bring great compatibility for those who love datatables.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Javascript spreadsheet with search and pagination\n\nThe following example shows how to create a javascript spreadsheet instance with a design similar to datatables jquery plugin.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.datatables.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    csv:'/tests/demo.csv',\n    csvHeaders:true,\n    search:true,\n    pagination:10,\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:200 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n     ]\n});    \n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/date-and-datetime-picker.md",
    "content": "title: Calendar with date and datetime picker\nkeywords: Jexcel, javascript, excel-like, spreadsheet, date, datetime, calendar\ndescription: Example from basic to advanced calendar usage, date and datetime picker\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Calendar column type\n\nThe example below shows how to use and customize special calendar column type.\n\nJspreadsheet uses the jSuites [Javascript Calendar](https://jsuites.net/docs/javascript-calendar) plugin\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Flag Fen', 'South East', '2019-01-01'],\n        ['Bristol Aero Collection (BAC)','South West','2019-04-03'],\n        ['Experience Barnsley', 'North','2018-12-03'],\n        ['Cinema Museum', 'London',''],\n        ['University of Hertfordshire Art Collection', 'South East',''],\n        ['LUX London', 'London','2016-11-03'],\n    ],\n    columns: [\n        {\n            type:'text',\n            title:'Museum',\n            width:'300',\n        },\n        {\n            type:'dropdown',\n            title:'Region',\n            source:['South East','South West','North','London'],\n            width:'200',\n        },\n        {\n            type:'calendar',\n            title:'Last visit',\n            options: { format:'DD/MM/YYYY' },\n            width:'100',\n        },\n    ]\n});\n</script>\n</html>\n```  \n\n## Date column customization\n\nCustomize the format and the behavior of your column through the initialization options, as follow:\n\n```javascript\n{\n    options : {\n        // Date format\n        format:'DD/MM/YYYY',\n        // Allow keyboard date entry\n        readonly:0,\n        // Today is default\n        today:0,\n        // Show timepicker\n        time:0,\n        // Show the reset button\n        resetButton:true,\n        // Placeholder\n        placeholder:'',\n        // Translations can be done here\n        months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],\n        weekdays_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'],\n        // Value\n        value:null,\n        // Events\n        onclose:null,\n        onchange:null,\n        // Fullscreen (this is automatic set for screensize < 800)\n        fullscreen:false,\n    };\n}\n```\n\n## JavaScript Calendar Picker\n\nMore information about the jSuites responsive [JavaScript calendar](https://jsuites.net/docs/javascript-calendar) plugin.\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/dropdown-and-autocomplete.md",
    "content": "title: Advanced dropdown column type\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: Full examples on how to handle simple, advanced, multiple, autocomplete and conditional dropdowns. Create amazing javascript tables using categories and images in your dropdowns.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Dropdown and autocomplete column type\n\nJspreadsheet brings a very powerful, reflexive and responsive dropdown column type to support a better user experience through your applications. The new dropdown column options include autocomplete, multiple options, data picker, different template types and much more advantages, such as:\n\n  * Create a simple dropdown from array\n  * Value or key-value select box is available\n  * Populate a dropdown from a external JSON request\n  * Dynamic autocomplete search based on another column value\n  * Conditional dropdowns: options from a dropdown based on a method return\n  * Multiple selection and internal dropdown search\n  * Responsive data picker with multiple render types\n  * Image icon and group items\n\n## Multiple and autocomplete options\n\nThe highlight features introduced in the most version of the vanilla javascript spreadsheet is definitely the autocomplete and multiple options. The following example shows the column Product Origin with the autocomplete and multiple directives initiated as true.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet1\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet1'), {\n    data: [\n        ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Grains', 'No', '2018-11-10'],\n        ['BR', 'Pasta', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        { type:'dropdown', width:'300', title:'Product Origin', source: ['CA', 'BR', 'UK', 'US', 'RU'], autocomplete:true, multiple:true },\n        { type:'text', width:'200', title:'Description' },\n        { type:'dropdown', width:'100', title:'Stock', source:['No','Yes'] },\n        { type:'calendar', width:'100', title:'Best before' },\n    ]\n});\n</script>\n</html>\n```  \n\n## Conditional dropdown\n\nThe example below shows the dependency of the second column in relation to the first.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\ndropdownFilter = function(instance, cell, c, r, source) {\n    var value = instance.jexcel.getValueFromCoords(c - 1, r);\n    if (value == 1) {\n        return ['Apples','Bananas','Oranges'];\n    } else if (value == 2) {\n        return ['Carrots'];\n    } else {\n        return source;\n    }\n}\n\njexcel(document.getElementById('spreadsheet2'), {\n    data: [\n        [3, 'Cheese', true],\n        [1, 'Apples', true],\n        [2, 'Carrots', true],\n        [1, 'Oranges', false],\n    ],\n    columns: [\n        { type:'dropdown', title:'Category', width:'300', source:[ {'id':'1', 'name':'Fruits'}, {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type:'dropdown', title:'Food', width:'200', source:['Apples','Bananas','Carrots','Oranges','Cheese'], filter:dropdownFilter },\n        { type: 'checkbox', title:'Buy', width:'100' },\n    ],\n    onchange:function(instance, cell, c, r, value) {\n        if (c == 0) {\n            let columnName = jexcel.getColumnNameFromId([c + 1, r]);\n            instance.jexcel.setValue(columnName, '');\n        }\n    }\n});\n</script>\n</html>\n```  \n\n## Group, images, and advanced render options\n\nImprove the user experience with a responsive data picker.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet3\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet3'), {\n    data: [\n        [1, 'Morning'],\n        [2, 'Morning'],\n        [3, 'Afternoon'],\n        [3, 'Evening'],\n    ],\n    columns: [\n        {\n            type:'dropdown',\n            title:'Category',\n            width:'300',\n            source:[\n                { id:'1', name:'Paulo', image:'/templates/jexcel-v3/img/1.jpg', title:'Admin', group:'Secretary' },\n                { id:'2', name:'Cosme Sergio', image:'/templates/jexcel-v3/img/2.jpg', title:'Teacher', group:'Docent' },\n                { id:'3', name:'Rose Mary', image:'/templates/jexcel-v3/img/3.png', title:'Teacher', group:'Docent' },\n                { id:'4', name:'Fernanda', image:'/templates/jexcel-v3/img/3.png', title:'Admin', group:'Secretary' },\n                { id:'5', name:'Roger', image:'/templates/jexcel-v3/img/3.png', title:'Teacher', group:'Docent' },\n            ]\n        },\n        {\n            type:'dropdown',\n            title:'Working hours',\n            width:'200',\n            source:['Morning','Afternoon','Evening'],\n            options: { type:'picker' },\n        },\n    ]\n});\n</script>\n</html>\n```\n\n## JavaScript Dropdown Component\n\nMore options for the dropdowns, please refer to the [jSuites JavaScript Dropdown Documentation](https://jsuites.net/docs/dropdown).\n\n  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/events.md",
    "content": "title: Handling events on Jspreadsheet\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table, grid, events\ndescription: Learn how to handle events on Jspreadsheet\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Handling events\n\n## Various tracking javascript methods\n\nBinding tracking events on your javascript spreadsheet\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n<div id=\"log\"></div>\n\n<script>\nlet log = document.getElementById('log');\nconst changed = function(instance, cell, x, y, value) {\n    let cellName = jexcel.getColumnNameFromId([x,y]);\n    log.append(' -  New change on cell ' + cellName + ' to: ' + value + '  ');\n}\n\nconst beforeChange = function(instance, cell, x, y, value) {\n    let cellName = jexcel.getColumnNameFromId([x,y]);\n    log.append(' -  The cell ' + cellName + ' will be changed');\n}\n\nconst insertedRow = function(instance) {\n    log.append(' -  Row added');\n}\n\nconst insertedColumn = function(instance) {\n    log.append(' -  Column added');\n}\n\nconst deletedRow = function(instance) {\n    log.append(' -  Row deleted');\n}\n\nconst deletedColumn = function(instance) {\n    log.append(' -  Column deleted');\n}\n\nconst sort = function(instance, cellNum, order) {\n    order = (order) ? 'desc' : 'asc';\n    log.append(' -  The column  ' + cellNum + ' sorted by ' + order + ' ');\n}\n\nconst resizeColumn = function(instance, cell, width) {\n    log.append(' -  The column  ' + cell + ' resized to width ' + width + ' px');\n}\n\nconst resizeRow = function(instance, cell, height) {\n    log.append(' -  The row  ' + cell + ' resized to height ' + height + ' px');\n}\n\nconst selectionActive = function(instance, x1, y1, x2, y2, origin) {\n    let cellName1 = jexcel.getColumnNameFromId([x1, y1]);\n    let cellName2 = jexcel.getColumnNameFromId([x2, y2]);\n    log.append(' -  Selection from ' + cellName1 + ' to ' + cellName2 + ' ');\n}\n\nconst loaded = function(instance) {\n    log.append(' -  New data is loaded');\n}\n\nconst moveRow = function(instance, from, to) {\n    log.append(' -  The row ' + from + ' was move to the position of ' + to + '  ');\n}\n\nconst moveColumn = function(instance, from, to) {\n    log.append(' -  The col ' + from + ' was move to the position of ' + to + '  ');\n}\n\nconst blur = function(instance) {\n    log.append(' -  The table is blur');\n}\n\nconst focus = function(instance) {\n    console.log(instance)\n    log.append(' -  The table is focus');\n}\n\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    rowResize:true,\n    columnDrag:true,\n    columns: [\n        { type: 'text', width:'200' },\n        { type: 'text', width:'100' },\n        { type: 'text', width:'100' },\n        { type: 'calendar', width:'100' },\n    ],\n    onchange: changed,\n    onbeforechange: beforeChange,\n    oninsertrow: insertedRow,\n    oninsertcolumn: insertedColumn,\n    ondeleterow: deletedRow,\n    ondeletecolumn: deletedColumn,\n    onselection: selectionActive,\n    onsort: sort,\n    onresizerow: resizeRow,\n    onresizecolumn: resizeColumn,\n    onmoverow: moveRow,\n    onmovecolumn: moveColumn,\n    onload: loaded,\n    onblur: blur,\n    onfocus: focus,\n});\n</script>\n</html>\n```  \n  \n## Advanced Example\n\nUpdate the chart on every change in your spreadsheet, using the onchange handler\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n\n<div id=\"container\"></div>\n<div id=\"spreadsheet1\"></div>\n\n<script>\nlet update = function (instance, cell, x, y, value) {\n    // If the related series does not exists create a new one\n    if (! chart.series[y]) {\n        // Create a new series row\n        let row = [];\n        for (i = 1; i < data1[y].length; i++) {\n            row.push(parseFloat(data1[y][i]));\n        }\n        // Append new series to the chart\n        chart.addSeries({ name:data1[y][0], data:row });\n    } else {\n        if (x == 0) {\n            // Update legend\n            chart.series[y].update({ name:value });\n        } else {\n            // Update chart data\n            chart.series[y].data[x-1].update({ y:parseFloat(value) });\n        }\n    }\n}\n\njexcel(document.getElementById('spreadsheet1'), {\n    data: [\n        ['Tokyo', 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6],\n        ['New York', -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5],\n        ['Berlin', -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0],\n        ['London', 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8],\n    ],\n    columns: [\n        { type: 'text', width:'200' },\n    ],\n    onchange:update,\n});\n\nlet chart = null;\n\nchart = Highcharts.chart('container', {\n    title: {\n        text: 'Monthly Average Temperature',\n        x: -20 //center\n    },\n    subtitle: {\n        text: 'Source: WorldClimate.com',\n        x: -20\n    },\n    xAxis: {\n        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n            'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n    },\n    yAxis: {\n        title: {\n            text: 'Temperature (°C)'\n        },\n        plotLines: [{\n            value: 0,\n            width: 1,\n            color: '#808080'\n        }]\n    },\n    tooltip: {\n        valueSuffix: '°C'\n    },\n    legend: {\n        layout: 'vertical',\n        align: 'right',\n        verticalAlign: 'middle',\n        borderWidth: 0\n    },\n    series: [{\n        name: 'Tokyo',\n        data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n    }, {\n        name: 'New York',\n        data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n    }, {\n        name: 'Berlin',\n        data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n    }, {\n        name: 'London',\n        data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n    }]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/headers.md",
    "content": "title: Nested headers and column header updates\nkeywords: Jexcel, spreadsheet, javascript, header updates, nested headers, javascript table\ndescription: Enabled nested headers in your spreadsheet and learn how to set or get header values\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Headers\n\n## Nested headers\n\nThe online spreadsheet implements nested headers natively though the directive **nestedHeaders**\n\n## Programmatically header updates\n\nThere are a few options to allow the user to interact with the header titles. Using the contextMenu over the desired header, by pressing an selected header and holding the click for 500ms, or via javascript.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<br/>\n<select id='columnNumber'>\n    <option value=\"0\">Column 0</option>\n    <option value=\"1\">Column 1</option>\n    <option value=\"2\">Column 2</option>\n</select>\n\n<input type='button' value='Set header title' id=\"btn1\" />\n<input type='button' value='Get header title' id=\"btn2\" />\n\n<script>\nlet table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ],\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '300',\n            source: ['BR', 'CA', 'US', 'GB']\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width:'100'\n        },\n    ],\n    nestedHeaders:[\n        [\n            {\n                title: 'Supermarket information',\n                colspan: '3',\n            },\n        ],\n        [\n            {\n                title: 'Location',\n                colspan: '1',\n            },\n            {\n                title: ' Other Information',\n                colspan: '2'\n            }\n        ],\n    ],\n    columnDrag:true,\n});\nbtn1.onclick = () => table.setHeader(document.getElementById('columnNumber').value)\nbtn2.onclick = () => alert(table.getHeader(document.getElementById('columnNumber').value))\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/image-upload.md",
    "content": "title: Embed images to your spreadsheet using base64\nkeywords: Jexcel, javascript, excel-like, spreadsheet, image upload, base64, embed images\ndescription: This examples shows how to embed and upload images to your spreadsheet.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Embed images into your spreadsheet\n\nThe following examples shows how to embed images into your spreadsheet.\n\n## Load a local image to your table\n\nLoad images from your local machine into your javascript spreadsheet\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet1\"></div>\n\n<script>\nlet data = [\n    ['Purple', 'data:image/jpeg;base64,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'],\n];\n\njexcel(document.getElementById('spreadsheet1'), {\n    data:data,\n    minDimensions: [2,4],\n    columns: [\n        { type:'text', width:300, title:'Title' },\n        { type:'image', width:120, title:'Cover' },\n\n     ]\n});\n</script>\n</html>\n```  \n\n## Embed remote images to your table\n\nAutomatic image rendering from a remote URL using updateTable method\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet2'), {\n    data: [\n        ['https://marketplace.canva.com/MACcZp2p4po/2/0/thumbnail_large/canva-black-white-acoustic-album-cover-MACcZp2p4po.jpg', 'Paul Parker'],\n        ['https://marketplace.canva.com/MACcY55adP4/1/0/thumbnail_large/canva-black-and-white-masculine-acoustic-modern-album-cover-MACcY55adP4.jpg', 'Mark Ellen']\n    ],\n    columns: [\n        { type:'text', width:300, title:'Cover' },\n        { type:'text', width:300, title:'Title' },\n    ],\n    updateTable: function (instance, cell, col, row, val, id) {\n        if (col == 0) {\n            cell.innerHTML = '<img src=\"' + val + '\" style=\"width:100px;height:100px\">';\n        }\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/import-data.md",
    "content": "title: Load data from CSV or JSON or XLSX\nkeywords: Jexcel, javascript, excel-like, spreadsheet, loading data, csv, json, xlsx.\ndescription: How to import data from an external CSV, json file or XLSX.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Create a javascript spreadsheet\n\nThere are a few different ways to load data to your javascript spreadsheet shown in the next four examples below\n\n## Based on a external CSV file\n\nThe example below helps you to create a javascript spreadsheet table based on a remote CSV file, including the headers.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet1\"></div>\n\n<p><button id='download'>Export my spreadsheet as CSV</button></p>\n\n<script>\nmySpreadsheet = jexcel(document.getElementById('spreadsheet1'), {\n    csv:'/jspreadsheet/arts.csv',\n    csvHeaders:true,\n    tableOverflow:true,\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:80 },\n        { type:'dropdown', width:120, source:['England','Wales','Northern Ireland','Scotland'] },\n        { type:'text', width:120 },\n        { type:'text', width:120 },\n     ]\n});\n\ndocument.getElementById('download').onclick = function () {\n    mySpreadsheet.download();\n}\n</script>\n</html>\n```  \n\n## Based on an external JSON file\n\nIn a similar way, you can create a table based on an external JSON file format by using the _url: directive_ as below.\n\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet2'), {\n    url:'/jspreadsheet/test.json',\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:100 },\n     ]\n});\n</script>\n</html>\n```  \n\n## Based on an JSON object\n\nThe data directive can be used to define a JSON object. In this case you can define by the name directive the order of the columns.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet3\"></div>\n\n<script>\n    jexcel(document.getElementById('spreadsheet3'), {\n        data:[\n            {\n                name:'Paulo',\n                id:'3',\n                age:'40',\n                gender:'Male'\n            },\n            {\n                name:'Cosme Sergio',\n                id:'4',\n                age:'48',\n                gender:'Male'\n            },\n            {\n                name:'Jorgina Santos',\n                id:'5',\n                age:'32',\n                gender:'Female'\n            },\n        ],\n        columns: [\n            {\n                type:'text',\n                width:'300',\n                name:'id'\n            },\n            {\n                type:'text',\n                width:'200',\n                name:'name'\n            },\n            {\n                type:'text',\n                width:'100',\n                name:'age'\n            },\n            {\n                type:'hidden',\n                name:'gender'\n            },\n         ]\n    });\n</script>\n</html>\n```  \n\n**NOTE** : This example is based on a customized version of the free version of SheetsJS. There is no guarantee in the use of this library. Please consider purchase their professional version.\n\n  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/jquery.md",
    "content": "title: Jspreadsheet with Jquery\nkeywords: Jexcel, javascript, using jspreadsheet and Jquery\ndescription: A full example on how to integrate Jspreadsheet with Jquery\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Jquery\n\nCreating a jspreadsheet javascript instance using jQuery\n\n### Source code\n\n{.ignore}\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<input type=\"button\" value=\"Add new row\" onclick=\"$('#spreadsheet').jexcel('insertRow')\" />\n\n<script>\nlet options = {\n    minDimensions:[10,10],\n    tableOverflow:true,\n}\n\n$('#spreadsheet').jexcel(options); \n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/lazy-loading.md",
    "content": "title: Dealing with big spreadsheets through lazy loading\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: This example brings a very nice feature to deal with large table datasets.\n\n[Back to Examples](/jspreadsheet/v3/examples)\n\n# Lazy loading\n\nThe following table is dealing with 60.000 columns. The lazy loading method allows render up to 130 rows at the same time and will render other rows based on the scrolling.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    csv:'/tests/demo1.csv',\n    csvHeaders:false,\n    tableOverflow:true,\n    lazyLoading:true,\n    loadingSpin:true,\n    columns: [\n        {\n            type:'text',\n            width:200,\n            title:'Name'\n        },\n        {\n            type:'dropdown',\n            width:100,\n            title:'Age',\n            source:[\n            {\n                id:1,name:'Male'\n            },\n            {\n                id:2,\n                name:'Female'\n            }]\n        },\n        {\n            type:'text',\n            width:200,\n            title:'City'\n        },\n     ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/merged-cells.md",
    "content": "title: How to merge the spreadsheet cells\nkeywords: Jexcel, spreadsheet, javascript, javascript table, merged cells\ndescription: Full example on how to handle merge cells in your javascript tables.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Merged cells\n\nYou can merge cells on your spreadsheet in the table initialization or programmatically as follows:\n\nThe following methods are available for merge cells management: `setMerge`, `getMerge`, `removeMerge`, `destroyMerged`_\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div> <div id=\"console\"></div><br/>\n\n<input type=\"button\" id=\"setMerge\" value=\"Merge cell A3 (colspan: 2, rowspan: 2)\" />\n<input type=\"button\" id=\"removeMerge\" value=\"Destroy merge from A3\" />\n<input type=\"button\" id=\"getAllMerged\" value=\"Get all merged cells\" />\n<input type=\"button\" id=\"destroyMerged\" value=\"Destroy all merged\" />\n<script>\nlet table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    colHeaders: ['Model', 'Year', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'calendar' },\n    ],\n    mergeCells:{\n        A1:[2,1]\n    },\n    minDimensions:[6,6]\n});\n\ndocument.getElementById(\"setMerge\").onclick = () => table.setMerge('A3', 2, 2);\ndocument.getElementById(\"removeMerge\").onclick = () => table.removeMerge('A3');\ndocument.getElementById(\"getAllMerged\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getMerge());\ndocument.getElementById(\"destroyMerged\").onclick = () => table.destroyMerged();\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/meta-information.md",
    "content": "title: Meta information\nkeywords: Javascript spreadsheet, javascript, javascript table, meta information\ndescription: Keep hidden information about your data grid cells using the Jspreadsheet meta information methods\n\n[Back to Examples](/jspreadsheet/v3/examples#more)\n\n# Meta information\n\nThis feature helps you keep import information about the cells hidden from users.\n\nYou can define any meta information during the initialization or programmatically after that thought getMeta or setMeta methods.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n<div id=\"console\"></div>\n\n<script>\nconst table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Apples', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Carrots', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Oranges', 'No', '2018-11-10'],\n        ['BR', 'Coconuts', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        { type: 'dropdown', title: 'Product Origin', width: '300px', source: ['CA', 'BR', 'US', 'UK', 'GB'], autocomplete: true, multiple: true },\n        { type: 'text', title: 'Description', width: '200px' },\n        { type: 'dropdown', title: 'Stock', width: '100px', source: ['No','Yes'] },\n        { type: 'calendar', title: 'Best before', width: '100px' },\n    ],\n    meta:{\n        A1: { myMeta: 'this is just a test', otherMetaInformation: 'other test' },\n        A2: { info: 'test' }\n    }\n});\ndocument.getElementById(\"setForMultiple\").onclick = () => table.setMeta({ C1: { id:'1', y:'2019' }, C2: { id:'2' } });\ndocument.getElementById(\"setForB2\").onclick = () => table.setMeta('B2', 'myMetaData', prompt('myMetaData:'));\ndocument.getElementById(\"getFromA1\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getMeta('A1'));\ndocument.getElementById(\"getAll\").onclick = () =>  document.getElementById('console').innerHTML =JSON.stringify(table.getMeta());\n</script>\n\n<br/>\n<input type=\"button\" id=\"setForMultiple\" value=\"Set meta data for multiple columns\" />\n<input type=\"button\" id=\"setForB2\" value=\"Set a meta information for B2\" />\n<input type=\"button\" id=\"getFromA1\" value=\"Get the meta information from A1\" />\n<input type=\"button\" id=\"getAll\" value=\"Get all meta information\" />\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/programmatically-updates.md",
    "content": "title: Update your table by javascript\nkeywords: Jexcel, javascript, excel-like, spreadsheet, javascript programmatically changes\ndescription: How to update your spreadsheet programmatically via JavaScript.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Programmatically table updates\n\n## Insert, remove and move columns and rows\n\nThe following example shows how to manage data programmatically in your javascript spreadsheet.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet1\"></div><br>\n\n<div>\n    <input type=\"button\" id=\"btn1\" value=\"Insert Blank Column\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn2\" value=\"Insert Five Blank Columns\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn3\" value=\"Insert a new column with pre-populated values at the end of the table\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn4\" value=\"Insert a new blank row at the end of the table\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn5\" value=\"Create ten rows at the end of the table\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn6\" value=\"Delete the first row\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn7\" value=\"Delete the last column\" style=\"margin: 2px\" />\n    <input type=\"button\" id=\"btn8\" value=\"Move the first column to the third position\" style=\"margin: 2px\" />\n</div>\n\n<script>\nlet table1 = jexcel(document.getElementById('spreadsheet1'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    columns: [\n        {\n            title: 'Product',\n            type: 'autocomplete',\n            source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ],\n            width:'300px',\n        },\n        {\n            title: 'Quantity',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Price',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Total',\n            type: 'number',\n            width:'100px',\n        },\n    ],\n    rowResize: true,\n    columnDrag: true,\n});\n\ndocument.getElementById(\"btn1\").onclick = () => table1.insertColumn();\ndocument.getElementById(\"btn2\").onclick = () => table1.insertColumn(5, 0, 1, null);\ndocument.getElementById(\"btn3\").onclick = () => table1.insertColumn([ '0.99', '1.22', '3.11', '2.21' ]);\ndocument.getElementById(\"btn4\").onclick = () => table1.insertRow();\ndocument.getElementById(\"btn5\").onclick = () => table1.insertRow(10);\ndocument.getElementById(\"btn6\").onclick = () => table1.deleteRow(0, 1);\ndocument.getElementById(\"btn7\").onclick = () => table1.deleteColumn();\ndocument.getElementById(\"btn8\").onclick = () => table1.moveColumn(0, 2);\n</script>\n\n</html>\n```  \n\n## Updating column width and row height\n\nUpdate the table width and height properties.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\nlet table2 = jexcel(document.getElementById('spreadsheet2'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    colHeaders: [ 'Product', 'Quantity', 'Price', 'Total' ],\n    colWidths: [ 300, 100, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ] },\n        { type: 'number' },\n        { type: 'number' },\n        { type: 'number' },\n    ],\n    rowResize:true,\n});\n\ndocument.getElementById(\"setWidth\").onclick = () => table2.setWidth(document.getElementById('columnNumber').value, 200);\ndocument.getElementById(\"setHeight\").onclick = () => table2.setHeight(0, 100);\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n\n<input type='button' id=\"setWidth\" value='Set column width to 200px' />\n<input type='button' id=\"setHeight\" value='Set first row to height 100px' />\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/react.md",
    "content": "title: Jspreadsheet with React\nkeywords: Jexcel, javascript, using Jspreadsheet and react\ndescription: A full example on how to integrate Jspreadsheet with React\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# The Javascript spreadsheet with React\n\nIntegrating Jspreadsheet with React\n\n[React with jspreadsheet sample project](https://codesandbox.io/s/jexcel-and-react-hmx0k)\n\n### Source code\n\n{.ignore}\n```javascript\nclass Jspreadsheet extends React.Component {\n        constructor(props) {\n            super(props);\n            this.options = props.options;\n        }\n    \n        componentDidMount = function() {\n            this.el = jexcel(ReactDOM.findDOMNode(this).children[0], this.options);\n        }\n    \n        addRow = function() {\n            this.el.insertRow();\n        }\n    \n        render() {\n            return (\n                <div>\n                    <div></div><br/><br/>\n                    <input type='button' value='Add new row' onClick={() => this.addRow()}></input>\n                </div>\n            );\n        }\n    }\n    \n    var options = {\n        data:[[]],\n        minDimensions:[10,10],\n    };\n    \n    ReactDOM.render(<Jspreadsheet options={options} />, document.getElementById('spreadsheet'))\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/readonly.md",
    "content": "title: Readonly Columns\nkeywords: Jexcel, spreadsheet, javascript, javascript table, readonly\ndescription: Example how to set up readonly cells on your spreadsheets. \n\n[Back to Examples](/jspreadsheet/v3/examples)\n\n# Readonly columns and cells\n\nSetting a readonly the whole column or a single specific cell.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, 1],\n        ['Peugeot', 2010, 5000, 1],\n        ['Honda Fit', 2009, 3000, 1],\n        ['Honda CRV', 2010, 6000, 0],\n    ],\n    columns: [\n        {\n            type: 'text',\n            title:'Description',\n            width:'200px',\n            readOnly:true,\n        },\n        {\n            type: 'text',\n            title:'Year',\n            width:'200px'\n        },\n        {\n            type: 'text',\n            title:'Price',\n            width:'100px',\n            mask:'#.##',\n        },\n        {\n            type: 'checkbox',\n            title:'Automatic',\n            width:'100px'\n        },\n    ],\n    updateTable: function(el, cell, x, y, source, value, id) {\n        if (x == 2 && y == 2) {\n            cell.classList.add('readonly');\n        }\n    } \n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/sorting.md",
    "content": "title: Sorting the spreadsheet columns\nkeywords: Jexcel, spreadsheet, javascript, javascript table, sorting\ndescription: Example how to sort the table by a column via javascript.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Sorting your table\n\n## Simple example\n\nYou can sort your javascript table by double a double click in the header, using the context menu or by javascript as follow:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns: [\n        { type: 'text', width:300 },\n        { type: 'text', width:80 },\n        { type: 'text', width:100 },\n        { type: 'calendar', width:100 },\n    ]\n});\ndocument.getElementById(\"orderBy\").onclick = () => table.orderBy(document.getElementById('columnNumber').value);\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n<input type='button' value='Sort By Column' id=\"orderBy\" />\n\n</html>\n```  \n\n## Disable the table sorting\n\nThe ordering is a native enabled feature. To disable that feature please use the columnSorting:false, directive in the initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns: [\n        { type: 'text', tile:'Model', width:300 },\n        { type: 'text', tile:'Year', width:100 },\n        { type: 'text', tile:'Price', width:100 },\n        { type: 'text', tile:'Date', width:100 },\n    ],\n    columnSorting:false,\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/spreadsheet-formulas.md",
    "content": "title: Basic to advance use of formulas\nkeywords: Jexcel, javascript, excel-like, spreadsheet, formulas, currency, calculations\ndescription: Unleash the power of your tables bringing formulas and custom javascript methods on your Jspreadsheet - the online spreadsheet.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Adding formulas on your online spreadsheet\n\n## Simple spreadsheet-like formula usage\n\nThe example below shows how to use spreadsheet like formulas on your javascript table spreadsheet.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Crayons Crayola only (No Rose Art)', 2, '5.01', '=B1*C1' ],\n        [ 'Colored Pencils Crayola only', 2, '4.41', '=B2*C2' ],\n        [ 'Expo Dry-erase Markers Wide', 4, '3.00', '=B3*C3' ],\n        [ 'Index Cards Unlined', 3, '6.00', '=B4*C4' ],\n        [ 'Tissues', 10, '1.90', '=B5*C5' ],\n        [ 'Ziploc Sandwich-size Bags', 5, '1.00', '=B6*C6' ],\n        [ 'Thin Markers Crayola only', 2, '3.00', '=B7*C7' ],\n        [ 'Highlighter', 4, '1.20', '=B8*C8' ],\n        [ 'Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '=SUM(D1:D8)' ],\n    ],\n    columns: [\n        { type: 'text', title:'Product', width:'300' },\n        { type: 'text', title:'Qtd', width:'80' },\n        { type: 'text', title:'Price', width:'100', mask:'#.##,00', decimal:',' },\n        { type: 'text', title:'Total', width:'100' },\n    ],\n    updateTable:function(instance, cell, col, row, val, label, cellName) {\n        if (cell.innerHTML == 'Total') {\n            cell.parentNode.style.backgroundColor = '#fffaa3';\n        }\n\n        if (col == 3) {\n            if (parseFloat(label) > 10) {\n                cell.style.color = 'red';\n            }  else {\n                cell.style.color = 'green';\n            }\n        }\n    },\n    columnSorting:false,\n});\n</script>\n</html>\n```  \n\n## Creating Custom formulas\n\nYou can declare custom javascript methods and use in your tables as the example below.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n\n<div id=\"spreadsheet1\"></div>\n\n<script>\n    \nconst COLORIZE = function(v) {\n    v = '<span style=\"color:' + v + '\">' + v.toUpperCase() + '</span>';\n    return v;\n}\n\nformula.setFormula({ COLORIZE })\n\njexcel(document.getElementById('spreadsheet1'), {\n    data: [\n        [ 'red', '=COLORIZE(A1)' ],\n        [ 'green', '=COLORIZE(A2)' ],\n        [ 'blue', '=COLORIZE(A3)' ],\n    ],\n    columns: [\n        { type: 'text', width:'300' },\n        { type: 'text', width:'200' },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/spreadsheet-toolbars.md",
    "content": "title: Enable and customize the toolbar on your spreadsheet\nkeywords: Jexcel, javascript, vanilla javascript, excel-like, spreadsheet, datatables, data, table, toolbars\ndescription: Full example on how to enable nor customize your javascript spreadsheet toolbar.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Custom toolbars\n\nThe following example shows how to include and customize a toolbar in your javascript spreadsheet.\n\n## Instructions\n\nThe toolbar can be customized with a few parameters. \n\n|                      |                                                                                                                                                      |\n|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| \n| **type**             | could be **i** for icon, **select** for a dropdown, **color** or a color picker.                                                                     |\n| **content**{.nowrap} | defines the icon (from material icons) when you use type: i; [click here for all possible keys](https://material.io/tools/icons/)                    |\n| **k**                | means the style should be apply to the cell;                                                                                                         |\n| **v**                | means the value of the style should be apply to the cell; When type:select, you can define an array of possibles values;                             |\n| **onclick**          | can be used together with type:i to implement any custom method. The method will receive the jspreadsheet instance and all marked cells by default.  |\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\ndata = [\n    ['Canada', 'Cheese', 1],\n    ['Japan', 'Apples', 0],\n    ['United States', 'Carrots', 1],\n    ['China', 'Oranges', 0],\n];\n\ntable = jexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        {\n            title: 'Country',\n            type: 'autocomplete',\n            source: ['Brazil','Canada','China','Japan','United States','United Kingdom'],\n            width: '300px',\n        },\n        {\n            title: 'Food',\n            type: 'dropdown',\n            source:['Apples','Bananas','Carrots','Oranges','Cheese'],\n            width: '150px',\n        },\n        {\n            title: 'Stock',\n            type: 'checkbox',\n            width: '100px',\n        },\n    ],\n    toolbar:[\n        {\n            type: 'i',\n            content: 'undo',\n            onclick: function() {\n                table.undo();\n            }\n        },\n        {\n            type: 'i',\n            content: 'redo',\n            onclick: function() {\n                table.redo();\n            }\n        },\n        {\n            type: 'i',\n            content: 'save',\n            onclick: function () {\n                table.download();\n            }\n        },\n        {\n            type: 'select',\n            k: 'font-family',\n            v: ['Arial','Verdana']\n        },\n        {\n            type: 'select',\n            k: 'font-size',\n            v: ['9px','10px','11px','12px','13px','14px','15px','16px','17px','18px','19px','20px']\n        },\n        {\n            type: 'i',\n            content: 'format_align_left',\n            k: 'text-align',\n            v: 'left'\n        },\n        {\n            type:'i',\n            content:'format_align_center',\n            k:'text-align',\n            v:'center'\n        },\n        {\n            type: 'i',\n            content: 'format_align_right', \n            k: 'text-align',\n            v: 'right'\n        },\n        {\n            type: 'i',\n            content: 'format_bold',\n            k: 'font-weight',\n            v: 'bold'\n        },\n        {\n            type: 'color',\n            content: 'format_color_text',\n            k: 'color'\n        },\n        {\n            type: 'color',\n            content: 'format_color_fill',\n            k: 'background-color'\n        },\n    ],\n});\n</script>\n</html>\n```\n\n**NOTE:** You need to include the material icons style sheet.\n\n{.ignore}\n```html\n<link rel=\"stylesheet\" type=\"text/css\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n```"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/table-overflow.md",
    "content": "title: Table overflow with Jspreadshee Version 3\nkeywords: Jexcel, javascript, javascript vanilla, javascript, table, table overflow\ndescription: How define a fixed width and height for the Jspreadsheet grids.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Table overflow\n\nDefine width and height for your Jspreadsheet table\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n        minDimensions: [10, 20],\n        defaultColWidth: 100,\n        tableOverflow: true,\n        tableWidth: \"600px\",\n        columns: [\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          }\n        ]\n    });\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/table-scripting.md",
    "content": "title: Customize the spreadsheet via javascript\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table scripting\ndescription: Customize the table behavior using javascript\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Table scripting and customizations\n\nCustomize the table behavior though javascript.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<div id=\"spreasheet2\"></div>\n\n<script>\n\nlet table2 = jexcel(document.getElementById('spreasheet2'), {\n    data: [\n        ['BR', 'Cheese', 1, 3.99],\n        ['CA', 'Apples', 0, 1.00],\n        ['US', 'Carrots', 1, 0.90],\n        ['GB', 'Oranges', 0, 1.20],\n        ['CH', 'Chocolats', 1, 0.40],\n        ['AR', 'Apples', 1, 1.10],\n        ['AR', 'Bananas', 1, 0.30],\n        ['BR', 'Oranges', 1, 0.95],\n        ['BR', 'Pears', 1, 0.90],\n        ['', '', '', '=ROUND(SUM(D1:D8), 2)'],\n    ],\n    columnSorting:false,\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '250',\n            source: ['BR', 'CA', 'US', 'UK', 'AR', 'CH', 'GB']\n        },\n        {\n            type: 'autocomplete',\n            title:'Food',\n            width:'150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese','Kiwi','Chocolats','Pears']\n        },\n        {\n            type: 'checkbox',\n            title:'Stock',\n            width:'100'\n        },\n        {\n            type: 'number',\n            title:'Price',\n            width:'100'\n        },\n    ],\n    updateTable:function(instance, cell, col, row, val, label, cellName) {\n        // Number formating\n        if (col == 3) {\n            // Get text\n            txt = cell.innerText;\n            // Format text and render\n            cell.innerHTML = jSuites.mask.render(txt, { mask: '$ 0.00' });\n        }\n\n        // Odd row colours\n        if (row % 2) {\n            cell.style.backgroundColor = '#edf3ff';\n        }\n\n        // Total row\n        if (row == 9) {\n            if (col < 3) {\n                cell.innerHTML = '';\n            } \n\n            if (col == 2) {\n                cell.innerHTML = 'Total';\n                cell.style.fontWeight = 'bold';\n            }\n\n            cell.className = '';\n            cell.style.backgroundColor = '#f46e42';\n            cell.style.color = '#ffffff';\n        }\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/table-style.md",
    "content": "title: Customize the spreadsheet style CSS\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table style, css\ndescription: Bring a very special touch to your applications customizing your javascript spreadsheet.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Custom javascript spreadsheet style\n\n## How to apply style to your table\n\nYou can define the CSS for specific columns during the initialization, or through programmatically javascript calls.\n\nBut, after the initialization is still possible to manage the cell style programmatically using the method getStyle or setStyle.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div><br/>\n\n<p><textarea id='console' style='width:100%;max-width:600px;height:100px;'></textarea></p>\n\n<input type=\"button\" id=\"setYellow\" value=\"Set A2 background\" />\n<input type=\"button\" id=\"setStyle\" value=\"Change A3, B3, C3, A4 style\" />\n<input type=\"button\" id=\"getA1Style\" value=\"Get A1 style\" />\n<input type=\"button\" id=\"getTableStyle\" value=\"Get the table style\" />\n\n<script>\nlet table = jexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n        ['BR', 'Oranges', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        {\n            type: 'dropdown',\n            title: 'Product Origin',\n            width: 300,\n            source: ['US', 'CA', 'BR', 'UK'],\n            autocomplete: true,\n            multiple: true\n        },\n        {\n            type: 'text',\n            title: 'Description',\n            width: 200\n        },\n        {\n            type: 'dropdown',\n            title: 'Stock',\n            width: 100 ,\n            source: ['No','Yes']\n        },\n        {\n            type: 'calendar',\n            title: 'Best before',\n            width: 100\n        },\n    ],\n    style: {\n        A1:'background-color: orange;',\n        B1:'background-color: orange;',\n    },\n});\n\ndocument.getElementById(\"setYellow\").onclick = () => table.setStyle('A2', 'background-color', 'yellow');\ndocument.getElementById(\"setStyle\").onclick = () => table.setStyle({ A3:'font-weight: bold; color:red;', B3:'background-color: yellow;', C3:'text-decoration: underline;', A4:'text-align:left;' });\ndocument.getElementById(\"getA1Style\").onclick = () => document.getElementById('console').innerHTML = table.getStyle('A1');\ndocument.getElementById(\"getTableStyle\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getStyle());\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/translations.md",
    "content": "title: Jspreadsheet Translations\nkeywords: Jexcel, spreadsheet, javascript, javascript table, translate, translations\ndescription: How to translate the default Jspreadsheet text and controls.\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# Internationalization\n\nHow to update the default texts from Jspreadsheet.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns:[\n        { type:'text', width:300 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n        { type:'calendar', width:100 },\n    ],\n    text:{\n        noRecordsFound:'Nenhum registro encontrado',\n        showingPage:'Mostrando página {0} de {1} entradas',\n        show:'Show',\n        entries:'entradas',\n        insertANewColumnBefore:'Inserir uma nova coluna antes de',\n        insertANewColumnAfter:'Inserir uma nova coluna depois de',\n        deleteSelectedColumns:'Excluir colunas selecionadas',\n        renameThisColumn:'Renomear esta coluna',\n        orderAscending:'ordem ascendente',\n        orderDescending:'Order decrescente',\n        insertANewRowBefore:'Inserir uma nova linha antes de',\n        insertANewRowAfter:'Inserir uma nova linha depois de',\n        deleteSelectedRows:'Excluir linhas selecionadas',\n        editComments:'Editar comentários',\n        addComments:'Adicionar comentários',\n        comments:'Comentários',\n        clearComments:'Limpar comentários',\n        copy:'Copiar ...',\n        paste:'Colar ...',\n        saveAs:'Salvar como ...',\n        areYouSureToDeleteTheSelectedRows:'Tem certeza de excluir as linhas selecionadas?',\n        areYouSureToDeleteTheSelectedColumns:'Tem certeza de excluir as colunas selecionadas?',\n        thisActionWillDestroyAnyExistingMergedCellsAreYouSure:'Esta ação irá destruir todas as células mescladas existentes. Você tem certeza?',\n        thisActionWillClearYourSearchResultsAreYouSure:'Esta ação limpará seus resultados de pesquisa. Você tem certeza?',\n        thereIsAConflictWithAnotherMergedCell:'Há um conflito com outra célula mesclada',\n        invalidMergeProperties:'Propriedades mescladas inválidas',\n        cellAlreadyMerged:'Cell já mesclado',\n        noCellsSelected:'Nenhuma célula selecionada',\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples/vue.md",
    "content": "title: Jspreadsheet with Vue\nkeywords: Jexcel, javascript, using Jspreadsheet and Vue\ndescription: A full example on how to integrate Jspreadsheet with Vue\n\n[Back to Examples](/jspreadsheet/v3/examples \"Back to the examples section\")\n\n# The Javascript spreadsheet with Vue\n\nIntegrating jspreadsheet with Vue\n\n[See a full example on codesandbox](https://codesandbox.io/embed/vue-default-template-p4hwn)\n\n[Get a source code of a sample Vue project](https://github.com/jspreadsheet/jexcel-with-vue)  \n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v3/jexcel.js\"></script>\n<script src=\"https://jsuites.net/v3/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v3/jexcel.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v3/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div><br>\n\n<input type=\"button\" value=\"Add new row\" id=\"addRow\" />\n\n<script>\nlet options = {\n    data:[[]],\n    minDimensions:[10,10],\n}\n\nlet vm = new Vue({\n    el: '#spreadsheet',\n    mounted: function() {\n        let spreadsheet = jspreadsheet(this.$el, options);\n        Object.assign(this, spreadsheet);\n    }\n}); \n\ndocument.getElementById(\"addRow\").onclick = () => vm.insertRow()\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v3/examples.md",
    "content": "title: Examples\nkeywords: Jexcel, javascript, examples\ndescription: Examples how to create web based spreadsheets using Jspreadsheet.\n\nJspreadsheet v3 Examples\n========================\n\nFor us, the best way to learn is via examples. We bring in this section various examples from basic to advance applications.\n\n* [React Implementation](/jspreadsheet/v3/examples/react \"Jspreadsheet with React\")  \n  A full example on how to integrate Jspreadsheet with React\n\n* [VUE Implementation](/jspreadsheet/v3/examples/vue \"Jspreadsheet with Vue\")  \n  A full example on how to integrate Jspreadsheet with Vue\n\n* [Angular Implementation](/jspreadsheet/v3/examples/angular \"Jexcel with Angular\")  \n  A full example on how to integrate Jspreadsheet with Angular\n\n* [Jquery Implementation](/jspreadsheet/v3/examples/jquery \"Jspreadsheet with Jquery\")  \n  A full example on how to integrate Jspreadsheet with Jquery\n\n* [Search and pagination](/jspreadsheet/v3/examples/datatables \"Searchable databatable\")  \n  Full spreadsheet example with search and pagination to bring great compatibility for those who love datatables.\n\n* [Column types](/jspreadsheet/v3/examples/column-types \"Column types\")  \n  Learn more about the powerful column types. This example brings all native column types and how to create your own custom type.\n\n* [Advanced dropdown](/jspreadsheet/v3/examples/dropdown-and-autocomplete \"Advanced dropdown column type\")  \n  Full examples on how to handle simple, advanced, multiple, autocomplete and conditional dropdowns. Create amazing javascript tables using categories and images in your dropdowns.\n\n* [Date and datetime picker](/jspreadsheet/v3/examples/date-and-datetime-picker \"Calendar with date and datetime picker\")  \n  Example from basic to advanced calendar usage, date and datetime picker\n\n* [Images](/jspreadsheet/v3/examples/image-upload \"Embed images to your spreadsheet using base64\")  \n  This examples shows how to embed and upload images to your spreadsheet\n\n* [Programmatically updates](/jspreadsheet/v3/examples/programmatically-updates \"Update your table by javascript\")  \n  How to update your spreadsheet and its data by javascript\n\n* [Table Style](/jspreadsheet/v3/examples/table-style \"Customize the spreasheet style CSS\")  \n  Bring a very special touch to your applications customizing your javascript spreadsheet.\n\n* [Table Scripting](/jspreadsheet/v3/examples/table-scripting \"Customize the spreasheet via javascript\")  \n  Customize the table behavior using javascript\n\n* [Events](/jspreadsheet/v3/examples/events \"Handling events on Jspreadsheet\")  \n  Learn how to handle events on Jspreadsheet\n\n* [Importing data](/jspreadsheet/v3/examples/import-data \"Load data from CSV or JSON or XLSX\")  \n  How to import data from an external CSV, json file or XLSX.\n\n* [Formulas](/jspreadsheet/v3/examples/spreadsheet-formulas \"Basic to advance use of formulas\")  \n  Unleash the power of your tables bringing formulas and custom javascript methods on your Jspreadsheet - the online spreadsheet.\n\n* [Custom toolbars](/jspreadsheet/v3/examples/spreadsheet-toolbars \"Enable and customize the toolbar on your spreadsheet\")  \n  Full example on how to enable nor customize your javascript spreadsheet toolbar.\n\n* [Column comments](/jspreadsheet/v3/examples/comments \"Allow comments in your javascript table\")  \n  Allow comments in your table spreadsheet.\n\n* [Headers](/jspreadsheet/v3/examples/headers \"Nested headers and column header updates\")  \n  Enabled nested headers in your spreadsheet and learn how to set or get header values\n\n* [Translations](/jspreadsheet/v3/examples/translations \"How to translate the default messages from Jspreadsheet\")  \n  How to translate the default messages from Jspreadsheet.\n\n* [Meta information](/jspreadsheet/v3/examples/meta-information \"Meta information\")  \n  Keep hidden information about your cells using meta information methods\n\n* [Merged cells](/jspreadsheet/v3/examples/merged-cells \"How to merge the spreadsheet cells\")  \n  Full example on how to handle merge cells in your javascript tables.\n\n* [Sorting columns](/jspreadsheet/v3/examples/sorting \"Sorting the spreadsheet columns\")  \n  Example how to sort the table by a column via javascript.\n\n* [Readonly Columns](/jspreadsheet/v3/examples/readonly \"Readonly Columns\")  \n  Example how to setup readonly cells\n\n* [Lazy loading](/jspreadsheet/v3/examples/lazy-loading \"Dealing with big spreadsheets through lazy loading\")  \n  This example brings a very nice feature to deal with large table datasets.\n\n* [Custom Contextmenu](/jspreadsheet/v3/examples/contextmenu \"Custom contextmenu\")  \n  How to customize Jspreadsheet contextmenu\n\n* [Table overflow](/jspreadsheet/v3/examples/table-overflow \"Table overflow\")  \n  How define a fixed width and height for the Jspreadsheet grids."
  },
  {
    "path": "docs/jspreadsheet/v3/getting-started.md",
    "content": "title: Getting Started with Jspreadsheet CE\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jspreadsheet CE.\n\n[Back to Documentation](/jspreadsheet/v3/docs)\n\n# Getting started\n\nJspreadsheet is a vanilla javascript plugin to embed a online spreadsheet in your web based applications. Bring highly dynamic datasets to your application and improve the user experience of your software.\n\n```bash\nnpm install jspreadsheet-ce\n```\n\nDownload from our github page: <https://github.com/jspreadsheet/ce>\n\n\n## Initialization\n\nYou can initiate a Jspreadsheet table including data from a HTML table, a JS array, a CSV or a JSON file, following the examples below:\n\n### Loading from a javascript array\n\n{.ignore}\n```html\n<div id='my-spreadsheet'></div>\n\n<script>\ndata = [\n    ['Mazda', 2001, 2000],\n    ['Peugeot', 2010, 5000],\n    ['Honda Fit', 2009, 3000],\n    ['Honda CRV', 2010, 6000],\n];\n\njexcel(document.getElementById('my-spreadsheet'), {\n    data:data,\n    columns:[\n        { title:'Model', width:300 },\n        { title:'Price', width:80 },\n        { title:'Model', width:100 }\n    ]\n});\n</script>\n```  \n\n### Loading from a JSON file\n\n{.ignore}\n```html\n<div id='my-spreadsheet'></div>\n\n<script>\njexcel(document.getElementById('my-spreadsheet'), {\n    url:'data.json',\n    columns:[\n        { title:'Model', width:300 },\n        { title:'Price', width:80 },\n        { title:'Model', width:100 }\n    ]\n});\n</script>\n```  \n\n### Loading from a CSV file\n\n{.ignore}\n```html\n<div id='my-spreadsheet'></div>\n\n<script>\njexcel(document.getElementById('my-spreadsheet'), {\n    csv:'/tests/demo.csv',\n    csvHeaders:true,\n    columns:[\n        { width:300 },\n        { width:80 },\n        { width:100 }\n    ]\n});\n</script>\n```\n\n[See a working example](/jspreadsheet/v3/examples/import-data)\n\n  \n\n## Destroying a table\n\nYou can destroy the table, all data and events related to an existing table by using the method _destroy_ as shown below.\n\n{.ignore}\n```html\n<script>\nlet table = jexcel(document.getElementById('my-spreadsheet'), {\n    csv:'/tests/demo.csv',\n    csvHeaders:true,\n    columns:[\n        { width:300 },\n        { width:80 },\n        { width:100 }\n    ]\n});\n\n// If second argument is true will destroy all handlers and you can't create any other instance.\njexcel.destroy(document.getElementById('my-spreadsheet'), true);\n</script>\n```\n\n## Header titles\n\nIf you do not define the column title, the default will be a letter starting in A just as any other spreadsheet software. But, if you would like to have custom column names you can use the directive title as in the example below:\n\n{.ignore}\n```html\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns:[\n        { title:'Model' },\n        { title:'Price' },\n        { title:'Model' }\n    ]\n});\n</script>\n```  \n\n### Headers from a CSV file\n\nIf you are loading your data from a CSV file, you can define the **csvHeader:true**, so the first row will be used as your column names.\n\n[See a working example](/jspreadsheet/v3/examples/import-data)\n\n  \n\n### Programmatically header updates\n\nThe methods **setHeader()** , **getHeader()** and **getHeaders()** are available for the developer to interact programmatically with the spreadsheet.\n\n[Working example](/jspreadsheet/v3/examples/headers#Programmatically-header-updates)\n\n  \n\n### Nested headers\n\nThe nested headers area available in the innitialization through the directive **nestedHeaders:[]**, and should be use follow:\n\n{.ignore}\n```html\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { type: 'autocomplete', title:'Country', width:'300', url:'/jexcel/countries' },\n        { type: 'dropdown', title:'Food', width:'150', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox', title:'Stock', width:'100' },\n    ],\n    nestedHeaders:[\n        [\n            { title:'Supermarket information', colspan:'3' },\n        ],\n        [\n            { title:'Location', colspan:'1' },\n            { title:' Other Information', colspan:'2' }\n        ],\n    ],\n});\n```\n\n[See this example in action](/jspreadsheet/v3/examples/headers)\n\n## Column width\n\nThe inital width can be defined in the width property in the column parameter.\n\n{.ignore}\n```html\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns:[\n        { title:'Model', width:300 },\n        { title:'Price', width:80 },\n        { title:'Model', width:100 }\n    ]\n});\n</script>\n```  \n\n### Programmatically column width updates\n\nThe methods setWidth(), getWidth() are available for the developer to update the column width via javascript.\n\n[See this example in action](/jspreadsheet/v3/examples/programmatically-updates#setWidth)\n\n## Row height\n\nThe inital row height can be defined in the height property include in the rows directive. It is also possible to enabled a resizeble row by using rowResize: true in the initialization.\n\n{.ignore}\n```html\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    rows:{ 3: { height:'500px' }},\n    rowResize: true,\n});\n</script>\n```  \n\n### Programmatically row height updates\n\nThe methods setHeight(), getHeight() are available for the developer to update the row height via javascript.\n\n[See this example in action](/jspreadsheet/v3/examples/programmatically-updates#setHeight)\n\n## Column types\n\nJspreadsheet has available some extra native column types in addition to the default input text. It means you have extended nice responsive ways to get data into your spreadsheet. In addition to that is available integration methods to facilitate you to bring any custom column to your tables. This makes the Jspreadsheet plugin a very flexible tool to enhance the user experience of your applications.\n\nJspreadsheet is integrate with jSuites, so it brings some native columns, such as: _**text, numeric, hidden, dropdown, autocomplete, checkbox, radio, calendar, image and color.**_\n\n{.ignore}\n```html\n<script>\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { title:'Model', width:300, type:'text'; },\n        { title:'Price', width:80, type:'numeric' },\n        { title:'Date', width:100, type:'calendar', options: { format:'DD/MM/YYYY' } },\n        { title:'Photo', width:150, type:'image' },\n        { title:'Condition', width:150, type:'dropdown', source:['New','Used'] },\n        { title:'Color', width:80, type:'color' },\n        { title:'Available', width:80, type:'checkbox' },\n    ]\n});\n</script>\n```\n\n### Calendar type\n\nWhen using the calendar column, you can change the behavior behavior of your calendar by sending some extra options as example above. The possible values are:\n\n```javascript\n{\n    options: {\n        // Date format\n        format:'DD/MM/YYYY',\n        // Allow keyboard date entry\n        readonly:0,\n        // Today is default\n        today:0,\n        // Show timepicker\n        time:0,\n        // Show the reset button\n        resetButton:true,\n        // Placeholder\n        placeholder:'',\n        // Translations can be done here\n        months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday', 'Friday','Saturday'],\n        weekdays_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'],\n        // Value\n        value:null,\n        // Events\n        onclose:null,\n        onchange:null,\n        // Fullscreen (this is automatic set for screensize < 800)\n        fullscreen:false,\n    }\n}\n```\n\n[See a working example](/jspreadsheet/v3/examples/date-and-datetime-picker)\n\n  \n\n### Dropdown and autocomplete type\n\nThere are different ways to work with dropdowns using Jspreadsheet. It is possible to define the parameter _source_ as a simple or key-value array. It is also possible to use the param _url_ to populate your dropdown from an external json format source. In addition to that it is possible to have conditional values. Basically, the values from one dropdown can be conditional to other dropdowns in your table.\n\nYou can set the autocomplete dropdown through the initial param _autocomplete:true_ and the multiple picker can be activate by _multiple:true_ property as shown in the following example:\n\n{.ignore}\n```html\n<script>\nlet data = [\n    ['Honda', 1, 'Civic', '4'],\n    ['Peugeot', 3,'1007', '2'],\n    ['Smart', 3,'Cabrio', '4;5'],\n];\n\njexcel(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        {\n            type:'dropdown',\n            title:'Region',\n            source:['South East','South West','North','London'],\n            width:'200',\n        },\n        {\n            type:'dropdown',\n            title:'Available in',\n            multiple:true,\n            source:[{id:1, name:'Red'},{id:2, name:'Yellow'},{id:3,name:'Blue'}],\n            width:'200',\n        },\n        {\n            type:'autocomplete',\n            title:'Region',\n            url:'values.json',\n            width:'200',\n        },\n    ]\n});\n</script>\n```\n\n[See a working example](/jspreadsheet/v3/examples/dropdown-and-autocomplete)\n\n### Custom type\n\nJspreadsheet makes possible to extend third party javascript plugins to create your custom columns. Basically to use this feature, you should implement some basic methods such as: openEditor, closeEditor, getValue, setValue as following.\n\n{.ignore}\n```html\n<script>\nlet data2 = [\n    ['PHP', '14:00'],\n    ['Javascript', '16:30'],\n];\n\nvar customColumn = {\n    // Methods\n    closeEditor : function(cell, save) {\n        var value = cell.children[0].value;\n        cell.innerHTML = value;\n        return value;\n    },\n    openEditor : function(cell) {\n        // Create input\n        var element = document.createElement('input');\n        element.value = cell.innerHTML;\n        // Update cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(element);\n        $(element).clockpicker({\n            afterHide:function() {\n                setTimeout(function() {\n                    // To avoid double call\n                    if (cell.children[0]) {\n                        spreadsheet.closeEditor(cell, true);\n                    }\n                });\n            }\n        });\n        // Focus on the element\n        element.focus();\n    },\n    getValue : function(cell) {\n        return cell.innerHTML;\n    },\n    setValue : function(cell, value) {\n        cell.innerHTML = value;\n    }\n}\n\nspreadsheet = jexcel(document.getElementById('custom'), {\n    data:data2,\n    columns: [\n        { type: 'text', title:'Course Title', width:300 },\n        { type: 'text', title:'Time', width:100,editor:customColumn },\n     ]\n});\n</script>\n```\n\n[See a working example](/jspreadsheet/v3/examples/column-types#custom)\n\n  \n  \n\n## Define a minimum table dimension size.\n\nThe follow example will create a data table with a minimum number of ten columns and five rows:\n\n{.ignore}\n```html\n<script>\nlet data3 = [\n    ['Mazda', 2001, 2000],\n    ['Peugeot', 2010, 5000],\n    ['Honda Fit', 2009, 3000],\n    ['Honda CRV', 2010, 6000],\n];\n\njexcel(document.getElementById('minExample'), {\n    data:data3,\n    minDimensions:[10,5],\n});\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/cases/data-persistence.md",
    "content": "title: Data persistence\nkeywords: Jexcel, javascript, cases, data persistence, database synchronization\ndescription: A backend data persistence example using Jspreadsheet.\n\n[Back to Use Cases](/jspreadsheet/v4/cases \"Back to the use cases section\")\n\n# Data persistence\n\nWith the persistence directive, each change in the data will be sent to a remote URL.  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nvar mySpreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {\n    url:'/jspreadsheet/books.json',\n    columns: [\n        {\n            type: 'text',\n            width: '50px',\n            title: 'Code',\n            name: 'id',\n            readOnly: true,\n            primaryKey: true\n        },\n        {\n            type: 'text',\n            width: '80px',\n            title:'Image',\n            name:'thumbnailUrl'\n        },\n        {\n            type: 'text',\n            width: '200px',\n            title:'Title',\n            name:'title'\n        },\n        {\n            type: 'text',\n            width: '55px',\n            title:'Pages',\n            name:'pageCount'\n        },\n        {\n            type: 'calendar',\n            width: '90px',\n            title:'Published',\n            name: 'publishedDate'\n        },\n        {\n            type: 'text',\n            width: '200px',\n            title:'Author',\n            name:'authors'\n        },\n        {\n            type: 'dropdown',\n            width: '180px',\n            title:'Categories',\n            name:'categories',\n            source:['Internet','Web Development', 'Java', 'Mobile', 'Open Source'],\n            multiple:true\n        },\n     ],\n     allowComments:true,\n     persistence: '/jspreadsheet/v4/save',\n     updateTable: function(instance, cell, col, row, val, label, cellName) {\n         if (col == 1) {\n             if (! val) {\n                 cell.innerHTML = '<img src=\"https://images-na.ssl-images-amazon.com/images/I/21%2Bwfxx2lyL._SX319_BO1,204,203,200_.jpg\" style=\"width:30px;\">';\n             } else {\n                 cell.innerHTML = '<img src=\"' + val + '\" style=\"width:30px;\">';\n             }\n         }\n\n         cell.style.overflow = 'hidden';\n     },\n     onevent:function() {\n         console.log(arguments);\n     }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/cases/food-store.md",
    "content": "title: Cases\nkeywords: Jexcel, javascript, cases, food store\ndescription: A food store inventory using Jspreadsheet.\n\n# Grocery Store\n\nA simple example including table scripting to perform a photo update and a progress bar added to any new task\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet data = [\n    [ 'data:image/jpeg;base64,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', 'Vegetables', 'Carrots', '2019-02-12', '14.00', '4', '=RATING(F1)' ],\n    [ 'data:image/jpeg;base64,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', 'Fruits', 'Apple', '2019-03-01', '45.00', '5', '=RATING(F2)' ],\n    [ 'data:image/jpeg;base64,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', 'Fruits', 'Pinapples', '2018-11-10', '30.00', '4', '=RATING(F3)' ],\n    [ 'data:image/jpeg;base64,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', 'Fruits', 'Plums', '2019-01-12', '14.00', '4', '=RATING(F4)' ],\n];\n\n\n// A custom method to create RATING stars html based on a number\nlet RATING = function(rating) {\n    let fullStar = `<span style=\"color: yellow; font-size: 15px;\">&#9733;</span>`;  // Filled star\n    let emptyStar = `<span style=\"color: gray; font-size: 15px;\">&#9733;</span>`;   // Empty star\n\n    let starRatingHtml = '';\n\n    // Add filled stars\n    for (let i = 0; i < Math.floor(rating); i++) {\n        starRatingHtml += fullStar;\n    }\n\n    // Add empty stars\n    for (let i = Math.floor(rating); i < 5; i++) {\n        starRatingHtml += emptyStar;\n    }\n\n    return starRatingHtml;\n}\n\nformula.setFormula({ RATING })\n\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: data,\n    search: true,\n    pagination: 10,\n    paginationOptions: [10, 25, 50],\n    columns: [\n        { type: 'image', width: '110px', title: 'Photo' },\n        {\n            type: 'dropdown',\n            width: '120px',\n            title: 'Category',\n            source: [\n                'Vegetables',\n                'Fruits',\n                'Grains, Beans and Nuts',\n                'Meat and Poultry',\n                'Fish and Seafood',\n                'Dairy Foods'\n            ]\n        },\n        { type: 'text', width: '150px', title: 'Description' },\n        { type: 'calendar', width: '100px', title: 'Best before' },\n        { type: 'text', width: '80px', title: 'Price', mask: '$ #,##' },\n        { type: 'text', width: '50px', title: 'Score' },\n        { type: 'text', width: '100px', title: 'Rating', stripHTML: false },\n    ],\n    updateTable: function (instance, cell, col, row, val, label, cellName) {\n        if (col == 5 && !val) {\n            instance.jexcel.setValue('F' + (row + 1), '=RATING(E' + (row + 1));\n        }\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/cases/highcharts.md",
    "content": "title: Highcharts with Jspreadsheet\nkeywords: Jexcel, javascript, highcharts, charts\ndescription: Integrating Highcharts with Jspreadsheet via events.\n\n# Highcharts\n\nCreating a dynamic chart using a online spreadsheet as a source.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n<script src=\"https://code.highcharts.com/highcharts.js\"></script>\n\n<div id=\"container\"></div>\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet data = [\n    ['Tokyo', 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6],\n    ['New York', -0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5],\n    ['Berlin', -0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0],\n    ['London', 3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8],\n];\n\nlet update = function (instance, cell, x, y, value) {\n    // If the related series does not exists create a new one\n    if (! chart.series[y]) {\n        // Create a new series row\n        let row = [];\n        for (i = 1; i < data[y].length; i++) {\n            row.push(parseFloat(data[y][i]));\n        }\n        // Append new series to the chart\n        chart.addSeries({ name:data[y][0], data:row });\n    } else {\n        if (x == 0) {\n            // Update legend\n            chart.series[y].update({ name:value });\n        } else {\n            // Update chart data\n            chart.series[y].data[x-1].update({ y:parseFloat(value) });\n        }\n    }\n}\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: data,\n    columns: [\n        { type: 'text', width:'200' },\n    ],\n    onchange:update,\n});\n\nconst chart = Highcharts.chart('container', {\n    title: {\n        text: 'Monthly Average Temperature',\n        x: -20 //center\n    },\n    subtitle: {\n        text: 'Source: WorldClimate.com',\n        x: -20\n    },\n    xAxis: {\n        categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n            'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n    },\n    yAxis: {\n        title: {\n            text: 'Temperature (°C)'\n        },\n        plotLines: [{\n            value: 0,\n            width: 1,\n            color: '#808080'\n        }]\n    },\n    tooltip: {\n        valueSuffix: '°C'\n    },\n    legend: {\n        layout: 'vertical',\n        align: 'right',\n        verticalAlign: 'middle',\n        borderWidth: 0\n    },\n    series: [{\n        name: 'Tokyo',\n        data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]\n    }, {\n        name: 'New York',\n        data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]\n    }, {\n        name: 'Berlin',\n        data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]\n    }, {\n        name: 'London',\n        data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]\n    }]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/cases/project-management.md",
    "content": "title: Project Management Spreadsheet with Jspreadsheet\nkeywords: Jexcel, javascript, cases, food store\ndescription: How to create a grocery store inventory using Jspreadsheet.\n\n# Project Management Spreadsheet\n\nA simple example including table scripting to perform a photo update and a progress bar added to any new task.  \n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// A custom method to create a PROGRESS BAR based on received percentage and color\nlet PROGRESS = function(percentage, color) {\n    percentage = percentage > 100 ? 100 : percentage;\n    percentage = percentage < 0 ? 0 : percentage;\n    percentage = percentage ? percentage : 0;\n\n    return `\n        <div style=\"width: 100%; border-radius: 10px;\">\n            <div style=\"width: ${percentage}%; background-color: ${color}; height: 20px; border-radius: 10px;\"></div>\n        </div>\n    `;\n}\n\nformula.setFormula({ PROGRESS })\n\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    minDimensions: [4, 10],\n    data: [\n        [ '=B1', '1', 'New products section', '2019-02-12', '80', '=PROGRESS(E1, \"darkgreen\")' ],\n        [ '=B2', '1', 'API integration', '2019-03-01', '100', '=PROGRESS(E2, \"darkgreen\")' ],\n        [ '=B3', '7359', 'Deck', '2018-11-10', '30', '=PROGRESS(E3, \"darkgreen\")' ],\n        [ '=B4', '1', 'Prototype', '2019-01-12', '0', '=PROGRESS(E4, \"darkgreen\")' ],\n    ],\n    columns: [\n        { type: 'text', width: '60px', title: 'Photo', readOnly:true },\n        { type: 'dropdown', width: '140px', title: 'Name', source: [ { id:'1', name: 'Paulo' }, { id:'7359', name:'Cosme Sergio' }] },\n        { type: 'text', width: '200px', title: 'Task' },\n        { type: 'calendar', width: '100px', title: 'When' },\n        { type: 'text', width: '50px', title: '%' },\n        { type: 'text', width: '200px', title: 'Progress', stripHTML: false },\n    ],\n    allowComments:true,\n    updateTable: function(instance, cell, col, row, val, label, cellName) {\n        if (col == 0) {\n            if (instance.jexcel.options.data[row][col+1]) {\n                cell.innerHTML = '<img src=\"/templates/default/img/' + instance.jexcel.options.data[row][col+1] + '.jpg\" style=\"width:16px;border-radius:16px\">';\n            } else {\n                cell.innerHTML = '<img src=\"/templates/default/img/nophoto.jpg\" style=\"width:16px;border-radius:16px\">';\n            }\n        }\n\n        if (col == 5 && ! val) {\n            instance.jexcel.setValue('F'+(row+1), '=PROGRESS(E' + (row + 1) + ', \"darkgreen\")');\n        }\n    }\n});\n</script>\n</html>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/v4/cases.md",
    "content": "title: Real-Life Applications and Integrations with Jspreadsheet  \nkeywords: Jspreadsheet, Jexcel, JavaScript integrations, real-world applications, data grid use cases, spreadsheet integrations, project management, data visualization  \ndescription: Explore real-life applications and integrations using Jspreadsheet. Discover practical examples of how Jspreadsheet enhances various projects and workflows.  \ncanonical: https://bossanova.uk/jspreadsheet/v4/cases\n\n# Jspreadsheet v4 Use Cases  \n\nExplore practical use cases and integrations with Jspreadsheet v4:  \n\n- [Data Persistence](/jspreadsheet/v4/cases/data-persistence)  \n- [Food Store Management](/jspreadsheet/v4/cases/food-store)  \n- [Highcharts Integration](/jspreadsheet/v4/cases/highcharts)  \n- [Project Management Tools](/jspreadsheet/v4/cases/project-management)  \n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/events.md",
    "content": "title: Spreadsheet Events with Jspreadsheet Version 4\nkeywords: Jspreadsheet, data grid, JavaScript, Excel-like features, spreadsheet events, event handling, customized actions, JavaScript integration, interactive spreadsheets, feature customization, event-driven programming, data grid events\ndescription: Learn more about Jspreadsheet’s comprehensive event system for advanced customization and integration.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/events\n\n# Spreadsheet Events\n\n## Custom table scripting after changes\n\nJspreadsheet offers a native feature to customize your table on the fly. You can define the method updateTable to create rules to customize the data should be shown to the user, as the example below.\n\n[See an example in action](/jspreadsheet/v4/examples/table-scripting)\n\n## Events\n\nJspreadsheet available events in this version.\n\n[Example on handling events on your spreasheet](/jspreadsheet/v4/examples/events)\n\n| Event                    | Description                                                                                                                              |  \n|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------|  \n| **onload**               | This method is called when the method setData                                                                                            |  \n| **onbeforechange**       | Before a column value is changed. NOTE: It is possible to overwrite the original value, by returning a new value on this method. v3.4.0+ |  \n| **onchange**             | After a column value is changed.                                                                                                         |  \n| **onafterchanges**       | After all changes are applied in the table.                                                                                              |  \n| **onpaste**              | After a paste action is performed in the JavaScript table.                                                                               |  \n| **onbeforepaste**        | Before the paste action is performed. Used to parse any input data, should return the data.                                              |  \n| **oninsertrow**          | After a new row is inserted.                                                                                                             |  \n| **onbeforeinsertrow**    | Before a new row is inserted. You can cancel the insert event by returning false.                                                        |  \n| **ondeleterow**          | After a row is excluded.                                                                                                                 |  \n| **onbeforedeleterow**    | Before a row is deleted. You can cancel the delete event by returning false.                                                             |  \n| **oninsertcolumn**       | After a new column is inserted.                                                                                                          |  \n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false.                                                     |  \n| **ondeletecolumn**       | After a column is excluded.                                                                                                              |  \n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false.                                                         |  \n| **onmoverow**            | After a row is moved to a new position.                                                                                                  |  \n| **onmovecolumn**         | After a column is moved to a new position.                                                                                               |  \n| **onresizerow**          | After a change in row height.                                                                                                            |  \n| **onresizecolumn**       | After a change in column width.                                                                                                          |  \n| **onselection**          | On the selection is changed.                                                                                                             |  \n| **onsort**               | After a column is sorted.                                                                                                                |  \n| **onfocus**              | On table focus                                                                                                                           |  \n| **onblur**               | On table blur                                                                                                                            |  \n| **onmerge**              | On column merge                                                                                                                          |  \n| **onchangeheader**       | On header change                                                                                                                         |  \n| **onundo**               | On undo is applied                                                                                                                       |  \n| **onredo**               | On redo is applied                                                                                                                       |  \n| **oneditionstart**       | When openEditor is called.                                                                                                               |  \n| **oneditionend**         | When closeEditor is called.                                                                                                              |  \n| **onchangestyle**        | When setStyle is called.                                                                                                                 |  \n| **onchangemeta**         | When setMeta is called.                                                                                                                  |\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/examples.md",
    "content": "title: Examples\nkeywords: Jexcel, javascript, examples\ndescription: Examples how to create web based spreadsheets using Jspreadsheet.\n\n# Jspreadsheet v4 Examples\n\nWe bring in this section various examples from basic to advance applications.\n\n*   [Angular](/jspreadsheet/v4/examples/angular)\n*   [Column Dragging](/jspreadsheet/v4/examples/column-dragging)\n*   [Column Filters](/jspreadsheet/v4/examples/column-filters)\n*   [Column Types](/jspreadsheet/v4/examples/column-types)\n*   [Comments](/jspreadsheet/v4/examples/comments)\n*   [Contextmenu](/jspreadsheet/v4/examples/contextmenu)\n*   [Create From Table](/jspreadsheet/v4/examples/create-from-table)\n*   [Custom Table Design](/jspreadsheet/v4/examples/custom-table-design)\n*   [Datatables](/jspreadsheet/v4/examples/datatables)\n*   [Date And Datetime Picker](/jspreadsheet/v4/examples/date-and-datetime-picker)\n*   [Dropdown And Autocomplete](/jspreadsheet/v4/examples/dropdown-and-autocomplete)\n*   [Events](/jspreadsheet/v4/examples/events)\n*   [Footers](/jspreadsheet/v4/examples/footers)\n*   [Freeze Columns](/jspreadsheet/v4/examples/freeze-columns)\n*   [Headers](/jspreadsheet/v4/examples/headers)\n*   [Image Upload](/jspreadsheet/v4/examples/image-upload)\n*   [Import Data](/jspreadsheet/v4/examples/import-data)\n*   [Jquery](/jspreadsheet/v4/examples/jquery)\n*   [Lazy Loading](/jspreadsheet/v4/examples/lazy-loading)\n*   [Merged Cells](/jspreadsheet/v4/examples/merged-cells)\n*   [Meta Information](/jspreadsheet/v4/examples/meta-information)\n*   [Nested Headers](/jspreadsheet/v4/examples/nested-headers)\n*   [Programmatically Updates](/jspreadsheet/v4/examples/programmatically-updates)\n*   [React](/jspreadsheet/v4/examples/react)\n*   [Readonly](/jspreadsheet/v4/examples/readonly)\n*   [Richtext Html Editor](/jspreadsheet/v4/examples/richtext-html-editor)\n*   [Sorting](/jspreadsheet/v4/examples/sorting)\n*   [Spreadsheet Formulas](/jspreadsheet/v4/examples/spreadsheet-formulas)\n*   [Spreadsheet Toolbars](/jspreadsheet/v4/examples/spreadsheet-toolbars)\n*   [Spreadsheet Webcomponent](/jspreadsheet/v4/examples/spreadsheet-webcomponent)\n*   [Table Overflow](/jspreadsheet/v4/examples/table-overflow)\n*   [Table Scripting](/jspreadsheet/v4/examples/table-scripting)\n*   [Table Style](/jspreadsheet/v4/examples/table-style)\n*   [Tabs](/jspreadsheet/v4/examples/tabs)\n*   [Translations](/jspreadsheet/v4/examples/translations)\n*   [Vue](/jspreadsheet/v4/examples/vue)\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/getting-started.md",
    "content": "title: Getting Started with Jspreadsheet CE Version 4\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jspreadsheet CE.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/getting-started\n\n# Getting started\n\nJspreadsheet is a vanilla javascript plugin to embed a online spreadsheet in your web based applications. Bring highly dynamic datasets to your application and improve the user experience of your software.\n\n## Install\n\n### NPM\n\n```bash\nnpm install jspreadsheet-ce@4\n```\n\n### CDN\n\nUse jspreadsheet directly from JSDelivr CDN\n\n\n{.ignore}\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css\" type=\"text/css\" />\n```\n\n\n### GitHub\n\nDownload from our GitHub page\n\n[https://github.com/jspreadsheet/ce](https://github.com/jspreadsheet/ce)\n\n\n\n## Initialization\n\nYou can initiate an online spreadsheet including data from a HTML table, a JS array, a CSV or a JSON file, following the examples below:\n\n### Loading from a javascript array\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:100 },\n        { title:'Price', width:80 },\n    ]\n});\n</script>\n</html>\n```\n\n\n### Loading from a JSON file\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    url: '/jspreadsheet/data.json',\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:100 },\n        { title:'Price', width:80 },\n    ]\n});\n</script>\n</html>\n```\n\n\n\n### Loading from a CSV file\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    csv: '/tests/demo.csv',\n    csvHeaders: true,\n    columns: [\n        { width:300 },\n        { width:80 },\n        { width:100 }\n    ],\n    tableOverflow: true,\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/import-data)\n\n\n### Headers from a CSV file\n\nIf you are loading your data from a CSV file, you can define the **csvHeader:true**, so the first row will be used as your column names.\n\n[See a working example](/jspreadsheet/v4/examples/import-data)\n\n\n\n### Programmatically header updates\n\nThe methods **setHeader()**, **getHeader()** and **getHeaders()** are available for the developer to interact programmatically with the spreadsheet.\n\n[Working example](/jspreadsheet/v4/examples/headers#Programmatically-header-updates)\n\n\n\n### Nested headers\n\nThe nested headers area available in the innitialization through the directive **nestedHeaders:[]**, and should be use follow:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ],\n    columns: [\n        { type: 'autocomplete', title:'Country', width:'300', url:'/jspreadsheet/countries' },\n        { type: 'dropdown', title:'Food', width:'150', source: ['Apples','Bananas','Carrots','Oranges','Cheese' ] },\n        { type: 'checkbox', title:'Stock', width:'100' },\n    ],\n    nestedHeaders: [\n        [\n            { title:'Supermarket information', colspan:'3' },\n        ],\n        [\n            { title:'Location', colspan:'1' },\n            { title:' Other Information', colspan:'2' }\n        ],\n    ],\n});\n</script>\n</html>\n```\n\n[See this example in action](/jspreadsheet/v4/examples/headers)\n\n## Column width\n\nThe initial width can be defined in the width property in the column parameter.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:80 },\n        { title:'Price', width:100 }\n    ]\n});\n</script>\n</html>\n```\n\n\n### Programmatically column width updates\n\nThe methods setWidth(), getWidth() are available for the developer to update the column width via javascript.\n\n[See this example in action](/jspreadsheet/v4/examples/programmatically-updates#setWidth)\n\n## Row height\n\nThe initial row height can be defined in the height property include in the rows directive. It is also possible to enabled a resizeble row by using rowResize: true in the initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['First'],\n        ['Second'],\n        ['Third']\n    ],\n    rows:{ 1: { height:'300px' }},\n    rowResize: true,\n    minDimensions: [4,4],\n});\n</script>\n</html>\n```\n\n\n### Programmatically row height updates\n\nThe methods setHeight(), getHeight() are available for the developer to update the row height via javascript.\n\n[See this example in action](/jspreadsheet/v4/examples/programmatically-updates#setHeight)\n\n## Column types\n\nJspreadsheet has available some extra native column types in addition to the default input text. It means you have extended nice responsive ways to get data into your spreadsheet. In addition to that is available integration methods to facilitate you to bring any custom column to your tables. This makes the Jspreadsheet plugin a very flexible tool to enhance the user experience of your applications.\n\nJspreadsheet is integrate with jSuites, so it brings some native columns, such as: **text, numeric, hidden, dropdown, autocomplete, checkbox, radio, calendar, image and color.**_\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { title:'Model', width:300, type:'text'; },\n        { title:'Price', width:80, type:'numeric' },\n        { title:'Date', width:100, type:'calendar', options: { format:'DD/MM/YYYY' } },\n        { title:'Photo', width:150, type:'image' },\n        { title:'Condition', width:150, type:'dropdown', source:['New','Used'] },\n        { title:'Color', width:80, type:'color' },\n        { title:'Available', width:80, type:'checkbox' },\n    ]\n});\n</script>\n</html>\n```\n\n### Calendar type\n\nWhen using the calendar column, you can change the behavior behavior of your calendar by sending some extra options as example above. The possible values are:\n\n{.ignore}\n```javascript\n{\n    options : {\n        // Date format\n        format:'DD/MM/YYYY',\n        // Allow keyboard date entry\n        readonly:0,\n        // Today is default\n        today:0,\n        // Show timepicker\n        time:0,\n        // Show the reset button\n        resetButton:true,\n        // Placeholder\n        placeholder:'',\n        // Translations can be done here\n        months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],\n        weekdays\\_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'],\n        // Value\n        value:null,\n        // Events\n        onclose:null,\n        onchange:null,\n        // Fullscreen (this is automatic set for screensize < 800)\n        fullscreen:false,\n    }\n}\n```\n\n[See a working example](/jspreadsheet/v4/examples/date-and-datetime-picker)\n\n\n\n### Dropdown and autocomplete type\n\nThere are different ways to work with dropdowns in Jspreadsheet. It is possible to define the parameter _source_ as a simple or key-value array. It is also possible to use the param _url_ to populate your dropdown from an external json format source. In addition to that it is possible to have conditional values. Basically, the values from one dropdown can be conditional to other dropdowns in your table.\n\nYou can set the autocomplete dropdown through the initial param _autocomplete:true_ and the multiple picker can be activate by _multiple:true_ property as shown in the following example:\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Honda', 1, 'Civic', '4'],\n        ['Peugeot', 3,'1007', '2'],\n        ['Smart', 3,'Cabrio', '4;5'],\n    ],\n    columns: [\n        {\n            type:'text',\n            title:'Model',\n            width:'200',\n        },\n        {\n            type:'dropdown',\n            title:'Available in',\n            multiple:true,\n            source: [{id:1, name:'Red'},{id:2, name:'Yellow'},{id:3,name:'Blue'}],\n            width:'200',\n        },\n        {\n            type:'autocomplete',\n            title:'Region',\n            url:'values.json',\n            width:'200',\n        },\n    ]\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/dropdown-and-autocomplete)\n\n### Custom type\n\nJspreadsheet makes possible to extend third party javascript plugins to create your custom columns. Basically to use this feature, you should implement some basic methods such as: openEditor, closeEditor, getValue, setValue as following.\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"jspreadsheet\"></div>\n\n<script>\nlet customColumn = {\n    // Methods\n    closeEditor : function(cell, save) {\n        let value = cell.children[0].value;\n        cell.innerHTML = value;\n        return value;\n    },\n    openEditor : function(cell) {\n        // Create input\n        let element = document.createElement('input');\n        element.value = cell.innerHTML;\n        // Update cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(element);\n        $(element).clockpicker({\n            afterHide:function() {\n                setTimeout(function() {\n                    // To avoid double call\n                    if (cell.children[0]) {\n                        spreadsheet.closeEditor(cell, true);\n                    }\n                });\n            }\n        });\n        // Focus on the element\n        element.focus();\n    },\n    getValue : function(cell) {\n        return cell.innerHTML;\n    },\n    setValue : function(cell, value) {\n        cell.innerHTML = value;\n    }\n}\n\nconst instance = jspreadsheet(document.getElementById('jspreadsheet'), {\n    data: [\n        ['PHP', '14:00' ],\n        ['Javascript', '16:30'],\n    ],\n    columns: [\n        { type: 'text', title:'Course Title', width:300 },\n        { type: 'text', title:'Time', width:100, editor:customColumn },\n     ]\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/column-types#custom)\n\n\n\n\n## Define a minimum table dimension size.\n\nThe follow example will create a data table with a minimum number of ten columns and five rows:\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    minDimensions:[10,5],\n});\n<script>\n</html>\n```"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/most-common-questions-and-answers.md",
    "content": "title: Common Questions\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features, questions, QA\ndescription: Questions and Answers about Jspreadsheet V4\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/most-common-questions-and-answers\n\n# Most common questions\n\n  \n\n  1. #### What is the best way to create odd/even rows on a Jspreadsheet spreadsheet and tables?\n\nSolution: Adding the following CSS code on your project.\n\n{.ignore}\n```css\n.jexcel tbody tr:nth-child(even) {\n  background-color: #EEE9F1 !important;\n}\n```\n\n  \n\n  2. #### How to transform multiple HTML static tables in dynamic Jspreadsheet tables?\n\n{.ignore}\n```javascript\nlet tables = document.querySelectorAll('table');\n\nfor (var i = 0; i < tables.length; i++) {\n    jspreadsheet(tables[i]);\n}\n```\n\n  \n\n  3. #### How to disable paste over a Jspreadsheet spreadsheet?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    onbeforepaste: function(instance, data, x, y) {\n        return false;\n    }\n});\n```\n\n  \n\n  4. #### How to intercept and change a pasted string over a Jspreadsheet table?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    onbeforepaste: function(instance, data, x, y) {\n        data = data.replace(',', '.', data);\n        return data;\n    }\n});\n```\n\n  \n\n  5. #### How to overwrite a type of a cell over a column type?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    columns: [\n        { type: 'text' },\n        { type: 'text' }, \n    ],\n    cells: {\n        B2: { type:'number', mask:'$ #,##.00', decimal:'.' },\n        B3: { type:'percent' },\n    }\n});\n```\n\n**NOTE:** Only available on the [Pro\ndistribution](https://jspreadsheet.com/).\n\n  \n\n  6. #### How to disabled the javascript contextmenu of my spreadsheet?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    columns: [\n        { type: 'text' },\n        { type: 'text' }, \n    ],\n    contextMenu: function() {\n        return false;\n    }\n});\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/programmatically-changes.md",
    "content": "title: Programmatically Data Grid Changes \nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jspreadsheet CE.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/programmatically-changes\n\n# Methods\n\nJspreadsheet has a comprehensive number of native methods to programmatically interact with your javascript spreadsheet and its data.\n\n[Go to a working example](/jspreadsheet/v4/examples/programmatically-updates)\n\n  \n\n## General Methods\n\n| Method | Description | Example |  \n| --- | --- | --- |  \n| **getData** | Get the full or partial table data<br>@Param boolean onlyHighlighedCells - Get only highlighted cells | spreadsheet.getData([bool]); |  \n| **getJson** | Get the full or partial table data in JSON format<br>@Param boolean onlyHighlighedCells - Get only highlighted cells | spreadsheet.getJson([bool]); |  \n| **getRowData** | Get the data from one row by number<br>@Param integer rowNumber - Row number | spreadsheet.getRowData([int]); |  \n| **setRowData** | Set the data from one row by number<br>@Param integer rowNumber - Row number<br>@Param array rowData - Row data | spreadsheet.setRowData([int], [array]); |  \n| **getColumnData** | Get the data from one column by number<br>@Param integer columnNumber - Column number | spreadsheet.getColumnData([int]); |  \n| **setColumnData** | Set the data from one column by number<br>@Param integer columnNumber - Column number<br>@Param array colData - Column data | spreadsheet.setColumnData([int], [array]); |  \n| **setData** | Set the table data<br>@Param json newData - New JSON data, null will reload what is in memory. | spreadsheet.setData([json]); |  \n| **setMerge** | Merge cells<br>@Param string columnName - Column name, such as A1.<br>@Param integer colspan - Number of columns<br>@Param integer rowspan - Number of rows | spreadsheet.setMerge([string], [int], [int]); |  \n| **getMerge** | Get merged cells properties<br>@Param string columnName - Column name, such as A1. | spreadsheet.getMerge([string]); |  \n| **removeMerge** | Destroy merged by column name<br>@Param string columnName - Column name, such as A1. | spreadsheet.removeMerge([string]); |  \n| **destroyMerged** | Destroy all merged cells | spreadsheet.destroyMerge(); |  \n| **getCell** | Get current cell DOM<br>@Param string columnName - String compatible with Excel, or as object. | spreadsheet.getCell([string]); |  \n| **getLabel** | Get current cell DOM innerHTML<br>@Param string columnName - String compatible with Excel, or as object. | spreadsheet.getLabel([string]); |  \n| **getValue** | Get current cell value<br>@Param mixed cellIdent - String compatible with Excel, or as object. | spreadsheet.getValue([string]); |  \n| **getValueFromCoords** | Get value from coordinates<br>@Param integer x<br>@Param integer y | spreadsheet.getValueFromCoords([integer], [integer]); |  \n| **setValue** | Change the cell value<br>@Param mixed cellIdent - String compatible with Excel, or as object.<br>@Param string Value - New value for the cell<br>@Param bool force - Update readonly columns | spreadsheet.setValue([string], [string], [bool]); |  \n| **setValueFromCoords** | Set value from coordinates<br>@Param integer x<br>@Param integer y<br>@Param string Value - New value for the cell<br>@Param bool force - Update readonly columns | spreadsheet.setValueFromCoords([integer], [integer], [string], [bool]); |  \n| **resetSelection** | Reset the table selection<br>@Param boolean executeBlur - Execute the blur from the table | spreadsheet.resetSelection([bool]); |  \n| **updateSelection** | Select cells<br>@Param object startCell - Cell object<br>@Param object endCell - Cell object<br>@Param boolean ignoreEvents - Ignore onselection event | spreadsheet.updateSelection([cell], [cell], true); |  \n| **updateSelectionFromCoords** | Select cells<br>@Param integer x1<br>@Param integer y1<br>@Param integer x2<br>@Param integer y2 | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |  \n| **getWidth** | Get the current column width<br>@Param integer columnNumber - Column number starting at zero | spreadsheet.getWidth([integer]); |  \n| **setWidth** | Change column width<br>@Param integer columnNumber - Column number starting at zero<br>@Param string newColumnWidth - New column width | spreadsheet.setWidth([integer], [integer]); |  \n| **getHeight** | Get the current row height<br>@Param integer rowNumber - Row number starting at zero | spreadsheet.getHeight([integer]); |  \n| **setHeight** | Change row height<br>@Param integer rowNumber - Row number starting at zero<br>@Param string newRowHeight - New row height | spreadsheet.setHeight([integer], [integer]); |  \n| **getHeader** | Get the current header by column number<br>@Param integer columnNumber - Column number starting at zero | spreadsheet.getHeader([integer]); |  \n| **getHeaders** | Get all header titles | spreadsheet.getHeaders(); |  \n| **setHeader** | Change header by column<br>@Param integer columnNumber - Column number starting at zero<br>@Param string columnTitle - New header title | spreadsheet.setHeader([integer], [string]); |  \n| **getStyle** | Get table or cell style<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getStyle([string]); |  \n| **setStyle** | Set cell(s) CSS style<br>@Param mixed - JSON with whole table style information or just one cell identification. Ex. A1.<br>@Param k [optional] - CSS key<br>@Param v [optional] - CSS value | spreadsheet.setStyle([object], [string], [string]); |  \n| **resetStyle** | Remove all style from a cell<br>@Param string columnName - Column name, example: A1, B3, etc | spreadsheet.resetStyle([string]); |  \n| **getComments** | Get cell comments<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getComments([string]); |  \n| **setComments** | Set cell comments<br>@Param cell - Cell identification<br>@Param text - Comments | spreadsheet.setComments([string], [string]); |  \n| **orderBy** | Reorder a column ascending or descending<br>@Param integer columnNumber - Column number starting at zero<br>@Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order | spreadsheet.orderBy([integer], [boolean]); |  \n| **getConfig** | Get table definitions | spreadsheet.getConfig(); |  \n| **insertColumn** | Add a new column<br>@Param mixed - Number of columns to be added or data to be added in one single column<br>@Param int columnNumber - Number of columns to be created<br>@Param boolean insertBefore<br>@Param object properties - Column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]); |  \n| **deleteColumn** | Remove column by number<br>@Param integer columnNumber - Which column should be excluded starting at zero<br>@Param integer numOfColumns - Number of columns to be excluded from the reference column | spreadsheet.deleteColumn([integer], [integer]); |  \n| **moveColumn** | Change the column position<br>@Param integer columnPosition<br>@Param integer newColumnPosition | spreadsheet.moveColumn([integer], [integer]); |  \n| **insertRow** | Add a new row<br>@Param mixed - Number of blank lines to be inserted or a single array with the data of the new row<br>@Param integer rowNumber - Reference row number<br>@Param boolean insertBefore | spreadsheet.insertRow([mixed], [integer], [boolean]); |  \n| **deleteRow** | Remove row by number<br>@Param integer rowNumber - Which row should be excluded starting at zero<br>@Param integer numOfRows - Number of lines to be excluded | spreadsheet.deleteRow([integer], [integer]); |  \n| **moveRow** | Change the row position<br>@Param integer rowPosition<br>@Param integer newRowPosition | spreadsheet.moveRow([integer], [integer]); |  \n| **download** | Get the current data as a CSV file<br>@Param bool - True to download parsed formulas. | spreadsheet.download([bool]); |  \n| **getMeta** | Get the table or cell meta information<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getMeta([string]); |  \n| **setMeta** | Set the table or cell meta information<br>@Param mixed - JSON with whole table meta information. | spreadsheet.setMeta([mixed]); |  \n| **fullscreen** | Toggle table fullscreen mode<br>@Param boolean fullscreen - Define fullscreen status as true or false | spreadsheet.fullscreen([bool]); |  \n| **getSelectedRows** | Get the selected rows<br>@Param boolean asIds - Get the rowNumbers or row DOM elements | spreadsheet.getSelectedRows([bool]); |  \n| **getSelectedColumns** | Get the selected columns | spreadsheet.getSelectedColumns(); |  \n| **showIndex** | Show column of index numbers | spreadsheet.showIndex(); |  \n| **hideIndex** | Hide column of index numbers | spreadsheet.hideIndex(); |  \n| **search** | Search in the table, only if directive is enabled during initialization.<br>@Param string - Search for word | spreadsheet.search([string]); |  \n| **resetSearch** | Reset search table | spreadsheet.resetSearch(); |  \n| **whichPage** | Which page showing on Jspreadsheet - Valid only when pagination is true. | spreadsheet.whichPage(); |  \n| **page** | Go to page number - Valid only when pagination is true.<br>@Param integer - Go to page number | spreadsheet.page([integer]); |  \n| **undo** | Undo last changes | spreadsheet.undo(); |  \n| **redo** | Redo changes | spreadsheet.redo(); |  \n\n\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/quick-reference.md",
    "content": "title: Quick Reference\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Quick Reference of the jspreadsheet properties\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/quick-reference\n\n# Quick reference\n\n  \n\n## Methods\n\n| Method                                                                                                                                                                                                                                                                           | Example                                                                            |  \n|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|  \n| **getData:** Get the full or partial table data  <br> @Param boolean onlyHighlighedCells - Get only highlighted cells                                                                                                                                                            | spreadsheet.getData([bool]);                                                       |  \n| **getJson:** Get the full or partial table data in JSON format  <br> @Param boolean onlyHighlighedCells - Get only highlighted cells                                                                                                                                             | spreadsheet.getJson([bool]);                                                       |  \n| **getRowData:** Get the data from one row by number  <br> @Param integer rowNumber - Row number                                                                                                                                                                                  | spreadsheet.getRowData([int]);                                                     |  \n| **setRowData:** Set the data from one row by number  <br> @Param integer rowNumber - Row number  <br> @Param array rowData - Row data                                                                                                                                            | spreadsheet.setRowData([int], [array]);                                            |  \n| **getColumnData:** Get the data from one column by number  <br> @Param integer columnNumber - Column number                                                                                                                                                                      | spreadsheet.getColumnData([int]);                                                  |  \n| **setColumnData:** Set the data from one column by number  <br> @Param integer columnNumber - Column number  <br> @Param array colData - Column data                                                                                                                             | spreadsheet.setColumnData([int], [array]);                                         |  \n| **setData:** Set the table data  <br> @Param json newData - New JSON data, null will reload what is in memory.                                                                                                                                                                   | spreadsheet.setData([json]);                                                       |  \n| **setMerge:** Merge cells  <br> @Param string columnName - Column name, such as A1.  <br> @Param integer colspan - Number of columns  <br> @Param integer rowspan - Number of rows                                                                                               | spreadsheet.setMerge([string], [int], [int]);                                      |  \n| **getMerge:** Get merged cells properties  <br> @Param string columnName - Column name, such as A1.                                                                                                                                                                              | spreadsheet.getMerge([string]);                                                    |  \n| **removeMerge:** Destroy merged by column name  <br> @Param string columnName - Column name, such as A1.                                                                                                                                                                         | spreadsheet.removeMerge([string]);                                                 |  \n| **destroyMerged:** Destroy all merged cells                                                                                                                                                                                                                                      | spreadsheet.destroyMerge();                                                        |  \n| **getCell** : get current cell DOM  <br> @Param string columnName - str compatible with Excel, or as object.                                                                                                                                                                     | spreadsheet.getCell([string]);                                                     |  \n| **getLabel** : get current cell DOM innerHTML  <br> @Param string columnName - str compatible with Excel, or as object.                                                                                                                                                          | spreadsheet.getLabel([string]);                                                    |  \n| **getValue:** get current cell value  <br> @Param mixed cellIdent - str compatible with Excel, or as object.                                                                                                                                                                     | spreadsheet.getValue([string]);                                                    |  \n| **getValueFromCoords:** get value from coords  <br> @Param integer x  <br> @Param integer y                                                                                                                                                                                      | spreadsheet.getValueFromCoords([integer], [integer]);                              |  \n| **setValue:** change the cell value  <br> @Param mixed cellIdent - str compatible with Excel, or as object.  <br> @Param string Value - new value for the cell  <br> @Param bool force - update readonly columns                                                                 | spreadsheet.setValue([string], [string], [bool]);                                  |  \n| **setValueFromCoords:** get value from coords  <br> @Param integer x  <br> @Param integer y  <br> @Param string Value - new value for the cell  <br> @Param bool force - update readonly columns                                                                                 | spreadsheet.setValueFromCoords([integer], [integer], [string], [bool]);            |  \n| **resetSelection:** Reset the table selection  <br> @Param boolean executeBlur - execute the blur from the table                                                                                                                                                                 | spreadsheet.resetSelection([bool]);                                                |  \n| **updateSelection:** select cells  <br> @Param object startCell - cell object  <br> @Param object endCell - cell object  <br> @Param boolean ignoreEvents - ignore onselection event                                                                                             | spreadsheet.updateSelection([cell], [cell], true);                                 |  \n| **updateSelectionFromCoords:** select cells  <br> @Param integer x1  <br> @Param integer y1  <br> @Param integer x2  <br> @Param integer y2                                                                                                                                      | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |  \n| **getWidth:** get the current column width  <br> @Param integer columnNumber - column number starting on zero                                                                                                                                                                    | spreadsheet.getWidth([integer]);                                                   |  \n| **setWidth:** change column width  <br> @Param integer columnNumber - column number starting on zero  <br> @Param string newColumnWidth - New column width                                                                                                                       | spreadsheet.setWidth([integer], [integer]);                                        |  \n| **getHeight:** get the current row height  <br> @Param integer rowNumber - row number starting on zero                                                                                                                                                                           | spreadsheet.getHeight([integer]);                                                  |  \n| **setHeight:** change row height  <br> @Param integer rowNumber - row number starting on zero  <br> @Param string newRowHeight- New row height                                                                                                                                   | spreadsheet.setHeight([integer], [integer]);                                       |  \n| **getHeader:** get the current header by column number  <br> @Param integer columnNumber - column number starting on zero                                                                                                                                                        | spreadsheet.getHeader([integer]);                                                  |  \n| **getHeaders:** get all header titles                                                                                                                                                                                                                                            | spreadsheet.getHeaders();                                                          |  \n| **setHeader:** change header by column  <br> @Param integer columnNumber - column number starting on zero  <br> @Param string columnTitle - New header title                                                                                                                     | spreadsheet.setHeader([integer], [string]);                                        |  \n| **getStyle:** get table or cell style  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                                      | spreadsheet.getStyle([string]));                                                   |  \n| **setStyle:** set cell(s) CSS style  <br> @Param mixed - json with whole table style information or just one cell identification. Ex. A1.  <br> @Param k [optional]- CSS key  <br> @Param v [optional]- CSS value                                                                | spreadsheet.setSyle([object], [string], [string]);                                 |  \n| **resetStyle:** remove all style from a cell  <br> @Param string columnName - Column name, example: A1, B3, etc                                                                                                                                                                  | spreadsheet.resetStyle([string]);                                                  |  \n| **getComments:** get cell comments  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                                         | spreadsheet.getComments([string]);                                                 |  \n| **setComments:** set cell comments  <br> @Param cell - cell identification  <br> @Param text - comments                                                                                                                                                                          | spreadsheet.setComments([string], [string]);                                       |  \n| **orderBy:** reorder a column asc or desc  <br> @Param integer columnNumber - column number starting on zero  <br> @Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order                                              | spreadsheet.orderBy([integer], [boolean]);                                         |  \n| **getConfig:** get table definitions                                                                                                                                                                                                                                             | spreadsheet.getConfig();                                                           |  \n| **insertColumn:** add a new column  <br> @Param mixed - num of columns to be added or data to be added in one single column  <br> @Param int columnNumber - number of columns to be created  <br> @Param boolean insertBefore  <br> @Param object properties - column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]);                 |  \n| **deleteColumn:** remove column by number  <br> @Param integer columnNumber - Which column should be excluded starting on zero  <br> @Param integer numOfColumns - number of columns to be excluded from the reference column                                                    | spreadsheet.deleteColumn([integer], [integer]);                                    |  \n| **moveColumn:** change the column position  <br> @Param integer columnPosition  <br> @Param integer newColumnPosition                                                                                                                                                            | spreadsheet.moveColumn([integer], [integer]);                                      |  \n| **insertRow:** add a new row  <br> @Param mixed - number of blank lines to be insert or a single array with the data of the new row  <br> @Param integer rowNumber - reference row number  <br> @Param boolean insertBefore                                                      | spreadsheet.insertRow([mixed], [integer], [boolean]);                              |  \n| **deleteRow:** remove row by number  <br> @Param integer rowNumber - Which row should be excluded starting on zero  <br> @Param integer numOfRows - number of lines to be excluded                                                                                               | spreadsheet.deleteRow([integer], [integer]);                                       |  \n| **moveRow:** change the row position  <br> @Param integer rowPosition  <br> @Param integer newRowPosition                                                                                                                                                                        | spreadsheet.moveRow([integer], [integer]);                                         |  \n| **download:** get the current data as a CSV file  <br> @Param bool - true to download parsed formulas.                                                                                                                                                                           | spreadsheet.download([bool]);                                                      |  \n| **getMeta:** get the table or cell meta information  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                        | spreadsheet.getMeta([string]);                                                     |  \n| **setMeta:** set the table or cell meta information  <br> @Param mixed - json with whole table meta information.                                                                                                                                                                 | spreadsheet.setMeta[mixed]);                                                       |  \n| **fullscreen:** Toogle table fullscreen mode  <br> @Param boolean fullscreen - define fullscreen status as true or false                                                                                                                                                         | spreadsheet.fullscreen([bool]);                                                    |  \n| **getSelectedRows:** Get the selected rows  <br> @Param boolean asIds - Get the rowNumbers or row DOM elements                                                                                                                                                                   | spreadsheet.getSelectedRows([bool]);                                               |  \n| **getSelectedColumns:** Get the selected columns  <br> @Param boolean asIds - Get the colNumbers or row DOM elements                                                                                                                                                             | spreadsheet.getSelectedColumns([bool]);                                            |  \n| **showColumn:** show column by number                                                                                                                                                                                                                                            | spreadsheet.showIndex([int]);                                                      |  \n| **hideColumn:** hide column by number                                                                                                                                                                                                                                            | spreadsheet.hideColumn([int]);                                                     |  \n| **showIndex:** show column of index numbers                                                                                                                                                                                                                                      | spreadsheet.showIndex();                                                           |  \n| **hideIndex:** hide column of index numbers                                                                                                                                                                                                                                      | spreadsheet.hideIndex();                                                           |  \n| **search:** search in the table, only if directive is enabled during initialization.  <br> @Param string - Search for word                                                                                                                                                       | spreadsheet.search([string]);                                                      |  \n| **resetSearch:** reset search table                                                                                                                                                                                                                                              | spreadsheet.resetSearch();                                                         |  \n| **whichPage:** Which page showing on Jspreadsheet - Valid only when pagination is true.                                                                                                                                                                                          | spreadsheet.whichPage();                                                           |  \n| **page:** Go to page number- Valid only when pagination is true.  <br> @Param integer - Go to page number                                                                                                                                                                        | spreadsheet.page([integer]);                                                       |  \n| **undo:** Undo last changes                                                                                                                                                                                                                                                      | spreadsheet.undo();                                                                |  \n| **redo:** Redo changes                                                                                                                                                                                                                                                           | spreadsheet.redo();                                                                |  \n\n\n  \n[Working example](/jspreadsheet/v4/examples/programmatically-updates)\n\n  \n  \n  \n\n## Events\n\n| Event | Description |\n| --- | --- |\n| **onload** | This method is called when the method setData |\n| **onbeforechange** | Before a column value is changed. NOTE: It is possible to overwrite the original value, by returning a new value on this method. v3.4.0+ |\n| **onchange** | After a column value is changed. |\n| **onafterchanges** | After all changes are applied in the table. |\n| **onpaste** | After a paste action is performed in the JavaScript table. |\n| **onbeforepaste** | Before the paste action is performed. Used to parse any input data, should return the data. |\n| **oninsertrow** | After a new row is inserted. |\n| **onbeforeinsertrow** | Before a new row is inserted. You can cancel the insert event by returning false. |\n| **ondeleterow** | After a row is excluded. |\n| **onbeforedeleterow** | Before a row is deleted. You can cancel the delete event by returning false. |\n| **oninsertcolumn** | After a new column is inserted. |\n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false. |\n| **ondeletecolumn** | After a column is excluded. |\n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false. |\n| **onmoverow** | After a row is moved to a new position. |\n| **onmovecolumn** | After a column is moved to a new position. |\n| **onresizerow** | After a change in row height. |\n| **onresizecolumn** | After a change in column width. |\n| **onselection** | On the selection is changed. |\n| **onsort** | After a column is sorted. |\n| **onfocus** | On table focus. |\n| **onblur** | On table blur. |\n| **onmerge** | On column merge. |\n| **onchangeheader** | On header change. |\n| **onundo** | On undo is applied. |\n| **onredo** | On redo is applied. |\n| **oneditionstart** | When an openEditor is called. |\n| **oneditionend** | When a closeEditor is called. |\n| **onchangestyle** | When a setStyle is called. |\n| **onchangemeta** | When a setMeta is called. |\n| **onchangepage** | When the page is changed. |\n\n  \n[Example on handling events on Jspreadsheet](/jspreadsheet/v4/examples/events)\n\n  \n  \n\n## Initialization\n\n| Parameter | Description |\n| --- | --- |\n| **url** | Load an external JSON file from this URL: string |\n| **data** | Load this data into the JavaScript table: array |\n| **copyCompatibility** | When true, copy and export will bring formula results; if false, will bring formulas: boolean |\n| **rows** | Row properties: height: object |\n| **columns** | Column type, title, width, align, dropdown options, text wrapping, mask, etc.: object |\n| **defaultColWidth** | Default width for a new column: integer |\n| **defaultColAlign** | Default align for a new column: [center, left, right] |\n| **minSpareRows** | Minimum number of spare rows: integer |\n| **minSpareCols** | Minimum number of spare columns: integer |\n| **minDimensions** | Minimum table dimensions: [columns, rows] |\n| **allowExport** | Allow table export: boolean |\n| **includeHeadersOnDownload** | Include header titles on download: boolean |\n| **columnSorting** | Allow column sorting: boolean |\n| **columnDrag** | Allow column dragging: boolean |\n| **columnResize** | Allow column resizing: boolean |\n| **rowResize** | Allow row resizing: boolean |\n| **rowDrag** | Allow row dragging: boolean |\n| **editable** | Allow table edition: boolean |\n| **allowInsertRow** | Allow insertion of a new row: boolean |\n| **allowManualInsertRow** | Allow user to insert a new row: boolean |\n| **allowInsertColumn** | Allow insertion of a new column: boolean |\n| **allowManualInsertColumn** | Allow user to create a new column: boolean |\n| **allowDeleteRow** | Allow deletion of a row: boolean |\n| **allowDeleteColumn** | Allow deletion of a column: boolean |\n| **allowRenameColumn** | Allow renaming of a column: boolean |\n| **allowComments** | Allow comments over the cells: boolean |\n| **wordWrap** | Global text wrapping: boolean |\n| **csv** | Load an external CSV file from this URL: string |\n| **csvFileName** | Default filename for a download method: string |\n| **csvHeaders** | Load header titles from the CSV file: boolean |\n| **csvDelimiter** | Default delimiter for the CSV file: string |\n| **selectionCopy** | Allow selection copy: boolean |\n| **mergeCells** | Cells to be merged in the table initialization: object |\n| **toolbar** | Add custom toolbars: object |\n| **search** | Allow search in the table: boolean |\n| **pagination** | Break the table by pages: integer |\n| **paginationOptions** | Number of records per page: [25, 50, 75, 100], for example |\n| **fullscreen** | Fullscreen mode: boolean |\n| **lazyLoading** | Activate the table lazy loading: boolean |\n| **loadingSpin** | Activate the loading spin: boolean |\n| **tableOverflow** | Allow table overflow: boolean |\n| **tableHeight** | Force the maximum height of the table: CSS String |\n| **tableWidth** | Force the maximum width of the table: CSS String |\n| **meta** | Meta information: object |\n| **style** | Cells style in the table initialization: object |\n| **parseFormulas** | Enable execution of formulas inside the table: boolean |\n| **autoIncrement** | Auto-increment actions when using the dragging corner: boolean |\n| **updateTable** | Method to configure custom script execution. NOTE: This does not work with lazyLoading, Pagination, or Search options. |\n| **nestedHeaders** | Define the nested headers, including title, colspan, etc.: object |\n| **contextMenu** | Context menu content: function() { return customMenu } |\n| **text** | All messages to be customized: object |\n\n  \n  \n\n## Translations\n\n| Key| Default value  |\n| ---|---  |\n| **noRecordsFound** | No records found |\n| **showingPage** | Showing page {0} of {1} entries |\n| **show** | Show |\n| **entries** | entries |\n| **insertANewColumnBefore** | Insert a new column before |\n| **insertANewColumnAfter** | Insert a new column after |\n| **deleteSelectedColumns** | Delete selected columns |\n| **renameThisColumn** | Rename this column |\n| **orderAscending** | Order ascending |\n| **orderDescending** | Order descending |\n| **insertANewRowBefore** | Insert a new row before |\n| **insertANewRowAfter** | Insert a new row after |\n| **deleteSelectedRows** | Delete selected rows |\n| **editComments** | Edit comments |\n| **addComments** | Add comments |\n| **comments** | Comments |\n| **clearComments** | Clear comments |\n| **copy** | Copy... |\n| **paste** | Paste... |\n| **saveAs** | Save as... |\n| **about** | About |\n| **areYouSureToDeleteTheSelectedRows** | Are you sure to delete the selected rows? |\n| **areYouSureToDeleteTheSelectedColumns** | Are you sure to delete the selected columns? |\n| **thisActionWillDestroyAnyExistingMergedCellsAreYouSure** | This action will destroy any existing merged cells. Are you sure? |\n| **thisActionWillClearYourSearchResultsAreYouSure** | This action will clear your search results. Are you sure? |\n| **thereIsAConflictWithAnotherMergedCell** | There is a conflict with another merged cell |\n| **invalidMergeProperties** | Invalid merged properties |\n| **cellAlreadyMerged** | Cell already merged |\n| **noCellsSelected** | No cells selected |\n\n  \n[Working example](/jspreadsheet/v4/examples/translations)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs/special-formulas.md",
    "content": "title: Special Formulas in Jspreadsheet\nkeywords: Jspreadsheet, spreadsheet formulas, custom methods, JavaScript spreadsheets, special formulas, progress bar, star rating, spreadsheet custom features\ndescription: Explore Jspreadsheet's special formulas and custom methods to enhance spreadsheet functionality.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/special-formulas\n\n# Special formulas\n\nJspreadsheet supports spreadsheet-like formulas, along with special formulas designed to enhance usability and functionality.\n\n## Custom Jspreadsheet Methods\n\n| Method                                                                                                                                          | Example                                              |\n|-------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|\n| **=PROGRESS(int, string)** Create a progress bar.<br/>**Parameters:**<br/>`int` percent (0-100)<br/>`string` color (Hex value)                  | [Example](/jspreadsheet/v4/cases/project-management) |\n| **=RATING(int)** Create a star rating based on an integer.<br/>**Parameters:**<br/>`int` value (0-5)                                            | [Example](/jspreadsheet/v4/cases/food-store)         |\n| **=TABLE()** Return the Jspreadsheet table instance.                                                                                            |                                                      |\n| **=COLUMN()** Return the column number where the formula is executed.                                                                           |                                                      |\n| **=ROW()** Return the row number where the formula is executed.                                                                                 |                                                      |\n| **=CELL()** Return the cell string identification.                                                                                              |                                                      |\n| **=VALUE(int, int)** Return the cell value based on the column and row numbers.<br/>**Parameters:**<br/>`int` colNumber<br/>`int` rowNumber     |                                                      |\n"
  },
  {
    "path": "docs/jspreadsheet/v4/docs.md",
    "content": "title: The JavaScript Spreadsheet Documentation  \nkeywords: Jspreadsheet, Jexcel, JavaScript spreadsheet, Excel-like table, data grid, interactive spreadsheets, data management tool  \ndescription: Official Jspreadsheet CE documentation. Learn how to use the JavaScript spreadsheet for efficient data management and explore its features, examples, and FAQs.  \ncanonical: https://bossanova.uk/jspreadsheet/v4/docs\n\n# Jspreadsheet v4 Documentation\n\nJspreadsheet CE is one of the best tools for data management on the Internet. Explore the official documentation to learn about its powerful features, integrations, and best practices.\n\n- [Quick Reference](/jspreadsheet/v4/docs/quick-reference)\n- [Programmatically Changes](/jspreadsheet/v4/docs/programmatically-changes)\n- [Special Formulas](/jspreadsheet/v4/docs/special-formulas)\n- [Events](/jspreadsheet/v4/docs/events)\n- [Most Common Questions and Answers](/jspreadsheet/v4/docs/most-common-questions-and-answers)  \n- [Use Cases](/jspreadsheet/v4/cases)\n- [Examples](/jspreadsheet/v4/examples)"
  },
  {
    "path": "docs/jspreadsheet/v4/events.md",
    "content": "title: Spreadsheet Events with Jspreadsheet Version 4\nkeywords: Jspreadsheet, data grid, JavaScript, Excel-like features, spreadsheet events, event handling, customized actions, JavaScript integration, interactive spreadsheets, feature customization, event-driven programming, data grid events\ndescription: Learn more about Jspreadsheet’s comprehensive event system for advanced customization and integration.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/events\n\n# Spreadsheet Events\n\n## Custom table scripting after changes\n\nJspreadsheet offers a native feature to customize your table on the fly. You can define the method updateTable to create rules to customize the data should be shown to the user, as the example below.\n\n[See an example in action](/jspreadsheet/v4/examples/table-scripting)\n\n## Events\n\nJspreadsheet available events in this version.\n\n[Example on handling events on your spreasheet](/jspreadsheet/v4/examples/events)\n\n| Event                    | Description                                                                                                                              |  \n|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------|  \n| **onload**               | This method is called when the method setData                                                                                            |  \n| **onbeforechange**       | Before a column value is changed. NOTE: It is possible to overwrite the original value, by returning a new value on this method. v3.4.0+ |  \n| **onchange**             | After a column value is changed.                                                                                                         |  \n| **onafterchanges**       | After all changes are applied in the table.                                                                                              |  \n| **onpaste**              | After a paste action is performed in the JavaScript table.                                                                               |  \n| **onbeforepaste**        | Before the paste action is performed. Used to parse any input data, should return the data.                                              |  \n| **oninsertrow**          | After a new row is inserted.                                                                                                             |  \n| **onbeforeinsertrow**    | Before a new row is inserted. You can cancel the insert event by returning false.                                                        |  \n| **ondeleterow**          | After a row is excluded.                                                                                                                 |  \n| **onbeforedeleterow**    | Before a row is deleted. You can cancel the delete event by returning false.                                                             |  \n| **oninsertcolumn**       | After a new column is inserted.                                                                                                          |  \n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false.                                                     |  \n| **ondeletecolumn**       | After a column is excluded.                                                                                                              |  \n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false.                                                         |  \n| **onmoverow**            | After a row is moved to a new position.                                                                                                  |  \n| **onmovecolumn**         | After a column is moved to a new position.                                                                                               |  \n| **onresizerow**          | After a change in row height.                                                                                                            |  \n| **onresizecolumn**       | After a change in column width.                                                                                                          |  \n| **onselection**          | On the selection is changed.                                                                                                             |  \n| **onsort**               | After a column is sorted.                                                                                                                |  \n| **onfocus**              | On table focus                                                                                                                           |  \n| **onblur**               | On table blur                                                                                                                            |  \n| **onmerge**              | On column merge                                                                                                                          |  \n| **onchangeheader**       | On header change                                                                                                                         |  \n| **onundo**               | On undo is applied                                                                                                                       |  \n| **onredo**               | On redo is applied                                                                                                                       |  \n| **oneditionstart**       | When openEditor is called.                                                                                                               |  \n| **oneditionend**         | When closeEditor is called.                                                                                                              |  \n| **onchangestyle**        | When setStyle is called.                                                                                                                 |  \n| **onchangemeta**         | When setMeta is called.                                                                                                                  |\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/angular.md",
    "content": "title: Angular Spreadsheet with Jspreadsheet CE\nkeywords: Jexcel, javascript, using Jspreadsheet and angular\ndescription: Integrating Jspreadsheet with Angular working example.\n\n# Angular Spreadsheet with Jspreadsheet CE\n\nThis example demonstrates how to use the Jspreadsheet library into an Angular application.\n\n[View the live project on CodeSandbox](https://codesandbox.io/s/jexcel-and-angular-cexs1)\n\n## Example\n\n### Template HTML\n\nAdd the following template to your app.component.html:\n\n{.ignore}\n```html\n<div #spreadsheet></div>\n```\n\n### JavaScript\n\nHere is the complete implementation for integrating Jspreadsheet with Angular:\n\n{.ignore}\n```javascript\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\n@Component({\n  selector: \"app-root\",\n  templateUrl: \"./app.component.html\",\n  styleUrls: [\"./app.component.css\"]\n})\nexport class AppComponent {\n  @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n  title = \"CodeSandbox\";\n\n  ngAfterViewInit() {\n    jspreadsheet(this.spreadsheet.nativeElement, {\n      data: [[]],\n      columns: [\n        { type: \"dropdown\", width: \"100px\", source: [\"Y\", \"N\"] },\n        { type: \"color\", width: \"100px\", render: \"square\" }\n      ],\n      minDimensions: [10, 10]\n    });\n  }\n}\n```  \n\n### Angular Styles Configuration\n\nTo ensure the styles for Jspreadsheet are applied correctly, update the angular.json file to include the Jspreadsheet CSS:\n\n```json\n\"styles\": [\"styles.css\",\"./node_modules/jspreadsheet-ce/dist/jspreadsheet.css\"],\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/column-dragging.md",
    "content": "title: Enable Column Dragging\nkeywords: Jexcel, spreadsheet, JavaScript, JavaScript table, column dragging\ndescription: Learn how to enable column dragging in Jspreadsheet CE.\n\n# Column Dragging\n\nBy default, column dragging is disabled in Jspreadsheet. To enable it, use the `columnDrag: true` option during initialization, as shown below:\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ],\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '300',\n            url: '/jspreadsheet/countries'\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width:'100'\n        },\n    ],\n    columnDrag:true,\n});\n</script>\n</html>\n```  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/column-filters.md",
    "content": "title: Applying Filters on Columns\nkeywords: Jexcel, JavaScript, column filters, filters, dynamic tables, online spreadsheet\ndescription: Learn how to enable column filters in Jspreadsheet to enhance functionality in your online spreadsheets.\n\n# Column Filters\n\nEnable column filters on your JavaScript dynamic tables to enhance data interaction and usability.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ],\n    columns: [\n        {\n            type: 'dropdown',\n            url:'/jspreadsheet/countries',\n            width:200,\n        },\n        {\n            type: 'text',\n            width:200,\n        },\n        {\n            type: 'calendar',\n            width:200,\n        },\n        {\n            type: 'checkbox',\n            width:200,\n        }\n     ],\n     filters: true,\n     allowComments:true,\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/column-types.md",
    "content": "title: Column types\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Learn more about the powerful column types. This example brings all native column types and how to create your own custom type.\n\n# Column types\n\nThe native available types in jspreadsheet javascript spreadsheet are the following:\n\n  * text\n  * numeric\n  * hidden\n  * dropdown\n  * autocomplete\n  * checkbox\n  * radio\n  * calendar\n  * image\n  * color\n  * html\n  \n\n## Native column types\n\nThere are several other properties to change the behavior of those columns, please check the dropdown, calendar examples to get more advanced examples.\n\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nvar data = [\n    ['Jazz', 'Honda', '2019-02-12', '', true, '$ 2.000,00', '#777700'],\n    ['Civic', 'Honda', '2018-07-11', '', true, '$ 4.000,01', '#007777'],\n];\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { type: 'text', title:'Car', width:120 },\n        { type: 'dropdown', title:'Make', width:200, source:[ \"Alfa Romeo\", \"Audi\", \"Bmw\" ] },\n        { type: 'calendar', title:'Available', width:200 },\n        { type: 'image', title:'Photo', width:120 },\n        { type: 'checkbox', title:'Stock', width:80 },\n        { type: 'numeric', title:'Price', width:100, mask:'$ #.##,00', decimal:',' },\n        { type: 'color', width:100, render:'square', }\n     ]\n});\n</script>\n</html>\n```  \n\n## Custom column type\n\nJspreadsheet is very powerful and flexible, and you can create custom column type based on any external plugins.\n\nA time custom column based on the [clockpicker plugin](https://weareoutman.github.io/clockpicker/) by weareoutman.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"http://weareoutman.github.io/clockpicker/dist/jquery-clockpicker.min.css\" />\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n<script src=\"http://weareoutman.github.io/clockpicker/dist/jquery-clockpicker.min.js\"></script>\n\n<div id=\"custom\"></div>\n\n<script>\nvar data2 = [\n    ['PHP', '14:00'],\n    ['Javascript', '16:30'],\n];\n\nvar customColumn = {\n    // Methods\n    closeEditor : function(cell, save) {\n        var value = cell.children[0].value;\n        cell.innerHTML = value;\n        return value;\n    },\n    openEditor : function(cell) {\n        // Create input\n        var element = document.createElement('input');\n        element.value = cell.innerHTML;\n        // Update cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(element);\n        $(element).clockpicker({\n            afterHide:function() {\n                setTimeout(function() {\n                    // To avoid double call\n                    if (cell.children[0]) {\n                        spreadsheet.closeEditor(cell, true);\n                    }\n                });\n            }\n        });\n        // Focus on the element\n        element.focus();\n    },\n    getValue : function(cell) {\n        return cell.innerHTML;\n    },\n    setValue : function(cell, value) {\n        cell.innerHTML = value;\n    }\n}\n\nspreadsheet = jspreadsheet(document.getElementById('custom'), {\n    data:data2,\n    columns: [\n        { type: 'text', title:'Course Title', width:300 },\n        { type: 'text', title:'Time', width:100, editor:customColumn },\n     ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/comments.md",
    "content": "title: Allow comments in your javascript table\nkeywords: Jexcel, spreadsheet, javascript, cell comments, javascript table\ndescription: Allow comments in your table spreadsheet.\n\n# Spreadsheet comments\n\nThe javascript spreadsheet plugin allows the user to set custom comments for each individual cells. By allowComments in the initialization, the user will be able to add comments in the rigth click contextMenu.\n\n## Manage cell comments programmatically\n\nTo apply comments via javascript, you can use the methods setComments or getComments, as follow:\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ],\n    columns: [\n        {\n            type: 'dropdown',\n            url:'/jspreadsheet/countries',\n            width:200,\n        },\n        {\n            type: 'text',\n            width:200,\n        },\n        {\n            type: 'calendar',\n            width:200,\n        }\n     ],\n     allowComments:true,\n});\n\ndocument.getElementById(\"setComments\").onclick = () => table.setComments('A1', 'This is the comments from A1');\ndocument.getElementById(\"getComments\").onclick = () => alert(table.getComments('A1'));\ndocument.getElementById(\"resetComments\").onclick = () => table.setComments('A1', '');\n</script>\n\n<br/>\n<button type=\"button\" id=\"setComments\" style=\"width:220px;\">Set A1 comments</button>\n<button type=\"button\" id=\"getComments\" style=\"width:220px;\">Get A1 comments</button>\n<button type=\"button\" id=\"resetComments\" style=\"width:220px;\">Reset A1 comments</button>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/contextmenu.md",
    "content": "title: Custom contextmenu\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: How to customize jspreadsheet contextmenu\n\n# Custom contextmenu\n\nThe following example remove the copy and paste from the contextmenu in order to create a custom contextMenu\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n        ['CA', 'Carrots', '2018-11-10'],\n        ['BR', 'Oranges', '2019-01-12'],\n    ],\n    columns: [\n        { type: 'dropdown', url:'/jspreadsheet/countries', width:200, },\n        { type: 'text', width:200, },\n        { type: 'calendar', width:100, }\n     ],\n     allowComments: true,\n     contextMenu: function(obj, x, y, e) {\n         var items = [];\n\n         if (y == null) {\n             // Insert a new column\n             if (obj.options.allowInsertColumn == true) {\n                 items.push({\n                     title:obj.options.text.insertANewColumnBefore,\n                     onclick:function() {\n                         obj.insertColumn(1, parseInt(x), 1);\n                     }\n                 });\n             }\n\n             if (obj.options.allowInsertColumn == true) {\n                 items.push({\n                     title:obj.options.text.insertANewColumnAfter,\n                     onclick:function() {\n                         obj.insertColumn(1, parseInt(x), 0);\n                     }\n                 });\n             }\n\n             // Delete a column\n             if (obj.options.allowDeleteColumn == true) {\n                 items.push({\n                     title:obj.options.text.deleteSelectedColumns,\n                     onclick:function() {\n                         obj.deleteColumn(obj.getSelectedColumns().length ? undefined : parseInt(x));\n                     }\n                 });\n             }\n\n             // Rename column\n             if (obj.options.allowRenameColumn == true) {\n                 items.push({\n                     title:obj.options.text.renameThisColumn,\n                     onclick:function() {\n                         obj.setHeader(x);\n                     }\n                 });\n             }\n\n             // Sorting\n             if (obj.options.columnSorting == true) {\n                 // Line\n                 items.push({ type:'line' });\n\n                 items.push({\n                     title:obj.options.text.orderAscending,\n                     onclick:function() {\n                         obj.orderBy(x, 0);\n                     }\n                 });\n                 items.push({\n                     title:obj.options.text.orderDescending,\n                     onclick:function() {\n                         obj.orderBy(x, 1);\n                     }\n                 });\n             }\n         } else {\n             // Insert new row\n             if (obj.options.allowInsertRow == true) {\n                 items.push({\n                     title:obj.options.text.insertANewRowBefore,\n                     onclick:function() {\n                         obj.insertRow(1, parseInt(y), 1);\n                     }\n                 });\n                 \n                 items.push({\n                     title:obj.options.text.insertANewRowAfter,\n                     onclick:function() {\n                         obj.insertRow(1, parseInt(y));\n                     }\n                 });\n             }\n\n             if (obj.options.allowDeleteRow == true) {\n                 items.push({\n                     title:obj.options.text.deleteSelectedRows,\n                     onclick:function() {\n                         obj.deleteRow(obj.getSelectedRows().length ? undefined : parseInt(y));\n                     }\n                 });\n             }\n\n             if (x) {\n                 if (obj.options.allowComments == true) {\n                     items.push({ type:'line' });\n\n                     var title = obj.records[y][x].getAttribute('title') || '';\n\n                     items.push({\n                         title: title ? obj.options.text.editComments : obj.options.text.addComments,\n                         onclick:function() {\n                             obj.setComments([ x, y ], prompt(obj.options.text.comments, title));\n                         }\n                     });\n\n                     if (title) {\n                         items.push({\n                             title:obj.options.text.clearComments,\n                             onclick:function() {\n                                 obj.setComments([ x, y ], '');\n                             }\n                         });\n                     }\n                 }\n             }\n         }\n\n         // Line\n         items.push({ type:'line' });\n\n         // Save\n         if (obj.options.allowExport) {\n             items.push({\n                 title: obj.options.text.saveAs,\n                 shortcut: 'Ctrl + S',\n                 onclick: function () {\n                     obj.download();\n                 }\n             });\n         }\n\n         // About\n         if (obj.options.about) {\n             items.push({\n                 title:obj.options.text.about,\n                 onclick:function() {\n                     alert(obj.options.about);\n                 }\n             });\n         }\n\n         return items;\n     }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/create-from-table.md",
    "content": "title: Create a Data Grid From a HTML table\nkeywords: Jexcel, javascript, create a dynamic jspreadsheet table from a HTML table element.\ndescription: A full example on how to create a dynamic jspreadsheet table from a HTML table.\n\n# Create a Data Grid From a HTML table\n\nFrom the v4+ is is possible to create a online spreadsheet from a static simple HTML table.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<h4>The Official Top 40 biggest albums of 2019</h4>\n\n<table id=\"spreadsheet\">\n<thead>\n<tr>\n<td colspan='4'>General</td>\n</tr>\n<tr>\n<td colspan='3'>Info</td>\n<td colspan='1'>Stats</td>\n</tr>\n<tr>\n<td id='pos'>POS</td>\n<td id='title'>TITLE</td>\n<td id='artist'>ARTIST</td>\n<td id='peak'>PEAK</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>DIVINELY UNINSPIRED TO A HELLISH EXTENT</td>\n<td>LEWIS CAPALDI</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>NO 6 COLLABORATIONS PROJECT</td>\n<td>ED SHEERAN</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>THE GREATEST SHOWMAN</td>\n<td>MOTION PICTURE CAST RECORDING</td>\n<td>1</td>\n</tr>\n<tr>\n<td>4</td>\n<td>WHEN WE ALL FALL ASLEEP WHERE DO WE GO</td>\n<td>BILLIE EILISH</td>\n<td>1</td>\n</tr>\n<tr>\n<td>5</td>\n<td>STAYING AT TAMARA'S</td>\n<td>GEORGE EZRA</td>\n<td>1</td>\n</tr>\n<tr>\n<td>6</td>\n<td>BOHEMIAN RHAPSODY - OST</td>\n<td>QUEEN</td>\n<td>3</td>\n</tr>\n<tr>\n<td>7</td>\n<td>THANK U NEXT</td>\n<td>ARIANA GRANDE</td>\n<td>1</td>\n</tr>\n<tr>\n<td>8</td>\n<td>WHAT A TIME TO BE ALIVE</td>\n<td>TOM WALKER</td>\n<td>1</td>\n</tr>\n<tr>\n<td>9</td>\n<td>A STAR IS BORN</td>\n<td>MOTION PICTURE CAST RECORDING</td>\n<td>1</td>\n</tr>\n<tr>\n<td>10</td>\n<td>YOU'RE IN MY HEART</td>\n<td>ROD STEWART</td>\n<td>1</td>\n</tr>\n</tbody>\n<tfoot>\n<tr>\n<td></td>\n<td></td>\n<td></td>\n<td>=SUMCOL(3)</td>\n</tr>\n</tfoot>\n</table>\n\n<br>\n\n<script>\n// A custom method to SUM all the cells in the current column\nlet SUMCOL = function(columnId) {\n    let instance = jspreadsheet.current\n\n    let total = 0;\n    for (let j = 0; j < instance.options.data.length; j++) {\n        if (Number(instance.records[j][columnId].innerHTML)) {\n            total += Number(instance.records[j][columnId].innerHTML);\n        }\n    }\n    return total;\n}\n\nformula.setFormula({ SUMCOL })\n\njspreadsheet(document.getElementById('spreadsheet')); \n</script>\n</html>\n```\n\n### More examples\n\n  * [Including merged cells](https://jsfiddle.net/spreadsheet/45h6odug/ \"Merged cells\")\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/custom-table-design.md",
    "content": "title: Custom Table Design\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Customized CSS for your datagrid\n\n# Create custom CSS for your javascript spreadsheet\n\nThe following example shows a CSS addon to change the core layout of your jquery tables.\n\n## Green borders and corners jquery spreadsheet\n\n[Bootstrap-like jquery spreadsheet example](/jspreadsheet/examples/a-custom- table-design)\n\n## Bootstrap-like jquery spreadsheet.\n\nYour jquery table can be customized by including an additional addon CSS. If you have created a nice design, please share with us.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<!-- Optional CSS addon -->\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jspreadsheet/2.0.0/css/jquery.jexcel.green.min.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/jspreadsheet/2.0.0/css/jquery.jexcel.bootstrap.min.css\" type=\"text/css\" />\n\n\n<div id=\"my\"></div>\n\n<script>\ndata = [\n    ['BR', 'Cheese', 1, 1900.01],\n    ['CA', 'Apples', 0, 1200.21],\n    ['US', 'Carrots', 1, 400.34],\n    ['GB', 'Oranges', 0, 900.43],\n];\n\n$('#my').jspreadsheet({\n    data:data,\n    colHeaders: ['Country', 'Food', 'Stock', 'Price'],\n    colWidths: [ 300, 100, 100, 100 ],\n    colAlignments: [ 'left', 'left', 'left', 'left' ],\n    columns: [\n        { type: 'autocomplete', url:'/jspreadsheet/countries' },\n        { type: 'autocomplete', source:['Apples','Bananas','Carrots','Oranges','Cheese'] },\n        { type: 'checkbox' },\n        { type: 'number' },\n    ]\n});\n</script>\n</html>\n```\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/datatables.md",
    "content": "title: Searchable datatable\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: Full spreadsheet example with search and pagination to bring great compatibility for those who love datatables.\n\n# Spreadsheet Search and Pagination\n\nThe following example shows how to create a javascript spreadsheet instance with a design similar to datatables jquery plugin.\n\n### Source code\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.datatables.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    csv:'/tests/demo.csv',\n    csvHeaders:true,\n    search:true,\n    pagination:10,\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:200 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n     ]\n});    \n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/date-and-datetime-picker.md",
    "content": "title: Calendar with date and datetime picker\nkeywords: Jexcel, javascript, excel-like, spreadsheet, date, datetime, calendar\ndescription: Example from basic to advanced calendar usage, date and datetime picker\n\n# Calendar column type\n\nThe example below shows how to use and customize special calendar column type.\n\nJspreadsheet uses the jSuites [Javascript Calendar](https://jsuites.net/docs/javascript-calendar) plugin\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Flag Fen', 'South East', '2019-01-01'],\n        ['Bristol Aero Collection (BAC)','South West','2019-04-03'],\n        ['Experience Barnsley', 'North','2018-12-03'],\n        ['Cinema Museum', 'London',''],\n        ['University of Hertfordshire Art Collection', 'South East',''],\n        ['LUX London', 'London','2016-11-03'],\n    ],\n    columns: [\n        {\n            type:'text',\n            title:'Museum',\n            width:'300',\n        },\n        {\n            type:'dropdown',\n            title:'Region',\n            source:['South East','South West','North','London'],\n            width:'200',\n        },\n        {\n            type:'calendar',\n            title:'Last visit',\n            options: { format:'DD/MM/YYYY' },\n            width:'100',\n        },\n    ]\n});\n</script>\n</html>\n```  \n\n## Date column customization\n\nCustomize the format and the behavior of your column through the initialization options, as follow:\n\n{.ignore}\n```javascript\noptions : {\n    // Date format\n    format:'DD/MM/YYYY',\n    // Allow keyboard date entry\n    readonly:0,\n    // Today is default\n    today:0,\n    // Show timepicker\n    time:0,\n    // Show the reset button\n    resetButton:true,\n    // Placeholder\n    placeholder:'',\n    // Translations can be done here\n    months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n    weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],\n    weekdays_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'],\n    // Value\n    value:null,\n    // Events\n    onclose:null,\n    onchange:null,\n    // Fullscreen (this is automatic set for screensize < 800)\n    fullscreen:false,\n};\n```\n\nMore information about the jSuites [Responsive date time picker](https://jsuites.net/docs/javascript-calendar)\n\n\nConsidering the example above, you can create a calendar including a time picker by simple send the option **time:1** as the following example.\n\n```html\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Flag Fen', 'South East', '2019-01-01'],\n        ['Bristol Aero Collection (BAC)','South West','2019-04-03'],\n        ['Experience Barnsley', 'North','2018-12-03'],\n        ['Cinema Museum', 'London',''],\n        ['University of Hertfordshire Art Collection', 'South East',''],\n        ['LUX London', 'London','2016-11-03'],\n    ],\n    columns: [\n        {\n            type:'text',\n            title:'Museum',\n            width:'300',\n        },\n        {\n            type:'dropdown',\n            title:'Region',\n            source:['South East','South West','North','London'],\n            width:'200',\n        },\n        {\n            type:'calendar',\n            title:'Last visit',\n            options: { format:'DD/MM/YYYY HH24:MI', time:1 },\n            width:'100',\n        },\n    ]\n});\n</script>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/dropdown-and-autocomplete.md",
    "content": "title: Advanced dropdown column type\nkeywords: Jexcel, jquery, javascript, excel-like, spreadsheet, jquery plugin, sorting, table, grid, order by\ndescription: Full examples on how to handle simple, advanced, multiple, autocomplete and conditional dropdowns. Create amazing javascript tables using categories and images in your dropdowns.\n\n# Dropdown and autocomplete column type\n\nJspreadsheet brings a very powerful, flexible and responsive dropdown column type to support a better user experience through your applications. The new dropdown column options include autocomplete, multiple options, data picker, different template types and much more advantages, such as:\n\n  * Create a simple dropdown from array\n  * Value or key-value select is available\n  * Populate a dropdown from a external JSON request\n  * Dynamic autocomplete search based on another column value\n  * Conditional dropdowns: options from a dropdown based on a method return\n  * Multiple selection and internal dropdown search\n  * Responsive data picker with multiple render types\n  * Image icon and group items\n\n  \n\n## Multiple and autocomplete options\n\nThe highlight features introduced in the most version of the vanilla javascript spreadsheet is definitely the autocomplete and multiple options.\n\nThe following example shows the column Product Origin with the autocomplete and multiple directives initiated as true.\n\n  \n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Wholemeal', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Breakfast Cereals', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Grains', 'No', '2018-11-10'],\n        ['BR', 'Pasta', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        { type:'dropdown', width:'300', title:'Product Origin', url:'/jspreadsheet/countries', autocomplete:true, multiple:true },\n        { type:'text', width:'200', title:'Description' },\n        { type:'dropdown', width:'100', title:'Stock', source:['No','Yes'] },\n        { type:'calendar', width:'100', title:'Best before' },\n    ]\n});    \n</script>\n</html>\n```  \n\n## Conditional dropdown\n\nThe example below shows the dependency of the second column in relation to the first.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nconst dropdownFilter = function(instance, cell, c, r, source) {\n    var value = instance.jexcel.getValueFromCoords(c - 1, r);\n    if (value == 1) {\n        return ['Apples','Bananas','Oranges'];\n    } else if (value == 2) {\n        return ['Carrots'];\n    } else {\n        return source;\n    }\n}\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [3, 'Cheese', true],\n        [1, 'Apples', true],\n        [2, 'Carrots', true],\n        [1, 'Oranges', false],\n    ],\n    columns: [\n        { type:'dropdown', title:'Category', width:'300', source:[ {'id':'1', 'name':'Fruits'}, {'id':'2', 'name':'Legumes'}, {'id':'3', 'name':'General Food'} ] },\n        { type:'dropdown', title:'Food', width:'200', source:['Apples','Bananas','Carrots','Oranges','Cheese'], filter:dropdownFilter },\n        { type: 'checkbox', title:'Buy', width:'100' },\n    ],\n    onchange:function(instance, cell, c, r, value) {\n        if (c == 0) {\n            var columnName = jspreadsheet.getColumnNameFromId([c + 1, r]);\n            instance.jexcel.setValue(columnName, '');\n        }\n    }\n});\n</script>\n</html>\n```  \n\n## Group, images, and advanced render options\n\nImprove the user experience with a responsive data picker.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [1, 'Morning'],\n        [2, 'Morning'],\n        [3, 'Afternoon'],\n        [3, 'Evening'],\n    ],\n    columns: [\n        {\n            type:'dropdown',\n            title:'Category',\n            width:'300',\n            source:[\n                { id:'1', name:'Paulo', image:'/templates/default/img/1.jpg', title:'Admin', group:'Secretary' },\n                { id:'2', name:'Cosme Sergio', image:'/templates/default/img/2.jpg', title:'Teacher', group:'Docent' },\n                { id:'3', name:'Rose Mary', image:'/templates/default/img/3.png', title:'Teacher', group:'Docent' },\n                { id:'4', name:'Fernanda', image:'/templates/default/img/3.png', title:'Admin', group:'Secretary' },\n                { id:'5', name:'Roger', image:'/templates/default/img/3.png', title:'Teacher', group:'Docent' },\n            ]\n        },\n        {\n            type:'dropdown',\n            title:'Working hours',\n            width:'200',\n            source:['Morning','Afternoon','Evening'],\n            options: { type:'picker' },\n        },\n    ]\n});\n</script>\n</html>\n```\n\nMore options for the dropdowns, please refer to the [jSuites documentation](https://jsuites.net/v4).\n\n  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/events.md",
    "content": "title: Handling events on Jspreadsheet\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table, grid, events\ndescription: Learn how to handle events on Jspreadsheet\n\n# Handling events\n\n## Various tracking javascript methods\n\nBinding events on your javascript spreadsheet.\n\n[See a list of all event handlers](/jspreadsheet/v4/docs/events)\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet changed = function(instance, cell, x, y, value) {\n    let cellName = jspreadsheet.getColumnNameFromId([x,y]);\n    $('#log').append('New change on cell ' + cellName + ' to: ' + value + '  ');\n}\n\nlet beforeChange = function(instance, cell, x, y, value) {\n    let cellName = jspreadsheet.getColumnNameFromId([x,y]);\n    $('#log').append('The cell ' + cellName + ' will be changed');\n}\n\nlet insertedRow = function(instance) {\n    $('#log').append('Row added');\n}\n\nlet insertedColumn = function(instance) {\n    $('#log').append('Column added');\n}\n\nlet deletedRow = function(instance) {\n    $('#log').append('Row deleted');\n}\n\nlet deletedColumn = function(instance) {\n    $('#log').append('Column deleted');\n}\n\nlet sort = function(instance, cellNum, order) {\n    order = (order) ? 'desc' : 'asc';\n    $('#log').append('The column  ' + cellNum + ' sorted by ' + order + ' ');\n}\n\nlet resizeColumn = function(instance, cell, width) {\n    $('#log').append('The column  ' + cell + ' resized to width ' + width + ' px');\n}\n\nlet resizeRow = function(instance, cell, height) {\n    $('#log').append('The row  ' + cell + ' resized to height ' + height + ' px');\n}\n\nlet selectionActive = function(instance, x1, y1, x2, y2, origin) {\n    let cellName1 = jspreadsheet.getColumnNameFromId([x1, y1]);\n    let cellName2 = jspreadsheet.getColumnNameFromId([x2, y2]);\n    $('#log').append('The selection from ' + cellName1 + ' to ' + cellName2 + ' ');\n}\n\nlet loaded = function(instance) {\n    $('#log').append('New data is loaded');\n}\n\nlet moveRow = function(instance, from, to) {\n    $('#log').append('The row ' + from + ' was move to the position of ' + to + '  ');\n}\n\nlet moveColumn = function(instance, from, to) {\n    $('#log').append('The col ' + from + ' was move to the position of ' + to + '  ');\n}\n\nlet blur = function(instance) {\n    $('#log').append('The table ' + $(instance).prop('id') + ' is blur');\n}\n\nlet focus = function(instance) {\n    $('#log').append('The table ' + $(instance).prop('id') + ' is focus');\n}\n\nlet paste = function(data) {\n    $('#log').append('Paste on the table ' + $(instance).prop('id') + ' ');\n}\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    rowResize:true,\n    columnDrag:true,\n    columns: [\n        { type: 'text', width:'200' },\n        { type: 'text', width:'100' },\n        { type: 'text', width:'100' },\n        { type: 'calendar', width:'100' },\n    ],\n    onchange: changed,\n    onbeforechange: beforeChange,\n    oninsertrow: insertedRow,\n    oninsertcolumn: insertedColumn,\n    ondeleterow: deletedRow,\n    ondeletecolumn: deletedColumn,\n    onselection: selectionActive,\n    onsort: sort,\n    onresizerow: resizeRow,\n    onresizecolumn: resizeColumn,\n    onmoverow: moveRow,\n    onmovecolumn: moveColumn,\n    onload: loaded,\n    onblur: blur,\n    onfocus: focus,\n    onpaste: paste,\n});\n</script>\n</html>\n```\n\n## Global Super event\n\nOne method to handle all events on the online spreadsheet.\n\n**NOTE** : Open the console to see the events.\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/footers.md",
    "content": "title: Adding formulas fixed on the table footer\nkeywords: Jexcel, javascript, multiple spreadsheets, formulas, table footer\ndescription: Adding formulas fixed on the table footer.\n\n# Table footer\n\nAdding fixed custom calculations in the footer of an online spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n// A custom method to SUM all the cells in the current column\nlet SUMCOL = function(columnId) {\n    let instance = jspreadsheet.current\n\n    let total = 0;\n    for (let j = 0; j < instance.options.data.length; j++) {\n        if (Number(instance.records[j][columnId].innerHTML)) {\n            total += Number(instance.records[j][columnId].innerHTML);\n        }\n    }\n    return total;\n}\n\nformula.setFormula({ SUMCOL })\n\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Cheese', 10, 6.00, \"=B1*C1\"],\n        ['Apples', 5, 4.00, \"=B2*C2\"],\n        ['Carrots', 5, 1.00, \"=B3*C3\"],\n        ['Oranges', 6, 2.00, \"=B4*C4\"],\n    ],\n    minDimensions: [4,10],\n    columnDrag:true,\n    footers: [['Total','=SUMCOL(1)','=SUMCOL(2)','=SUMCOL(3)']],\n    columns: [{\n        width:'200px',\n    }]\n});\n</script>\n</html>\n```  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/freeze-columns.md",
    "content": "title: Freeze columns\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data, frezee columns\ndescription: Setup freeze columns in Jspreadsheet\n\n# Freeze columns\n\nDefine the number of freeze columns by using the freezeColumn directive with tableOverflow as follow\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: null,\n    minDimensions: [20,50000],\n    tableOverflow: true,\n    lazyLoading: true,\n    tableWidth: '600px',\n    freezeColumns: 2\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/headers.md",
    "content": "title: Header updates & column dragging\nkeywords: Jexcel, spreadsheet, javascript, header updates, programmatically header updates, enable column dragging\ndescription: Header updates and column dragging\n\n# Header updates\n\nThere are three ways to change a header title.\n\n  * The user clicks in a selected header and hold the mouse for 2 seconds, a prompt will request the new title;\n  * Via contextMenu. the user right clicks in the column and select the option Rename column\n  * Using the method setHeader(colNumber, title) as example below:\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ],\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '300',\n            url: '/jspreadsheet/countries'\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width:'100'\n        },\n    ]\n});\n\nsetHeaderBtn.onclick = () => table.setHeader(document.getElementById('columnNumber').value)\ngetHeaderBtn.onclick = () => alert(table.getHeader(document.getElementById('columnNumber').value))\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value=\"0\">Column 0</option>\n    <option value=\"1\">Column 1</option>\n    <option value=\"2\">Column 2</option>\n</select>\n\n<input type='button' value='Set header title' id=\"setHeaderBtn\" />\n<input type='button' value='Get header title' id=\"getHeaderBtn\" />\n</html>\n```  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/image-upload.md",
    "content": "title: Embed images to your spreadsheet using base64\nkeywords: Jexcel, javascript, excel-like, spreadsheet, image upload, base64, embed images\ndescription: This examples shows how to embed and upload images to your spreadsheet\n\n# Embed images into your spreadsheet\n\nThe following examples shows how to embed images into your spreadsheet.\n\n  \n\n## Load a local image to your table\n\nLoad images from your local machine into your javascript spreadsheet\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet data = [\n    ['Purple', 'data:image/jpeg;base64,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'],\n];\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: data,\n    minDimensions: [2,4],\n    columns: [\n        { type:'text', width:300, title:'Title' },\n        { type:'image', width:120, title:'Cover' },\n\n     ]\n});    \n</script>\n</html>\n```  \n  \n\n## Embed remote images to your table\n\nAutomatic image rendering from a remote URL using updateTable method\n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['https://marketplace.canva.com/MACcZp2p4po/2/0/thumbnail_large/canva-black-white-acoustic-album-cover-MACcZp2p4po.jpg', 'Paul Parker'],\n        ['https://marketplace.canva.com/MACcY55adP4/1/0/thumbnail_large/canva-black-and-white-masculine-acoustic-modern-album-cover-MACcY55adP4.jpg', 'Mark Ellen']\n    ],\n    columns: [\n        { type:'text', width:300, title:'Cover' },\n        { type:'text', width:300, title:'Title' },\n    ],\n    updateTable: function (instance, cell, col, row, val, id) {\n        if (col == 0) {\n            cell.innerHTML = '<img src=\"' + val + '\" style=\"width:100px;height:100px\">';\n        }\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/import-data.md",
    "content": "title: Load data from CSV or JSON or XLSX\nkeywords: Jexcel, javascript, excel-like, spreadsheet, loading data, csv, json, xlsx.\ndescription: How to import data from an external CSV, json file or XLSX.\n\n# Create a javascript spreadsheet\n\nThere are a few different ways to load data to your javascript spreadsheet shown in the next four examples below\n\n  \n\n## Based on a external CSV file\n\nThe example below helps you to create a javascript spreadsheet table based on a remote CSV file, including the headers.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<p><button id='download'>Export my spreadsheet as CSV</button></p>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    csv:'/jspreadsheet/arts.csv',\n    csvHeaders:true,\n    tableOverflow:true,\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:80 },\n        { type:'dropdown', width:120, source:['England','Wales','Northern Ireland','Scotland'] },\n        { type:'text', width:120 },\n        { type:'text', width:120 },\n     ]\n});\n\ndocument.getElementById('download').onclick = function () {\n    table.download();\n}\n</script>\n</html>\n```  \n  \n\n## Based on an external JSON file\n\nIn a similar way, you can create a table based on an external JSON file format by using the _url: directive_ as below.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet2\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet2'), {\n    url: '/jspreadsheet/test.json',\n    columns: [\n        { type:'text', width:300 },\n        { type:'text', width:100 },\n     ]\n});\n</script>\n</html>\n```  \n  \n  \n\n## Based on an JSON object\n\nThe data directiva can be used to define a JSON object. In this case you can define by the name directive the order of the columns.\n\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data:[\n        {\n            name:'Paulo',\n            id:'3',\n            age:'40',\n            gender:'Male'\n        },\n        {\n            name:'Cosme Sergio',\n            id:'4',\n            age:'48',\n            gender:'Male'\n        },\n        {\n            name:'Jorgina Santos',\n            id:'5',\n            age:'32',\n            gender:'Female'\n        },\n    ],\n    columns: [\n        {\n            type:'text',\n            width:'40',\n            name:'id',\n            title:'Id',\n        },\n        {\n            type:'text',\n            width:'200',\n            name:'name',\n            title:'Name',\n        },\n        {\n            type:'text',\n            width:'100',\n            name:'age',\n            title:'Age',\n        },\n        {\n            type:'hidden',\n            name:'gender'\n        },\n     ]\n});\n</script>\n</html>\n```  \n  \n  \n\n## Importing from a XLSX file\n\nThe following example imports the data from a XLSX file using a thirdy party library, slighly customized in order to improve the CSS parser.\n\nIMPORTANT: This is an experimental implementation and there is no garantee your spreadsheet will be correctly parsed.\n\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.13.5/jszip.js\"></script>\n<script src=\"https://bossanova.uk/jspreadsheet/v3/xlsx.js\"></script>\n\n<div id=\"jspreadsheet\"></div>\n\n<script>\njspreadsheet.fromSpreadsheet('/jspreadsheet/list.xlsx', function(result) {\n    if (! result.length) {\n        console.error('jspreadsheet: Something went wrong.');\n    } else {\n        if (result.length == 1) {\n            jspreadsheet(document.getElementById('jspreadsheet'), result[0]);\n        } else {\n            jspreadsheet.createTabs(document.getElementById('jspreadsheet'), result);\n        }\n    }\n});\n</script>\n</html>\n```\n\n**NOTE** : This example is based on a customized version of the free version of [Sheetjs](https://sheetjs.com/). There is no garantee in the use of this library. Please consider purchase their professional version.\n\n  \n  \n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/jquery.md",
    "content": "title: Jspreadsheet with Jquery\nkeywords: Jexcel, javascript, using Jspreadsheet and Jquery\ndescription: A full example on how to integrate Jspreadsheet with Jquery\n\n# Jquery\n\nCreating a Jspreadsheet javascript instance using jQuery\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<br/>\n<input type=\"button\" value=\"Add new row\" id=\"addRow\" />\n\n<script>\nlet options = {\n    minDimensions:[10,10],\n    tableOverflow:true,\n}\n\nlet table = jspreadsheet($('#spreadsheet')[0], options); \n\ndocument.getElementById(\"addRow\").onclick = () => table.insertRow()\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/lazy-loading.md",
    "content": "title: Dealing with big spreadsheets through lazy loading\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data\ndescription: This example brings a very nice feature to deal with large table datasets.\n\n# Lazy loading\n\nThe following table is dealing with 60.000 columns. The lazyloading method allows render up to 130 rows at the same time and will render other rows based on the scrolling.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    csv:'/jspreadsheet/demo1.csv',\n    csvHeaders:false,\n    tableOverflow:true,\n    lazyLoading:true,\n    loadingSpin:true,\n    columns: [\n        {\n            type:'text',\n            width:200,\n            title:'Name'\n        },\n        {\n            type:'dropdown',\n            width:100,\n            title:'Age',\n            source:[\n            {\n                id:1,name:'Male'\n            },\n            {\n                id:2,\n                name:'Female'\n            }]\n        },\n        {\n            type:'text',\n            width:200,\n            title:'City'\n        },\n     ]\n});    \n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/merged-cells.md",
    "content": "title: How to merge the spreadsheet cells\nkeywords: Jexcel, spreadsheet, javascript, javascript table, merged cells\ndescription: Full example on how to handle merge cells in your javascript tables.\n\n# Merged cells\n\nYou can merge cells on your spreadsheet in the table initialization or programatically as follow:\n\nThe following methods are available for merge cells management: _setMerge, getMerge, removeMerge, destroyMerged_\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n<div id=\"console\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    colHeaders: ['Model', 'Year', 'Price', 'Date'],\n    colWidths: [ 300, 80, 100, 100 ],\n    columns: [\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'text' },\n        { type: 'calendar' },\n    ],\n    mergeCells:{\n        A1:[2,1]\n    },\n    minDimensions:[10,10]\n});\n\ndocument.getElementById(\"setMerge\").onclick = () => table.setMerge('A3', 2, 2);\ndocument.getElementById(\"removeMerge\").onclick = () => table.removeMerge('A3');\ndocument.getElementById(\"getAllMerged\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getMerge());\ndocument.getElementById(\"destroyMerged\").onclick = () => table.destroyMerged();\n</script>\n\n<br/>\n<button type=\"button\" id=\"setMerge\">Merge cell A3 (colspan: 2, rowspan: 2)</button>\n<button type=\"button\" id=\"removeMerge\">Destroy merge from A3</button>\n<button type=\"button\" id=\"getAllMerged\">Get all merged cells</button>\n<button type=\"button\" id=\"destroyMerged\">Destroy all merged</button>\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/meta-information.md",
    "content": "title: Meta information\nkeywords: Javascript spreadsheet, javascript, javascript table, meta information\ndescription: Keep hidden information about your cells using meta information methods\n\n# Meta information\n\nThis feature helps you keep import information about the cells hidden from users.\n\nYou can define any meta information during the initialization or programatically after that thought getMeta or setMeta methods.\n\nSet meta data for multiple columns Set a meta information for B2 Get the meta information for A1 Get all meta information  \n\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n<div id=\"console\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Apples', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Carrots', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Oranges', 'No', '2018-11-10'],\n        ['BR', 'Coconuts', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        { type: 'dropdown', title: 'Product Origin', width: '300px', url: '/jspreadsheet/countries', autocomplete: true, multiple: true },\n        { type: 'text', title: 'Description', width: '200px' },\n        { type: 'dropdown', title: 'Stock', width: '100px', source: ['No','Yes'] },\n        { type: 'calendar', title: 'Best before', width: '100px' },\n    ],\n    meta:{\n        A1: { myMeta: 'this is just a test', otherMetaInformation: 'other test' },\n        A2: { info: 'test' }\n    }\n});\n\ndocument.getElementById(\"setForMultiple\").onclick = () => table.setMeta({ C1: { id:'1', y:'2019' }, C2: { id:'2' } });\ndocument.getElementById(\"setForB2\").onclick = () => table.setMeta('B2', 'myMetaData', prompt('myMetaData:'));\ndocument.getElementById(\"getFromA1\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getMeta('A1'));\ndocument.getElementById(\"getAll\").onclick = () =>  document.getElementById('console').innerHTML =JSON.stringify(table.getMeta());\n</script>\n\n<br/>\n<button type=\"button\" id=\"setForMultiple\">Set meta data for multiple columns</button>\n<button type=\"button\" id=\"setForB2\">Set a meta information for B2</button>\n<button type=\"button\" id=\"getFromA1\">Get the meta information from A1</button>\n<button type=\"button\" id=\"getAll\">Get all meta information</button>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/nested-headers.md",
    "content": "title: Nested headers and column header updates\nkeywords: Jexcel, spreadsheet, javascript, header updates, nested headers, javascript table\ndescription: Enabled nested headers in your spreadsheet and learn how to set or get header values\n\n# Headers\n\n## Nested headers\n\nThe online spreadsheet implements nested headers natively though the directive **nestedHeaders** , as example below:\n\n  \n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nvar data = [\n    ['BR', 'Cheese', 1],\n    ['CA', 'Apples', 0],\n    ['US', 'Carrots', 1],\n    ['GB', 'Oranges', 0],\n];\n\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '300',\n            url: '/jspreadsheet/countries'\n        },\n        {\n            type: 'dropdown',\n            title: 'Food',\n            width: '150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese']\n        },\n        {\n            type: 'checkbox',\n            title: 'Stock',\n            width:'100'\n        },\n    ],\n    nestedHeaders:[\n        [\n            {\n                title: 'Supermarket information',\n                colspan: '3',\n            },\n        ],\n        [\n            {\n                title: 'Location',\n                colspan: '1',\n            },\n            {\n                title: ' Other Information',\n                colspan: '2'\n            }\n        ],\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/programmatically-changes.md",
    "content": "title: Programmatic Data Grid Updates in Jspreadsheet v4  \nkeywords: Jexcel, JavaScript, spreadsheet updates, programmatically modify data, JavaScript data grid, Excel-like functionality  \ndescription: Learn how to programmatically update your spreadsheet and its data using JavaScript with Jspreadsheet v4.  \ncanonical: https://bossanova.uk/jspreadsheet/v4/examples/programmatically-updates\n\n# Programmatically Data Grid Updates\n\n## Insert, remove and move columns and rows\n\nThe following example shows how to manage data programmatically in your javascript spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<div style=\"display:flex; flex-direction: column; flex-wrap: wrap; width: 500px; padding: 25px; justify-content: space-around;\" >\n    <input type=\"button\" id=\"btn1\" value=\"Insert Blank Column\" />\n    <input type=\"button\" id=\"btn2\" value=\"Insert Five Blank Columns\" />\n    <input type=\"button\" id=\"btn3\" value=\"Insert a new column with pre-populated values at the end of the table\" />\n    <input type=\"button\" id=\"btn4\" value=\"Insert a new blank row at the end of the table\" />\n    <input type=\"button\" id=\"btn5\" value=\"Create ten rows at the end of the table\" />\n    <input type=\"button\" id=\"btn6\" value=\"Delete the first row\" />\n    <input type=\"button\" id=\"btn7\" value=\"Delete the last column\" />\n    <input type=\"button\" id=\"btn8\" value=\"Move the first column to the third position\" />\n</div><br>\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    columns: [\n        {\n            title: 'Product',\n            type: 'autocomplete',\n            source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ],\n            width:'300px',\n        },\n        {\n            title: 'Quantity',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Price',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Total',\n            type: 'number',\n            width:'100px',\n        },\n    ],\n    rowResize: true,\n    columnDrag: true,\n});\n\ndocument.getElementById(\"btn1\").onclick = () => table.insertColumn();\ndocument.getElementById(\"btn2\").onclick = () => table.insertColumn(5, 0, 1, null);\ndocument.getElementById(\"btn3\").onclick = () => table.insertColumn([ '0.99', '1.22', '3.11', '2.21' ]);\ndocument.getElementById(\"btn4\").onclick = () => table.insertRow();\ndocument.getElementById(\"btn5\").onclick = () => table.insertRow(10);\ndocument.getElementById(\"btn6\").onclick = () => table.deleteRow(0, 1);\ndocument.getElementById(\"btn7\").onclick = () => table.deleteColumn();\ndocument.getElementById(\"btn8\").onclick = () => table.moveColumn(0, 2);\n</script>\n\n\n\n</html>\n```  \n  \n\n## Updating column width and row height\n\nUpdate the table width and height properties.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    colHeaders: [ 'Product', 'Quantity', 'Price', 'Total' ],\n    colWidths: [ 300, 100, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ] },\n        { type: 'number' },\n        { type: 'number' },\n        { type: 'number' },\n    ],\n    rowResize:true,\n});\n\ndocument.getElementById(\"setWidth\").onclick = () => table.setWidth(document.getElementById('columnNumber').value, 200);\ndocument.getElementById(\"setHeight\").onclick = () => table.setHeight(0, 100);\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n\n<input type='button' id=\"setWidth\" value='Set column width to 200px' />\n<input type='button' id=\"setHeight\" value='Set first row to height 100px' />\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/programmatically-updates.md",
    "content": "title: Programmatic Data Grid Updates in Jspreadsheet v4  \nkeywords: Jexcel, JavaScript, spreadsheet updates, programmatically modify data, JavaScript data grid, Excel-like functionality  \ndescription: Learn how to programmatically update your spreadsheet and its data using JavaScript with Jspreadsheet v4.  \ncanonical: https://bossanova.uk/jspreadsheet/v4/examples/programmatically-updates\n\n# Programmatically Data Grid Updates\n\n## Insert, remove and move columns and rows\n\nThe following example shows how to manage data programmatically in your javascript spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<div style=\"display:flex; flex-direction: column; flex-wrap: wrap; width: 500px; padding: 25px; justify-content: space-around;\" >\n    <input type=\"button\" id=\"btn1\" value=\"Insert Blank Column\" />\n    <input type=\"button\" id=\"btn2\" value=\"Insert Five Blank Columns\" />\n    <input type=\"button\" id=\"btn3\" value=\"Insert a new column with pre-populated values at the end of the table\" />\n    <input type=\"button\" id=\"btn4\" value=\"Insert a new blank row at the end of the table\" />\n    <input type=\"button\" id=\"btn5\" value=\"Create ten rows at the end of the table\" />\n    <input type=\"button\" id=\"btn6\" value=\"Delete the first row\" />\n    <input type=\"button\" id=\"btn7\" value=\"Delete the last column\" />\n    <input type=\"button\" id=\"btn8\" value=\"Move the first column to the third position\" />\n</div><br>\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    columns: [\n        {\n            title: 'Product',\n            type: 'autocomplete',\n            source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ],\n            width:'300px',\n        },\n        {\n            title: 'Quantity',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Price',\n            type: 'number',\n            width:'100px',\n        },\n        {\n            title: 'Total',\n            type: 'number',\n            width:'100px',\n        },\n    ],\n    rowResize: true,\n    columnDrag: true,\n});\n\ndocument.getElementById(\"btn1\").onclick = () => table.insertColumn();\ndocument.getElementById(\"btn2\").onclick = () => table.insertColumn(5, 0, 1, null);\ndocument.getElementById(\"btn3\").onclick = () => table.insertColumn([ '0.99', '1.22', '3.11', '2.21' ]);\ndocument.getElementById(\"btn4\").onclick = () => table.insertRow();\ndocument.getElementById(\"btn5\").onclick = () => table.insertRow(10);\ndocument.getElementById(\"btn6\").onclick = () => table.deleteRow(0, 1);\ndocument.getElementById(\"btn7\").onclick = () => table.deleteColumn();\ndocument.getElementById(\"btn8\").onclick = () => table.moveColumn(0, 2);\n</script>\n\n\n\n</html>\n```  \n  \n\n## Updating column width and row height\n\nUpdate the table width and height properties.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Cheese', 10, 1.10, '=B1*C1'],\n        [ 'Apples', 30, 0.40, '=B2*C2'],\n        [ 'Carrots', 15, 0.45, '=B3*C3'],\n        [ 'Oranges', 20, 0.49, '=B4*C4'],\n    ],\n    colHeaders: [ 'Product', 'Quantity', 'Price', 'Total' ],\n    colWidths: [ 300, 100, 100, 100 ],\n    columns: [\n        { type: 'autocomplete', source:[ 'Apples','Bananas','Carrots','Oranges','Cheese','Pears' ] },\n        { type: 'number' },\n        { type: 'number' },\n        { type: 'number' },\n    ],\n    rowResize:true,\n});\n\ndocument.getElementById(\"setWidth\").onclick = () => table.setWidth(document.getElementById('columnNumber').value, 200);\ndocument.getElementById(\"setHeight\").onclick = () => table.setHeight(0, 100);\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n\n<input type='button' id=\"setWidth\" value='Set column width to 200px' />\n<input type='button' id=\"setHeight\" value='Set first row to height 100px' />\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/react.md",
    "content": "title: Jspreadsheet with React\nkeywords: Jexcel, javascript, using jspreadsheet and react\ndescription: A full example on how to integrate Jspreadsheet with React\n\n# The Javascript spreadsheet with React\n\n\n### 1\\. Integrating Jspreadsheet with React\n\n[React with Jspreadsheet sample project](https://codesandbox.io/s/jexcel-and-react-k7nf0)\n\n{.ignore}\n```jsx\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport jexcel from \"jexcel\";\n\nimport \"./styles.css\";\nimport \"../node_modules/jexcel/dist/jexcel.css\";\n\nclass App extends React.Component {\n  constructor(props) {\n    super(props);\n    this.options = props.options;\n    this.wrapper = React.createRef();\n  }\n\n  componentDidMount = function() {\n    this.el = jexcel(this.wrapper.current, this.options);\n  };\n\n  addRow = function() {\n    this.el.insertRow();\n  };\n\n  render() {\n    return (\n      <div>\n        <div ref={this.wrapper} />\n        <br />\n        <input\n          type=\"button\"\n          value=\"Add new row\"\n          onClick={() => this.addRow()}\n        />\n      </div>\n    );\n  }\n}\n\nlet options = {\n  data: [[]],\n  minDimensions: [10, 10]\n};\n\nconst rootElement = document.getElementById(\"root\");\nReactDOM.render(<App options={options} />, rootElement);\n```\n\n\n### 2\\. React component implementation\n\n{.ignore}\n```javascript\nclass Jspreadsheet extends React.Component {\n    constructor(props) {\n        super(props);\n        this.options = props.options;\n        this.wrapper = React.createRef();\n    }\n\n    componentDidMount = function() {\n        this.el = jspreadsheet(this.wrapper.current, this.options);\n    }\n\n    addRow = function() {\n        this.el.insertRow();\n    }\n\n    render() {\n        return (\n            <div>\n                <div></div><br/><br/>\n                <input type='button' value='Add new row' onClick={() => this.addRow()}></input>\n            </div>\n        );\n    }\n}\n\nlet options = {\n    data:[[]],\n    minDimensions:[10,10],\n};\n\nReactDOM.render(<Jspreadsheet options={options} />, document.getElementById('spreadsheet'))\n```  \n  \n\n### 3\\. Jspreadsheet implementation with react component with hooks\n\n[Working example](https://codesandbox.io/s/jspreadsheet-ce-and-react-dzpqj)\n\n{.ignore}\n```jsx\nimport React, { useRef, useEffect } from \"react\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"../node_modules/jspreadsheet-ce/dist/jexcel.css\";\n\nexport default function App() {\n  const jRef = useRef(null);\n  const options = {\n    data: [[]],\n    minDimensions: [10, 10]\n  };\n\n  useEffect(() => {\n    if (!jRef.current.jspreadsheet) {\n      jspreadsheet(jRef.current, options);\n    }\n  }, [options]);\n\n  const addRow = () => {\n    jRef.current.jexcel.insertRow();\n  };\n\n  return (\n    <div>\n      <div ref={jRef} />\n      <br />\n      <input type=\"button\" onClick={addRow} value=\"Add new row\">\n    </div>\n  );\n}\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/readonly.md",
    "content": "title: Readonly Columns\nkeywords: Jexcel, spreadsheet, javascript, javascript table, readonly\ndescription: Example how to setup readonly cells\n\n# Readonly columns and cells\n\nSetting a readonly the whole column or a single specific cell.\n\n## Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, 1],\n        ['Peugeot', 2010, 5000, 1],\n        ['Honda Fit', 2009, 3000, 1],\n        ['Honda CRV', 2010, 6000, 0],\n    ],\n    columns: [\n        {\n            type: 'text',\n            title:'Description',\n            width:'200px',\n            readOnly:true,\n        },\n        {\n            type: 'text',\n            title:'Year',\n            width:'200px'\n        },\n        {\n            type: 'text',\n            title:'Price',\n            width:'100px',\n            mask:'#.##',\n        },\n        {\n            type: 'checkbox',\n            title:'Automatic',\n            width:'100px'\n        },\n    ],\n    updateTable: function(el, cell, x, y, source, value, id) {\n        if (x == 2 && y == 2) {\n            cell.classList.add('readonly');\n        }\n    } \n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/richtext-html-editor.md",
    "content": "title: HTML Editor with Jspreadsheet Version 4\nkeywords: Jexcel, javascript, excel-like, spreadsheet, html editor, rich text\ndescription: This examples shows how to create a rich text column\n\n# Rich text and HTML editor column type\n\nAdding a HTML Editor input in your spreadsheet\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet options = {\n    data: [\n       [ 'the Sorcerer`s Stone', '<img align=\"left\" style=\"width:100px\" src=\"data:image/jpeg;base64,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\"><span>On his 11th birthday, <b>Harry</b> receives a letter inviting him to study magic at the Hogwarts School of Witchcraft and Wizardry. <b>Harry</b> discovers that not only is he a wizard, but he is a famous one. He meets two best friends, Ron Weasley and Hermione Granger, and makes his first enemy, Draco Malfoy.</span>' ]\n    ],\n    columns: [\n        { type:'text', width:200, title:'Title' },\n        { type:'html', width:400, title:'HTML' },\n     ],\n     minDimensions:[2,2],\n}\n\njspreadsheet(document.getElementById('spreadsheet'), options);\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/sorting.md",
    "content": "title: Sorting the spreadsheet columns\nkeywords: Jexcel, spreadsheet, javascript, javascript table, sorting\ndescription: Example how to sort the table by a column via javascript.\n\n# Sorting your table\n\n## Simple example\n\nYou can sort your javascript table by double a double click in the header, using the context menu or by javascript as follow:\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns: [\n        { type: 'text', width:300 },\n        { type: 'text', width:80 },\n        { type: 'text', width:100 },\n        { type: 'calendar', width:100 },\n    ]\n});\n\ndocument.getElementById(\"orderBy\").onclick = () => table.orderBy(document.getElementById('columnNumber').value);\n</script>\n\n<br/>\n<select id='columnNumber'>\n    <option value='0'>Column 1</option>\n    <option value='1'>Column 2</option>\n    <option value='2'>Column 3</option>\n    <option value='3'>Column 4</option>\n</select>\n<input type='button' value='Sort By Column' id=\"orderBy\" />\n\n</html>\n```  \n\n## Disable the table sorting\n\nThe ordering is a native enabled feature. To disable that feature please use the columnSorting:false, directive in the initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns: [\n        { type: 'text', tile:'Model', width:300 },\n        { type: 'text', tile:'Year', width:100 },\n        { type: 'text', tile:'Price', width:100 },\n        { type: 'text', tile:'Date', width:100 },\n    ],\n    columnSorting:false,\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/spreadsheet-formulas.md",
    "content": "title: Basic to advance use of formulas\nkeywords: Jexcel, javascript, excel-like, spreadsheet, formulas, currency, calculations\ndescription: Unleash the power of your tables bringing formulas and custom javascript methods on your Jspreadsheet spreadsheet.\n\n# Adding formulas on your online spreadsheet\n\n## Simple spreadsheet-like formula usage\n\nThe example below shows how to use spreadsheet like formulas on your javascript table spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'Crayons Crayola only (No Rose Art)', 2, 5.01, '=B1*C1' ],\n        [ 'Colored Pencils Crayola only', 2, 4.41, '=B2*C2' ],\n        [ 'Expo Dry-erase Markers Wide', 4, 3.00, '=B3*C3' ],\n        [ 'Index Cards Unlined', 3, 6.00, '=B4*C4' ],\n        [ 'Tissues', 10, '1.90', '=B5*C5' ],\n        [ 'Ziploc Sandwich-size Bags', 5, 1.00, '=B6*C6' ],\n        [ 'Thin Markers Crayola only', 2, 3.00, '=B7*C7' ],\n        [ 'Highlighter', 4, 1.20, '=B8*C8' ],\n        [ 'Total', '=SUM(B1:B8)', '=ROUND(SUM(C1:C8), 2)', '=SUM(D1:D8)' ],\n    ],\n    columns: [\n        { type: 'text', title:'Product', width:'300' },\n        { type: 'text', title:'Qtd', width:'80' },\n        { type: 'text', title:'Price', width:'100', mask:'#.##,00', decimal:',' },\n        { type: 'text', title:'Total', width:'100' },\n    ],\n    updateTable:function(instance, cell, col, row, val, label, cellName) {\n        if (cell.innerHTML == 'Total') {\n            cell.parentNode.style.backgroundColor = '#fffaa3';\n        }\n\n        if (col == 3) {\n            if (parseFloat(label) > 10) {\n                cell.style.color = 'red';\n            }  else {\n                cell.style.color = 'green';\n            }\n        }\n    },\n    columnSorting:false,\n});\n</script>\n</html>\n```  \n\n## Creating Custom formulas\n\nYou can declare custom javascript methods and use in your tables as the example below.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdn.jsdelivr.net/npm/@jspreadsheet/formula/dist/index.min.js\"></script>\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet COLORIZE = function(v) {\n    v = '<span style=\"color:' + v + '\">' + v.toUpperCase() + '</span>';\n    return v;\n}\n\nformula.setFormula({ COLORIZE })\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        [ 'red', '=COLORIZE(A1)' ],\n        [ 'green', '=COLORIZE(A2)' ],\n        [ 'blue', '=COLORIZE(A3)' ],\n    ],\n    stripHTML: false,\n    columns: [\n        { type: 'text', width:'300' },\n        { type: 'text', width:'200' },\n    ]\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/spreadsheet-toolbars.md",
    "content": "title: Enable and customize the toolbar on your spreadsheet\nkeywords: Jexcel, javascript, vanilla javascript, excel-like, spreadsheet, datatables, data, table, toolbars\ndescription: Full example on how to enable nor customize your javascript spreadsheet toolbar.\n\n# Custom toolbars\n\nThe following example shows how to include and customize a toolbar in your javascript spreadsheet.\n\n\n## Instructions\n\nThe toolbar can be customized with a few parameters.\n|    |     | \n| ---|---  |\n| _**type**_|  could be **i** for icon, **select** for a dropdown, **color** or a colorpicker.  |\n| _**content**_|  defines the icon (from material icons) when you use type:i; [click here for all possible keys](https://material.io/tools/icons/)  |\n| _**k**_|  means the style should be apply to the cell;  |\n| _**v**_|  means the value of the style should be apply to the cell; When type:select, you can define an array of possibles values;  |\n| _**onclick**_|  can be used together with type:i to implement any custom method. The method will receive the Jspreadsheet instance and all marked cells by default.  |\n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Canada', 'Cheese', 1],\n        ['Japan', 'Apples', 0],\n        ['United States', 'Carrots', 1],\n        ['China', 'Oranges', 0],\n    ],\n    columns: [\n        {\n            title: 'Country',\n            type: 'autocomplete',\n            source: ['Brazil','Canada','China','Japan','United States','United Kingdom'],\n            width: '300px',\n        },\n        {\n            title: 'Food',\n            type: 'dropdown',\n            source:['Apples','Bananas','Carrots','Oranges','Cheese'],\n            width: '150px',\n        },\n        {\n            title: 'Stock',\n            type: 'checkbox',\n            width: '100px',\n        },\n    ],\n    toolbar:[\n        {\n            type: 'i',\n            content: 'undo',\n            onclick: function() {\n                table.undo();\n            }\n        },\n        {\n            type: 'i',\n            content: 'redo',\n            onclick: function() {\n                table.redo();\n            }\n        },\n        {\n            type: 'i',\n            content: 'save',\n            onclick: function () {\n                table.download();\n            }\n        },\n        {\n            type: 'select',\n            k: 'font-family',\n            v: ['Arial','Verdana']\n        },\n        {\n            type: 'select',\n            k: 'font-size',\n            v: ['9px','10px','11px','12px','13px','14px','15px','16px','17px','18px','19px','20px']\n        },\n        {\n            type: 'i',\n            content: 'format_align_left',\n            k: 'text-align',\n            v: 'left'\n        },\n        {\n            type:'i',\n            content:'format_align_center',\n            k:'text-align',\n            v:'center'\n        },\n        {\n            type: 'i',\n            content: 'format_align_right', \n            k: 'text-align',\n            v: 'right'\n        },\n        {\n            type: 'i',\n            content: 'format_bold',\n            k: 'font-weight',\n            v: 'bold'\n        },\n        {\n            type: 'color',\n            content: 'format_color_text',\n            k: 'color'\n        },\n        {\n            type: 'color',\n            content: 'format_color_fill',\n            k: 'background-color'\n        },\n    ],\n});\n</script>\n</html>\n```\n\n**NOTE:** don't forget to include the material icons style sheet.\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/spreadsheet-webcomponent.md",
    "content": "title: Jspreadsheet Webcomponent\nkeywords: Jexcel, javascript, javascript vanilla, javascript plugin, plugin, excel-like, spreadsheet, table, tables, grid, datatables, data, webcomponent\ndescription: Use the Jspreadsheet datagrid as webcomponent\n\n# Javascript web component online spreadsheet\n\n## Create a online javascript spreadsheet using Jspreadsheet Ce.\n\n### Javascript\n\n{.ignore}\n```javascript\nclass Jspreadsheet extends HTMLElement {\n    constructor() {\n        super();\n    }\n\n    init(o) {\n        // Shadow root\n        const shadowRoot = this.attachShadow({mode: 'open'});\n\n        // Style\n        const css = document.createElement('link');\n        css.rel = 'stylesheet';\n        css.type = 'text/css'\n        css.href = 'https://bossanova.uk/spreadsheet/v4/spreadsheet.css';\n        shadowRoot.appendChild(css);\n\n        const css2 = document.createElement('link');\n        css2.rel = 'stylesheet';\n        css2.type = 'text/css'\n        css2.href = 'https://bossanova.uk/spreadsheet/v4/jsuites.css';\n        shadowRoot.appendChild(css2);\n\n        // Jspreadsheet container\n        var container = document.createElement('div'); \n        shadowRoot.appendChild(container);\n\n        // Create element\n        this.el = jspreadsheet(container, {\n            root: shadowRoot,\n            minDimensions: [10,10]\n        });\n    }\n\n    connectedCallback() {\n        this.init(this);\n    }\n\n    disconnectedCallback() {\n    }\n\n    attributeChangedCallback() {\n    }\n}\n\nwindow.customElements.define('j-spreadsheet', Jspreadsheet);\n```\n\n### HTML\n\n```html\n<html>\n<script type=\"text/javascript\" src=\"/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script type=\"text/javascript\" src=\"/jsuites/v3/jsuites.js\"></script>\n<script type=\"text/javascript\" src=\"/spreadsheet/v4/spreadsheet.webcomponent.js\"></script>\n\n<j-spreadsheet></j-spreadsheet>\n\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/table-overflow.md",
    "content": "title: Table Overflow with Jspreadsheet Version 4\nkeywords: Jexcel, javascript, javascript vanilla, javascript, table, table overflow\ndescription: How define a fixed width and height for the jspreadsheet tables.\n\n# Table overflow\n\nDefine width and height for your online javascript spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n        minDimensions: [10, 20],\n        defaultColWidth: 100,\n        tableOverflow: true,\n        tableWidth: \"600px\",\n        columns: [\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          },\n          {\n            type: \"dropdown\",\n            source: [{ id: 1, name: \"yes\" }, { id: 2, name: \"no\" }]\n          }\n        ]\n    });\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/table-scripting.md",
    "content": "title: Customize the spreadsheet via javascript\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table scripting\ndescription: Customize the table behavior using javascript\n\n# Table scripting and customizations\n\nCustomize the table behavior though javascript.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/numeral.js/2.0.6/numeral.min.js\"></script>\n\n<div id=\"spreadsheet\"></div>\n\n<script>\n\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1, 3.99],\n        ['CA', 'Apples', 0, 1.00],\n        ['US', 'Carrots', 1, 0.90],\n        ['GB', 'Oranges', 0, 1.20],\n        ['CH', 'Chocolats', 1, 0.40],\n        ['AR', 'Apples', 1, 1.10],\n        ['AR', 'Bananas', 1, 0.30],\n        ['BR', 'Oranges', 1, 0.95],\n        ['BR', 'Pears', 1, 0.90],\n        ['', '', '', '=ROUND(SUM(D1:D8), 2)'],\n    ],\n    columnSorting:false,\n    columns: [\n        {\n            type: 'autocomplete',\n            title: 'Country',\n            width: '250',\n            url:'/jspreadsheet/countries'\n        },\n        {\n            type: 'autocomplete',\n            title:'Food',\n            width:'150',\n            source: ['Apples','Bananas','Carrots','Oranges','Cheese','Kiwi','Chocolats','Pears']\n        },\n        {\n            type: 'checkbox',\n            title:'Stock',\n            width:'100'\n        },\n        {\n            type: 'number',\n            title:'Price',\n            width:'100'\n        },\n    ],\n    updateTable:function(instance, cell, col, row, val, label, cellName) {\n        // Number formating\n        if (col == 3) {\n            // Get text\n            txt = cell.innerText;\n            // Update cell value with formated text\n            cell.innerHTML = jSuites.mask.render(txt, { mask: '$ #.##0,00'}, true)\n        }\n\n        // Total row\n        if (row == 9) {\n            if (col < 3) {\n                cell.innerHTML = '';\n            } \n\n            if (col == 2) {\n                cell.innerHTML = 'Total';\n                cell.style.fontWeight = 'bold';\n            }\n\n            cell.className = '';\n            cell.style.backgroundColor = '#f46e42';\n            cell.style.color = '#ffffff';\n        }\n    }\n});\n</script>\n\n<style>\n.jexcel tbody tr:nth-child(even) {\n  background-color: #EEE9F1 !important;\n}\n</style>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/table-style.md",
    "content": "title: Customize the spreadsheet style CSS\nkeywords: Jexcel, javascript, excel-like, spreadsheet, table style, css\ndescription: Bring a very special touch to your applications customizing your javascript spreadsheet.\n\n# Custom javascript spreasheet style\n\n## How to apply style to your table\n\nYou can define the CSS for specific columns during the initialization, or through programmatically javascript calls.\n\nBut, after the initialization is still possible to manage the cell style programmatically using the method getStyle or setStyle.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['US', 'Cheese', 'Yes', '2019-02-12'],\n        ['CA;US;UK', 'Apples', 'Yes', '2019-03-01'],\n        ['CA;BR', 'Carrots', 'No', '2018-11-10'],\n        ['BR', 'Oranges', 'Yes', '2019-01-12'],\n    ],\n    columns: [\n        {\n            type: 'dropdown',\n            title: 'Product Origin',\n            width: 300,\n            url: '/jspreadsheet/countries', // Remote source for your dropdown\n            autocomplete: true,\n            multiple: true\n        },\n        {\n            type: 'text',\n            title: 'Description',\n            width: 200\n        },\n        {\n            type: 'dropdown',\n            title: 'Stock',\n            width: 100 ,\n            source: ['No','Yes']\n        },\n        {\n            type: 'calendar',\n            title: 'Best before',\n            width: 100\n        },\n    ],\n    style: {\n        A1:'background-color: orange;',\n        B1:'background-color: orange;',\n    },\n});\n\ndocument.getElementById(\"setYellow\").onclick = () => table.setStyle('A2', 'background-color', 'yellow');\ndocument.getElementById(\"setStyle\").onclick = () => table.setStyle({ A3:'font-weight: bold; color:red;', B3:'background-color: yellow;', C3:'text-decoration: underline;', A4:'text-align:left;' });\ndocument.getElementById(\"getA1Style\").onclick = () => document.getElementById('console').innerHTML = table.getStyle('A1');\ndocument.getElementById(\"getTableStyle\").onclick = () => document.getElementById('console').innerHTML = JSON.stringify(table.getStyle());\n</script>\n\n<br/>\n\n<p><textarea id='console' style='width:100%;max-width:600px;height:100px;'></textarea></p>\n\n<br/>\n<button type=\"button\" id=\"setYellow\">Set A2 background</button>\n<button type=\"button\" id=\"setStyle\">Change A3, B3, C3, A4 style</button>\n<button type=\"button\" id=\"getA1Style\">Get A1 style</button>\n<button type=\"button\" id=\"getTableStyle\">Get the table style</button>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/tabs.md",
    "content": "title: Grouping multiple spreadsheets in tabs\nkeywords: Jexcel, javascript, multiple spreadsheets, tabs\ndescription: Grouping multiple spreadsheets in tabs.\n\n# Tabs\n\nGrouping different spreadsheets in tabs\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n\n<div id=\"root\"></div>\n\n<br/> \n<input type=\"button\" value=\"Add new tab\" id=\"addTab\" style=\"width:200px;\">\n<input type=\"button\" value=\"Download selected tab\" id=\"download\" style=\"width:200px;\">\n\n<script>\n/**\n * Create worksheet container with two jexcel instances\n */\nconst sheets = [\n    {\n        sheetName: 'Countries',\n        minDimensions:[10,10]\n    },\n    {\n        sheetName: 'Cities',\n        minDimensions:[10,10]\n    }\n];\n\nlet table = jspreadsheet.tabs(document.getElementById('root'), sheets);\n\n/**\n * Add new worksheet\n */\nconst add = function() {\n    let sheets = [];\n\n    sheets.push({\n        sheetName: prompt('Create a new tab', 'New tab ' + document.getElementById('root').jexcel.length),\n        minDimensions:[10,10]\n    });\n\n    jspreadsheet.tabs(document.getElementById('root'), sheets);\n}\n\n/**\n * Download current worksheet\n */\nconst download = function() {\n\n    // Get selected tab\n    let worksheet = document.getElementById('root').children[0].querySelector('.selected').getAttribute('data-spreadsheet');\n\n    console.log(worksheet)\n    // Download\n    document.getElementById('root').jexcel[worksheet].download();\n}\n\ndocument.getElementById(\"addTab\").onclick = () => add()\ndocument.getElementById(\"download\").onclick = () => download()\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/translations.md",
    "content": "title: How to translate the default messages from Jspreadsheet Version 4\nkeywords: Jexcel, spreadsheet, javascript, javascript table, translate, translations\ndescription: How to translate the default messages from Jspreadsheet\n\n# Jspreadsheet internationalization\n\nHow to update the default texts from the online spreadsheet.\n\n### Source code\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000, '2006-01-01'],\n        ['Peugeot', 2010, 5000, '2005-01-01'],\n        ['Honda Fit', 2009, 3000, '2004-01-01'],\n        ['Honda CRV', 2010, 6000, '2003-01-01'],\n    ],\n    columns:[\n        { type:'text', width:300 },\n        { type:'text', width:100 },\n        { type:'text', width:100 },\n        { type:'calendar', width:100 },\n    ],\n    text:{\n        noRecordsFound:'Nenhum registro encontrado',\n        showingPage:'Mostrando página {0} de {1} entradas',\n        show:'Show',\n        entries:'entradas',\n        insertANewColumnBefore:'Inserir uma nova coluna antes de',\n        insertANewColumnAfter:'Inserir uma nova coluna depois de',\n        deleteSelectedColumns:'Excluir colunas selecionadas',\n        renameThisColumn:'Renomear esta coluna',\n        orderAscending:'ordem ascendente',\n        orderDescending:'Order decrescente',\n        insertANewRowBefore:'Inserir uma nova linha antes de',\n        insertANewRowAfter:'Inserir uma nova linha depois de',\n        deleteSelectedRows:'Excluir linhas selecionadas',\n        editComments:'Editar comentários',\n        addComments:'Adicionar comentários',\n        comments:'Comentários',\n        clearComments:'Limpar comentários',\n        copy:'Copiar ...',\n        paste:'Colar ...',\n        saveAs:'Salvar como ...',\n        areYouSureToDeleteTheSelectedRows:'Tem certeza de excluir as linhas selecionadas?',\n        areYouSureToDeleteTheSelectedColumns:'Tem certeza de excluir as colunas selecionadas?',\n        thisActionWillDestroyAnyExistingMergedCellsAreYouSure:'Esta ação irá destruir todas as células mescladas existentes. Você tem certeza?',\n        thisActionWillClearYourSearchResultsAreYouSure:'Esta ação limpará seus resultados de pesquisa. Você tem certeza?',\n        thereIsAConflictWithAnotherMergedCell:'Há um conflito com outra célula mesclada',\n        invalidMergeProperties:'Propriedades mescladas inválidas',\n        cellAlreadyMerged:'Cell já mesclado',\n        noCellsSelected:'Nenhuma célula selecionada',\n    }\n});\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples/vue.md",
    "content": "title: Jspreadsheet with Vue\nkeywords: Jexcel, javascript, using jspreadsheet and Vue\ndescription: A full example on how to integrate Jspreadsheet with Vue\n\n# The Javascript spreadsheet with Vue\n\nIntegrating Jspreadsheet with Vue\n\n[See a full example on codesandbox](https://codesandbox.io/embed/vue-default-template-p4hwn)\n\n[Get a source code of a sample Vue project](https://github.com/jspreadsheet/jexcel-with-vue)  \n  \n\n### Source code\n\n```html\n<html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js\"></script>\n\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<br/>\n<input type=\"button\" value=\"Add new row\" id=\"addRow\" />\n\n<script>\nlet options = {\n    data:[[]],\n    minDimensions:[10,10],\n}\n\nlet vm = new Vue({\n    el: '#spreadsheet',\n    mounted: function() {\n        let spreadsheet = jspreadsheet(this.$el, options);\n        Object.assign(this, spreadsheet);\n    }\n}); \n\ndocument.getElementById(\"addRow\").onclick = () => vm.insertRow()\n</script>\n</html>\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/examples.md",
    "content": "title: Examples\nkeywords: Jexcel, javascript, examples\ndescription: Examples how to create web based spreadsheets using Jspreadsheet.\n\n# Jspreadsheet v4 Examples\n\nWe bring in this section various examples from basic to advance applications.\n\n*   [Angular](/jspreadsheet/v4/examples/angular)\n*   [Column Dragging](/jspreadsheet/v4/examples/column-dragging)\n*   [Column Filters](/jspreadsheet/v4/examples/column-filters)\n*   [Column Types](/jspreadsheet/v4/examples/column-types)\n*   [Comments](/jspreadsheet/v4/examples/comments)\n*   [Contextmenu](/jspreadsheet/v4/examples/contextmenu)\n*   [Create From Table](/jspreadsheet/v4/examples/create-from-table)\n*   [Custom Table Design](/jspreadsheet/v4/examples/custom-table-design)\n*   [Datatables](/jspreadsheet/v4/examples/datatables)\n*   [Date And Datetime Picker](/jspreadsheet/v4/examples/date-and-datetime-picker)\n*   [Dropdown And Autocomplete](/jspreadsheet/v4/examples/dropdown-and-autocomplete)\n*   [Events](/jspreadsheet/v4/examples/events)\n*   [Footers](/jspreadsheet/v4/examples/footers)\n*   [Freeze Columns](/jspreadsheet/v4/examples/freeze-columns)\n*   [Headers](/jspreadsheet/v4/examples/headers)\n*   [Image Upload](/jspreadsheet/v4/examples/image-upload)\n*   [Import Data](/jspreadsheet/v4/examples/import-data)\n*   [Jquery](/jspreadsheet/v4/examples/jquery)\n*   [Lazy Loading](/jspreadsheet/v4/examples/lazy-loading)\n*   [Merged Cells](/jspreadsheet/v4/examples/merged-cells)\n*   [Meta Information](/jspreadsheet/v4/examples/meta-information)\n*   [Nested Headers](/jspreadsheet/v4/examples/nested-headers)\n*   [Programmatically Updates](/jspreadsheet/v4/examples/programmatically-updates)\n*   [React](/jspreadsheet/v4/examples/react)\n*   [Readonly](/jspreadsheet/v4/examples/readonly)\n*   [Richtext Html Editor](/jspreadsheet/v4/examples/richtext-html-editor)\n*   [Sorting](/jspreadsheet/v4/examples/sorting)\n*   [Spreadsheet Formulas](/jspreadsheet/v4/examples/spreadsheet-formulas)\n*   [Spreadsheet Toolbars](/jspreadsheet/v4/examples/spreadsheet-toolbars)\n*   [Spreadsheet Webcomponent](/jspreadsheet/v4/examples/spreadsheet-webcomponent)\n*   [Table Overflow](/jspreadsheet/v4/examples/table-overflow)\n*   [Table Scripting](/jspreadsheet/v4/examples/table-scripting)\n*   [Table Style](/jspreadsheet/v4/examples/table-style)\n*   [Tabs](/jspreadsheet/v4/examples/tabs)\n*   [Translations](/jspreadsheet/v4/examples/translations)\n*   [Vue](/jspreadsheet/v4/examples/vue)\n"
  },
  {
    "path": "docs/jspreadsheet/v4/getting-started.md",
    "content": "title: Getting Started with Jspreadsheet CE v4  \nkeywords: Jspreadsheet CE, Jexcel, JavaScript data grid, spreadsheets, JavaScript tables, Excel-like data grids, web-based spreadsheets, interactive data grid controls, spreadsheet features  \ndescription: Learn how to create and manage data grids with powerful spreadsheet controls using Jspreadsheet CE v4.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/getting-started\n\n# Getting started\n\nJspreadsheet is a vanilla javascript plugin to embed a online spreadsheet in your web based applications. Bring highly dynamic datasets to your application and improve the user experience of your software.\n\n## Install\n\n### NPM\n\n```bash\nnpm install jspreadsheet-ce@4\n```\n\n### CDN\n\nUse jspreadsheet directly from JSDelivr CDN\n\n\n{.ignore}\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/index.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script src=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css\" type=\"text/css\" />\n```\n\n\n### GitHub\n\nDownload from our GitHub page\n\n[https://github.com/jspreadsheet/ce](https://github.com/jspreadsheet/ce)\n\n\n\n## Initialization\n\nYou can initiate an online spreadsheet including data from a HTML table, a JS array, a CSV or a JSON file, following the examples below:\n\n### Loading from a javascript array\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:100 },\n        { title:'Price', width:80 },\n    ]\n});\n</script>\n</html>\n```\n\n\n### Loading from a JSON file\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    url: '/jspreadsheet/data.json',\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:100 },\n        { title:'Price', width:80 },\n    ]\n});\n</script>\n</html>\n```\n\n\n\n### Loading from a CSV file\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    csv: '/tests/demo.csv',\n    csvHeaders: true,\n    columns: [\n        { width:300 },\n        { width:80 },\n        { width:100 }\n    ],\n    tableOverflow: true,\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/import-data)\n\n\n### Headers from a CSV file\n\nIf you are loading your data from a CSV file, you can define the **csvHeader:true**, so the first row will be used as your column names.\n\n[See a working example](/jspreadsheet/v4/examples/import-data)\n\n\n\n### Programmatically header updates\n\nThe methods **setHeader()**, **getHeader()** and **getHeaders()** are available for the developer to interact programmatically with the spreadsheet.\n\n[Working example](/jspreadsheet/v4/examples/headers#Programmatically-header-updates)\n\n\n\n### Nested headers\n\nThe nested headers area available in the innitialization through the directive **nestedHeaders:[]**, and should be use follow:\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['BR', 'Cheese', 1],\n        ['CA', 'Apples', 0],\n        ['US', 'Carrots', 1],\n        ['GB', 'Oranges', 0],\n    ],\n    columns: [\n        { type: 'autocomplete', title:'Country', width:'300', url:'/jspreadsheet/countries' },\n        { type: 'dropdown', title:'Food', width:'150', source: ['Apples','Bananas','Carrots','Oranges','Cheese' ] },\n        { type: 'checkbox', title:'Stock', width:'100' },\n    ],\n    nestedHeaders: [\n        [\n            { title:'Supermarket information', colspan:'3' },\n        ],\n        [\n            { title:'Location', colspan:'1' },\n            { title:' Other Information', colspan:'2' }\n        ],\n    ],\n});\n</script>\n</html>\n```\n\n[See this example in action](/jspreadsheet/v4/examples/headers)\n\n## Column width\n\nThe initial width can be defined in the width property in the column parameter.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id='spreadsheet'></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    columns: [\n        { title:'Model', width:300 },\n        { title:'Year', width:80 },\n        { title:'Price', width:100 }\n    ]\n});\n</script>\n</html>\n```\n\n\n### Programmatically column width updates\n\nThe methods setWidth(), getWidth() are available for the developer to update the column width via javascript.\n\n[See this example in action](/jspreadsheet/v4/examples/programmatically-updates#setWidth)\n\n## Row height\n\nThe initial row height can be defined in the height property include in the rows directive. It is also possible to enabled a resizeble row by using rowResize: true in the initialization.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['First'],\n        ['Second'],\n        ['Third']\n    ],\n    rows:{ 1: { height:'300px' }},\n    rowResize: true,\n    minDimensions: [4,4],\n});\n</script>\n</html>\n```\n\n\n### Programmatically row height updates\n\nThe methods setHeight(), getHeight() are available for the developer to update the row height via javascript.\n\n[See this example in action](/jspreadsheet/v4/examples/programmatically-updates#setHeight)\n\n## Column types\n\nJspreadsheet has available some extra native column types in addition to the default input text. It means you have extended nice responsive ways to get data into your spreadsheet. In addition to that is available integration methods to facilitate you to bring any custom column to your tables. This makes the Jspreadsheet plugin a very flexible tool to enhance the user experience of your applications.\n\nJspreadsheet is integrate with jSuites, so it brings some native columns, such as: **text, numeric, hidden, dropdown, autocomplete, checkbox, radio, calendar, image and color.**_\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data:data,\n    columns: [\n        { title:'Model', width:300, type:'text'; },\n        { title:'Price', width:80, type:'numeric' },\n        { title:'Date', width:100, type:'calendar', options: { format:'DD/MM/YYYY' } },\n        { title:'Photo', width:150, type:'image' },\n        { title:'Condition', width:150, type:'dropdown', source:['New','Used'] },\n        { title:'Color', width:80, type:'color' },\n        { title:'Available', width:80, type:'checkbox' },\n    ]\n});\n</script>\n</html>\n```\n\n### Calendar type\n\nWhen using the calendar column, you can change the behavior behavior of your calendar by sending some extra options as example above. The possible values are:\n\n{.ignore}\n```javascript\n{\n    options : {\n        // Date format\n        format:'DD/MM/YYYY',\n        // Allow keyboard date entry\n        readonly:0,\n        // Today is default\n        today:0,\n        // Show timepicker\n        time:0,\n        // Show the reset button\n        resetButton:true,\n        // Placeholder\n        placeholder:'',\n        // Translations can be done here\n        months:['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],\n        weekdays:['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],\n        weekdays\\_short:['S', 'M', 'T', 'W', 'T', 'F', 'S'],\n        // Value\n        value:null,\n        // Events\n        onclose:null,\n        onchange:null,\n        // Fullscreen (this is automatic set for screensize < 800)\n        fullscreen:false,\n    }\n}\n```\n\n[See a working example](/jspreadsheet/v4/examples/date-and-datetime-picker)\n\n\n\n### Dropdown and autocomplete type\n\nThere are different ways to work with dropdowns in Jspreadsheet. It is possible to define the parameter _source_ as a simple or key-value array. It is also possible to use the param _url_ to populate your dropdown from an external json format source. In addition to that it is possible to have conditional values. Basically, the values from one dropdown can be conditional to other dropdowns in your table.\n\nYou can set the autocomplete dropdown through the initial param _autocomplete:true_ and the multiple picker can be activate by _multiple:true_ property as shown in the following example:\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Honda', 1, 'Civic', '4'],\n        ['Peugeot', 3,'1007', '2'],\n        ['Smart', 3,'Cabrio', '4;5'],\n    ],\n    columns: [\n        {\n            type:'text',\n            title:'Model',\n            width:'200',\n        },\n        {\n            type:'dropdown',\n            title:'Available in',\n            multiple:true,\n            source: [{id:1, name:'Red'},{id:2, name:'Yellow'},{id:3,name:'Blue'}],\n            width:'200',\n        },\n        {\n            type:'autocomplete',\n            title:'Region',\n            url:'values.json',\n            width:'200',\n        },\n    ]\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/dropdown-and-autocomplete)\n\n### Custom type\n\nJspreadsheet makes possible to extend third party javascript plugins to create your custom columns. Basically to use this feature, you should implement some basic methods such as: openEditor, closeEditor, getValue, setValue as following.\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"jspreadsheet\"></div>\n\n<script>\nlet customColumn = {\n    // Methods\n    closeEditor : function(cell, save) {\n        let value = cell.children[0].value;\n        cell.innerHTML = value;\n        return value;\n    },\n    openEditor : function(cell) {\n        // Create input\n        let element = document.createElement('input');\n        element.value = cell.innerHTML;\n        // Update cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(element);\n        $(element).clockpicker({\n            afterHide:function() {\n                setTimeout(function() {\n                    // To avoid double call\n                    if (cell.children[0]) {\n                        spreadsheet.closeEditor(cell, true);\n                    }\n                });\n            }\n        });\n        // Focus on the element\n        element.focus();\n    },\n    getValue : function(cell) {\n        return cell.innerHTML;\n    },\n    setValue : function(cell, value) {\n        cell.innerHTML = value;\n    }\n}\n\nconst instance = jspreadsheet(document.getElementById('jspreadsheet'), {\n    data: [\n        ['PHP', '14:00' ],\n        ['Javascript', '16:30'],\n    ],\n    columns: [\n        { type: 'text', title:'Course Title', width:300 },\n        { type: 'text', title:'Time', width:100, editor:customColumn },\n     ]\n});\n</script>\n</html>\n```\n\n[See a working example](/jspreadsheet/v4/examples/column-types#custom)\n\n\n\n\n## Define a minimum table dimension size.\n\nThe follow example will create a data table with a minimum number of ten columns and five rows:\n\n{.ignore}\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: [\n        ['Mazda', 2001, 2000],\n        ['Peugeot', 2010, 5000],\n        ['Honda Fit', 2009, 3000],\n        ['Honda CRV', 2010, 6000],\n    ],\n    minDimensions:[10,5],\n});\n<script>\n</html>\n```"
  },
  {
    "path": "docs/jspreadsheet/v4/most-common-questions-and-answers.md",
    "content": "title: Common Questions\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features, questions, QA\ndescription: Questions and Answers about Jspreadsheet V4\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/most-common-questions-and-answers\n\n# Most common questions\n\n  \n\n  1. #### What is the best way to create odd/even rows on a Jspreadsheet spreadsheet and tables?\n\nSolution: Adding the following CSS code on your project.\n\n{.ignore}\n```css\n.jexcel tbody tr:nth-child(even) {\n  background-color: #EEE9F1 !important;\n}\n```\n\n  \n\n  2. #### How to transform multiple HTML static tables in dynamic Jspreadsheet tables?\n\n{.ignore}\n```javascript\nlet tables = document.querySelectorAll('table');\n\nfor (var i = 0; i < tables.length; i++) {\n    jspreadsheet(tables[i]);\n}\n```\n\n  \n\n  3. #### How to disable paste over a Jspreadsheet spreadsheet?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    onbeforepaste: function(instance, data, x, y) {\n        return false;\n    }\n});\n```\n\n  \n\n  4. #### How to intercept and change a pasted string over a Jspreadsheet table?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    onbeforepaste: function(instance, data, x, y) {\n        data = data.replace(',', '.', data);\n        return data;\n    }\n});\n```\n\n  \n\n  5. #### How to overwrite a type of a cell over a column type?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    columns: [\n        { type: 'text' },\n        { type: 'text' }, \n    ],\n    cells: {\n        B2: { type:'number', mask:'$ #,##.00', decimal:'.' },\n        B3: { type:'percent' },\n    }\n});\n```\n\n**NOTE:** Only available on the [Pro\ndistribution](https://jspreadsheet.com/).\n\n  \n\n  6. #### How to disabled the javascript contextmenu of my spreadsheet?\n\n{.ignore}\n```javascript\njspreadsheet(document.getElementById('spreadsheet'), {\n    columns: [\n        { type: 'text' },\n        { type: 'text' }, \n    ],\n    contextMenu: function() {\n        return false;\n    }\n});\n```\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/programmatically-changes.md",
    "content": "title: Programmatically Data Grid Changes \nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Create data grids with spreadsheet controls with Jspreadsheet CE.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/programmatically-changes\n\n# Methods\n\nJspreadsheet has a comprehensive number of native methods to programmatically interact with your javascript spreadsheet and its data.\n\n[Go to a working example](/jspreadsheet/v4/examples/programmatically-updates)\n\n  \n\n## General Methods\n\n| Method | Description | Example |  \n| --- | --- | --- |  \n| **getData** | Get the full or partial table data<br>@Param boolean onlyHighlighedCells - Get only highlighted cells | spreadsheet.getData([bool]); |  \n| **getJson** | Get the full or partial table data in JSON format<br>@Param boolean onlyHighlighedCells - Get only highlighted cells | spreadsheet.getJson([bool]); |  \n| **getRowData** | Get the data from one row by number<br>@Param integer rowNumber - Row number | spreadsheet.getRowData([int]); |  \n| **setRowData** | Set the data from one row by number<br>@Param integer rowNumber - Row number<br>@Param array rowData - Row data | spreadsheet.setRowData([int], [array]); |  \n| **getColumnData** | Get the data from one column by number<br>@Param integer columnNumber - Column number | spreadsheet.getColumnData([int]); |  \n| **setColumnData** | Set the data from one column by number<br>@Param integer columnNumber - Column number<br>@Param array colData - Column data | spreadsheet.setColumnData([int], [array]); |  \n| **setData** | Set the table data<br>@Param json newData - New JSON data, null will reload what is in memory. | spreadsheet.setData([json]); |  \n| **setMerge** | Merge cells<br>@Param string columnName - Column name, such as A1.<br>@Param integer colspan - Number of columns<br>@Param integer rowspan - Number of rows | spreadsheet.setMerge([string], [int], [int]); |  \n| **getMerge** | Get merged cells properties<br>@Param string columnName - Column name, such as A1. | spreadsheet.getMerge([string]); |  \n| **removeMerge** | Destroy merged by column name<br>@Param string columnName - Column name, such as A1. | spreadsheet.removeMerge([string]); |  \n| **destroyMerged** | Destroy all merged cells | spreadsheet.destroyMerge(); |  \n| **getCell** | Get current cell DOM<br>@Param string columnName - String compatible with Excel, or as object. | spreadsheet.getCell([string]); |  \n| **getLabel** | Get current cell DOM innerHTML<br>@Param string columnName - String compatible with Excel, or as object. | spreadsheet.getLabel([string]); |  \n| **getValue** | Get current cell value<br>@Param mixed cellIdent - String compatible with Excel, or as object. | spreadsheet.getValue([string]); |  \n| **getValueFromCoords** | Get value from coordinates<br>@Param integer x<br>@Param integer y | spreadsheet.getValueFromCoords([integer], [integer]); |  \n| **setValue** | Change the cell value<br>@Param mixed cellIdent - String compatible with Excel, or as object.<br>@Param string Value - New value for the cell<br>@Param bool force - Update readonly columns | spreadsheet.setValue([string], [string], [bool]); |  \n| **setValueFromCoords** | Set value from coordinates<br>@Param integer x<br>@Param integer y<br>@Param string Value - New value for the cell<br>@Param bool force - Update readonly columns | spreadsheet.setValueFromCoords([integer], [integer], [string], [bool]); |  \n| **resetSelection** | Reset the table selection<br>@Param boolean executeBlur - Execute the blur from the table | spreadsheet.resetSelection([bool]); |  \n| **updateSelection** | Select cells<br>@Param object startCell - Cell object<br>@Param object endCell - Cell object<br>@Param boolean ignoreEvents - Ignore onselection event | spreadsheet.updateSelection([cell], [cell], true); |  \n| **updateSelectionFromCoords** | Select cells<br>@Param integer x1<br>@Param integer y1<br>@Param integer x2<br>@Param integer y2 | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |  \n| **getWidth** | Get the current column width<br>@Param integer columnNumber - Column number starting at zero | spreadsheet.getWidth([integer]); |  \n| **setWidth** | Change column width<br>@Param integer columnNumber - Column number starting at zero<br>@Param string newColumnWidth - New column width | spreadsheet.setWidth([integer], [integer]); |  \n| **getHeight** | Get the current row height<br>@Param integer rowNumber - Row number starting at zero | spreadsheet.getHeight([integer]); |  \n| **setHeight** | Change row height<br>@Param integer rowNumber - Row number starting at zero<br>@Param string newRowHeight - New row height | spreadsheet.setHeight([integer], [integer]); |  \n| **getHeader** | Get the current header by column number<br>@Param integer columnNumber - Column number starting at zero | spreadsheet.getHeader([integer]); |  \n| **getHeaders** | Get all header titles | spreadsheet.getHeaders(); |  \n| **setHeader** | Change header by column<br>@Param integer columnNumber - Column number starting at zero<br>@Param string columnTitle - New header title | spreadsheet.setHeader([integer], [string]); |  \n| **getStyle** | Get table or cell style<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getStyle([string]); |  \n| **setStyle** | Set cell(s) CSS style<br>@Param mixed - JSON with whole table style information or just one cell identification. Ex. A1.<br>@Param k [optional] - CSS key<br>@Param v [optional] - CSS value | spreadsheet.setStyle([object], [string], [string]); |  \n| **resetStyle** | Remove all style from a cell<br>@Param string columnName - Column name, example: A1, B3, etc | spreadsheet.resetStyle([string]); |  \n| **getComments** | Get cell comments<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getComments([string]); |  \n| **setComments** | Set cell comments<br>@Param cell - Cell identification<br>@Param text - Comments | spreadsheet.setComments([string], [string]); |  \n| **orderBy** | Reorder a column ascending or descending<br>@Param integer columnNumber - Column number starting at zero<br>@Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order | spreadsheet.orderBy([integer], [boolean]); |  \n| **getConfig** | Get table definitions | spreadsheet.getConfig(); |  \n| **insertColumn** | Add a new column<br>@Param mixed - Number of columns to be added or data to be added in one single column<br>@Param int columnNumber - Number of columns to be created<br>@Param boolean insertBefore<br>@Param object properties - Column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]); |  \n| **deleteColumn** | Remove column by number<br>@Param integer columnNumber - Which column should be excluded starting at zero<br>@Param integer numOfColumns - Number of columns to be excluded from the reference column | spreadsheet.deleteColumn([integer], [integer]); |  \n| **moveColumn** | Change the column position<br>@Param integer columnPosition<br>@Param integer newColumnPosition | spreadsheet.moveColumn([integer], [integer]); |  \n| **insertRow** | Add a new row<br>@Param mixed - Number of blank lines to be inserted or a single array with the data of the new row<br>@Param integer rowNumber - Reference row number<br>@Param boolean insertBefore | spreadsheet.insertRow([mixed], [integer], [boolean]); |  \n| **deleteRow** | Remove row by number<br>@Param integer rowNumber - Which row should be excluded starting at zero<br>@Param integer numOfRows - Number of lines to be excluded | spreadsheet.deleteRow([integer], [integer]); |  \n| **moveRow** | Change the row position<br>@Param integer rowPosition<br>@Param integer newRowPosition | spreadsheet.moveRow([integer], [integer]); |  \n| **download** | Get the current data as a CSV file<br>@Param bool - True to download parsed formulas. | spreadsheet.download([bool]); |  \n| **getMeta** | Get the table or cell meta information<br>@Param mixed - Cell identification or null for the whole table. | spreadsheet.getMeta([string]); |  \n| **setMeta** | Set the table or cell meta information<br>@Param mixed - JSON with whole table meta information. | spreadsheet.setMeta([mixed]); |  \n| **fullscreen** | Toggle table fullscreen mode<br>@Param boolean fullscreen - Define fullscreen status as true or false | spreadsheet.fullscreen([bool]); |  \n| **getSelectedRows** | Get the selected rows<br>@Param boolean asIds - Get the rowNumbers or row DOM elements | spreadsheet.getSelectedRows([bool]); |  \n| **getSelectedColumns** | Get the selected columns | spreadsheet.getSelectedColumns(); |  \n| **showIndex** | Show column of index numbers | spreadsheet.showIndex(); |  \n| **hideIndex** | Hide column of index numbers | spreadsheet.hideIndex(); |  \n| **search** | Search in the table, only if directive is enabled during initialization.<br>@Param string - Search for word | spreadsheet.search([string]); |  \n| **resetSearch** | Reset search table | spreadsheet.resetSearch(); |  \n| **whichPage** | Which page showing on Jspreadsheet - Valid only when pagination is true. | spreadsheet.whichPage(); |  \n| **page** | Go to page number - Valid only when pagination is true.<br>@Param integer - Go to page number | spreadsheet.page([integer]); |  \n| **undo** | Undo last changes | spreadsheet.undo(); |  \n| **redo** | Redo changes | spreadsheet.redo(); |  \n\n\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/quick-reference.md",
    "content": "title: Quick Reference\nkeywords: Jspreadsheet CE, Jexcel, JavaScript Data Grid, Spreadsheets, JavaScript tables, Excel-like data grid, web-based spreadsheets, data grid controls, data grid features\ndescription: Quick Reference of the jspreadsheet properties\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/quick-reference\n\n# Quick reference\n\n  \n\n## Methods\n\n| Method                                                                                                                                                                                                                                                                           | Example                                                                            |  \n|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|  \n| **getData:** Get the full or partial table data  <br> @Param boolean onlyHighlighedCells - Get only highlighted cells                                                                                                                                                            | spreadsheet.getData([bool]);                                                       |  \n| **getJson:** Get the full or partial table data in JSON format  <br> @Param boolean onlyHighlighedCells - Get only highlighted cells                                                                                                                                             | spreadsheet.getJson([bool]);                                                       |  \n| **getRowData:** Get the data from one row by number  <br> @Param integer rowNumber - Row number                                                                                                                                                                                  | spreadsheet.getRowData([int]);                                                     |  \n| **setRowData:** Set the data from one row by number  <br> @Param integer rowNumber - Row number  <br> @Param array rowData - Row data                                                                                                                                            | spreadsheet.setRowData([int], [array]);                                            |  \n| **getColumnData:** Get the data from one column by number  <br> @Param integer columnNumber - Column number                                                                                                                                                                      | spreadsheet.getColumnData([int]);                                                  |  \n| **setColumnData:** Set the data from one column by number  <br> @Param integer columnNumber - Column number  <br> @Param array colData - Column data                                                                                                                             | spreadsheet.setColumnData([int], [array]);                                         |  \n| **setData:** Set the table data  <br> @Param json newData - New JSON data, null will reload what is in memory.                                                                                                                                                                   | spreadsheet.setData([json]);                                                       |  \n| **setMerge:** Merge cells  <br> @Param string columnName - Column name, such as A1.  <br> @Param integer colspan - Number of columns  <br> @Param integer rowspan - Number of rows                                                                                               | spreadsheet.setMerge([string], [int], [int]);                                      |  \n| **getMerge:** Get merged cells properties  <br> @Param string columnName - Column name, such as A1.                                                                                                                                                                              | spreadsheet.getMerge([string]);                                                    |  \n| **removeMerge:** Destroy merged by column name  <br> @Param string columnName - Column name, such as A1.                                                                                                                                                                         | spreadsheet.removeMerge([string]);                                                 |  \n| **destroyMerged:** Destroy all merged cells                                                                                                                                                                                                                                      | spreadsheet.destroyMerge();                                                        |  \n| **getCell** : get current cell DOM  <br> @Param string columnName - str compatible with Excel, or as object.                                                                                                                                                                     | spreadsheet.getCell([string]);                                                     |  \n| **getLabel** : get current cell DOM innerHTML  <br> @Param string columnName - str compatible with Excel, or as object.                                                                                                                                                          | spreadsheet.getLabel([string]);                                                    |  \n| **getValue:** get current cell value  <br> @Param mixed cellIdent - str compatible with Excel, or as object.                                                                                                                                                                     | spreadsheet.getValue([string]);                                                    |  \n| **getValueFromCoords:** get value from coords  <br> @Param integer x  <br> @Param integer y                                                                                                                                                                                      | spreadsheet.getValueFromCoords([integer], [integer]);                              |  \n| **setValue:** change the cell value  <br> @Param mixed cellIdent - str compatible with Excel, or as object.  <br> @Param string Value - new value for the cell  <br> @Param bool force - update readonly columns                                                                 | spreadsheet.setValue([string], [string], [bool]);                                  |  \n| **setValueFromCoords:** get value from coords  <br> @Param integer x  <br> @Param integer y  <br> @Param string Value - new value for the cell  <br> @Param bool force - update readonly columns                                                                                 | spreadsheet.setValueFromCoords([integer], [integer], [string], [bool]);            |  \n| **resetSelection:** Reset the table selection  <br> @Param boolean executeBlur - execute the blur from the table                                                                                                                                                                 | spreadsheet.resetSelection([bool]);                                                |  \n| **updateSelection:** select cells  <br> @Param object startCell - cell object  <br> @Param object endCell - cell object  <br> @Param boolean ignoreEvents - ignore onselection event                                                                                             | spreadsheet.updateSelection([cell], [cell], true);                                 |  \n| **updateSelectionFromCoords:** select cells  <br> @Param integer x1  <br> @Param integer y1  <br> @Param integer x2  <br> @Param integer y2                                                                                                                                      | spreadsheet.updateSelectionFromCoords([integer], [integer], [integer], [integer]); |  \n| **getWidth:** get the current column width  <br> @Param integer columnNumber - column number starting on zero                                                                                                                                                                    | spreadsheet.getWidth([integer]);                                                   |  \n| **setWidth:** change column width  <br> @Param integer columnNumber - column number starting on zero  <br> @Param string newColumnWidth - New column width                                                                                                                       | spreadsheet.setWidth([integer], [integer]);                                        |  \n| **getHeight:** get the current row height  <br> @Param integer rowNumber - row number starting on zero                                                                                                                                                                           | spreadsheet.getHeight([integer]);                                                  |  \n| **setHeight:** change row height  <br> @Param integer rowNumber - row number starting on zero  <br> @Param string newRowHeight- New row height                                                                                                                                   | spreadsheet.setHeight([integer], [integer]);                                       |  \n| **getHeader:** get the current header by column number  <br> @Param integer columnNumber - column number starting on zero                                                                                                                                                        | spreadsheet.getHeader([integer]);                                                  |  \n| **getHeaders:** get all header titles                                                                                                                                                                                                                                            | spreadsheet.getHeaders();                                                          |  \n| **setHeader:** change header by column  <br> @Param integer columnNumber - column number starting on zero  <br> @Param string columnTitle - New header title                                                                                                                     | spreadsheet.setHeader([integer], [string]);                                        |  \n| **getStyle:** get table or cell style  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                                      | spreadsheet.getStyle([string]));                                                   |  \n| **setStyle:** set cell(s) CSS style  <br> @Param mixed - json with whole table style information or just one cell identification. Ex. A1.  <br> @Param k [optional]- CSS key  <br> @Param v [optional]- CSS value                                                                | spreadsheet.setSyle([object], [string], [string]);                                 |  \n| **resetStyle:** remove all style from a cell  <br> @Param string columnName - Column name, example: A1, B3, etc                                                                                                                                                                  | spreadsheet.resetStyle([string]);                                                  |  \n| **getComments:** get cell comments  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                                         | spreadsheet.getComments([string]);                                                 |  \n| **setComments:** set cell comments  <br> @Param cell - cell identification  <br> @Param text - comments                                                                                                                                                                          | spreadsheet.setComments([string], [string]);                                       |  \n| **orderBy:** reorder a column asc or desc  <br> @Param integer columnNumber - column number starting on zero  <br> @Param smallint sortType - One will order DESC, zero will order ASC, anything else will toggle the current order                                              | spreadsheet.orderBy([integer], [boolean]);                                         |  \n| **getConfig:** get table definitions                                                                                                                                                                                                                                             | spreadsheet.getConfig();                                                           |  \n| **insertColumn:** add a new column  <br> @Param mixed - num of columns to be added or data to be added in one single column  <br> @Param int columnNumber - number of columns to be created  <br> @Param boolean insertBefore  <br> @Param object properties - column properties | spreadsheet.insertColumn([mixed], [integer], [boolean], [object]);                 |  \n| **deleteColumn:** remove column by number  <br> @Param integer columnNumber - Which column should be excluded starting on zero  <br> @Param integer numOfColumns - number of columns to be excluded from the reference column                                                    | spreadsheet.deleteColumn([integer], [integer]);                                    |  \n| **moveColumn:** change the column position  <br> @Param integer columnPosition  <br> @Param integer newColumnPosition                                                                                                                                                            | spreadsheet.moveColumn([integer], [integer]);                                      |  \n| **insertRow:** add a new row  <br> @Param mixed - number of blank lines to be insert or a single array with the data of the new row  <br> @Param integer rowNumber - reference row number  <br> @Param boolean insertBefore                                                      | spreadsheet.insertRow([mixed], [integer], [boolean]);                              |  \n| **deleteRow:** remove row by number  <br> @Param integer rowNumber - Which row should be excluded starting on zero  <br> @Param integer numOfRows - number of lines to be excluded                                                                                               | spreadsheet.deleteRow([integer], [integer]);                                       |  \n| **moveRow:** change the row position  <br> @Param integer rowPosition  <br> @Param integer newRowPosition                                                                                                                                                                        | spreadsheet.moveRow([integer], [integer]);                                         |  \n| **download:** get the current data as a CSV file  <br> @Param bool - true to download parsed formulas.                                                                                                                                                                           | spreadsheet.download([bool]);                                                      |  \n| **getMeta:** get the table or cell meta information  <br> @Param mixed - cell identification or null for the whole table.                                                                                                                                                        | spreadsheet.getMeta([string]);                                                     |  \n| **setMeta:** set the table or cell meta information  <br> @Param mixed - json with whole table meta information.                                                                                                                                                                 | spreadsheet.setMeta[mixed]);                                                       |  \n| **fullscreen:** Toogle table fullscreen mode  <br> @Param boolean fullscreen - define fullscreen status as true or false                                                                                                                                                         | spreadsheet.fullscreen([bool]);                                                    |  \n| **getSelectedRows:** Get the selected rows  <br> @Param boolean asIds - Get the rowNumbers or row DOM elements                                                                                                                                                                   | spreadsheet.getSelectedRows([bool]);                                               |  \n| **getSelectedColumns:** Get the selected columns  <br> @Param boolean asIds - Get the colNumbers or row DOM elements                                                                                                                                                             | spreadsheet.getSelectedColumns([bool]);                                            |  \n| **showColumn:** show column by number                                                                                                                                                                                                                                            | spreadsheet.showIndex([int]);                                                      |  \n| **hideColumn:** hide column by number                                                                                                                                                                                                                                            | spreadsheet.hideColumn([int]);                                                     |  \n| **showIndex:** show column of index numbers                                                                                                                                                                                                                                      | spreadsheet.showIndex();                                                           |  \n| **hideIndex:** hide column of index numbers                                                                                                                                                                                                                                      | spreadsheet.hideIndex();                                                           |  \n| **search:** search in the table, only if directive is enabled during initialization.  <br> @Param string - Search for word                                                                                                                                                       | spreadsheet.search([string]);                                                      |  \n| **resetSearch:** reset search table                                                                                                                                                                                                                                              | spreadsheet.resetSearch();                                                         |  \n| **whichPage:** Which page showing on Jspreadsheet - Valid only when pagination is true.                                                                                                                                                                                          | spreadsheet.whichPage();                                                           |  \n| **page:** Go to page number- Valid only when pagination is true.  <br> @Param integer - Go to page number                                                                                                                                                                        | spreadsheet.page([integer]);                                                       |  \n| **undo:** Undo last changes                                                                                                                                                                                                                                                      | spreadsheet.undo();                                                                |  \n| **redo:** Redo changes                                                                                                                                                                                                                                                           | spreadsheet.redo();                                                                |  \n\n\n  \n[Working example](/jspreadsheet/v4/examples/programmatically-updates)\n\n  \n  \n  \n\n## Events\n\n| Event | Description |\n| --- | --- |\n| **onload** | This method is called when the method setData |\n| **onbeforechange** | Before a column value is changed. NOTE: It is possible to overwrite the original value, by returning a new value on this method. v3.4.0+ |\n| **onchange** | After a column value is changed. |\n| **onafterchanges** | After all changes are applied in the table. |\n| **onpaste** | After a paste action is performed in the JavaScript table. |\n| **onbeforepaste** | Before the paste action is performed. Used to parse any input data, should return the data. |\n| **oninsertrow** | After a new row is inserted. |\n| **onbeforeinsertrow** | Before a new row is inserted. You can cancel the insert event by returning false. |\n| **ondeleterow** | After a row is excluded. |\n| **onbeforedeleterow** | Before a row is deleted. You can cancel the delete event by returning false. |\n| **oninsertcolumn** | After a new column is inserted. |\n| **onbeforeinsertcolumn** | Before a new column is inserted. You can cancel the insert event by returning false. |\n| **ondeletecolumn** | After a column is excluded. |\n| **onbeforedeletecolumn** | Before a column is excluded. You can cancel the insert event by returning false. |\n| **onmoverow** | After a row is moved to a new position. |\n| **onmovecolumn** | After a column is moved to a new position. |\n| **onresizerow** | After a change in row height. |\n| **onresizecolumn** | After a change in column width. |\n| **onselection** | On the selection is changed. |\n| **onsort** | After a column is sorted. |\n| **onfocus** | On table focus. |\n| **onblur** | On table blur. |\n| **onmerge** | On column merge. |\n| **onchangeheader** | On header change. |\n| **onundo** | On undo is applied. |\n| **onredo** | On redo is applied. |\n| **oneditionstart** | When an openEditor is called. |\n| **oneditionend** | When a closeEditor is called. |\n| **onchangestyle** | When a setStyle is called. |\n| **onchangemeta** | When a setMeta is called. |\n| **onchangepage** | When the page is changed. |\n\n  \n[Example on handling events on Jspreadsheet](/jspreadsheet/v4/examples/events)\n\n  \n  \n\n## Initialization\n\n| Parameter | Description |\n| --- | --- |\n| **url** | Load an external JSON file from this URL: string |\n| **data** | Load this data into the JavaScript table: array |\n| **copyCompatibility** | When true, copy and export will bring formula results; if false, will bring formulas: boolean |\n| **rows** | Row properties: height: object |\n| **columns** | Column type, title, width, align, dropdown options, text wrapping, mask, etc.: object |\n| **defaultColWidth** | Default width for a new column: integer |\n| **defaultColAlign** | Default align for a new column: [center, left, right] |\n| **minSpareRows** | Minimum number of spare rows: integer |\n| **minSpareCols** | Minimum number of spare columns: integer |\n| **minDimensions** | Minimum table dimensions: [columns, rows] |\n| **allowExport** | Allow table export: boolean |\n| **includeHeadersOnDownload** | Include header titles on download: boolean |\n| **columnSorting** | Allow column sorting: boolean |\n| **columnDrag** | Allow column dragging: boolean |\n| **columnResize** | Allow column resizing: boolean |\n| **rowResize** | Allow row resizing: boolean |\n| **rowDrag** | Allow row dragging: boolean |\n| **editable** | Allow table edition: boolean |\n| **allowInsertRow** | Allow insertion of a new row: boolean |\n| **allowManualInsertRow** | Allow user to insert a new row: boolean |\n| **allowInsertColumn** | Allow insertion of a new column: boolean |\n| **allowManualInsertColumn** | Allow user to create a new column: boolean |\n| **allowDeleteRow** | Allow deletion of a row: boolean |\n| **allowDeleteColumn** | Allow deletion of a column: boolean |\n| **allowRenameColumn** | Allow renaming of a column: boolean |\n| **allowComments** | Allow comments over the cells: boolean |\n| **wordWrap** | Global text wrapping: boolean |\n| **csv** | Load an external CSV file from this URL: string |\n| **csvFileName** | Default filename for a download method: string |\n| **csvHeaders** | Load header titles from the CSV file: boolean |\n| **csvDelimiter** | Default delimiter for the CSV file: string |\n| **selectionCopy** | Allow selection copy: boolean |\n| **mergeCells** | Cells to be merged in the table initialization: object |\n| **toolbar** | Add custom toolbars: object |\n| **search** | Allow search in the table: boolean |\n| **pagination** | Break the table by pages: integer |\n| **paginationOptions** | Number of records per page: [25, 50, 75, 100], for example |\n| **fullscreen** | Fullscreen mode: boolean |\n| **lazyLoading** | Activate the table lazy loading: boolean |\n| **loadingSpin** | Activate the loading spin: boolean |\n| **tableOverflow** | Allow table overflow: boolean |\n| **tableHeight** | Force the maximum height of the table: CSS String |\n| **tableWidth** | Force the maximum width of the table: CSS String |\n| **meta** | Meta information: object |\n| **style** | Cells style in the table initialization: object |\n| **parseFormulas** | Enable execution of formulas inside the table: boolean |\n| **autoIncrement** | Auto-increment actions when using the dragging corner: boolean |\n| **updateTable** | Method to configure custom script execution. NOTE: This does not work with lazyLoading, Pagination, or Search options. |\n| **nestedHeaders** | Define the nested headers, including title, colspan, etc.: object |\n| **contextMenu** | Context menu content: function() { return customMenu } |\n| **text** | All messages to be customized: object |\n\n  \n  \n\n## Translations\n\n| Key| Default value  |\n| ---|---  |\n| **noRecordsFound** | No records found |\n| **showingPage** | Showing page {0} of {1} entries |\n| **show** | Show |\n| **entries** | entries |\n| **insertANewColumnBefore** | Insert a new column before |\n| **insertANewColumnAfter** | Insert a new column after |\n| **deleteSelectedColumns** | Delete selected columns |\n| **renameThisColumn** | Rename this column |\n| **orderAscending** | Order ascending |\n| **orderDescending** | Order descending |\n| **insertANewRowBefore** | Insert a new row before |\n| **insertANewRowAfter** | Insert a new row after |\n| **deleteSelectedRows** | Delete selected rows |\n| **editComments** | Edit comments |\n| **addComments** | Add comments |\n| **comments** | Comments |\n| **clearComments** | Clear comments |\n| **copy** | Copy... |\n| **paste** | Paste... |\n| **saveAs** | Save as... |\n| **about** | About |\n| **areYouSureToDeleteTheSelectedRows** | Are you sure to delete the selected rows? |\n| **areYouSureToDeleteTheSelectedColumns** | Are you sure to delete the selected columns? |\n| **thisActionWillDestroyAnyExistingMergedCellsAreYouSure** | This action will destroy any existing merged cells. Are you sure? |\n| **thisActionWillClearYourSearchResultsAreYouSure** | This action will clear your search results. Are you sure? |\n| **thereIsAConflictWithAnotherMergedCell** | There is a conflict with another merged cell |\n| **invalidMergeProperties** | Invalid merged properties |\n| **cellAlreadyMerged** | Cell already merged |\n| **noCellsSelected** | No cells selected |\n\n  \n[Working example](/jspreadsheet/v4/examples/translations)\n\n"
  },
  {
    "path": "docs/jspreadsheet/v4/special-formulas.md",
    "content": "title: Special Formulas in Jspreadsheet\nkeywords: Jspreadsheet, spreadsheet formulas, custom methods, JavaScript spreadsheets, special formulas, progress bar, star rating, spreadsheet custom features\ndescription: Explore Jspreadsheet's special formulas and custom methods to enhance spreadsheet functionality.\ncanonical: https://bossanova.uk/jspreadsheet/v4/docs/special-formulas\n\n# Special formulas\n\nJspreadsheet supports spreadsheet-like formulas, along with special formulas designed to enhance usability and functionality.\n\n## Custom Jspreadsheet Methods\n\n| Method                                                                                                                                          | Example                                              |\n|-------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|\n| **=PROGRESS(int, string)** Create a progress bar.<br/>**Parameters:**<br/>`int` percent (0-100)<br/>`string` color (Hex value)                  | [Example](/jspreadsheet/v4/cases/project-management) |\n| **=RATING(int)** Create a star rating based on an integer.<br/>**Parameters:**<br/>`int` value (0-5)                                            | [Example](/jspreadsheet/v4/cases/food-store)         |\n| **=TABLE()** Return the Jspreadsheet table instance.                                                                                            |                                                      |\n| **=COLUMN()** Return the column number where the formula is executed.                                                                           |                                                      |\n| **=ROW()** Return the row number where the formula is executed.                                                                                 |                                                      |\n| **=CELL()** Return the cell string identification.                                                                                              |                                                      |\n| **=VALUE(int, int)** Return the cell value based on the column and row numbers.<br/>**Parameters:**<br/>`int` colNumber<br/>`int` rowNumber     |                                                      |\n"
  },
  {
    "path": "docs/jspreadsheet/v4.md",
    "content": "title: Jspreadsheet CE v4 Documentation  \nkeywords: Jspreadsheet CE, Jexcel, JavaScript spreadsheet, Excel-like table, data grid, version 4 features, summary, Jspreadsheet table  \ndescription: Explore the key features and a detailed summary of Jspreadsheet CE version 4, the versatile JavaScript-based Excel-like data grid.\ncanonical: https://bossanova.uk/jspreadsheet/v4\n\n# Jspreadsheet v4: The JavaScript Spreadsheet\n\n**Jexcel** has been renamed to **Jspreadsheet**.\n\nJspreadsheet CE is a lightweight JavaScript spreadsheet component designed to create interactive web-based data grids with spreadsheet-like controls fully compatible with software such as Excel and Google Sheets. You can easily create and embed a spreadsheet in your application using a JavaScript array, JSON, CSV, or XLSX files. It supports copying data from Excel and pasting it directly into your online spreadsheet and vice versa. Integration with third-party JavaScript plugins is straightforward, allowing the creation of custom columns, editors, and additional features. With a wide range of native column types, Jspreadsheet is a comprehensive solution for web data management.\n\nCreate versatile online spreadsheets with Jspreadsheet and integrate them into your application in just a few steps. Impress your clients with an enhanced user experience powered by this robust data interaction tool.\n\n- Build rich, user-friendly data grid interfaces and applications.\n- Handle complex data inputs intuitively.\n- Enhance your data grid user experience with interactions familiar with spreadsheet software.\n- Develop rich CRUD applications with a beautiful UI.\n- Excel compatibility: copy and paste data using standard shortcuts.\n- Easily customizable with third-party plugin integrations.\n- Lightweight, fast, and easy to use.\n- Proven success across thousands of use cases.\n- Streamline data entry tasks in web-based applications.\n- Share and collaborate on stunning online spreadsheets.\n\n\nInstallation\n------------\n\n### From the NPM\n\n```bash\nnpm install jspreadsheet-ce\n```\n\n### From a CDN\n\n{.ignore}\n```javascript\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/jspreadsheet.min.css\" type=\"text/css\" />\n<script type=\"text/javascript\" src=\"https://cdn.jsdelivr.net/npm/jspreadsheet-ce@4/dist/index.min.js\"></script>\n```\n\n### Clone our project\n\nhttp://github.com/jspreadsheet/ce\n\n\n\n## Create amazing online spreadsheets\n\nA example how to embed a simple javascript spreadsheet in your application. You can check out for more [examples](/jspreadsheet/v4/examples) here.\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nvar data = [\n    ['Jazz', 'Honda', '2019-02-12', '', true, '$ 2.000,00', '#777700'],\n    ['Civic', 'Honda', '2018-07-11', '', true, '$ 4.000,01', '#007777'],\n];\n\njspreadsheet(document.getElementById('spreadsheet'), {\n    data: data,\n    columns: [\n        {\n            type: 'text',\n            title:'Car',\n            width:90\n        },\n        {\n            type: 'dropdown',\n            title:'Make',\n            width:120,\n            source:[\n                \"Alfa Romeo\",\n                \"Audi\",\n                \"Bmw\",\n                \"Chevrolet\",\n                \"Chrystler\",\n                // (...)\n              ]\n        },\n        {\n            type: 'calendar',\n            title:'Available',\n            width:120\n        },\n        {\n            type: 'image',\n            title:'Photo',\n            width:120\n        },\n        {\n            type: 'checkbox',\n            title:'Stock',\n            width:80\n        },\n        {\n            type: 'numeric',\n            title:'Price',\n            mask:'$ #.##,00',\n            width:80,\n            decimal:','\n        },\n        {\n            type: 'color',\n            width:80,\n            render:'square',\n        },\n     ]\n});\n</script>\n</html>\n```\n\n## Jspreadsheet History\n\n### Jspreadsheet 4.6.0\n- Jexcel is renamed to Jspreadsheet.\n- Integration with Jsuites v4.\n\n### Jspreadsheet 4.2.3\n- The spreadsheet plugin is now compatible with Jsuites v3.\n- New flags and security implementations.\n- New DOM element references are in the toolbar, and worksheet events are tabbed.\n\n### Jspreadsheet 4.0.0\nSpecial thanks to [FDL - Fonds de Dotation du Libre](https://www.fdl-lef.org/) for their support and sponsorship, which made the new version possible with many exciting features.\n\n- Workbook/tab support for spreadsheets.\n- Create dynamic spreadsheets from static HTML elements.\n- Highlight selected cells in the spreadsheet after CTRL+C.\n- Footer with formula support.\n- Multiple column resizing.\n- JSON update support (helpers to update a remote server).\n- Centralized event dispatch method for all spreadsheet events.\n- Custom helpers: `=PROGRESS` (progress bar), `=RATING` (5-star rating).\n- Custom formula helpers: `=COLUMN`, `=ROW`, `=CELL`, `=TABLE`, `=VALUE`.\n- Dynamic nested header updates.\n- New HTML editing column type.\n- New flags: `includeHeadersOnCopy`, `persistence`, `filters`, `autoCasting`, `freezeColumns`.\n- New events: `onevent`, `onchangepage`, `onbeforesave`, `onsave`.\n- More examples and documentation.\n\n### Jspreadsheet 3.9.0\n- New methods.\n- General fixes.\n\n### Jspreadsheet 3.6.0\n- Improved spreadsheet formula parsing.\n- New spreadsheet events.\n- New initialization options.\n- General fixes.\n\n### Jspreadsheet 3.2.3\n- `getMeta`, `setMeta` methods.\n- NPM package with jSuites.\n- General fixes.\n\n### Jspreadsheet 3.0.1\nJspreadsheet v3 is a complete rebuild of the JavaScript spreadsheet (previously a jQuery plugin). Due to the changes, full compatibility could not be ensured. If upgrading, your code may require some updates. For more information, refer to the article on upgrading from Jspreadsheet v2 or Handsontable.\n\nNew features in Jspreadsheet v3:\n- Drag and drop columns.\n- Resizable rows.\n- Merge columns.\n- Search functionality.\n- Pagination.\n- Lazy loading.\n- Full-screen mode.\n- Image upload.\n- Native color picker.\n- Better mobile compatibility.\n- Enhanced nested headers support.\n- Advanced keyboard navigation.\n- Better hidden column management.\n- Data picker enhancements: dropdown, autocomplete, multiple selection, group options, and icons.\n- Import from XLSX (experimental).\n\nMajor improvements:\n- A new formula engine with faster results and no external dependencies.\n- No use of selectors, leading to faster performance.\n- New native column types.\n- No jQuery required.\n- Examples for React, Vue, and Angular.\n- XLSX support via a custom SheetJS integration (experimental).\n\n### Jspreadsheet 2.1.0\n- Mobile touch improvements.\n- Paste fixes and a new CSV parser.\n\n### Jspreadsheet 2.0.0\n- New radio column type.\n- There is a new dropdown with autocomplete and multiple selection options.\n- Header/body separation for better scroll and column resize behaviour.\n- Text-wrap improvements, including Excel-compatible `alt+enter`.\n- New `set/get` meta information.\n- New `set/get` configuration parameters.\n- Programmatic `set/get` cell styles.\n- `set/get` cell comments.\n- Custom toolbar for tables.\n- Responsive calendar picker.\n\n### Jspreadsheet 1.5.7\n- Improvements to checkbox column type.\n- Updates to table destruction in jQuery.\n\n### Jspreadsheet 1.5.1\n- Spreadsheet data overflow and fixed headers.\n- Navigation improvements.\n\n### Jspreadsheet 1.5.0\n- Relative `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`.\n- Redo and undo support for `insertRow`, `deleteRow`, `insertColumn`, `deleteColumn`, `moveRow`.\n- New formula column recursive chain.\n- There is a new alternative design option (Bootstrap-like).\n- `updateSettings` improvements.\n\n## Copyright and License\nJspreadsheet CE is released under the MIT license.\n\n## About Jspreadsheet\nJspreadsheet is an original JavaScript software that facilitates data manipulation in web-based applications. It was inspired by other spreadsheet software and designed to be a lightweight, easy-to-use data input tool for users.\n\nThis free software was developed as a lightweight alternative to create amazing online JavaScript spreadsheets.\n\n"
  },
  {
    "path": "docs/jspreadsheet.md",
    "content": "title: Jspreadsheet CE - The JavaScript Data Grid with Spreadsheet Controls  \nkeywords: Jspreadsheet CE, JavaScript spreadsheet, data grid, Excel-like functionality, JavaScript plugins, web components, data table, interactive spreadsheets, customizable grid, spreadsheet integration  \ndescription: Jspreadsheet CE is a lightweight JavaScript data grid with powerful spreadsheet controls. Easily create interactive, customizable, and Excel-compatible web-based spreadsheets for seamless data management and enhanced user experience.\ncanonical: https://bossanova.uk/jspreadsheet\n\n<div class=\"home homepage\">\n\n<div class=\"row\">\n    <div class=\"column f5\">\n        <div class=\"limit-75\">\n            <h1>JavaScript component to create web applications with spreadsheet UI</h1>\n            <p>Free and open-source JavaScript component to create web applications with spreadsheet UI.</p>\n            <button class=\"button main\" style=\"width: 130px; margin-top: 20px\"><a href=\"/jspreadsheet/docs\">Get Started</a></button>\n        </div>\n    </div>\n    <div class=\"column f4 big-screen-only\">\n        <img src=\"img/home/title-image.svg\" alt=\"Worksheet\" style=\"width: 100%;\">\n   </div>\n</div>\n\n<div class=\"space100\"></div>\n\n<div class=\"stats\">\n    <div class=\"stats-item\">\n        <img src=\"img/home/npm-logo.svg\" alt=\"NPM Logo\" style=\"height: 22px;\">\n        <div>\n            <h4>23k+</h4>\n            <span>Weekly downloads</span>\n        </div>\n    </div>\n    <div class=\"stats-item\">\n        <img src=\"img/home/github-logo.svg\" alt=\"GitHub Logo\" style=\"height: 36px;\">\n        <div>\n            <h4>6700+</h4>\n            <span>GitHub stars</span>\n        </div>\n    </div>\n    <div class=\"stats-item\">\n        <img src=\"img/home/license-icon.svg\" alt=\"MIT License Icon\" style=\"height: 36px;\">\n        <div>\n            <h4>MIT License</h4>\n            <span>Free and open-source</span>\n        </div>\n    </div>\n</div>\n\n<div class=\"space100\"></div>\n\n<div class=\"row center frameworks\" style=\"justify-content: center;\">\n    <div class=\"column\" style=\"max-width: 520px;\">\n        <h2>Compatible with<br>React, Angular, and Vue.</h2>\n        <p>Jspreadsheet CE provides compatibility across different environments, ensuring your spreadsheet solution fits perfectly into your chosen stack.</p>\n        <br><br>\n        <img src=\"img/javascript.png\" alt=\"JavaScript\" />\n        <img src=\"img/angular.png\" alt=\"Angular\" />\n        <img src=\"img/react.png\" alt=\"ReactJS\" />\n        <img src=\"img/vuejs.png\" alt=\"VueJS\" />\n        <img src=\"img/lemonadejs.png\" alt=\"LemonadeJS\" />\n    </div>\n</div>\n\n<div class=\"space200\"></div>\n\n<div class=\"row big-screen-only\">\n    <div class=\"column f4\">\n        <div>\n            <h2>A complete solution to make rich and user-friendly web applications</h2>\n            <p>Fully customizable JavaScript spreadsheet library, offering various components to enhance web development projects.</p>\n            <div>\n                <div class=\"example-selectable-card selected\" onclick=\"showText('home-column-types', this)\">\n                    <h4>Column Types</h4>\n                    <p>A variety of customisable column types</p>\n                </div>\n                <div class=\"example-selectable-card\" onclick=\"showText('home-nested-headers', this)\">\n                    <h4>Nested Headers</h4>\n                    <p>Create multi-level column headers</p>\n                </div>\n                <div class=\"example-selectable-card\" onclick=\"showText('home-cell-comments', this)\">\n                    <h4>Cell Comments</h4>\n                    <p>Add comments to your spreadsheet cells</p>\n                </div>\n                <div class=\"example-selectable-card\" onclick=\"showText('home-custom-toolbar', this)\">\n                    <h4>Custom Toolbar</h4>\n                    <p>Personalize your spreadsheet toolbar</p>\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"column f5 code-block-col\">\n\n<div id=\"home-column-types\" class=\"code-block active\">\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\nlet table = jspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['Hello', 13123, '', 'Yes', true, '#AA4411'],\n            ['World!', 8, '', 'No', false, '#99BE23']\n        ],\n        columns: [\n            { type: 'text', title: 'Text' },\n            { type: 'numeric', title: 'Numeric', mask:'$ #.##,00', decimal: ',' },\n            { type: 'calendar', title: 'Calendar' },\n            { type: 'dropdown', source: ['Yes', 'No', 'Maybe'] },\n            { type: 'checkbox', title: 'Checkbox' },\n            { type: 'color', title: 'Color', width: 50, render: 'square' }\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Data\n    const data = [\n        ['Hello', 13123, '', 'Yes', true, '#AA4411'],\n        ['World!', 8, '', 'No', false, '#99BE23']\n    ];\n    // Columns\n    const columns = [\n        { type: 'text', title: 'Text' },\n        { type: 'numeric', title: 'Numeric', mask:'$ #.##,00', decimal: ',' },\n        { type: 'calendar', title: 'Calendar' },\n        { type: 'dropdown', source: ['Yes', 'No', 'Maybe'] },\n        { type: 'checkbox', title: 'Checkbox' },\n        { type: 'color', title: 'Color', width: 50, render: 'square' }\n    ];\n\n    // Render component\n    return (\n        <Spreadsheet ref={spreadsheet}>\n            <Worksheet data={data} columns={columns} />\n        </Spreadsheet>\n    );\n}\n```\n```vue\n<template>\n    <Spreadsheet ref=\"spreadsheetRef\">\n        <Worksheet \n            :data=\"data\" \n            :columns=\"columns\" \n        />\n    </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([\n    ['Hello', 13123, '', 'Yes', true, '#AA4411'],\n    ['World!', 8, '', 'No', false, '#99BE23']\n]);\n\n// Columns\nconst columns = ref([\n    { type: 'text', title: 'Text' },\n    { type: 'numeric', title: 'Numeric', mask:'$ #.##,00', decimal: ',' },\n    { type: 'calendar', title: 'Calendar' },\n    { type: 'dropdown', source: ['Yes', 'No', 'Maybe'] },\n    { type: 'checkbox', title: 'Checkbox' },\n    { type: 'color', title: 'Color', width: 50, render: 'square' }\n]);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [\n                    ['Hello', 13123, '', 'Yes', true, '#AA4411'],\n                    ['World!', 8, '', 'No', false, '#99BE23']\n                ],\n                columns: [\n                    { type: 'text', title: 'Text' },\n                    { type: 'numeric', title: 'Numeric', mask:'$ #.##,00', decimal: ',' },\n                    { type: 'calendar', title: 'Calendar' },\n                    { type: 'dropdown', source: ['Yes', 'No', 'Maybe'] },\n                    { type: 'checkbox', title: 'Checkbox' },\n                    { type: 'color', title: 'Color', width: 50, render: 'square' }\n                ]\n            }],\n        });\n    }\n}\n```\n\n</div>\n<div id=\"home-nested-headers\" class=\"code-block\">\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [[]],\n        minDimensions: [5, 2],\n        nestedHeaders:[\n            [\n                {\n                    title: 'Supermarket information',\n                    colspan: '6',\n                },\n            ],\n            [\n                {\n                    title: 'Location',\n                    colspan: '1',\n                },\n                {\n                    title: ' Other Information',\n                    colspan: '2'\n                },\n                {\n                    title: ' Costs',\n                    colspan: '3'\n                }\n            ],\n        ]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    // Spreadsheet array of worksheets\n    const spreadsheet = useRef();\n    // Nested headers\n    const nestedHeaders = [\n        [\n            {\n                title: 'Supermarket information',\n                colspan: '6',\n            },\n        ],\n        [\n            {\n                title: 'Location',\n                colspan: '1',\n            },\n            {\n                title: ' Other Information',\n                colspan: '2'\n            },\n            {\n                title: ' Costs',\n                colspan: '3'\n            }\n        ],\n    ];\n    // Render component\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet nestedHeaders={nestedHeaders} minDimensions={[5,2]} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheet\">\n      <Worksheet \n          :nestedHeaders=\"nestedHeaders\" \n          :minDimensions=\"[5,2]\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Nested headers\nconst nestedHeaders = ref([\n  [\n      {\n          title: 'Supermarket information',\n          colspan: '6',\n      },\n  ],\n  [\n      {\n          title: 'Location',\n          colspan: '1',\n      },\n      {\n          title: ' Other Information',\n          colspan: '2'\n      },\n      {\n          title: ' Costs',\n          colspan: '3'\n      }\n  ],\n]);\n\nconst spreadsheet = ref(null);\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [{\n                data: [[]],\n                minDimensions: [5, 2],\n                nestedHeaders:[\n                    [\n                        {\n                            title: 'Supermarket information',\n                            colspan: '6',\n                        },\n                    ],\n                    [\n                        {\n                            title: 'Location',\n                            colspan: '1',\n                        },\n                        {\n                            title: ' Other Information',\n                            colspan: '2'\n                        },\n                        {\n                            title: ' Costs',\n                            colspan: '3'\n                        }\n                    ],\n                ]\n            }]\n        });\n    }\n}\n```\n\n</div>\n<div id=\"home-cell-comments\" class=\"code-block\">\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    worksheets: [{\n        data: [\n            ['US', 'Cheese', '2019-02-12'],\n            ['CA', 'Apples', '2019-03-01'],\n        ],\n        columns: [\n            {width: '170px'},\n            {width: '170px'},\n            {width: '170px'},\n        ],\n        allowComments: true,\n        comments: {\n            B1: 'Initial comments on B1',\n            C1: 'Initial comments on C1'\n        },\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\nconst spreadsheet = useRef();\n\n    const data = [\n        ['US', 'Cheese', '2019-02-12'],\n        ['CA', 'Apples', '2019-03-01'],\n    ];\n\n    const columns = [\n        { width: '170px' },\n        { width: '170px' },\n        { width: '170px' },\n    ];\n\n    const comments = {\n        B1: 'Initial comments on B1',\n        C1: 'Initial comments on C1'\n    };\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet}>\n                <Worksheet data={data} columns={columns} comments={comments} allowComments />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\">\n      <Worksheet \n          :data=\"data\" \n          :columns=\"columns\" \n          :comments=\"comments\" \n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\n// Data\nconst data = ref([\n  [\"US\", \"Cheese\", \"2019-02-12\"],\n  [\"CA\", \"Apples\", \"2019-03-01\"],\n]);\n\n// Columns configuration\nconst columns = ref([\n  { width: \"170px\" },\n  { width: \"170px\" },\n  { width: \"170px\" }\n]);\n\n// Initial comments\nconst comments = ref({\n  B1: \"Initial comments on B1\",\n  C1: \"Initial comments on C1\",\n});\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    worksheets: jspreadsheet.worksheetInstance[];\n\n    ngAfterViewInit() {\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Cheese', '2019-02-12'],\n                        ['CA', 'Apples', '2019-03-01'],\n                        ['CA', 'Carrots', '2018-11-10'],\n                        ['BR', 'Oranges', '2019-01-12'],\n                    ],\n                    columns: [\n                        { width: '170px' },\n                        { width: '170px' },\n                        { width: '170px' },\n                    ],\n                    allowComments: true,\n                    comments: {\n                        B1: 'Initial comments on B1',\n                        C1: 'Initial comments on C1'\n                    },\n                }\n            ],\n        });\n    }\n}\n```\n\n</div>\n<div id=\"home-custom-toolbar\" class=\"code-block\">\n\n```html\n<html>\n<script src=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v5/jspreadsheet.css\" type=\"text/css\" />\n<script src=\"https://jsuites.net/v5/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v5/jsuites.css\" type=\"text/css\" />\n\n\n<div id=\"spreadsheet\"></div>\n\n<script>\njspreadsheet(document.getElementById('spreadsheet'), {\n    toolbar: function(toolbar) {\n        // Add a new custom item in the end of my toolbar\n        toolbar.items.push({\n            tooltip: 'My custom item',\n            content: 'share',\n            onclick: function() {\n                alert('Custom click');\n            }\n        });\n\n        return toolbar;\n    },\n    worksheets: [{\n        minDimensions: [6, 2]\n    }]\n});\n</script>\n</html>\n```\n```jsx\nimport React, { useRef } from \"react\";\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/react\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\";\n\nexport default function App() {\n    const spreadsheet = useRef();\n\n    const toolbar = function(toolbar) {\n        // Add a new custom item in the end of my toolbar\n        toolbar.items.push({\n            tooltip: 'My custom item',\n            content: 'share',\n            onclick: function() {\n                alert('Custom click');\n            }\n        });\n\n        return toolbar;\n    }\n\n    return (\n        <>\n            <Spreadsheet ref={spreadsheet} toolbar={toolbar}>\n                <Worksheet minDimensions={[6, 2]} />\n            </Spreadsheet>\n        </>\n    );\n}\n```\n```vue\n<template>\n  <Spreadsheet ref=\"spreadsheetRef\" :toolbar=\"toolbar\">\n      <Worksheet \n          :minDimensions=\"[5, 2]\"\n      />\n  </Spreadsheet>\n</template>\n\n<script setup>\nimport { ref } from 'vue';\nimport { Spreadsheet, Worksheet } from \"@jspreadsheet-ce/vue\";\nimport \"jsuites/dist/jsuites.css\";\nimport \"jspreadsheet-ce/dist/spreadsheet.css\";\n\n// Spreadsheet ref\nconst spreadsheetRef = ref(null);\n\nconst toolbar = (toolbar) => {\n    // Add a new custom item in the end of my toolbar\n    toolbar.items.push({\n        tooltip: 'My custom item',\n        content: 'share',\n        onclick: function() {\n            alert('Custom click');\n        }\n    });\n    return toolbar;\n}\n</script>\n```\n```angularjs\nimport { Component, ViewChild, ElementRef } from \"@angular/core\";\nimport jspreadsheet from \"jspreadsheet-ce\";\n\nimport \"jspreadsheet-ce/dist/jspreadsheet.css\"\nimport \"jsuites/dist/jsuites.css\"\n\n// Create component\n@Component({\n    standalone: true,\n    selector: \"app-root\",\n    template: `<div #spreadsheet></div>`,\n})\nexport class AppComponent {\n    @ViewChild(\"spreadsheet\") spreadsheet: ElementRef;\n    // Worksheets\n    worksheets: jspreadsheet.worksheetInstance[];\n    // Create a new data grid\n    ngAfterViewInit() {\n        // Create spreadsheet\n        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {\n            toolbar: function(toolbar) {\n                // Add a new custom item in the end of my toolbar\n                toolbar.items.push({\n                    tooltip: 'My custom item',\n                    content: 'share',\n                    onclick: function() {\n                        alert('Custom click');\n                    }\n                });\n        \n                return toolbar;\n            },\n            worksheets: [{\n                minDimensions: [6, 2]\n            }]\n        });\n    }\n}\n```\n\n</div>\n\n</div>\n</div>\n\n<div class=\"space200 line\"></div>\n\n<div class=\"row\">\n    <div class=\"column f1\">\n        <div style=\"max-width: 500px;\">\n            <h2>Create an online spreadsheet from various data formats</h2>\n            <p>With Jspreadsheet, you can quickly create online spreadsheets using JavaScript arrays, JSON, CSV, and XLSX files. This flexibility simplifies data integration and minimizes your application's need for complex processing.</p>\n        </div>\n   </div>\n    <div class=\"column f1 video-wrapper big-screen-only\">\n        <video src=\"media/home-video.mp4\" playsinline autoplay muted loop type=\"video/mp4\" style=\"width:95%;height: 95%;\"></video>\n    </div>\n</div>\n\n<div class=\"space200 line\"></div>\n\n<div class=\"container center\" style=\"max-width: 680px;\">\n    <h2>Deliver high-quality interfaces and applications to your end-user</h2>\n    <p>Jspreadsheet reduces customers development time.<br>Here are some of their experiences.</p>\n    <br>\n</div>\n\n<div class=\"box\" data-number=\"3\">\n    <div>\n        <img src=\"img/home/grid/rich-interfaces.svg\" style=\"height: 36px; width: 36px;\" alt=\"Rich Interfaces\">\n        <h4>Rich Interfaces</h4>\n        <p>Make rich and user-friendly web interfaces and applications</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/user-friendly-inputs.svg\" style=\"height: 36px; width: 36px;\" alt=\"User-Friendly Inputs\">\n        <h4>User-Friendly Inputs</h4>\n        <p>You can easily handle complicated data inputs in a way users are used to</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/enhanced-experience.svg\" style=\"height: 36px; width: 36px;\" alt=\"Enhanced Experience\">\n        <h4>Enhanced Experience</h4>\n        <p>Improve your user software experience</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/beautiful-cruds.svg\" style=\"height: 36px; width: 36px;\" alt=\"Beautiful CRUDs\">\n        <h4>Beautiful CRUDs</h4>\n        <p>Create rich CRUDS and beautiful UI</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/compatibility-with-excel.svg\" style=\"height: 36px; width: 36px;\" alt=\"Compatibility with excel spreadsheets\">\n        <h4>Compatibility with excel spreadsheets</h4>\n        <p>Users can move data around with common copy and paste shortcuts</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/easy-customization.svg\" style=\"height: 36px; width: 36px;\" alt=\"Easy Customization\">\n        <h4>Easy Customization</h4>\n        <p>Easy customizations with easy third-party plugin integrations</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/fast-and-simple.svg\" style=\"height: 36px; width: 36px;\" alt=\"Fast and Simple\">\n        <h4>Fast and Simple</h4>\n        <p>Lean, fast and simple to use</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/faster-data-entry.svg\" style=\"height: 36px; width: 36px;\" alt=\"Faster Data Entry\">\n        <h4>Faster Data Entry</h4>\n        <p>Speed up your work dealing with difficult data entry in a web-based software</p>\n    </div>\n    <div>\n        <img src=\"img/home/grid/shareable-spreadsheets.svg\" style=\"height: 36px; width: 36px;\" alt=\"Shareable Spreadsheets\">\n        <h4>Shareable Spreadsheets</h4>\n        <p>Create and share amazing online spreadsheets</p>\n    </div>\n</div>\n\n<div class=\"space200 line\"></div>\n\n<div class=\"container center p20\" style=\"max-width: 650px;\">\n    <h2>Component Ecosystem</h2>\n    <p>Explore the powerful and versatile components designed to elevate your productivity. From data management to collaboration, our ecosystem seamlessly integrates to meet your needs.</p>\n    <br>\n</div>\n\n<div class=\"box\" data-number=\"2\">\n    <div>\n        <a href=\"https://jspreadsheet.com\"><img src=\"img/logo/jspreadsheet-pro-logo.svg\" style=\"height: 44px; width: 44px;\" alt=\"Jspreadsheet Pro - JavaScript Data Grid With Spreadsheet Controls\"></a>\n        <h4>Jspreadsheet Pro</h4>\n        <p>Enterprise JavaScript data grid component to integrate spreadsheet UI into your web-based application.</p>\n    </div>\n    <div>\n        <a href=\"https://intrasheets.com\"><img src=\"img/logo/intrasheets-logo.svg\" style=\"height: 44px; width: 44px;\" alt=\"Intrasheets - Collaborative Private Spreadsheets\"></a>\n        <h4>Intrasheets</h4>\n        <p>Collaborate with ease using Intrasheets, an intuitive tool for managing spreadsheets across teams, ensuring that everyone stays on the same page.</p>\n    </div>\n    <div>\n        <a href=\"https://jsuites.net\"><img src=\"img/logo/jsuites-logo.svg\" style=\"height: 44px; width: 44px;\" alt=\"jSuites - JavaScript Plugins\"></a>\n        <h4>Jsuites</h4>\n        <p>Comprehensive JavaScript plugins and web components for diverse web-based applications and requirements.</p>\n    </div>\n    <div>\n        <a href=\"https://lemonadejs.com\"><img src=\"img/logo/lemonadejs-logo.svg\" style=\"height: 44px; width: 44px;\"  alt=\"LemonadeJS - Micro Agnostic JavaScript Reactive library\"></a>\n        <h4>LemonadeJS</h4>\n        <p>A light and easy-to-use solution for creating elegant UI elements, giving your web apps a refreshing boost in both style and functionality.</p>\n    </div>\n</div>\n\n<div class=\"space100\"></div>\n<div class=\"bg-section\" style=\"background-color: #46077a27; height: 450px;\"></div>\n<div class=\"space100\"></div>\n\n<div class=\"row middle center\" style=\"justify-content: center;\">\n    <div class=\"column\">\n        <br><br>\n        <h2>Subscribe to our newsletter</h2>\n        <p>Tech news, tips and technical advice</p>\n        <div>\n            <div id=\"mc_embed_signup\">\n                <form action=\"https://bossanova.us20.list-manage.com/subscribe/post?u=f9b5adf8223e3d5a8f575b0bb&amp;id=37f97a936c\" method=\"post\" id=\"mc-embedded-subscribe-form\" name=\"mc-embedded-subscribe-form\" class=\"validate\" target=\"_blank\" novalidate>\n                    <div id=\"mc_embed_signup_scroll\">\n                        <div class=\"mc-field-group\">\n                            <input type=\"email\" value=\"\" name=\"EMAIL\" class=\"required email\" id=\"mce-EMAIL\" placeholder=\"user@email.com\"> <input type=\"submit\" value=\"Subscribe\" name=\"subscribe\" id=\"mc-embedded-subscribe\" class='main button'>\n                        </div>\n                        <div id=\"mce-responses\" class=\"clear\">\n                            <div class=\"response\" id=\"mce-error-response\" style=\"display:none\"></div>\n                            <div class=\"response\" id=\"mce-success-response\" style=\"display:none\"></div>\n                        </div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->\n                        <div style=\"position: absolute; left: -5000px;\" aria-hidden=\"true\"><input type=\"text\" name=\"b_f9b5adf8223e3d5a8f575b0bb_37f97a936c\" tabindex=\"-1\" value=\"\"></div>\n                    </div>\n                </form>\n            </div>\n        </div>\n    </div>\n</div>\n\n<div class=\"space100\"></div>\n\n<div class=\"block-space\"></div>\n\n</div>\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import js from '@eslint/js';\nimport globals from 'globals';\n\nexport default [\n    js.configs.recommended,\n    {\n        ignores: ['dist/**', 'node_modules/**'],\n    },\n    {\n        files: ['**/*.{js,mjs,cjs}'],\n        languageOptions: {\n            ecmaVersion: 2022,\n            sourceType: 'module',\n            globals: {\n                ...globals.browser,\n                ...globals.node,\n                ...globals.es2022,\n                jspreadsheet: 'readonly',\n            },\n        },\n        rules: {\n            'no-unused-vars': 'off',\n        },\n    },\n    {\n        files: ['test/**/*.js', 'mocha.config.js'],\n        languageOptions: {\n            globals: {\n                ...globals.node,\n                ...globals.mocha,\n                describe: 'readonly',\n                it: 'readonly',\n                before: 'readonly',\n                after: 'readonly',\n                beforeEach: 'readonly',\n                afterEach: 'readonly',\n                root: 'readonly',\n            },\n        },\n    },\n    {\n        files: ['webpack.config.js', 'build.cjs'],\n        languageOptions: {\n            globals: {\n                ...globals.node,\n                __dirname: 'readonly',\n                __filename: 'readonly',\n                module: 'readonly',\n                require: 'readonly',\n                exports: 'readonly',\n                process: 'readonly',\n            },\n        },\n    },\n];\n"
  },
  {
    "path": "mocha.config.js",
    "content": "#! /usr/bin/env node\n\nrequire('jsdom-global')(undefined, { url: 'https://localhost' });\n\nglobal.root = document.createElement('div');\nglobal.root.style.width = '100%';\nglobal.root.style.height = '100%';\nglobal.root.innerHTML = '';\ndocument.body.appendChild(global.root);\n\nexports.mochaHooks = {\n    afterEach(done) {\n        // destroy datagrid component\n        global.root.innerHTML = '';\n        done();\n    },\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"jspreadsheet-ce\",\n    \"title\": \"The Javascript Spreadsheet\",\n    \"description\": \"Jspreadsheet is a lightweight, vanilla javascript plugin to create amazing web-based interactive data grids with spreadsheet like controls compatible with Excel, Google Spreadsheets and any other spreadsheet software.\",\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"https://github.com/jspreadsheet/ce.git\"\n    },\n    \"author\": {\n        \"name\": \"Contact <contact@jspreadsheet.com>\"\n    },\n    \"keywords\": [\n        \"spreadsheet\",\n        \"spreadsheets\",\n        \"tables\",\n        \"table\",\n        \"excel\",\n        \"grid\",\n        \"data grid\",\n        \"data tables\",\n        \"javascript data grid\",\n        \"javascript spreadsheet\",\n        \"data spreadsheet\",\n        \"react\",\n        \"grid\",\n        \"datagrid\",\n        \"reactjs\",\n        \"react-component\",\n        \"angular\",\n        \"data-grid\",\n        \"vue\",\n        \"csv\",\n        \"datatable\",\n        \"vuejs\",\n        \"angular-component\",\n        \"javascript\",\n        \"data-table\",\n        \"web-components\",\n        \"xlsx\",\n        \"react-table\",\n        \"sheets\",\n        \"sheet\",\n        \"export\",\n        \"markdown\",\n        \"parser\",\n        \"xls\",\n        \"datasheet\"\n    ],\n    \"dependencies\": {\n        \"@jspreadsheet/formula\": \"^2.0.2\",\n        \"jsuites\": \"^5.12.0\"\n    },\n    \"devDependencies\": {\n        \"@babel/cli\": \"^7.7.4\",\n        \"@babel/core\": \"^7.7.4\",\n        \"@babel/preset-env\": \"^7.7.4\",\n        \"@babel/register\": \"^7.7.4\",\n        \"@eslint/js\": \"^9.34.0\",\n        \"c8\": \"^7.13.0\",\n        \"chai\": \"^4.3.7\",\n        \"copy-webpack-plugin\": \"^11.0.0\",\n        \"cross-env\": \"^7.0.3\",\n        \"css-loader\": \"^6.8.1\",\n        \"eslint\": \"^8.57.1\",\n        \"eslint-config-prettier\": \"^8.8.0\",\n        \"globals\": \"^16.3.0\",\n        \"html-replace-webpack-plugin\": \"^2.6.0\",\n        \"html-webpack-plugin\": \"^5.5.0\",\n        \"jsdoc\": \"^4.0.2\",\n        \"jsdom\": \"^22.0.0\",\n        \"jsdom-global\": \"^3.0.2\",\n        \"mini-css-extract-plugin\": \"^2.7.6\",\n        \"mocha\": \"^10.2.0\",\n        \"mochawesome\": \"^7.1.3\",\n        \"prettier\": \"2.8.8\",\n        \"style-loader\": \"^3.3.1\",\n        \"webpack\": \"^5.88.2\",\n        \"webpack-cli\": \"^5.1.4\",\n        \"webpack-dev-server\": \"^4.15.1\"\n    },\n    \"main\": \"dist/index.js\",\n    \"types\": \"dist/index.d.ts\",\n    \"version\": \"5.0.4\",\n    \"bugs\": \"https://github.com/jspreadsheet/ce/issues\",\n    \"homepage\": \"https://github.com/jspreadsheet/ce\",\n    \"download\": \"https://github.com/jspreadsheet/ce/archive/master.zip\",\n    \"scripts\": {\n        \"format\": \"npm run prettier:fix && npm run lint:fix\",\n        \"lint\": \"eslint .\",\n        \"lint:fix\": \"eslint --fix .\",\n        \"prettier\": \"prettier --check .\",\n        \"prettier:fix\": \"prettier --write . --ignore-path .prettierignore\",\n        \"start\": \"webpack serve --history-api-fallback\",\n        \"prebuild\": \"npm run prettier:fix && npm run lint:fix\",\n        \"build\": \"cross-env NODE_ENV=production webpack --config webpack.config.js\",\n        \"test\": \"npm run build && mocha --recursive --require=mocha.config.js\"\n    }\n}\n"
  },
  {
    "path": "packages/vue/README.md",
    "content": "# Jspreasheet CE\n\n[Jspreadsheet CE](https://bossanova.uk/jspreadsheet/)\n"
  },
  {
    "path": "packages/vue/dist/index.d.ts",
    "content": "import { DefineComponent } from 'vue';\nimport type JSpreadsheetCore from 'jspreadsheet-ce';\n\n// Get all the static types from the core library\ntype JSpreadsheetBase = typeof JSpreadsheetCore;\n\n// Create interface that extends the base type and adds call signature\ninterface JSpreadsheetInterface extends JSpreadsheetBase {\n    (element: HTMLElement | HTMLDivElement | null, options: JSpreadsheetCore.Spreadsheet): Array<JSpreadsheetCore.WorksheetInstance>;\n}\n\nexport declare const Worksheet: DefineComponent<JSpreadsheetCore.Worksheet>;\nexport declare const Spreadsheet: DefineComponent<JSpreadsheetCore.Spreadsheet>;\n\nexport declare const jspreadsheet: JSpreadsheetInterface;\n\ndeclare const _default: {\n    Worksheet: DefineComponent<JSpreadsheetCore.Worksheet>;\n    Spreadsheet: DefineComponent<JSpreadsheetCore.Spreadsheet>;\n    jspreadsheet: JSpreadsheetInterface;\n};\n\nexport default _default;\n"
  },
  {
    "path": "packages/vue/dist/index.js",
    "content": "import { h, getCurrentInstance, camelize } from 'vue';\nimport jss from 'jspreadsheet-ce';\n\nexport const Worksheet = {\n    name: 'Worksheet',\n};\n\nexport const Spreadsheet = {\n    inheritAttrs: false,\n    mounted() {\n        const { attrs, slots } = getCurrentInstance();\n\n        let options = {};\n        for (const key in attrs) {\n            options[camelize(key)] = attrs[key];\n        }\n\n        if (slots && typeof slots.default === 'function') {\n            options.worksheets = slots.default().reduce((acc, vnode) => {\n                if (vnode.type.name === 'Worksheet') {\n                    let worksheetProps = {};\n\n                    for (const key in vnode.props) {\n                        worksheetProps[camelize(key)] = vnode.props[key];\n                    }\n\n                    acc.push({\n                        minDimensions: [4, 4],\n                        ...worksheetProps,\n                    });\n                }\n                return acc;\n            }, []);\n        } else {\n            if (attrs.worksheets) {\n                options.worksheets = attrs.worksheets;\n            }\n        }\n\n        if (attrs.id) {\n            this.$refs.container.id = attrs.id;\n        }\n\n        this.el = this.$refs.container;\n\n        this.current = jss(this.$refs.container, options);\n    },\n    setup() {\n        let containerProps = {\n            ref: 'container',\n        };\n        return () => h('div', containerProps);\n    },\n};\n\nexport let jspreadsheet = jss;\n\nexport default { Worksheet, Spreadsheet, jspreadsheet: jss };\n"
  },
  {
    "path": "packages/vue/package.json",
    "content": "{\n    \"name\": \"@jspreadsheet-ce/vue\",\n    \"title\": \"Jspreadsheet CE Vue Wrapper\",\n    \"description\": \"Jspreadsheet CE is a lightweight, vanilla JavaScript plugin for creating powerful, web-based interactive tables and spreadsheets that are compatible with other spreadsheet software.\",\n    \"author\": {\n        \"name\": \"Contact <contact@jspreadsheet.com>\",\n        \"url\": \"https://bossanova.uk/jspreadsheet/\"\n    },\n    \"keywords\": [\n        \"spreadsheet\",\n        \"spreadsheets\",\n        \"tables\",\n        \"table\",\n        \"excel\",\n        \"grid\",\n        \"grid-editor\",\n        \"data-table\",\n        \"data-grid\",\n        \"data-spreadsheet\",\n        \"vue\"\n    ],\n    \"dependencies\": {\n        \"jspreadsheet-ce\": \"^5.0.1\"\n    },\n    \"main\": \"dist/index.js\",\n    \"types\": \"dist/index.d.ts\",\n    \"version\": \"5.0.1\"\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover\" />\n        <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n        <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\" />\n        <meta name=\"theme-color\" content=\"#2196f3\" />\n        <meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap:; img-src * 'self' data: blob:;\" />\n        <meta name=\"format-detection\" content=\"telephone=no\" />\n        <meta name=\"msapplication-tap-highlight\" content=\"no\" />\n        <title>CE</title>\n        <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Material+Icons\" />\n    </head>\n    <body>\n        <div id=\"root\"></div>\n\n        <script type=\"text/javascript\" src=\"http://localhost:8000/index.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "resources/lang/de_DE.json",
    "content": "{\n    \"noRecordsFound\": \"Keine Einträge vorhanden.\",\n    \"showingPage\": \"Seite {0} von {1}\",\n    \"show\": \"Zeige \",\n    \"search\": \"Suchen\",\n    \"entries\": \"Einträge\",\n    \"columnName\": \"Spaltenname\",\n    \"insertANewColumnBefore\": \"Eine neue Spalte davor einfügen\",\n    \"insertANewColumnAfter\": \"Eine neue Spalte danach einfügen\",\n    \"deleteSelectedColumns\": \"Ausgewählte Spalten löschen\",\n    \"renameThisColumn\": \"Diese Spalte umbenennen\",\n    \"orderAscending\": \"Aufsteigend sortieren\",\n    \"orderDescending\": \"Absteigend sortieren\",\n    \"insertANewRowBefore\": \"Eine neue Zeile davor einfügen\",\n    \"insertANewRowAfter\": \"Eine neue Zeile danach einfügen\",\n    \"deleteSelectedRows\": \"Ausgewählte Zeilen löschen\",\n    \"editComments\": \"Kommentare bearbeiten\",\n    \"addComments\": \"Einen Kommentar \",\n    \"comments\": \"Kommentare\",\n    \"clearComments\": \"Kommentare entfernen\",\n    \"copy\": \"Kopieren...\",\n    \"paste\": \"Einfügen...\",\n    \"saveAs\": \"Speichern als...\",\n    \"about\": \"Über\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Möchten Sie die ausgewählten Zeilen wirklich löschen?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Möchten Sie die ausgewählten Spalten wirklich löschen?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Diese Aktion zerstört alle verbundenen Zellen. Möchten Sie fortfahren?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Dieser Vorgang wird Ihre Suchergebnisse entfernen. Möchten Sie fortfahren?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Es liegt ein Konflikt mit einer anderen verbundenen Zelle vor\",\n    \"invalidMergeProperties\": \"Eigenschaften zum Verbinden ungültig\",\n    \"cellAlreadyMerged\": \"Zelle bereits verbunden\",\n    \"noCellsSelected\": \"Keine Zellen ausgewählt\"\n}\n"
  },
  {
    "path": "resources/lang/dk_DA.json",
    "content": "{\n    \"noRecordsFound\": \"Ingen felter fundet\",\n    \"showingPage\": \"Viser side {0} af {1}\",\n    \"show\": \"Vis \",\n    \"search\": \"Søg\",\n    \"entries\": \"indgange\",\n    \"columnName\": \"Søjlenavn\",\n    \"insertANewColumnBefore\": \"Indsæt ny søjle før\",\n    \"insertANewColumnAfter\": \"Indsæt ny søjle efter\",\n    \"deleteSelectedColumns\": \"Slet valgte søjler\",\n    \"renameThisColumn\": \"Omdøb denne søjle\",\n    \"orderAscending\": \"Sorter voksende\",\n    \"orderDescending\": \"Sorter aftagende\",\n    \"insertANewRowBefore\": \"Indsæt ny række før\",\n    \"insertANewRowAfter\": \"Indsæt ny række efter\",\n    \"deleteSelectedRows\": \"Slet valgte rækker\",\n    \"editComments\": \"Rediger kommentarer\",\n    \"addComments\": \"Tilføj kommentarer\",\n    \"comments\": \"Kommentarer\",\n    \"clearComments\": \"Ryd kommentarer\",\n    \"copy\": \"Kopier...\",\n    \"paste\": \"Indsæt...\",\n    \"saveAs\": \"Gem som...\",\n    \"about\": \"Om\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Sikker på at slette de valgte rækker?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Sikker på at slette de valgte søjler?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Dette sletter eksisterende forbundne celler. Sikker?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Dette rydder dine søgeresultater. Sikker?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Der er en konflikt med en anden forbundet celle.\",\n    \"invalidMergeProperties\": \"Ugyldige forbundne egenskaber\",\n    \"cellAlreadyMerged\": \"Celle allerede forbundet\",\n    \"noCellsSelected\": \"Ingen celler valgt\"\n}\n"
  },
  {
    "path": "resources/lang/en_GB.json",
    "content": "{\n    \"noRecordsFound\": \"No records found\",\n    \"showingPage\": \"Showing page {0} of {1} entries\",\n    \"show\": \"Show \",\n    \"search\": \"Search\",\n    \"entries\": \" entries\",\n    \"columnName\": \"Column name\",\n    \"insertANewColumnBefore\": \"Insert a new column before\",\n    \"insertANewColumnAfter\": \"Insert a new column after\",\n    \"deleteSelectedColumns\": \"Delete selected columns\",\n    \"renameThisColumn\": \"Rename this column\",\n    \"orderAscending\": \"Order ascending\",\n    \"orderDescending\": \"Order descending\",\n    \"insertANewRowBefore\": \"Insert a new row before\",\n    \"insertANewRowAfter\": \"Insert a new row after\",\n    \"deleteSelectedRows\": \"Delete selected rows\",\n    \"editComments\": \"Edit comments\",\n    \"addComments\": \"Add comments\",\n    \"comments\": \"Comments\",\n    \"clearComments\": \"Clear comments\",\n    \"copy\": \"Copy...\",\n    \"paste\": \"Paste...\",\n    \"saveAs\": \"Save as...\",\n    \"about\": \"About\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Are you sure to delete the selected rows?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Are you sure to delete the selected columns?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"This action will destroy any existing merged cells. Are you sure?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"This action will clear your search results. Are you sure?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"There is a conflict with another merged cell\",\n    \"invalidMergeProperties\": \"Invalid merged properties\",\n    \"cellAlreadyMerged\": \"Cell already merged\",\n    \"noCellsSelected\": \"No cells selected\"\n}\n"
  },
  {
    "path": "resources/lang/fr_FR.json",
    "content": "{\n    \"noRecordsFound\": \"Aucune donnée trouvée\",\n    \"showingPage\": \"Afficher la page {0} sur {1}\",\n    \"show\": \"Afficher \",\n    \"entries\": \"lignes\",\n    \"insertANewColumnBefore\": \"Insérer une nouvelle colonne avant\",\n    \"insertANewColumnAfter\": \"Insérer une nouvelle colonne après\",\n    \"deleteSelectedColumns\": \"Supprimer les colonnes sélectionnées\",\n    \"renameThisColumn\": \"Renommer la colonne\",\n    \"orderAscending\": \"Trier par ordre croissant\",\n    \"orderDescending\": \"Trier par ordre décroissant\",\n    \"insertANewRowBefore\": \"Insérer une nouvelle ligne avant\",\n    \"insertANewRowAfter\": \"Insérer une nouvelle ligne après\",\n    \"deleteSelectedRows\": \"Supprimer les lignes sélectionnées\",\n    \"editComments\": \"Modifier le commentaire\",\n    \"addComments\": \"Ajouter un commentaire\",\n    \"comments\": \"Commentaire\",\n    \"clearComments\": \"Supprimer le commentaire\",\n    \"copy\": \"Copier ...\",\n    \"paste\": \"Coller ...\",\n    \"saveAs\": \"Enregistrer sous ...\",\n    \"about\": \"A propos\",\n    \"search\": \"Rechercher\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Etes vous sur de vouloir supprimer les lignes sélectionnées ?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Etes vous sur de vouloir supprimer les colonnes sélectionnées ?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Cette action détruira toutes les cellules fusionnées existantes. Voulez vous continuer ?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Cette action effacera votre recherche. Voulez-vous continuer ?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Il y a un conflit avec une autre cellule fusionnée\",\n    \"invalidMergeProperties\": \"Propriétés de fusion invalides\",\n    \"cellAlreadyMerged\": \"Cellule déjà fusionnée\",\n    \"noCellsSelected\": \"Aucune cellule sélectionnée\"\n}\n"
  },
  {
    "path": "resources/lang/it_IT.json",
    "content": "{\n    \"noRecordsFound\": \"Nessun record trovato\",\n    \"showingPage\": \"Pagina {0} di {1}\",\n    \"show\": \"Mostra \",\n    \"search\": \"Cerca\",\n    \"entries\": \" elementi\",\n    \"columnName\": \"Nome colonna\",\n    \"insertANewColumnBefore\": \"Inserisci una nuova colonna prima\",\n    \"insertANewColumnAfter\": \"Inserisci una nuova colonna dopo\",\n    \"deleteSelectedColumns\": \"Delete selected columns\",\n    \"renameThisColumn\": \"Rinomina questa colonna\",\n    \"orderAscending\": \"Ordina ascendente\",\n    \"orderDescending\": \"Ordina decrescente\",\n    \"insertANewRowBefore\": \"Inserisci una nuova riga prima\",\n    \"insertANewRowAfter\": \"Inserisci una nuova riga dopo\",\n    \"deleteSelectedRows\": \"Elimina le righe selezionate\",\n    \"editComments\": \"Modifica commenti\",\n    \"addComments\": \"Aggiungi commenti\",\n    \"comments\": \"Commenti\",\n    \"clearComments\": \"Pulisci i commenti\",\n    \"copy\": \"Copia...\",\n    \"paste\": \"Incolla...\",\n    \"saveAs\": \"Salva come...\",\n    \"about\": \"Info\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Si � sicuri di voler eliminare queste righe?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Si � sicuri di voler eliminare queste colonne?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Questa azione eliminer� qualsiasi cella unita. Confermi?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Questa azione pulir� i risultati della ricerca. Confermi?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"C'� un conflitto con un'altra cella unita\",\n    \"invalidMergeProperties\": \"Propriet� di unione invalide\",\n    \"cellAlreadyMerged\": \"Celle gi� unite\",\n    \"noCellsSelected\": \"Nessuna cella selezionata\"\n}\n"
  },
  {
    "path": "resources/lang/pl_PL.json",
    "content": "{\n    \"noRecordsFound\": \"Nic nie znaleziono\",\n    \"showingPage\": \"Strona {0} z {1}\",\n    \"show\": \"Pokaż \",\n    \"search\": \"Szukaj/filtruj listę\",\n    \"entries\": \" rekordów\",\n    \"columnName\": \"Nazwa kolumny\",\n    \"insertANewColumnBefore\": \"Wstaw nową kolumnę przed\",\n    \"insertANewColumnAfter\": \"Wstaw nową kolumnę po\",\n    \"deleteSelectedColumns\": \"Usuń wybrane kolumny\",\n    \"renameThisColumn\": \"Zmień nazwę kolumny\",\n    \"orderAscending\": \"Sortuj rosnąco A-Z\",\n    \"orderDescending\": \"Sortuj malejąco Z-A\",\n    \"insertANewRowBefore\": \"Wstaw nowy wiersz przed\",\n    \"insertANewRowAfter\": \"Wstaw nowy wiersz po\",\n    \"deleteSelectedRows\": \"Usuń wybrane wiersze\",\n    \"editComments\": \"Edytuj komentarz\",\n    \"addComments\": \"Dodaj komentarz\",\n    \"comments\": \"Komentarze\",\n    \"clearComments\": \"Wyczyść komentarze\",\n    \"copy\": \"Kopiuj...\",\n    \"paste\": \"Wklej...\",\n    \"saveAs\": \"Zapisz jako...\",\n    \"about\": \"Informacja o Jspreadsheet CE \",\n    \"areYouSureToDeleteTheSelectedRows\": \"Czy na pewno chcesz usuńąć wybrane wiersze?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Czy na pewno chcesz usuńąć wybrane kolumny?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Ta operacja zniszczy istniejące komórki scalone. Czy na pewno?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Ta operacja wyczyści wyniki wyszukiwania. Czy na pewno?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Istnieje konflikt z inną scaloną komórką\",\n    \"invalidMergeProperties\": \"Nieprawidłowe właściwości scalenia\",\n    \"cellAlreadyMerged\": \"Komórka już scalona\",\n    \"noCellsSelected\": \"Nie wybrano żadnych komórek\"\n}\n"
  },
  {
    "path": "resources/lang/pt_BR.json",
    "content": "{\n    \"noRecordsFound\": \"Nenhum registro encontrado\",\n    \"showingPage\": \"Exibindo {0} de {1} páginas\",\n    \"show\": \"Exibir \",\n    \"search\": \"Buscar\",\n    \"entries\": \" Entradas\",\n    \"columnName\": \"Nome da coluna\",\n    \"insertANewColumnBefore\": \"Inserir uma nova coluna antes\",\n    \"insertANewColumnAfter\": \"Inserir uma nova coluna depois\",\n    \"deleteSelectedColumns\": \"Deletar as colunas selecionadas\",\n    \"renameThisColumn\": \"Renomar essa coluna\",\n    \"orderAscending\": \"Ordenar ascedente\",\n    \"orderDescending\": \"Ordenar descedente\",\n    \"insertANewRowBefore\": \"Inserir um novo registro antes\",\n    \"insertANewRowAfter\": \"Inserir um novo registro depois\",\n    \"deleteSelectedRows\": \"Deletar as linhas selecionadas\",\n    \"editComments\": \"Editar comentários\",\n    \"addComments\": \"Adicionar comentários\",\n    \"comments\": \"Comentários\",\n    \"clearComments\": \"Limpar comentários\",\n    \"copy\": \"Copiar...\",\n    \"paste\": \"Colar...\",\n    \"saveAs\": \"Salvar como...\",\n    \"about\": \"Sobre\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Tem certeza que deseja apagar as linhas selecionadas?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Tem certeza que deseja apagar as colunas selecionadas?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Essa ação vai destroir qualquer celular mesclada. Tem certeza?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Essa ação vai limpar o filtro de pesquisa. Tem certeza?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Existe um conflito com outra celula mesclada\",\n    \"invalidMergeProperties\": \"Propriedas de mesclagem inválidas\",\n    \"cellAlreadyMerged\": \"A celula já está mesclada\",\n    \"noCellsSelected\": \"Nenhuma celula selecionada\"\n}\n"
  },
  {
    "path": "resources/lang/vi_VN.json",
    "content": "{\n    \"noRecordsFound\": \"Khôn có dữ liệu\",\n    \"showingPage\": \"Hiển thị trang {0} trên tổng số {1} trang\",\n    \"show\": \"Hiện \",\n    \"search\": \"Tìm kiếm\",\n    \"entries\": \"dòng\",\n    \"columnName\": \"Tên cột\",\n    \"insertANewColumnBefore\": \"Thêm một cột bên trái\",\n    \"insertANewColumnAfter\": \"Thêm một cột bên phải\",\n    \"deleteSelectedColumns\": \"Xoá các cột đã chọn\",\n    \"renameThisColumn\": \"Đổi tên cột\",\n    \"orderAscending\": \"Sắp xếp A-Z\",\n    \"orderDescending\": \"Sắp xếp Z-A\",\n    \"insertANewRowBefore\": \"Thêm một dòng bên trên\",\n    \"insertANewRowAfter\": \"Thêm một dòng bên dưới\",\n    \"deleteSelectedRows\": \"Xoá các dòng đã chọn\",\n    \"editComments\": \"Chỉnh sửa bình luận\",\n    \"addComments\": \"Thêm bình luận\",\n    \"comments\": \"Bình luận\",\n    \"clearComments\": \"Xoá bình luận\",\n    \"copy\": \"Copy...\",\n    \"paste\": \"Paste...\",\n    \"saveAs\": \"Lưu...\",\n    \"about\": \"Thông tin\",\n    \"areYouSureToDeleteTheSelectedRows\": \"Bạn chắc chắn rằng mình muốn xoá dòng?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"Bạn chắc chắn rằng mình muốn xoá cột?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"Bạn chắc chắn rằng mình muốn xoá tất cả các ô đã được merge?\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"Bạn chắc chắn rằng mình muốn xoá dữ liệu tìm kiếm?\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"Có sự khác nhau giữa 2 ô được merge\",\n    \"invalidMergeProperties\": \"Thông tin merge không chính xác\",\n    \"cellAlreadyMerged\": \"Ô này đã được merge\",\n    \"noCellsSelected\": \"Không có ô nào được chọn\"\n}\n"
  },
  {
    "path": "resources/lang/zh_CN.json",
    "content": "{\n    \"noRecordsFound\": \"未找到\",\n    \"showingPage\": \"显示 {1} 条中的第 {0} 条\",\n    \"show\": \"显示 \",\n    \"search\": \"搜索\",\n    \"entries\": \" 条目\",\n    \"columnName\": \"列标题\",\n    \"insertANewColumnBefore\": \"在此前插入列\",\n    \"insertANewColumnAfter\": \"在此后插入列\",\n    \"deleteSelectedColumns\": \"删除选定列\",\n    \"renameThisColumn\": \"重命名列\",\n    \"orderAscending\": \"升序\",\n    \"orderDescending\": \"降序\",\n    \"insertANewRowBefore\": \"在此前插入行\",\n    \"insertANewRowAfter\": \"在此后插入行\",\n    \"deleteSelectedRows\": \"删除选定行\",\n    \"editComments\": \"编辑批注\",\n    \"addComments\": \"插入批注\",\n    \"comments\": \"批注\",\n    \"clearComments\": \"删除批注\",\n    \"copy\": \"复制...\",\n    \"paste\": \"粘贴...\",\n    \"saveAs\": \"保存为...\",\n    \"about\": \"关于\",\n    \"areYouSureToDeleteTheSelectedRows\": \"确定删除选定行?\",\n    \"areYouSureToDeleteTheSelectedColumns\": \"确定删除选定列?\",\n    \"thisActionWillDestroyAnyExistingMergedCellsAreYouSure\": \"这一操作会破坏所有现存的合并单元格，确认操作？\",\n    \"thisActionWillClearYourSearchResultsAreYouSure\": \"这一操作会清空搜索结果，确认操作？\",\n    \"thereIsAConflictWithAnotherMergedCell\": \"与其他合并单元格有冲突\",\n    \"invalidMergeProperties\": \"无效的合并属性\",\n    \"cellAlreadyMerged\": \"单元格已合并\",\n    \"noCellsSelected\": \"未选定单元格\"\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import jSuites from 'jsuites';\n\nimport libraryBase from './utils/libraryBase.js';\n\nimport Factory from './utils/factory.js';\nimport { destroyEvents } from './utils/events.js';\n\nimport * as helpers from './utils/helpers.js';\nimport dispatch from './utils/dispatch.js';\nimport version from './utils/version.js';\n\nlibraryBase.jspreadsheet = function (el, options) {\n    try {\n        let worksheets = [];\n\n        // Create spreadsheet\n        Factory.spreadsheet(el, options, worksheets).then((spreadsheet) => {\n            libraryBase.jspreadsheet.spreadsheet.push(spreadsheet);\n\n            // Global onload event\n            dispatch.call(spreadsheet, 'onload', spreadsheet);\n        });\n\n        return worksheets;\n    } catch (e) {\n        console.error(e);\n    }\n};\n\nlibraryBase.jspreadsheet.getWorksheetInstanceByName = function (worksheetName, namespace) {\n    const targetSpreadsheet = libraryBase.jspreadsheet.spreadsheet.find((spreadsheet) => {\n        return spreadsheet.config.namespace === namespace;\n    });\n\n    if (targetSpreadsheet) {\n        return {};\n    }\n\n    if (typeof worksheetName === 'undefined' || worksheetName === null) {\n        const namespaceEntries = targetSpreadsheet.worksheets.map((worksheet) => {\n            return [worksheet.options.worksheetName, worksheet];\n        });\n\n        return Object.fromEntries(namespaceEntries);\n    }\n\n    return targetSpreadsheet.worksheets.find((worksheet) => {\n        return worksheet.options.worksheetName === worksheetName;\n    });\n};\n\n// Define dictionary\nlibraryBase.jspreadsheet.setDictionary = function (o) {\n    jSuites.setDictionary(o);\n};\n\nlibraryBase.jspreadsheet.destroy = function (element, destroyEventHandlers) {\n    if (element.spreadsheet) {\n        const spreadsheetIndex = libraryBase.jspreadsheet.spreadsheet.indexOf(element.spreadsheet);\n        libraryBase.jspreadsheet.spreadsheet.splice(spreadsheetIndex, 1);\n\n        const root = element.spreadsheet.config.root || document;\n\n        element.spreadsheet = null;\n        element.innerHTML = '';\n\n        if (destroyEventHandlers) {\n            destroyEvents(root);\n        }\n    }\n};\n\nlibraryBase.jspreadsheet.destroyAll = function () {\n    for (let spreadsheetIndex = 0; spreadsheetIndex < libraryBase.jspreadsheet.spreadsheet.length; spreadsheetIndex++) {\n        const spreadsheet = libraryBase.jspreadsheet.spreadsheet[spreadsheetIndex];\n\n        libraryBase.jspreadsheet.destroy(spreadsheet.element);\n    }\n};\n\nlibraryBase.jspreadsheet.current = null;\n\nlibraryBase.jspreadsheet.spreadsheet = [];\n\nlibraryBase.jspreadsheet.helpers = {};\n\nlibraryBase.jspreadsheet.version = function () {\n    return version;\n};\n\nObject.entries(helpers).forEach(([key, value]) => {\n    libraryBase.jspreadsheet.helpers[key] = value;\n});\n\nexport default libraryBase.jspreadsheet;\n"
  },
  {
    "path": "src/jspreadsheet.css",
    "content": ":root {\n    --jss-border-color: #000;\n}\n\n.jss_spreadsheet {\n    outline: none;\n}\n\n.jss_container {\n    display: inline-block;\n    padding-right: 2px;\n    box-sizing: border-box;\n    overscroll-behavior: contain;\n    outline: none;\n}\n\n.fullscreen {\n    position: fixed !important;\n    top: 0px;\n    left: 0px;\n    width: 100%;\n    height: 100%;\n    z-index: 21;\n\n    display: flex;\n    flex-direction: column;\n\n    background-color: #ffffff;\n}\n\n.fullscreen .jtabs-content {\n    flex: 1;\n    overflow: hidden;\n}\n\n.fullscreen .jss_content {\n    overflow: auto;\n    width: 100% !important;\n    height: 100%;\n    max-height: 100% !important;\n}\n\n.fullscreen .jss_container {\n    height: 100%;\n}\n\n.jss_content {\n    display: inline-block;\n    box-sizing: border-box;\n    padding-right: 3px;\n    padding-bottom: 3px;\n    position: relative;\n    scrollbar-width: thin;\n    scrollbar-color: #666 transparent;\n}\n\n@supports (-moz-appearance: none) {\n    .jss_content {\n        padding-right: 10px;\n    }\n}\n\n.jss_content::-webkit-scrollbar {\n    width: 8px;\n    height: 8px;\n}\n\n.jss_content::-webkit-scrollbar-track {\n    background: #eee;\n}\n\n.jss_content::-webkit-scrollbar-thumb {\n    background: #666;\n}\n\n.jss_worksheet {\n    border-collapse: separate;\n    table-layout: fixed;\n    white-space: nowrap;\n    empty-cells: show;\n    border: 0px;\n    background-color: #fff;\n    width: 0;\n\n    border-top: 1px solid transparent;\n    border-left: 1px solid transparent;\n    border-right: 1px solid #ccc;\n    border-bottom: 1px solid #ccc;\n}\n\n.jss_worksheet > thead > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    background-color: #f3f3f3;\n    padding: 2px;\n    cursor: pointer;\n    box-sizing: border-box;\n    overflow: hidden;\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0;\n    z-index: 2;\n}\n\n.jss_worksheet > thead > tr > td.dragging {\n    opacity: 0.5;\n}\n\n.jss_worksheet > thead > tr > td.selected {\n    background-color: #dcdcdc;\n}\n\n.jss_worksheet > thead > tr > td.arrow-up {\n    background-repeat: no-repeat;\n    background-position: center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration: underline;\n}\n\n.jss_worksheet > thead > tr > td.arrow-down {\n    background-repeat: no-repeat;\n    background-position: center right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E\");\n    text-decoration: underline;\n}\n\n.jss_worksheet > tbody > tr > td:first-child {\n    position: relative;\n    background-color: #f3f3f3;\n    text-align: center;\n}\n\n.jss_worksheet > tbody.resizable > tr > td:first-child::before {\n    content: '\\00a0';\n    width: 100%;\n    height: 3px;\n    position: absolute;\n    bottom: 0px;\n    left: 0px;\n    cursor: row-resize;\n}\n\n.jss_worksheet > tbody.draggable > tr > td:first-child::after {\n    content: '\\00a0';\n    width: 3px;\n    height: 100%;\n    position: absolute;\n    top: 0px;\n    right: 0px;\n    cursor: move;\n}\n\n.jss_worksheet > tbody > tr.dragging > td {\n    background-color: #eee;\n    opacity: 0.5;\n}\n\n.jss_worksheet > tbody > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    padding: 4px;\n    white-space: nowrap;\n    box-sizing: border-box;\n    line-height: 1em;\n}\n\n.jss_overflow > tbody > tr > td {\n    overflow: hidden;\n}\n\n.jss_worksheet > tbody > tr > td:last-child {\n    overflow: hidden;\n}\n\n.jss_worksheet > tbody > tr > td > img {\n    display: inline-block;\n    max-width: 100px;\n}\n\n.jss_worksheet > tbody > tr > td.readonly {\n    color: rgba(0, 0, 0, 0.3);\n}\n.jss_worksheet > tbody > tr.selected > td:first-child {\n    background-color: #dcdcdc;\n}\n.jss_worksheet > tbody > tr > td > select,\n.jss_worksheet > tbody > tr > td > input,\n.jss_worksheet > tbody > tr > td > textarea {\n    border: 0px;\n    border-radius: 0px;\n    outline: 0px;\n    width: 100%;\n    margin: 0px;\n    padding: 0px;\n    padding-right: 2px;\n    background-color: transparent;\n    box-sizing: border-box;\n}\n\n.jss_worksheet > tbody > tr > td > textarea {\n    resize: none;\n    padding-top: 6px !important;\n}\n\n.jss_worksheet > tbody > tr > td > input[type='checkbox'] {\n    width: 12px;\n    margin-top: 2px;\n}\n.jss_worksheet > tbody > tr > td > input[type='radio'] {\n    width: 12px;\n    margin-top: 2px;\n}\n\n.jss_worksheet > tbody > tr > td > select {\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    appearance: none;\n    background-repeat: no-repeat;\n    background-position-x: 100%;\n    background-position-y: 40%;\n    background-image: url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+);\n}\n\n.jss_worksheet > tbody > tr > td.jss_dropdown {\n    background-repeat: no-repeat;\n    background-position: top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    overflow-x: hidden;\n}\n\n.jss_worksheet > tbody > tr > td.jss_dropdown.jss_comments {\n    background: url(\"data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E\")\n            top 50% right 5px no-repeat,\n        url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=')\n            top right no-repeat;\n}\n\n.jss_worksheet > tbody > tr > td > .color {\n    width: 90%;\n    height: 10px;\n    margin: auto;\n}\n\n.jss_worksheet > tbody > tr > td > a {\n    text-decoration: underline;\n}\n\n.jss_worksheet > tbody > tr > td.highlight > a {\n    color: blue;\n    cursor: pointer;\n}\n\n.jss_worksheet > tfoot > tr > td {\n    border-top: 1px solid #ccc;\n    border-left: 1px solid #ccc;\n    border-right: 1px solid transparent;\n    border-bottom: 1px solid transparent;\n    background-color: #f3f3f3;\n    padding: 2px;\n    cursor: pointer;\n    box-sizing: border-box;\n    overflow: hidden;\n}\n\n.jss_worksheet .highlight {\n    background-color: rgba(0, 0, 0, 0.05);\n}\n\n.jss_worksheet .highlight-top {\n    border-top: 1px solid #000;\n    box-shadow: 0px -1px #ccc;\n}\n\n.jss_worksheet .highlight-left {\n    border-left: 1px solid #000;\n    box-shadow: -1px 0px #ccc;\n}\n\n.jss_worksheet .highlight-right {\n    border-right: 1px solid #000;\n}\n\n.jss_worksheet .highlight-bottom {\n    border-bottom: 1px solid #000;\n}\n\n.jss_worksheet .highlight-top.highlight-left {\n    box-shadow: -1px -1px #ccc;\n    -webkit-box-shadow: -1px -1px #ccc;\n    -moz-box-shadow: -1px -1px #ccc;\n}\n\n.jss_worksheet .highlight-selected {\n    background-color: rgba(0, 0, 0, 0);\n}\n.jss_worksheet .selection {\n    background-color: rgba(0, 0, 0, 0.05);\n}\n.jss_worksheet .selection-left {\n    border-left: 1px dotted #000;\n}\n.jss_worksheet .selection-right {\n    border-right: 1px dotted #000;\n}\n.jss_worksheet .selection-top {\n    border-top: 1px dotted #000;\n}\n.jss_worksheet .selection-bottom {\n    border-bottom: 1px dotted #000;\n}\n.jss_corner {\n    position: absolute;\n    background-color: rgb(0, 0, 0);\n    height: 1px;\n    width: 1px;\n    border: 1px solid rgb(255, 255, 255);\n    top: -2000px;\n    left: -2000px;\n    cursor: crosshair;\n    box-sizing: initial;\n    z-index: 20;\n    padding: 2px;\n}\n\n.jss_worksheet .editor {\n    outline: 0px solid transparent;\n    overflow: visible;\n    white-space: nowrap;\n    text-align: left;\n    padding: 0px;\n    box-sizing: border-box;\n    overflow: visible !important;\n}\n\n.jss_worksheet .editor > input {\n    padding-left: 4px;\n}\n\n.jss_worksheet .editor .jupload {\n    position: fixed;\n    top: 100%;\n    z-index: 40;\n    user-select: none;\n    -webkit-font-smoothing: antialiased;\n    font-size: 0.875rem;\n    letter-spacing: 0.2px;\n    -webkit-border-radius: 4px;\n    border-radius: 4px;\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    padding: 10px;\n    background-color: #fff;\n    width: 300px;\n    min-height: 225px;\n    margin-top: 2px;\n}\n\n.jss_worksheet .editor .jupload img {\n    width: 100%;\n    height: auto;\n}\n\n.jss_worksheet .editor .jss_richtext {\n    position: fixed;\n    top: 100%;\n    z-index: 40;\n    user-select: none;\n    -webkit-font-smoothing: antialiased;\n    font-size: 0.875rem;\n    letter-spacing: 0.2px;\n    -webkit-box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);\n    padding: 10px;\n    background-color: #fff;\n    width: 358px;\n    margin-top: 2px;\n    text-align: left;\n    white-space: initial;\n}\n\n.jss_worksheet .editor .jclose:after {\n    position: absolute;\n    top: 0;\n    right: 0;\n    margin: 10px;\n    content: 'close';\n    font-family: 'Material icons';\n    font-size: 24px;\n    width: 24px;\n    height: 24px;\n    line-height: 24px;\n    cursor: pointer;\n    text-shadow: 0px 0px 5px #fff;\n}\n\n.jss_worksheet,\n.jss_worksheet td,\n.jss_corner {\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n    -webkit-user-drag: none;\n    -khtml-user-drag: none;\n    -moz-user-drag: none;\n    -o-user-drag: none;\n    user-drag: none;\n}\n\n.jss_textarea {\n    position: absolute;\n    top: -999px;\n    left: -999px;\n    width: 1px;\n    height: 1px;\n}\n.jss_worksheet .dragline {\n    position: absolute;\n}\n.jss_worksheet .dragline div {\n    position: relative;\n    top: -6px;\n    height: 5px;\n    width: 22px;\n}\n.jss_worksheet .dragline div:hover {\n    cursor: move;\n}\n\n.jss_worksheet .onDrag {\n    background-color: rgba(0, 0, 0, 0.6);\n}\n\n.jss_worksheet .error {\n    border: 1px solid red;\n}\n\n.jss_worksheet thead td.resizing {\n    border-right-style: dotted !important;\n    border-right-color: red !important;\n}\n\n.jss_worksheet tbody tr.resizing > td {\n    border-bottom-style: dotted !important;\n    border-bottom-color: red !important;\n}\n\n.jss_worksheet tbody td.resizing {\n    border-right-style: dotted !important;\n    border-right-color: red !important;\n}\n\n.jss_worksheet .jdropdown-header {\n    border: 0px !important;\n    outline: none !important;\n    width: 100% !important;\n    height: 100% !important;\n    padding: 0px !important;\n    padding-left: 8px !important;\n}\n\n.jss_worksheet .jdropdown-container {\n    margin-top: 1px;\n}\n\n.jss_worksheet .jdropdown-container-header {\n    padding: 0px;\n    margin: 0px;\n    height: inherit;\n}\n\n.jss_worksheet .jdropdown-picker {\n    border: 0px !important;\n    padding: 0px !important;\n    width: inherit;\n    height: inherit;\n}\n\n.jss_worksheet .jss_comments {\n    background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=');\n    background-repeat: no-repeat;\n    background-position: top right;\n}\n\n.jss_worksheet .sp-replacer {\n    margin: 2px;\n    border: 0px;\n}\n\n.jss_worksheet > thead > tr.jss_filter > td > input {\n    border: 0px;\n    width: 100%;\n    outline: none;\n}\n\n.jss_about {\n    float: right;\n    font-size: 0.7em;\n    padding: 2px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    display: none;\n}\n.jss_about a {\n    color: #ccc;\n    text-decoration: none;\n}\n\n.jss_about img {\n    display: none;\n}\n\n.jss_filter {\n    display: flex;\n    justify-content: space-between;\n    margin-bottom: 4px;\n}\n\n.jss_filter > div {\n    padding: 8px;\n    align-items: center;\n}\n\n.jss_pagination {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n}\n\n.jss_pagination > div {\n    display: flex;\n    padding: 10px;\n}\n\n.jss_pagination > div:last-child {\n    padding-right: 10px;\n    padding-top: 10px;\n}\n\n.jss_pagination > div > div {\n    text-align: center;\n    width: 36px;\n    height: 36px;\n    line-height: 34px;\n    border: 1px solid #ccc;\n    box-sizing: border-box;\n    margin-left: 2px;\n    cursor: pointer;\n}\n\n.jss_page {\n    font-size: 0.8em;\n}\n\n.jss_page_selected {\n    font-weight: bold;\n    background-color: #f3f3f3;\n}\n\n.jss_toolbar {\n    display: flex;\n    background-color: #f3f3f3;\n    border: 1px solid #ccc;\n    padding: 4px;\n    margin: 0px 2px 4px 1px;\n}\n\n.jss_toolbar:empty {\n    display: none;\n}\n\n.jss_worksheet .dragging-left {\n    background-repeat: no-repeat;\n    background-position: top 50% left 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E\");\n}\n\n.jss_worksheet .dragging-right {\n    background-repeat: no-repeat;\n    background-position: top 50% right 0px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E\");\n}\n\n.jss_hidden_index > tbody > tr > td:first-child,\n.jss_hidden_index > thead > tr > td:first-child,\n.jss_hidden_index > tfoot > tr > td:first-child,\n.jss_hidden_index > colgroup > col:first-child {\n    display: none;\n}\n\n.jss_worksheet .jrating {\n    display: inline-flex;\n}\n.jss_worksheet .jrating > div {\n    zoom: 0.55;\n}\n\n.jss_worksheet .copying-top {\n    border-top: 1px dashed #000;\n}\n\n.jss_worksheet .copying-left {\n    border-left: 1px dashed #000;\n}\n\n.jss_worksheet .copying-right {\n    border-right: 1px dashed #000;\n}\n\n.jss_worksheet .copying-bottom {\n    border-bottom: 1px dashed #000;\n}\n\n.jss_worksheet .jss_column_filter {\n    background-repeat: no-repeat;\n    background-position: top 50% right 5px;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='gray' width='18px' height='18px'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E\");\n    text-overflow: ellipsis;\n    overflow: hidden;\n    padding: 0px;\n    padding-left: 6px;\n    padding-right: 20px;\n}\n\n.jss_worksheet thead .jss_freezed,\n.jss_worksheet tfoot .jss_freezed {\n    left: 0px;\n    z-index: 3 !important;\n    box-shadow: 2px 0px 2px 0.2px #ccc !important;\n    -webkit-box-shadow: 2px 0px 2px 0.2px #ccc !important;\n    -moz-box-shadow: 2px 0px 2px 0.2px #ccc !important;\n}\n\n.jss_worksheet tbody .jss_freezed {\n    position: relative;\n    background-color: #fff;\n    box-shadow: 1px 1px 1px 1px #ccc !important;\n    -webkit-box-shadow: 2px 4px 4px 0.1px #ccc !important;\n    -moz-box-shadow: 2px 4px 4px 0.1px #ccc !important;\n}\n\n.red {\n    color: red;\n}\n\n.jss_worksheet > tbody > tr > td.readonly > input[type='checkbox'],\n.jss_worksheet > tbody > tr > td.readonly > input[type='radio'] {\n    pointer-events: none;\n    opacity: 0.5;\n}\n"
  },
  {
    "path": "src/jspreadsheet.themes.css",
    "content": ".jss_worksheet > thead > tr > td {\n    border-top: 1px solid var(--border_color, #ccc);\n    border-left: 1px solid var(--border_color, #ccc);\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n}\n\n.jss_worksheet > thead > tr > td.selected {\n    background-color: var(--header_background_highlighted, #dcdcdc);\n    color: var(--header_color_highlighted, #000);\n}\n\n.jss_worksheet > tbody > tr > td:first-child {\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n}\n\n.jss_worksheet > tbody > tr > td {\n    background-color: var(--content_background, #fff);\n    color: var(--content_color, #000);\n    border-top: 1px solid var(--border_color, #ccc);\n    border-left: 1px solid var(--border_color, #ccc);\n}\n\n.jss_worksheet > tbody > tr.selected > td:first-child {\n    background-color: var(--header_background_highlighted, #dcdcdc);\n    color: var(--header_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight {\n    background-color: var(--selection, rgba(0, 0, 0, 0.05));\n}\n\n.jss_worksheet .highlight-top {\n    border-top: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-left {\n    border-left: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-right {\n    border-right: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-bottom {\n    border-bottom: 1px solid var(--border_color_highlighted, #000);\n}\n\n.jss_worksheet .highlight-selected {\n    background-color: var(--cursor, #eee);\n}\n\n.jss_pagination > div > div {\n    color: var(--header_color, #000);\n    background: var(--header_background, #f3f3f3);\n    border: 1px solid var(--border_color, #ccc);\n}\n\n.jss_toolbar {\n    background-color: var(--header_background, #f3f3f3);\n    color: var(--header_color, #000);\n    border: 1px solid var(--border_color, #ccc);\n}\n\n.jss_toolbar .jtoolbar-item i {\n    color: var(--content_color, #000);\n}\n\n.jss_toolbar .jtoolbar-item:not(.jtoolbar-divisor):hover,\n.jss_toolbar .jtoolbar-item.jpicker:hover > .jpicker-header {\n    background-color: var(--content_background_highlighted, #f3f3f3);\n    color: var(--content_color_highlighted, #000);\n}\n\n.jss_toolbar .jtoolbar-divisor {\n    background: var(--header_color, #ddd);\n}\n\n.jss_contextmenu {\n    border: 1px solid var(--border_color, #ccc);\n    background: var(--menu_background, #fff);\n    color: var(--menu_color, #555);\n    box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n    -webkit-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n    -moz-box-shadow: var(--menu_box_shadow, 2px 2px 2px 0px rgba(143, 144, 145, 1));\n}\n\n.jss_contextmenu > div a {\n    color: var(--menu_color, #555);\n}\n\n.jss_contextmenu > div:not(.contextmenu-line):hover a {\n    color: var(--menu_color_highlighted, #555);\n}\n\n.jss_contextmenu > div:not(.contextmenu-line):hover {\n    background: var(--menu_background_highlighted, #ebebeb);\n}\n\n.jss_container input {\n    color: var(--header_color, #000);\n    background: var(--header_background, #f3f3f3);\n}\n"
  },
  {
    "path": "src/test.js",
    "content": "import jspreadsheet from './index.js';\n\nimport './jspreadsheet.css';\nimport 'jsuites/dist/jsuites.css';\n\nwindow.jss = jspreadsheet;\n\nwindow.instance = jspreadsheet(document.getElementById('root'), {\n    tabs: true,\n    toolbar: true,\n    worksheets: [\n        {\n            minDimensions: [6, 6],\n        },\n    ],\n});\n"
  },
  {
    "path": "src/utils/cells.js",
    "content": "import { getCoordsFromCellName } from './helpers.js';\n\nexport const setReadOnly = function (cell, state) {\n    const obj = this;\n\n    let record;\n\n    if (typeof cell === 'string') {\n        const coords = getCoordsFromCellName(cell);\n\n        record = obj.records[coords[1]][coords[0]];\n    } else {\n        const x = parseInt(cell.getAttribute('data-x'));\n        const y = parseInt(cell.getAttribute('data-y'));\n\n        record = obj.records[y][x];\n    }\n\n    if (state) {\n        record.element.classList.add('readonly');\n    } else {\n        record.element.classList.remove('readonly');\n    }\n};\n\nexport const isReadOnly = function (x, y) {\n    const obj = this;\n\n    if (typeof x === 'string' && typeof y === 'undefined') {\n        const coords = getCoordsFromCellName(x);\n\n        [x, y] = coords;\n    }\n\n    return obj.records[y][x].element.classList.contains('readonly');\n};\n"
  },
  {
    "path": "src/utils/columns.js",
    "content": "import jSuites from 'jsuites';\nimport dispatch from './dispatch.js';\nimport { getColumnName } from './helpers.js';\nimport { setHistory } from './history.js';\nimport { isColMerged } from './merges.js';\nimport { createCell, updateTableReferences } from './internal.js';\nimport { conditionalSelectionUpdate, updateCornerPosition } from './selection.js';\nimport { setFooter } from './footer.js';\nimport { getColumnNameFromId, injectArray } from './internalHelpers.js';\n\nexport const getNumberOfColumns = function () {\n    const obj = this;\n\n    let numberOfColumns = (obj.options.columns && obj.options.columns.length) || 0;\n\n    if (obj.options.data && typeof obj.options.data[0] !== 'undefined') {\n        // Data keys\n        const keys = Object.keys(obj.options.data[0]);\n\n        if (keys.length > numberOfColumns) {\n            numberOfColumns = keys.length;\n        }\n    }\n\n    if (obj.options.minDimensions && obj.options.minDimensions[0] > numberOfColumns) {\n        numberOfColumns = obj.options.minDimensions[0];\n    }\n\n    return numberOfColumns;\n};\n\nexport const createCellHeader = function (colNumber) {\n    const obj = this;\n\n    // Create col global control\n    const colWidth = (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].width) || obj.options.defaultColWidth || 100;\n    const colAlign = (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].align) || obj.options.defaultColAlign || 'center';\n\n    // Create header cell\n    obj.headers[colNumber] = document.createElement('td');\n    obj.headers[colNumber].textContent =\n        (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].title) || getColumnName(colNumber);\n\n    obj.headers[colNumber].setAttribute('data-x', colNumber);\n    obj.headers[colNumber].style.textAlign = colAlign;\n    if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].title) {\n        obj.headers[colNumber].setAttribute('title', obj.headers[colNumber].innerText);\n    }\n    if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].id) {\n        obj.headers[colNumber].setAttribute('id', obj.options.columns[colNumber].id);\n    }\n\n    // Width control\n    const colElement = document.createElement('col');\n    colElement.setAttribute('width', colWidth);\n\n    obj.cols[colNumber] = {\n        colElement,\n        x: colNumber,\n    };\n\n    // Hidden column\n    if (obj.options.columns && obj.options.columns[colNumber] && obj.options.columns[colNumber].type == 'hidden') {\n        obj.headers[colNumber].style.display = 'none';\n        colElement.style.display = 'none';\n    }\n};\n\n/**\n * Insert a new column\n *\n * @param mixed - num of columns to be added or data to be added in one single column\n * @param int columnNumber - number of columns to be created\n * @param bool insertBefore\n * @param object properties - column properties\n * @return void\n */\nexport const insertColumn = function (mixed, columnNumber, insertBefore, properties) {\n    const obj = this;\n\n    // Configuration\n    if (obj.options.allowInsertColumn != false) {\n        // Records\n        var records = [];\n\n        // Data to be insert\n        let data = [];\n\n        // The insert could be lead by number of rows or the array of data\n        let numOfColumns;\n        if (!Array.isArray(mixed)) {\n            numOfColumns = typeof mixed === 'number' ? mixed : 1;\n        } else {\n            numOfColumns = 1;\n\n            if (mixed) {\n                data = mixed;\n            }\n        }\n\n        // Direction\n        insertBefore = insertBefore ? true : false;\n\n        // Current column number\n        const currentNumOfColumns = Math.max(\n            obj.options.columns.length,\n            ...obj.options.data.map(function (row) {\n                return row.length;\n            })\n        );\n\n        const lastColumn = currentNumOfColumns - 1;\n\n        // Confirm position\n        if (columnNumber == undefined || columnNumber >= parseInt(lastColumn) || columnNumber < 0) {\n            columnNumber = lastColumn;\n        }\n\n        // Create default properties\n        if (!properties) {\n            properties = [];\n        }\n\n        for (let i = 0; i < numOfColumns; i++) {\n            if (!properties[i]) {\n                properties[i] = {};\n            }\n        }\n\n        const columns = [];\n\n        if (!Array.isArray(mixed)) {\n            for (let i = 0; i < mixed; i++) {\n                const column = {\n                    column: columnNumber + i + (insertBefore ? 0 : 1),\n                    options: Object.assign({}, properties[i]),\n                };\n\n                columns.push(column);\n            }\n        } else {\n            const data = [];\n\n            for (let i = 0; i < obj.options.data.length; i++) {\n                data.push(i < mixed.length ? mixed[i] : '');\n            }\n\n            const column = {\n                column: columnNumber + (insertBefore ? 0 : 1),\n                options: Object.assign({}, properties[0]),\n                data,\n            };\n\n            columns.push(column);\n        }\n\n        // Onbeforeinsertcolumn\n        if (dispatch.call(obj, 'onbeforeinsertcolumn', obj, columns) === false) {\n            return false;\n        }\n\n        // Merged cells\n        if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n            if (isColMerged.call(obj, columnNumber, insertBefore).length) {\n                if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                    return false;\n                } else {\n                    obj.destroyMerge();\n                }\n            }\n        }\n\n        // Insert before\n        const columnIndex = !insertBefore ? columnNumber + 1 : columnNumber;\n        obj.options.columns = injectArray(obj.options.columns, columnIndex, properties);\n\n        // Open space in the containers\n        const currentHeaders = obj.headers.splice(columnIndex);\n        const currentColgroup = obj.cols.splice(columnIndex);\n\n        // History\n        const historyHeaders = [];\n        const historyColgroup = [];\n        const historyRecords = [];\n        const historyData = [];\n        const historyFooters = [];\n\n        // Add new headers\n        for (let col = columnIndex; col < numOfColumns + columnIndex; col++) {\n            createCellHeader.call(obj, col);\n            obj.headerContainer.insertBefore(obj.headers[col], obj.headerContainer.children[col + 1]);\n            obj.colgroupContainer.insertBefore(obj.cols[col].colElement, obj.colgroupContainer.children[col + 1]);\n\n            historyHeaders.push(obj.headers[col]);\n            historyColgroup.push(obj.cols[col]);\n        }\n\n        // Add new footer cells\n        if (obj.options.footers) {\n            for (let j = 0; j < obj.options.footers.length; j++) {\n                historyFooters[j] = [];\n                for (let i = 0; i < numOfColumns; i++) {\n                    historyFooters[j].push('');\n                }\n                obj.options.footers[j].splice(columnIndex, 0, historyFooters[j]);\n            }\n        }\n\n        // Adding visual columns\n        for (let row = 0; row < obj.options.data.length; row++) {\n            // Keep the current data\n            const currentData = obj.options.data[row].splice(columnIndex);\n            const currentRecord = obj.records[row].splice(columnIndex);\n\n            // History\n            historyData[row] = [];\n            historyRecords[row] = [];\n\n            for (let col = columnIndex; col < numOfColumns + columnIndex; col++) {\n                // New value\n                const value = data[row] ? data[row] : '';\n                obj.options.data[row][col] = value;\n                // New cell\n                const td = createCell.call(obj, col, row, obj.options.data[row][col]);\n                obj.records[row][col] = {\n                    element: td,\n                    y: row,\n                };\n                // Add cell to the row\n                if (obj.rows[row]) {\n                    obj.rows[row].element.insertBefore(td, obj.rows[row].element.children[col + 1]);\n                }\n\n                if (obj.options.columns && obj.options.columns[col] && typeof obj.options.columns[col].render === 'function') {\n                    obj.options.columns[col].render(td, value, parseInt(col), parseInt(row), obj, obj.options.columns[col]);\n                }\n\n                // Record History\n                historyData[row].push(value);\n                historyRecords[row].push({ element: td, x: col, y: row });\n            }\n\n            // Copy the data back to the main data\n            Array.prototype.push.apply(obj.options.data[row], currentData);\n            Array.prototype.push.apply(obj.records[row], currentRecord);\n        }\n\n        Array.prototype.push.apply(obj.headers, currentHeaders);\n        Array.prototype.push.apply(obj.cols, currentColgroup);\n\n        for (let i = columnIndex; i < obj.cols.length; i++) {\n            obj.cols[i].x = i;\n        }\n\n        for (let j = 0; j < obj.records.length; j++) {\n            for (let i = 0; i < obj.records[j].length; i++) {\n                obj.records[j][i].x = i;\n            }\n        }\n\n        // Adjust nested headers\n        if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0 && obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {\n            for (let j = 0; j < obj.options.nestedHeaders.length; j++) {\n                const colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan) + numOfColumns;\n                obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan = colspan;\n                obj.thead.children[j].children[obj.thead.children[j].children.length - 1].setAttribute('colspan', colspan);\n                let o = obj.thead.children[j].children[obj.thead.children[j].children.length - 1].getAttribute('data-column');\n                o = o.split(',');\n                for (let col = columnIndex; col < numOfColumns + columnIndex; col++) {\n                    o.push(col);\n                }\n                obj.thead.children[j].children[obj.thead.children[j].children.length - 1].setAttribute('data-column', o);\n            }\n        }\n\n        // Keep history\n        setHistory.call(obj, {\n            action: 'insertColumn',\n            columnNumber: columnNumber,\n            numOfColumns: numOfColumns,\n            insertBefore: insertBefore,\n            columns: properties,\n            headers: historyHeaders,\n            cols: historyColgroup,\n            records: historyRecords,\n            footers: historyFooters,\n            data: historyData,\n        });\n\n        // Remove table references\n        updateTableReferences.call(obj);\n\n        // Events\n        dispatch.call(obj, 'oninsertcolumn', obj, columns);\n    }\n};\n\n/**\n * Move column\n *\n * @return void\n */\nexport const moveColumn = function (o, d) {\n    const obj = this;\n\n    if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n        let insertBefore;\n        if (o > d) {\n            insertBefore = 1;\n        } else {\n            insertBefore = 0;\n        }\n\n        if (isColMerged.call(obj, o).length || isColMerged.call(obj, d, insertBefore).length) {\n            if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                return false;\n            } else {\n                obj.destroyMerge();\n            }\n        }\n    }\n\n    o = parseInt(o);\n    d = parseInt(d);\n\n    if (o > d) {\n        obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d]);\n        obj.colgroupContainer.insertBefore(obj.cols[o].colElement, obj.cols[d].colElement);\n\n        for (let j = 0; j < obj.rows.length; j++) {\n            obj.rows[j].element.insertBefore(obj.records[j][o].element, obj.records[j][d].element);\n        }\n    } else {\n        obj.headerContainer.insertBefore(obj.headers[o], obj.headers[d].nextSibling);\n        obj.colgroupContainer.insertBefore(obj.cols[o].colElement, obj.cols[d].colElement.nextSibling);\n\n        for (let j = 0; j < obj.rows.length; j++) {\n            obj.rows[j].element.insertBefore(obj.records[j][o].element, obj.records[j][d].element.nextSibling);\n        }\n    }\n\n    obj.options.columns.splice(d, 0, obj.options.columns.splice(o, 1)[0]);\n    obj.headers.splice(d, 0, obj.headers.splice(o, 1)[0]);\n    obj.cols.splice(d, 0, obj.cols.splice(o, 1)[0]);\n\n    const firstAffectedIndex = Math.min(o, d);\n    const lastAffectedIndex = Math.max(o, d);\n\n    for (let j = 0; j < obj.rows.length; j++) {\n        obj.options.data[j].splice(d, 0, obj.options.data[j].splice(o, 1)[0]);\n        obj.records[j].splice(d, 0, obj.records[j].splice(o, 1)[0]);\n    }\n\n    for (let i = firstAffectedIndex; i <= lastAffectedIndex; i++) {\n        obj.cols[i].x = i;\n    }\n\n    for (let j = 0; j < obj.records.length; j++) {\n        for (let i = firstAffectedIndex; i <= lastAffectedIndex; i++) {\n            obj.records[j][i].x = i;\n        }\n    }\n\n    // Update footers position\n    if (obj.options.footers) {\n        for (let j = 0; j < obj.options.footers.length; j++) {\n            obj.options.footers[j].splice(d, 0, obj.options.footers[j].splice(o, 1)[0]);\n        }\n    }\n\n    // Keeping history of changes\n    setHistory.call(obj, {\n        action: 'moveColumn',\n        oldValue: o,\n        newValue: d,\n    });\n\n    // Update table references\n    updateTableReferences.call(obj);\n\n    // Events\n    dispatch.call(obj, 'onmovecolumn', obj, o, d, 1);\n};\n\n/**\n * Delete a column by number\n *\n * @param integer columnNumber - reference column to be excluded\n * @param integer numOfColumns - number of columns to be excluded from the reference column\n * @return void\n */\nexport const deleteColumn = function (columnNumber, numOfColumns) {\n    const obj = this;\n\n    // Global Configuration\n    if (obj.options.allowDeleteColumn != false) {\n        if (obj.headers.length > 1) {\n            // Delete column definitions\n            if (columnNumber == undefined) {\n                const number = obj.getSelectedColumns(true);\n\n                if (!number.length) {\n                    // Remove last column\n                    columnNumber = obj.headers.length - 1;\n                    numOfColumns = 1;\n                } else {\n                    // Remove selected\n                    columnNumber = parseInt(number[0]);\n                    numOfColumns = parseInt(number.length);\n                }\n            }\n\n            // Lasat column\n            const lastColumn = obj.options.data[0].length - 1;\n\n            if (columnNumber == undefined || columnNumber > lastColumn || columnNumber < 0) {\n                columnNumber = lastColumn;\n            }\n\n            // Minimum of columns to be delete is 1\n            if (!numOfColumns) {\n                numOfColumns = 1;\n            }\n\n            // Can't delete more than the limit of the table\n            if (numOfColumns > obj.options.data[0].length - columnNumber) {\n                numOfColumns = obj.options.data[0].length - columnNumber;\n            }\n\n            const removedColumns = [];\n            for (let i = 0; i < numOfColumns; i++) {\n                removedColumns.push(i + columnNumber);\n            }\n\n            // onbeforedeletecolumn\n            if (dispatch.call(obj, 'onbeforedeletecolumn', obj, removedColumns) === false) {\n                return false;\n            }\n\n            // Can't remove the last column\n            if (parseInt(columnNumber) > -1) {\n                // Merged cells\n                let mergeExists = false;\n                if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n                    for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {\n                        if (isColMerged.call(obj, col, null).length) {\n                            mergeExists = true;\n                        }\n                    }\n                }\n                if (mergeExists) {\n                    if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                        return false;\n                    } else {\n                        obj.destroyMerge();\n                    }\n                }\n\n                // Delete the column properties\n                const columns = obj.options.columns ? obj.options.columns.splice(columnNumber, numOfColumns) : undefined;\n\n                for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {\n                    obj.cols[col].colElement.className = '';\n                    obj.headers[col].className = '';\n                    obj.cols[col].colElement.parentNode.removeChild(obj.cols[col].colElement);\n                    obj.headers[col].parentNode.removeChild(obj.headers[col]);\n                }\n\n                const historyHeaders = obj.headers.splice(columnNumber, numOfColumns);\n                const historyColgroup = obj.cols.splice(columnNumber, numOfColumns);\n                const historyRecords = [];\n                const historyData = [];\n                const historyFooters = [];\n\n                for (let row = 0; row < obj.options.data.length; row++) {\n                    for (let col = columnNumber; col < columnNumber + numOfColumns; col++) {\n                        obj.records[row][col].element.className = '';\n                        obj.records[row][col].element.parentNode.removeChild(obj.records[row][col].element);\n                    }\n                }\n\n                // Delete headers\n                for (let row = 0; row < obj.options.data.length; row++) {\n                    // History\n                    historyData[row] = obj.options.data[row].splice(columnNumber, numOfColumns);\n                    historyRecords[row] = obj.records[row].splice(columnNumber, numOfColumns);\n                }\n\n                for (let i = columnNumber; i < obj.cols.length; i++) {\n                    obj.cols[i].x = i;\n                }\n\n                for (let j = 0; j < obj.records.length; j++) {\n                    for (let i = columnNumber; i < obj.records[j].length; i++) {\n                        obj.records[j][i].x = i;\n                    }\n                }\n\n                // Delete footers\n                if (obj.options.footers) {\n                    for (let row = 0; row < obj.options.footers.length; row++) {\n                        historyFooters[row] = obj.options.footers[row].splice(columnNumber, numOfColumns);\n                    }\n                }\n\n                // Remove selection\n                conditionalSelectionUpdate.call(obj, 0, columnNumber, columnNumber + numOfColumns - 1);\n\n                // Adjust nested headers\n                if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0 && obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {\n                    for (let j = 0; j < obj.options.nestedHeaders.length; j++) {\n                        const colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan) - numOfColumns;\n                        obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan = colspan;\n                        obj.thead.children[j].children[obj.thead.children[j].children.length - 1].setAttribute('colspan', colspan);\n                    }\n                }\n\n                // Keeping history of changes\n                setHistory.call(obj, {\n                    action: 'deleteColumn',\n                    columnNumber: columnNumber,\n                    numOfColumns: numOfColumns,\n                    insertBefore: 1,\n                    columns: columns,\n                    headers: historyHeaders,\n                    cols: historyColgroup,\n                    records: historyRecords,\n                    footers: historyFooters,\n                    data: historyData,\n                });\n\n                // Update table references\n                updateTableReferences.call(obj);\n\n                // Delete\n                dispatch.call(obj, 'ondeletecolumn', obj, removedColumns);\n            }\n        } else {\n            console.error('Jspreadsheet: It is not possible to delete the last column');\n        }\n    }\n};\n\n/**\n * Get the column width\n *\n * @param int column column number (first column is: 0)\n * @return int current width\n */\nexport const getWidth = function (column) {\n    const obj = this;\n\n    let data;\n\n    if (typeof column === 'undefined') {\n        // Get all headers\n        data = [];\n        for (let i = 0; i < obj.headers.length; i++) {\n            data.push((obj.options.columns && obj.options.columns[i] && obj.options.columns[i].width) || obj.options.defaultColWidth || 100);\n        }\n    } else {\n        data = parseInt(obj.cols[column].colElement.getAttribute('width'));\n    }\n\n    return data;\n};\n\n/**\n * Set the column width\n *\n * @param int column number (first column is: 0)\n * @param int new column width\n * @param int old column width\n */\nexport const setWidth = function (column, width, oldWidth) {\n    const obj = this;\n\n    if (width) {\n        if (Array.isArray(column)) {\n            // Oldwidth\n            if (!oldWidth) {\n                oldWidth = [];\n            }\n            // Set width\n            for (let i = 0; i < column.length; i++) {\n                if (!oldWidth[i]) {\n                    oldWidth[i] = parseInt(obj.cols[column[i]].colElement.getAttribute('width'));\n                }\n                const w = Array.isArray(width) && width[i] ? width[i] : width;\n                obj.cols[column[i]].colElement.setAttribute('width', w);\n\n                if (!obj.options.columns) {\n                    obj.options.columns = [];\n                }\n\n                if (!obj.options.columns[column[i]]) {\n                    obj.options.columns[column[i]] = {};\n                }\n\n                obj.options.columns[column[i]].width = w;\n            }\n        } else {\n            // Oldwidth\n            if (!oldWidth) {\n                oldWidth = parseInt(obj.cols[column].colElement.getAttribute('width'));\n            }\n            // Set width\n            obj.cols[column].colElement.setAttribute('width', width);\n\n            if (!obj.options.columns) {\n                obj.options.columns = [];\n            }\n\n            if (!obj.options.columns[column]) {\n                obj.options.columns[column] = {};\n            }\n\n            obj.options.columns[column].width = width;\n        }\n\n        // Keeping history of changes\n        setHistory.call(obj, {\n            action: 'setWidth',\n            column: column,\n            oldValue: oldWidth,\n            newValue: width,\n        });\n\n        // On resize column\n        dispatch.call(obj, 'onresizecolumn', obj, column, width, oldWidth);\n\n        // Update corner position\n        updateCornerPosition.call(obj);\n    }\n};\n\n/**\n * Show column\n */\nexport const showColumn = function (colNumber) {\n    const obj = this;\n\n    if (!Array.isArray(colNumber)) {\n        colNumber = [colNumber];\n    }\n\n    for (let i = 0; i < colNumber.length; i++) {\n        const columnIndex = colNumber[i];\n\n        obj.headers[columnIndex].style.display = '';\n        obj.cols[columnIndex].colElement.style.display = '';\n        if (obj.filter && obj.filter.children.length > columnIndex + 1) {\n            obj.filter.children[columnIndex + 1].style.display = '';\n        }\n        for (let j = 0; j < obj.options.data.length; j++) {\n            obj.records[j][columnIndex].element.style.display = '';\n        }\n    }\n\n    // Update footers\n    if (obj.options.footers) {\n        setFooter.call(obj);\n    }\n\n    obj.resetSelection();\n};\n\n/**\n * Hide column\n */\nexport const hideColumn = function (colNumber) {\n    const obj = this;\n\n    if (!Array.isArray(colNumber)) {\n        colNumber = [colNumber];\n    }\n\n    for (let i = 0; i < colNumber.length; i++) {\n        const columnIndex = colNumber[i];\n\n        obj.headers[columnIndex].style.display = 'none';\n        obj.cols[columnIndex].colElement.style.display = 'none';\n        if (obj.filter && obj.filter.children.length > columnIndex + 1) {\n            obj.filter.children[columnIndex + 1].style.display = 'none';\n        }\n        for (let j = 0; j < obj.options.data.length; j++) {\n            obj.records[j][columnIndex].element.style.display = 'none';\n        }\n    }\n\n    // Update footers\n    if (obj.options.footers) {\n        setFooter.call(obj);\n    }\n\n    obj.resetSelection();\n};\n\n/**\n * Get a column data by columnNumber\n */\nexport const getColumnData = function (columnNumber, processed) {\n    const obj = this;\n\n    const dataset = [];\n    // Go through the rows to get the data\n    for (let j = 0; j < obj.options.data.length; j++) {\n        if (processed) {\n            dataset.push(obj.records[j][columnNumber].element.innerHTML);\n        } else {\n            dataset.push(obj.options.data[j][columnNumber]);\n        }\n    }\n    return dataset;\n};\n\n/**\n * Set a column data by colNumber\n */\nexport const setColumnData = function (colNumber, data, force) {\n    const obj = this;\n\n    for (let j = 0; j < obj.rows.length; j++) {\n        // Update cell\n        const columnName = getColumnNameFromId([colNumber, j]);\n        // Set value\n        if (data[j] != null) {\n            obj.setValue(columnName, data[j], force);\n        }\n    }\n};\n"
  },
  {
    "path": "src/utils/comments.js",
    "content": "import dispatch from './dispatch.js';\nimport { getCoordsFromCellName } from './helpers.js';\nimport { setHistory } from './history.js';\nimport { getColumnNameFromId, getIdFromColumnName } from './internalHelpers.js';\n\n/**\n * Get cell comments, null cell for all\n */\nexport const getComments = function (cell) {\n    const obj = this;\n\n    if (cell) {\n        if (typeof cell !== 'string') {\n            return getComments.call(obj);\n        }\n\n        cell = getIdFromColumnName(cell, true);\n\n        return obj.records[cell[1]][cell[0]].element.getAttribute('title') || '';\n    } else {\n        const data = {};\n        for (let j = 0; j < obj.options.data.length; j++) {\n            for (let i = 0; i < obj.options.columns.length; i++) {\n                const comments = obj.records[j][i].element.getAttribute('title');\n                if (comments) {\n                    const cell = getColumnNameFromId([i, j]);\n                    data[cell] = comments;\n                }\n            }\n        }\n        return data;\n    }\n};\n\n/**\n * Set cell comments\n */\nexport const setComments = function (cellId, comments) {\n    const obj = this;\n\n    let commentsObj;\n\n    if (typeof cellId == 'string') {\n        commentsObj = { [cellId]: comments };\n    } else {\n        commentsObj = cellId;\n    }\n\n    const oldValue = {};\n\n    Object.entries(commentsObj).forEach(function ([cellName, comment]) {\n        const cellCoords = getCoordsFromCellName(cellName);\n\n        // Keep old value\n        oldValue[cellName] = obj.records[cellCoords[1]][cellCoords[0]].element.getAttribute('title');\n\n        // Set new values\n        obj.records[cellCoords[1]][cellCoords[0]].element.setAttribute('title', comment ? comment : '');\n\n        // Remove class if there is no comment\n        if (comment) {\n            obj.records[cellCoords[1]][cellCoords[0]].element.classList.add('jss_comments');\n\n            if (!obj.options.comments) {\n                obj.options.comments = {};\n            }\n\n            obj.options.comments[cellName] = comment;\n        } else {\n            obj.records[cellCoords[1]][cellCoords[0]].element.classList.remove('jss_comments');\n\n            if (obj.options.comments && obj.options.comments[cellName]) {\n                delete obj.options.comments[cellName];\n            }\n        }\n    });\n\n    // Save history\n    setHistory.call(obj, {\n        action: 'setComments',\n        newValue: commentsObj,\n        oldValue: oldValue,\n    });\n\n    // Set comments\n    dispatch.call(obj, 'oncomments', obj, commentsObj, oldValue);\n};\n"
  },
  {
    "path": "src/utils/config.js",
    "content": "/**\n * Get table config information\n */\nexport const getWorksheetConfig = function () {\n    const obj = this;\n\n    return obj.options;\n};\n\nexport const getSpreadsheetConfig = function () {\n    const spreadsheet = this;\n\n    return spreadsheet.config;\n};\n\nexport const setConfig = function (config, spreadsheetLevel) {\n    const obj = this;\n\n    const keys = Object.keys(config);\n\n    let spreadsheet;\n\n    if (!obj.parent) {\n        spreadsheetLevel = true;\n\n        spreadsheet = obj;\n    } else {\n        spreadsheet = obj.parent;\n    }\n\n    keys.forEach(function (key) {\n        if (spreadsheetLevel) {\n            spreadsheet.config[key] = config[key];\n\n            if (key === 'toolbar') {\n                if (config[key] === true) {\n                    spreadsheet.showToolbar();\n                } else if (config[key] === false) {\n                    spreadsheet.hideToolbar();\n                }\n            }\n        } else {\n            obj.options[key] = config[key];\n        }\n    });\n};\n"
  },
  {
    "path": "src/utils/copyPaste.js",
    "content": "import jSuites from 'jsuites';\n\nimport { parseCSV } from './helpers.js';\nimport dispatch from './dispatch.js';\nimport { setHistory } from './history.js';\nimport { updateCell, updateFormulaChain, updateTable, updateTableReferences } from './internal.js';\nimport { downGet, rightGet } from './keys.js';\nimport { hash, removeCopyingSelection, updateSelectionFromCoords } from './selection.js';\nimport { getColumnNameFromId } from './internalHelpers.js';\n\n/**\n * Copy method\n *\n * @param bool highlighted - Get only highlighted cells\n * @param delimiter - \\t default to keep compatibility with excel\n * @return string value\n */\nexport const copy = function (highlighted, delimiter, returnData, includeHeaders, download, isCut, processed) {\n    const obj = this;\n\n    if (!delimiter) {\n        delimiter = '\\t';\n    }\n\n    const div = new RegExp(delimiter, 'ig');\n\n    // Controls\n    const header = [];\n    let col = [];\n    let colLabel = [];\n    const row = [];\n    const rowLabel = [];\n    const x = obj.options.data[0].length;\n    const y = obj.options.data.length;\n    let tmp = '';\n    let copyHeader = false;\n    let headers = '';\n    let nestedHeaders = '';\n    let numOfCols = 0;\n    let numOfRows = 0;\n\n    // Partial copy\n    let copyX = 0;\n    let copyY = 0;\n    let isPartialCopy = true;\n    // Go through the columns to get the data\n    for (let j = 0; j < y; j++) {\n        for (let i = 0; i < x; i++) {\n            // If cell is highlighted\n            if (!highlighted || obj.records[j][i].element.classList.contains('highlight')) {\n                if (copyX <= i) {\n                    copyX = i;\n                }\n                if (copyY <= j) {\n                    copyY = j;\n                }\n            }\n        }\n    }\n    if (x === copyX + 1 && y === copyY + 1) {\n        isPartialCopy = false;\n    }\n\n    if (download && (obj.parent.config.includeHeadersOnDownload == true || includeHeaders)) {\n        // Nested headers\n        if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0) {\n            tmp = obj.options.nestedHeaders;\n\n            for (let j = 0; j < tmp.length; j++) {\n                const nested = [];\n                for (let i = 0; i < tmp[j].length; i++) {\n                    const colspan = parseInt(tmp[j][i].colspan);\n                    nested.push(tmp[j][i].title);\n                    for (let c = 0; c < colspan - 1; c++) {\n                        nested.push('');\n                    }\n                }\n                nestedHeaders += nested.join(delimiter) + '\\r\\n';\n            }\n        }\n\n        copyHeader = true;\n    }\n\n    // Reset container\n    obj.style = [];\n\n    // Go through the columns to get the data\n    for (let j = 0; j < y; j++) {\n        col = [];\n        colLabel = [];\n\n        for (let i = 0; i < x; i++) {\n            // If cell is highlighted\n            if (!highlighted || obj.records[j][i].element.classList.contains('highlight')) {\n                if (copyHeader == true) {\n                    header.push(obj.headers[i].textContent);\n                }\n                // Values\n                let value = obj.options.data[j][i];\n                if (value.match && (value.match(div) || value.match(/,/g) || value.match(/\\n/) || value.match(/\"/))) {\n                    value = value.replace(new RegExp('\"', 'g'), '\"\"');\n                    value = '\"' + value + '\"';\n                }\n                col.push(value);\n\n                // Labels\n                let label;\n\n                if (obj.options.columns && obj.options.columns[i] && (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio')) {\n                    label = value;\n                } else {\n                    label = obj.records[j][i].element.innerHTML;\n                    if (label.match && (label.match(div) || label.match(/,/g) || label.match(/\\n/) || label.match(/\"/))) {\n                        // Scape double quotes\n                        label = label.replace(new RegExp('\"', 'g'), '\"\"');\n                        label = '\"' + label + '\"';\n                    }\n                }\n                colLabel.push(label);\n\n                // Get style\n                tmp = obj.records[j][i].element.getAttribute('style');\n                tmp = tmp.replace('display: none;', '');\n                obj.style.push(tmp ? tmp : '');\n            }\n        }\n\n        if (col.length) {\n            if (copyHeader) {\n                numOfCols = col.length;\n                row.push(header.join(delimiter));\n            }\n            row.push(col.join(delimiter));\n        }\n        if (colLabel.length) {\n            numOfRows++;\n            if (copyHeader) {\n                rowLabel.push(header.join(delimiter));\n                copyHeader = false;\n            }\n            rowLabel.push(colLabel.join(delimiter));\n        }\n    }\n\n    if (x == numOfCols && y == numOfRows) {\n        headers = nestedHeaders;\n    }\n\n    // Final string\n    const str = headers + row.join('\\r\\n');\n    let strLabel = headers + rowLabel.join('\\r\\n');\n\n    // Create a hidden textarea to copy the values\n    if (!returnData) {\n        // Paste event\n        const selectedRange = [\n            Math.min(obj.selectedCell[0], obj.selectedCell[2]),\n            Math.min(obj.selectedCell[1], obj.selectedCell[3]),\n            Math.max(obj.selectedCell[0], obj.selectedCell[2]),\n            Math.max(obj.selectedCell[1], obj.selectedCell[3]),\n        ];\n\n        const result = dispatch.call(obj, 'oncopy', obj, selectedRange, strLabel, isCut);\n\n        if (result) {\n            strLabel = result;\n        } else if (result === false) {\n            return false;\n        }\n\n        obj.textarea.value = strLabel;\n        obj.textarea.select();\n        document.execCommand('copy');\n    }\n\n    // Keep data\n    if (processed == true) {\n        obj.data = strLabel;\n    } else {\n        obj.data = str;\n    }\n    // Keep non visible information\n    obj.hashString = hash.call(obj, obj.data);\n\n    // Any exiting border should go\n    if (!returnData) {\n        removeCopyingSelection.call(obj);\n\n        // Border\n        if (obj.highlighted) {\n            for (let i = 0; i < obj.highlighted.length; i++) {\n                obj.highlighted[i].element.classList.add('copying');\n                if (obj.highlighted[i].element.classList.contains('highlight-left')) {\n                    obj.highlighted[i].element.classList.add('copying-left');\n                }\n                if (obj.highlighted[i].element.classList.contains('highlight-right')) {\n                    obj.highlighted[i].element.classList.add('copying-right');\n                }\n                if (obj.highlighted[i].element.classList.contains('highlight-top')) {\n                    obj.highlighted[i].element.classList.add('copying-top');\n                }\n                if (obj.highlighted[i].element.classList.contains('highlight-bottom')) {\n                    obj.highlighted[i].element.classList.add('copying-bottom');\n                }\n            }\n        }\n    }\n\n    return obj.data;\n};\n\n/**\n * Jspreadsheet paste method\n *\n * @param x target column\n * @param y target row\n * @param data paste data. if data hash is the same as the copied data, apply style from copied cells\n * @return string value\n */\nexport const paste = function (x, y, data) {\n    const obj = this;\n\n    // Controls\n    const dataHash = hash(data);\n    let style = dataHash == obj.hashString ? obj.style : null;\n\n    // Depending on the behavior\n    if (dataHash == obj.hashString) {\n        data = obj.data;\n    }\n\n    // Split new line\n    data = parseCSV(data, '\\t');\n\n    const ex = obj.selectedCell[2];\n    const ey = obj.selectedCell[3];\n\n    const w = ex - x + 1;\n    const h = ey - y + 1;\n\n    // Modify data to allow wor extending paste range in multiples of input range\n    const srcW = data[0].length;\n    if ((w > 1) & Number.isInteger(w / srcW)) {\n        const repeats = w / srcW;\n        if (style) {\n            const newStyle = [];\n            for (let i = 0; i < style.length; i += srcW) {\n                const chunk = style.slice(i, i + srcW);\n                for (let j = 0; j < repeats; j++) {\n                    newStyle.push(...chunk);\n                }\n            }\n            style = newStyle;\n        }\n\n        const arrayB = data.map(function (row, i) {\n            const arrayC = Array.apply(null, { length: repeats * row.length }).map(function (e, i) {\n                return row[i % row.length];\n            });\n            return arrayC;\n        });\n        data = arrayB;\n    }\n    const srcH = data.length;\n    if ((h > 1) & Number.isInteger(h / srcH)) {\n        const repeats = h / srcH;\n        if (style) {\n            const newStyle = [];\n            for (let j = 0; j < repeats; j++) {\n                newStyle.push(...style);\n            }\n            style = newStyle;\n        }\n        const arrayB = Array.apply(null, { length: repeats * srcH }).map(function (e, i) {\n            return data[i % srcH];\n        });\n        data = arrayB;\n    }\n\n    // Paste filter\n    const ret = dispatch.call(\n        obj,\n        'onbeforepaste',\n        obj,\n        data.map(function (row) {\n            return row.map(function (item) {\n                return { value: item };\n            });\n        }),\n        x,\n        y\n    );\n\n    if (ret === false) {\n        return false;\n    } else if (ret) {\n        data = ret;\n    }\n\n    if (x != null && y != null && data) {\n        // Records\n        let i = 0;\n        let j = 0;\n        const records = [];\n        const newStyle = {};\n        const oldStyle = {};\n        let styleIndex = 0;\n\n        // Index\n        let colIndex = parseInt(x);\n        let rowIndex = parseInt(y);\n        let row = null;\n\n        const hiddenColCount = obj.headers.slice(colIndex).filter((x) => x.style.display === 'none').length;\n        const expandedColCount = colIndex + hiddenColCount + data[0].length;\n        const currentColCount = obj.headers.length;\n        if (expandedColCount > currentColCount) {\n            obj.skipUpdateTableReferences = true;\n            obj.insertColumn(expandedColCount - currentColCount);\n        }\n        const hiddenRowCount = obj.rows.slice(rowIndex).filter((x) => x.element.style.display === 'none').length;\n        const expandedRowCount = rowIndex + hiddenRowCount + data.length;\n        const currentRowCount = obj.rows.length;\n        if (expandedRowCount > currentRowCount) {\n            obj.skipUpdateTableReferences = true;\n            obj.insertRow(expandedRowCount - currentRowCount);\n        }\n\n        if (obj.skipUpdateTableReferences) {\n            obj.skipUpdateTableReferences = false;\n            updateTableReferences.call(obj);\n        }\n\n        // Go through the columns to get the data\n        while ((row = data[j])) {\n            i = 0;\n            colIndex = parseInt(x);\n\n            while (row[i] != null) {\n                let value = row[i];\n\n                if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'calendar') {\n                    value = jSuites.calendar.extractDateFromString(\n                        value,\n                        (obj.options.columns[i].options && obj.options.columns[i].options.format) || 'YYYY-MM-DD'\n                    );\n                }\n\n                // Update and keep history\n                const record = updateCell.call(obj, colIndex, rowIndex, value);\n                // Keep history\n                records.push(record);\n                // Update all formulas in the chain\n                updateFormulaChain.call(obj, colIndex, rowIndex, records);\n                // Style\n                if (style && style[styleIndex]) {\n                    const columnName = getColumnNameFromId([colIndex, rowIndex]);\n                    newStyle[columnName] = style[styleIndex];\n                    oldStyle[columnName] = obj.getStyle(columnName);\n                    obj.records[rowIndex][colIndex].element.setAttribute('style', style[styleIndex]);\n                    styleIndex++;\n                }\n                i++;\n                if (row[i] != null) {\n                    if (colIndex >= obj.headers.length - 1) {\n                        // If the pasted column is out of range, create it if possible\n                        if (obj.options.allowInsertColumn != false) {\n                            obj.insertColumn();\n                            // Otherwise skip the pasted data that overflows\n                        } else {\n                            break;\n                        }\n                    }\n                    colIndex = rightGet.call(obj, colIndex, rowIndex);\n                }\n            }\n\n            j++;\n            if (data[j]) {\n                if (rowIndex >= obj.rows.length - 1) {\n                    // If the pasted row is out of range, create it if possible\n                    if (obj.options.allowInsertRow != false) {\n                        obj.insertRow();\n                        // Otherwise skip the pasted data that overflows\n                    } else {\n                        break;\n                    }\n                }\n                rowIndex = downGet.call(obj, x, rowIndex);\n            }\n        }\n\n        // Select the new cells\n        updateSelectionFromCoords.call(obj, x, y, colIndex, rowIndex);\n\n        // Update history\n        setHistory.call(obj, {\n            action: 'setValue',\n            records: records,\n            selection: obj.selectedCell,\n            newStyle: newStyle,\n            oldStyle: oldStyle,\n        });\n\n        // Update table\n        updateTable.call(obj);\n\n        // Paste event\n        const eventRecords = [];\n\n        for (let j = 0; j < data.length; j++) {\n            for (let i = 0; i < data[j].length; i++) {\n                eventRecords.push({\n                    x: i + x,\n                    y: j + y,\n                    value: data[j][i],\n                });\n            }\n        }\n\n        dispatch.call(obj, 'onpaste', obj, eventRecords);\n\n        // On after changes\n        const onafterchangesRecords = records.map(function (record) {\n            return {\n                x: record.x,\n                y: record.y,\n                value: record.value,\n                oldValue: record.oldValue,\n            };\n        });\n\n        dispatch.call(obj, 'onafterchanges', obj, onafterchangesRecords);\n    }\n\n    removeCopyingSelection();\n};\n"
  },
  {
    "path": "src/utils/data.js",
    "content": "import { createRow } from './rows.js';\nimport { updateCell, updateFormulaChain, updateTable } from './internal.js';\nimport { getIdFromColumnName } from './internalHelpers.js';\nimport dispatch from './dispatch.js';\nimport { setHistory } from './history.js';\nimport { updatePagination } from './pagination.js';\nimport { setMerge } from './merges.js';\nimport { getCoordsFromRange } from './helpers.js';\n\nexport const setData = function (data) {\n    const obj = this;\n\n    // Update data\n    if (data) {\n        obj.options.data = data;\n    }\n\n    // Data\n    if (!obj.options.data) {\n        obj.options.data = [];\n    }\n\n    // Prepare data\n    if (obj.options.data && obj.options.data[0]) {\n        if (!Array.isArray(obj.options.data[0])) {\n            data = [];\n            for (let j = 0; j < obj.options.data.length; j++) {\n                const row = [];\n                for (let i = 0; i < obj.options.columns.length; i++) {\n                    row[i] = obj.options.data[j][obj.options.columns[i].name];\n                }\n                data.push(row);\n            }\n\n            obj.options.data = data;\n        }\n    }\n\n    // Adjust minimal dimensions\n    let j = 0;\n    let i = 0;\n    const size_i = (obj.options.columns && obj.options.columns.length) || 0;\n    const size_j = obj.options.data.length;\n    const min_i = obj.options.minDimensions[0];\n    const min_j = obj.options.minDimensions[1];\n    const max_i = min_i > size_i ? min_i : size_i;\n    const max_j = min_j > size_j ? min_j : size_j;\n\n    for (j = 0; j < max_j; j++) {\n        for (i = 0; i < max_i; i++) {\n            if (obj.options.data[j] == undefined) {\n                obj.options.data[j] = [];\n            }\n\n            if (obj.options.data[j][i] == undefined) {\n                obj.options.data[j][i] = '';\n            }\n        }\n    }\n\n    // Reset containers\n    obj.rows = [];\n    obj.results = null;\n    obj.records = [];\n    obj.history = [];\n\n    // Reset internal controllers\n    obj.historyIndex = -1;\n\n    // Reset data\n    obj.tbody.innerHTML = '';\n\n    let startNumber;\n    let finalNumber;\n\n    // Lazy loading\n    if (obj.options.lazyLoading == true) {\n        // Load only 100 records\n        startNumber = 0;\n        finalNumber = obj.options.data.length < 100 ? obj.options.data.length : 100;\n\n        if (obj.options.pagination) {\n            obj.options.pagination = false;\n            console.error('Jspreadsheet: Pagination will be disable due the lazyLoading');\n        }\n    } else if (obj.options.pagination) {\n        // Pagination\n        if (!obj.pageNumber) {\n            obj.pageNumber = 0;\n        }\n        var quantityPerPage = obj.options.pagination;\n        startNumber = obj.options.pagination * obj.pageNumber;\n        finalNumber = obj.options.pagination * obj.pageNumber + obj.options.pagination;\n\n        if (obj.options.data.length < finalNumber) {\n            finalNumber = obj.options.data.length;\n        }\n    } else {\n        startNumber = 0;\n        finalNumber = obj.options.data.length;\n    }\n\n    // Append nodes to the HTML\n    for (j = 0; j < obj.options.data.length; j++) {\n        // Create row\n        const row = createRow.call(obj, j, obj.options.data[j]);\n        // Append line to the table\n        if (j >= startNumber && j < finalNumber) {\n            obj.tbody.appendChild(row.element);\n        }\n    }\n\n    if (obj.options.lazyLoading == true) {\n        // Do not create pagination with lazyloading activated\n    } else if (obj.options.pagination) {\n        updatePagination.call(obj);\n    }\n\n    // Merge cells\n    if (obj.options.mergeCells) {\n        const keys = Object.keys(obj.options.mergeCells);\n        for (let i = 0; i < keys.length; i++) {\n            const num = obj.options.mergeCells[keys[i]];\n            setMerge.call(obj, keys[i], num[0], num[1], 1);\n        }\n    }\n\n    // Updata table with custom configurations if applicable\n    updateTable.call(obj);\n};\n\n/**\n * Get the value from a cell\n *\n * @param object cell\n * @return string value\n */\nexport const getValue = function (cell, processedValue) {\n    const obj = this;\n\n    let x;\n    let y;\n\n    if (typeof cell !== 'string') {\n        return null;\n    }\n\n    cell = getIdFromColumnName(cell, true);\n    x = cell[0];\n    y = cell[1];\n\n    let value = null;\n\n    if (x != null && y != null) {\n        if (obj.records[y] && obj.records[y][x] && processedValue) {\n            value = obj.records[y][x].element.innerHTML;\n        } else {\n            if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {\n                value = obj.options.data[y][x];\n            }\n        }\n    }\n\n    return value;\n};\n\n/**\n * Get the value from a coords\n *\n * @param int x\n * @param int y\n * @return string value\n */\nexport const getValueFromCoords = function (x, y, processedValue) {\n    const obj = this;\n\n    let value = null;\n\n    if (x != null && y != null) {\n        if (obj.records[y] && obj.records[y][x] && processedValue) {\n            value = obj.records[y][x].element.innerHTML;\n        } else {\n            if (obj.options.data[y] && obj.options.data[y][x] != 'undefined') {\n                value = obj.options.data[y][x];\n            }\n        }\n    }\n\n    return value;\n};\n\n/**\n * Set a cell value\n *\n * @param mixed cell destination cell\n * @param string value value\n * @return void\n */\nexport const setValue = function (cell, value, force) {\n    const obj = this;\n\n    const records = [];\n\n    if (typeof cell == 'string') {\n        const columnId = getIdFromColumnName(cell, true);\n        const x = columnId[0];\n        const y = columnId[1];\n\n        // Update cell\n        records.push(updateCell.call(obj, x, y, value, force));\n\n        // Update all formulas in the chain\n        updateFormulaChain.call(obj, x, y, records);\n    } else {\n        let x = null;\n        let y = null;\n        if (cell && cell.getAttribute) {\n            x = cell.getAttribute('data-x');\n            y = cell.getAttribute('data-y');\n        }\n\n        // Update cell\n        if (x != null && y != null) {\n            records.push(updateCell.call(obj, x, y, value, force));\n\n            // Update all formulas in the chain\n            updateFormulaChain.call(obj, x, y, records);\n        } else {\n            const keys = Object.keys(cell);\n            if (keys.length > 0) {\n                for (let i = 0; i < keys.length; i++) {\n                    let x, y;\n\n                    if (typeof cell[i] == 'string') {\n                        const columnId = getIdFromColumnName(cell[i], true);\n                        x = columnId[0];\n                        y = columnId[1];\n                    } else {\n                        if (cell[i].x != null && cell[i].y != null) {\n                            x = cell[i].x;\n                            y = cell[i].y;\n                            // Flexible setup\n                            if (cell[i].value != null) {\n                                value = cell[i].value;\n                            }\n                        } else {\n                            x = cell[i].getAttribute('data-x');\n                            y = cell[i].getAttribute('data-y');\n                        }\n                    }\n\n                    // Update cell\n                    if (x != null && y != null) {\n                        records.push(updateCell.call(obj, x, y, value, force));\n\n                        // Update all formulas in the chain\n                        updateFormulaChain.call(obj, x, y, records);\n                    }\n                }\n            }\n        }\n    }\n\n    // Update history\n    setHistory.call(obj, {\n        action: 'setValue',\n        records: records,\n        selection: obj.selectedCell,\n    });\n\n    // Update table with custom configurations if applicable\n    updateTable.call(obj);\n\n    // On after changes\n    const onafterchangesRecords = records.map(function (record) {\n        return {\n            x: record.x,\n            y: record.y,\n            value: record.value,\n            oldValue: record.oldValue,\n        };\n    });\n\n    dispatch.call(obj, 'onafterchanges', obj, onafterchangesRecords);\n};\n\n/**\n * Set a cell value based on coordinates\n *\n * @param int x destination cell\n * @param int y destination cell\n * @param string value\n * @return void\n */\nexport const setValueFromCoords = function (x, y, value, force) {\n    const obj = this;\n\n    const records = [];\n    records.push(updateCell.call(obj, x, y, value, force));\n\n    // Update all formulas in the chain\n    updateFormulaChain.call(obj, x, y, records);\n\n    // Update history\n    setHistory.call(obj, {\n        action: 'setValue',\n        records: records,\n        selection: obj.selectedCell,\n    });\n\n    // Update table with custom configurations if applicable\n    updateTable.call(obj);\n\n    // On after changes\n    const onafterchangesRecords = records.map(function (record) {\n        return {\n            x: record.x,\n            y: record.y,\n            value: record.value,\n            oldValue: record.oldValue,\n        };\n    });\n\n    dispatch.call(obj, 'onafterchanges', obj, onafterchangesRecords);\n};\n\n/**\n * Get the whole table data\n *\n * @param bool get highlighted cells only\n * @return array data\n */\nexport const getData = function (highlighted, processed, delimiter, asJson) {\n    const obj = this;\n\n    // Control vars\n    const dataset = [];\n    let px = 0;\n    let py = 0;\n\n    // Column and row length\n    const x = Math.max(\n        ...obj.options.data.map(function (row) {\n            return row.length;\n        })\n    );\n    const y = obj.options.data.length;\n\n    // Go through the columns to get the data\n    for (let j = 0; j < y; j++) {\n        px = 0;\n        for (let i = 0; i < x; i++) {\n            // Cell selected or fullset\n            if (!highlighted || obj.records[j][i].element.classList.contains('highlight')) {\n                // Get value\n                if (!dataset[py]) {\n                    dataset[py] = [];\n                }\n                if (processed) {\n                    dataset[py][px] = obj.records[j][i].element.innerHTML;\n                } else {\n                    dataset[py][px] = obj.options.data[j][i];\n                }\n                px++;\n            }\n        }\n        if (px > 0) {\n            py++;\n        }\n    }\n\n    if (delimiter) {\n        return (\n            dataset\n                .map(function (row) {\n                    return row.join(delimiter);\n                })\n                .join('\\r\\n') + '\\r\\n'\n        );\n    }\n\n    if (asJson) {\n        return dataset.map(function (row) {\n            const resultRow = {};\n\n            row.forEach(function (item, index) {\n                resultRow[index] = item;\n            });\n\n            return resultRow;\n        });\n    }\n\n    return dataset;\n};\n\nexport const getDataFromRange = function (range, processed) {\n    const obj = this;\n\n    const coords = getCoordsFromRange(range);\n\n    const dataset = [];\n\n    for (let y = coords[1]; y <= coords[3]; y++) {\n        dataset.push([]);\n\n        for (let x = coords[0]; x <= coords[2]; x++) {\n            if (processed) {\n                dataset[dataset.length - 1].push(obj.records[y][x].element.innerHTML);\n            } else {\n                dataset[dataset.length - 1].push(obj.options.data[y][x]);\n            }\n        }\n    }\n\n    return dataset;\n};\n"
  },
  {
    "path": "src/utils/dispatch.js",
    "content": "import jSuites from 'jsuites';\n\n/**\n * Prepare JSON in the correct format\n */\nconst prepareJson = function (data) {\n    const obj = this;\n\n    const rows = [];\n    for (let i = 0; i < data.length; i++) {\n        const x = data[i].x;\n        const y = data[i].y;\n        const k = obj.options.columns[x].name ? obj.options.columns[x].name : x;\n\n        // Create row\n        if (!rows[y]) {\n            rows[y] = {\n                row: y,\n                data: {},\n            };\n        }\n        rows[y].data[k] = data[i].value;\n    }\n\n    // Filter rows\n    return rows.filter(function (el) {\n        return el != null;\n    });\n};\n\n/**\n * Post json to a remote server\n */\nconst save = function (url, data) {\n    const obj = this;\n\n    // Parse anything in the data before sending to the server\n    const ret = dispatch.call(obj.parent, 'onbeforesave', obj.parent, obj, data);\n    if (ret) {\n        data = ret;\n    } else {\n        if (ret === false) {\n            return false;\n        }\n    }\n\n    // Remove update\n    jSuites.ajax({\n        url: url,\n        method: 'POST',\n        dataType: 'json',\n        data: { data: JSON.stringify(data) },\n        success: function (result) {\n            // Event\n            dispatch.call(obj, 'onsave', obj.parent, obj, data);\n        },\n    });\n};\n\n/**\n * Trigger events\n */\nconst dispatch = function (event) {\n    const obj = this;\n    let ret = null;\n\n    let spreadsheet = obj.parent ? obj.parent : obj;\n\n    // Dispatch events\n    if (!spreadsheet.ignoreEvents) {\n        // Call global event\n        if (typeof spreadsheet.config.onevent == 'function') {\n            ret = spreadsheet.config.onevent.apply(this, arguments);\n        }\n        // Call specific events\n        if (typeof spreadsheet.config[event] == 'function') {\n            ret = spreadsheet.config[event].apply(this, Array.prototype.slice.call(arguments, 1));\n        }\n\n        if (typeof spreadsheet.plugins === 'object') {\n            const pluginKeys = Object.keys(spreadsheet.plugins);\n\n            for (let pluginKeyIndex = 0; pluginKeyIndex < pluginKeys.length; pluginKeyIndex++) {\n                const key = pluginKeys[pluginKeyIndex];\n                const plugin = spreadsheet.plugins[key];\n\n                if (typeof plugin.onevent === 'function') {\n                    ret = plugin.onevent.apply(this, arguments);\n                }\n            }\n        }\n    }\n\n    if (event == 'onafterchanges') {\n        const scope = arguments;\n\n        if (typeof spreadsheet.plugins === 'object') {\n            Object.entries(spreadsheet.plugins).forEach(function ([, plugin]) {\n                if (typeof plugin.persistence === 'function') {\n                    plugin.persistence(obj, 'setValue', { data: scope[2] });\n                }\n            });\n        }\n\n        if (obj.options.persistence) {\n            const url = obj.options.persistence == true ? obj.options.url : obj.options.persistence;\n            const data = prepareJson.call(obj, arguments[2]);\n            save.call(obj, url, data);\n        }\n    }\n\n    return ret;\n};\n\nexport default dispatch;\n"
  },
  {
    "path": "src/utils/download.js",
    "content": "import { copy } from './copyPaste.js';\n\n/**\n * Download CSV table\n *\n * @return null\n */\nexport const download = function (includeHeaders, processed) {\n    const obj = this;\n\n    if (obj.parent.config.allowExport == false) {\n        console.error('Export not allowed');\n    } else {\n        // Data\n        let data = '';\n\n        // Get data\n        data += copy.call(obj, false, obj.options.csvDelimiter, true, includeHeaders, true, undefined, processed);\n\n        // Download element\n        const blob = new Blob(['\\uFEFF' + data], { type: 'text/csv;charset=utf-8;' });\n\n        // IE Compatibility\n        if (window.navigator && window.navigator.msSaveOrOpenBlob) {\n            window.navigator.msSaveOrOpenBlob(blob, (obj.options.csvFileName || obj.options.worksheetName) + '.csv');\n        } else {\n            // Download element\n            const pom = document.createElement('a');\n            pom.setAttribute('target', '_top');\n            const url = URL.createObjectURL(blob);\n            pom.href = url;\n            pom.setAttribute('download', (obj.options.csvFileName || obj.options.worksheetName) + '.csv');\n            document.body.appendChild(pom);\n            pom.click();\n            pom.parentNode.removeChild(pom);\n        }\n    }\n};\n"
  },
  {
    "path": "src/utils/editor.js",
    "content": "import jSuites from 'jsuites';\n\nimport dispatch from './dispatch.js';\nimport { getMask, isFormula, updateCell } from './internal.js';\nimport { setHistory } from './history.js';\n\n/**\n * Open the editor\n *\n * @param object cell\n * @return void\n */\nexport const openEditor = function (cell, empty, e) {\n    const obj = this;\n\n    // Get cell position\n    const y = cell.getAttribute('data-y');\n    const x = cell.getAttribute('data-x');\n\n    // On edition start\n    dispatch.call(obj, 'oneditionstart', obj, cell, parseInt(x), parseInt(y));\n\n    // Overflow\n    if (x > 0) {\n        obj.records[y][x - 1].element.style.overflow = 'hidden';\n    }\n\n    // Create editor\n    const createEditor = function (type) {\n        // Cell information\n        const info = cell.getBoundingClientRect();\n\n        // Create dropdown\n        const editor = document.createElement(type);\n        editor.style.width = info.width + 'px';\n        editor.style.height = info.height - 2 + 'px';\n        editor.style.minHeight = info.height - 2 + 'px';\n\n        // Edit cell\n        cell.classList.add('editor');\n        cell.innerHTML = '';\n        cell.appendChild(editor);\n\n        return editor;\n    };\n\n    // Readonly\n    if (cell.classList.contains('readonly') == true) {\n        // Do nothing\n    } else {\n        // Holder\n        obj.edition = [obj.records[y][x].element, obj.records[y][x].element.innerHTML, x, y];\n\n        // If there is a custom editor for it\n        if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {\n            // Custom editors\n            obj.options.columns[x].type.openEditor(cell, obj.options.data[y][x], parseInt(x), parseInt(y), obj, obj.options.columns[x], e);\n\n            // On edition start\n            dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n        } else {\n            // Native functions\n            if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'hidden') {\n                // Do nothing\n            } else if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio')) {\n                // Get value\n                const value = cell.children[0].checked ? false : true;\n                // Toogle value\n                obj.setValue(cell, value);\n                // Do not keep edition open\n                obj.edition = null;\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {\n                // Get current value\n                let value = obj.options.data[y][x];\n                if (obj.options.columns[x].multiple && !Array.isArray(value)) {\n                    value = value.split(';');\n                }\n\n                // Create dropdown\n                let source;\n\n                if (typeof obj.options.columns[x].filter == 'function') {\n                    source = obj.options.columns[x].filter(obj.element, cell, x, y, obj.options.columns[x].source);\n                } else {\n                    source = obj.options.columns[x].source;\n                }\n\n                // Do not change the original source\n                const data = [];\n                if (source) {\n                    for (let j = 0; j < source.length; j++) {\n                        data.push(source[j]);\n                    }\n                }\n\n                // Create editor\n                const editor = createEditor('div');\n\n                // On edition start\n                dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n\n                const options = {\n                    data: data,\n                    multiple: obj.options.columns[x].multiple ? true : false,\n                    autocomplete: obj.options.columns[x].autocomplete ? true : false,\n                    opened: true,\n                    value: value,\n                    width: '100%',\n                    height: editor.style.minHeight,\n                    position: obj.options.tableOverflow == true || obj.parent.config.fullscreen == true ? true : false,\n                    onclose: function () {\n                        closeEditor.call(obj, cell, true);\n                    },\n                };\n                if (obj.options.columns[x].options && obj.options.columns[x].options.type) {\n                    options.type = obj.options.columns[x].options.type;\n                }\n                jSuites.dropdown(editor, options);\n            } else if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'calendar' || obj.options.columns[x].type == 'color')) {\n                // Value\n                const value = obj.options.data[y][x];\n                // Create editor\n                const editor = createEditor('input');\n\n                dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n\n                editor.value = value;\n\n                const options = obj.options.columns[x].options ? { ...obj.options.columns[x].options } : {};\n\n                if (obj.options.tableOverflow == true || obj.parent.config.fullscreen == true) {\n                    options.position = true;\n                }\n                options.value = obj.options.data[y][x];\n                options.opened = true;\n                options.onclose = function (el, value) {\n                    closeEditor.call(obj, cell, true);\n                };\n                // Current value\n                if (obj.options.columns[x].type == 'color') {\n                    jSuites.color(editor, options);\n\n                    const rect = cell.getBoundingClientRect();\n\n                    if (options.position) {\n                        editor.nextSibling.children[1].style.top = rect.top + rect.height + 'px';\n                        editor.nextSibling.children[1].style.left = rect.left + 'px';\n                    }\n                } else {\n                    if (!options.format) {\n                        options.format = 'YYYY-MM-DD';\n                    }\n\n                    jSuites.calendar(editor, options);\n                }\n                // Focus on editor\n                editor.focus();\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {\n                const value = obj.options.data[y][x];\n                // Create editor\n                const editor = createEditor('div');\n\n                dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n\n                editor.style.position = 'relative';\n                const div = document.createElement('div');\n                div.classList.add('jss_richtext');\n                editor.appendChild(div);\n                jSuites.editor(div, {\n                    focus: true,\n                    value: value,\n                });\n                const rect = cell.getBoundingClientRect();\n                const rectContent = div.getBoundingClientRect();\n                if (window.innerHeight < rect.bottom + rectContent.height) {\n                    div.style.top = rect.bottom - (rectContent.height + 2) + 'px';\n                } else {\n                    div.style.top = rect.top + 'px';\n                }\n\n                if (window.innerWidth < rect.left + rectContent.width) {\n                    div.style.left = rect.right - (rectContent.width + 2) + 'px';\n                } else {\n                    div.style.left = rect.left + 'px';\n                }\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {\n                // Value\n                const img = cell.children[0];\n                // Create editor\n                const editor = createEditor('div');\n\n                dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n\n                editor.style.position = 'relative';\n                const div = document.createElement('div');\n                div.classList.add('jclose');\n                if (img && img.src) {\n                    div.appendChild(img);\n                }\n                editor.appendChild(div);\n                jSuites.image(div, obj.options.columns[x]);\n                const rect = cell.getBoundingClientRect();\n                const rectContent = div.getBoundingClientRect();\n                if (window.innerHeight < rect.bottom + rectContent.height) {\n                    div.style.top = rect.top - (rectContent.height + 2) + 'px';\n                } else {\n                    div.style.top = rect.top + 'px';\n                }\n\n                div.style.left = rect.left + 'px';\n            } else {\n                // Value\n                const value = empty == true ? '' : obj.options.data[y][x];\n\n                // Basic editor\n                let editor;\n\n                if (\n                    (!obj.options.columns || !obj.options.columns[x] || obj.options.columns[x].wordWrap != false) &&\n                    (obj.options.wordWrap == true || (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].wordWrap == true))\n                ) {\n                    editor = createEditor('textarea');\n                } else {\n                    editor = createEditor('input');\n                }\n\n                dispatch.call(obj, 'oncreateeditor', obj, cell, parseInt(x), parseInt(y), null, obj.options.columns[x]);\n\n                editor.focus();\n                editor.value = value;\n\n                // Column options\n                const options = obj.options.columns && obj.options.columns[x];\n\n                // Apply format when is not a formula\n                if (!isFormula(value)) {\n                    if (options) {\n                        // Format\n                        const opt = getMask(options);\n\n                        if (opt) {\n                            // Masking\n                            if (!options.disabledMaskOnEdition) {\n                                if (options.mask) {\n                                    const m = options.mask.split(';');\n                                    editor.setAttribute('data-mask', m[0]);\n                                } else if (options.locale) {\n                                    editor.setAttribute('data-locale', options.locale);\n                                }\n                            }\n                            // Input\n                            opt.input = editor;\n                            // Configuration\n                            editor.mask = opt;\n                            // Do not treat the decimals\n                            jSuites.mask.render(value, opt, false);\n                        }\n                    }\n                }\n\n                editor.onblur = function () {\n                    closeEditor.call(obj, cell, true);\n                };\n                editor.scrollLeft = editor.scrollWidth;\n            }\n        }\n    }\n};\n\n/**\n * Close the editor and save the information\n *\n * @param object cell\n * @param boolean save\n * @return void\n */\nexport const closeEditor = function (cell, save) {\n    const obj = this;\n\n    const x = parseInt(cell.getAttribute('data-x'));\n    const y = parseInt(cell.getAttribute('data-y'));\n\n    let value;\n\n    // Get cell properties\n    if (save == true) {\n        // If custom editor\n        if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {\n            // Custom editor\n            value = obj.options.columns[x].type.closeEditor(cell, save, parseInt(x), parseInt(y), obj, obj.options.columns[x]);\n        } else {\n            // Native functions\n            if (\n                obj.options.columns &&\n                obj.options.columns[x] &&\n                (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio' || obj.options.columns[x].type == 'hidden')\n            ) {\n                // Do nothing\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {\n                value = cell.children[0].dropdown.close(true);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {\n                value = cell.children[0].calendar.close(true);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {\n                value = cell.children[0].color.close(true);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {\n                value = cell.children[0].children[0].editor.getData();\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {\n                const img = cell.children[0].children[0].children[0];\n                value = img && img.tagName == 'IMG' ? img.src : '';\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'numeric') {\n                value = cell.children[0].value;\n                if (('' + value).substr(0, 1) != '=') {\n                    if (value == '') {\n                        value = obj.options.columns[x].allowEmpty ? '' : 0;\n                    }\n                }\n                cell.children[0].onblur = null;\n            } else {\n                value = cell.children[0].value;\n                cell.children[0].onblur = null;\n\n                // Column options\n                const options = obj.options.columns && obj.options.columns[x];\n\n                if (options) {\n                    // Format\n                    const opt = getMask(options);\n                    if (opt) {\n                        // Keep numeric in the raw data\n                        if (value !== '' && !isFormula(value) && typeof value !== 'number') {\n                            const t = jSuites.mask.extract(value, opt, true);\n                            if (t && t.value !== '') {\n                                value = t.value;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        // Ignore changes if the value is the same\n        if (obj.options.data[y][x] == value) {\n            cell.innerHTML = obj.edition[1];\n        } else {\n            obj.setValue(cell, value);\n        }\n    } else {\n        if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object') {\n            // Custom editor\n            obj.options.columns[x].type.closeEditor(cell, save, parseInt(x), parseInt(y), obj, obj.options.columns[x]);\n        } else {\n            if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {\n                cell.children[0].dropdown.close(true);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {\n                cell.children[0].calendar.close(true);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {\n                cell.children[0].color.close(true);\n            } else {\n                cell.children[0].onblur = null;\n            }\n        }\n\n        // Restore value\n        cell.innerHTML = obj.edition && obj.edition[1] ? obj.edition[1] : '';\n    }\n\n    // On edition end\n    dispatch.call(obj, 'oneditionend', obj, cell, x, y, value, save);\n\n    // Remove editor class\n    cell.classList.remove('editor');\n\n    // Finish edition\n    obj.edition = null;\n};\n\n/**\n * Toogle\n */\nexport const setCheckRadioValue = function () {\n    const obj = this;\n\n    const records = [];\n    const keys = Object.keys(obj.highlighted);\n    for (let i = 0; i < keys.length; i++) {\n        const x = obj.highlighted[i].element.getAttribute('data-x');\n        const y = obj.highlighted[i].element.getAttribute('data-y');\n\n        if (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio') {\n            // Update cell\n            records.push(updateCell.call(obj, x, y, !obj.options.data[y][x]));\n        }\n    }\n\n    if (records.length) {\n        // Update history\n        setHistory.call(obj, {\n            action: 'setValue',\n            records: records,\n            selection: obj.selectedCell,\n        });\n\n        // On after changes\n        const onafterchangesRecords = records.map(function (record) {\n            return {\n                x: record.x,\n                y: record.y,\n                value: record.value,\n                oldValue: record.oldValue,\n            };\n        });\n\n        dispatch.call(obj, 'onafterchanges', obj, onafterchangesRecords);\n    }\n};\n"
  },
  {
    "path": "src/utils/events.js",
    "content": "import jSuites from 'jsuites';\n\nimport { closeEditor, openEditor, setCheckRadioValue } from './editor.js';\nimport libraryBase from './libraryBase.js';\nimport { down, first, last, left, right, up } from './keys.js';\nimport { isColMerged, isRowMerged } from './merges.js';\nimport { copyData, removeCopySelection, resetSelection, selectAll, updateCornerPosition, updateSelectionFromCoords } from './selection.js';\nimport { copy, paste } from './copyPaste.js';\nimport { openFilter } from './filter.js';\nimport { loadDown, loadUp } from './lazyLoading.js';\nimport { setWidth } from './columns.js';\nimport { moveRow, setHeight } from './rows.js';\nimport version from './version.js';\nimport { getCellNameFromCoords } from './helpers.js';\n\nconst getElement = function (element) {\n    let jssSection = 0;\n    let jssElement = 0;\n\n    function path(element) {\n        if (element.className) {\n            if (element.classList.contains('jss_container')) {\n                jssElement = element;\n            }\n\n            if (element.classList.contains('jss_spreadsheet')) {\n                jssElement = element.querySelector(':scope > .jtabs-content > .jtabs-selected');\n            }\n        }\n\n        if (element.tagName == 'THEAD') {\n            jssSection = 1;\n        } else if (element.tagName == 'TBODY') {\n            jssSection = 2;\n        }\n\n        if (element.parentNode) {\n            if (!jssElement) {\n                path(element.parentNode);\n            }\n        }\n    }\n\n    path(element);\n\n    return [jssElement, jssSection];\n};\n\nconst mouseUpControls = function (e) {\n    if (libraryBase.jspreadsheet.current) {\n        // Update cell size\n        if (libraryBase.jspreadsheet.current.resizing) {\n            // Columns to be updated\n            if (libraryBase.jspreadsheet.current.resizing.column) {\n                // New width\n                const newWidth = parseInt(\n                    libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.getAttribute('width')\n                );\n                // Columns\n                const columns = libraryBase.jspreadsheet.current.getSelectedColumns();\n                if (columns.length > 1) {\n                    const currentWidth = [];\n                    for (let i = 0; i < columns.length; i++) {\n                        currentWidth.push(parseInt(libraryBase.jspreadsheet.current.cols[columns[i]].colElement.getAttribute('width')));\n                    }\n                    // Previous width\n                    const index = columns.indexOf(parseInt(libraryBase.jspreadsheet.current.resizing.column));\n                    currentWidth[index] = libraryBase.jspreadsheet.current.resizing.width;\n                    setWidth.call(libraryBase.jspreadsheet.current, columns, newWidth, currentWidth);\n                } else {\n                    setWidth.call(\n                        libraryBase.jspreadsheet.current,\n                        parseInt(libraryBase.jspreadsheet.current.resizing.column),\n                        newWidth,\n                        libraryBase.jspreadsheet.current.resizing.width\n                    );\n                }\n                // Remove border\n                libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.resizing.column].classList.remove('resizing');\n                for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {\n                    if (libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.resizing.column]) {\n                        libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.resizing.column].element.classList.remove('resizing');\n                    }\n                }\n            } else {\n                // Remove Class\n                libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.children[0].classList.remove('resizing');\n                let newHeight = libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.getAttribute('height');\n                setHeight.call(\n                    libraryBase.jspreadsheet.current,\n                    libraryBase.jspreadsheet.current.resizing.row,\n                    newHeight,\n                    libraryBase.jspreadsheet.current.resizing.height\n                );\n                // Remove border\n                libraryBase.jspreadsheet.current.resizing.element.classList.remove('resizing');\n            }\n            // Reset resizing helper\n            libraryBase.jspreadsheet.current.resizing = null;\n        } else if (libraryBase.jspreadsheet.current.dragging) {\n            // Reset dragging helper\n            if (libraryBase.jspreadsheet.current.dragging) {\n                if (libraryBase.jspreadsheet.current.dragging.column) {\n                    // Target\n                    const columnId = e.target.getAttribute('data-x');\n                    // Remove move style\n                    libraryBase.jspreadsheet.current.headers[libraryBase.jspreadsheet.current.dragging.column].classList.remove('dragging');\n                    for (let j = 0; j < libraryBase.jspreadsheet.current.rows.length; j++) {\n                        if (libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.dragging.column]) {\n                            libraryBase.jspreadsheet.current.records[j][libraryBase.jspreadsheet.current.dragging.column].element.classList.remove('dragging');\n                        }\n                    }\n                    for (let i = 0; i < libraryBase.jspreadsheet.current.headers.length; i++) {\n                        libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-left');\n                        libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-right');\n                    }\n                    // Update position\n                    if (columnId) {\n                        if (libraryBase.jspreadsheet.current.dragging.column != libraryBase.jspreadsheet.current.dragging.destination) {\n                            libraryBase.jspreadsheet.current.moveColumn(\n                                libraryBase.jspreadsheet.current.dragging.column,\n                                libraryBase.jspreadsheet.current.dragging.destination\n                            );\n                        }\n                    }\n                } else {\n                    let position;\n\n                    if (libraryBase.jspreadsheet.current.dragging.element.nextSibling) {\n                        position = parseInt(libraryBase.jspreadsheet.current.dragging.element.nextSibling.getAttribute('data-y'));\n                        if (libraryBase.jspreadsheet.current.dragging.row < position) {\n                            position -= 1;\n                        }\n                    } else {\n                        position = parseInt(libraryBase.jspreadsheet.current.dragging.element.previousSibling.getAttribute('data-y'));\n                    }\n                    if (libraryBase.jspreadsheet.current.dragging.row != libraryBase.jspreadsheet.current.dragging.destination) {\n                        moveRow.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.dragging.row, position, true);\n                    }\n                    libraryBase.jspreadsheet.current.dragging.element.classList.remove('dragging');\n                }\n                libraryBase.jspreadsheet.current.dragging = null;\n            }\n        } else {\n            // Close any corner selection\n            if (libraryBase.jspreadsheet.current.selectedCorner) {\n                libraryBase.jspreadsheet.current.selectedCorner = false;\n\n                // Data to be copied\n                if (libraryBase.jspreadsheet.current.selection.length > 0) {\n                    // Copy data\n                    copyData.call(\n                        libraryBase.jspreadsheet.current,\n                        libraryBase.jspreadsheet.current.selection[0],\n                        libraryBase.jspreadsheet.current.selection[libraryBase.jspreadsheet.current.selection.length - 1]\n                    );\n\n                    // Remove selection\n                    removeCopySelection.call(libraryBase.jspreadsheet.current);\n                }\n            }\n        }\n    }\n\n    // Clear any time control\n    if (libraryBase.jspreadsheet.timeControl) {\n        clearTimeout(libraryBase.jspreadsheet.timeControl);\n        libraryBase.jspreadsheet.timeControl = null;\n    }\n\n    // Mouse up\n    libraryBase.jspreadsheet.isMouseAction = false;\n};\n\nconst mouseDownControls = function (e) {\n    e = e || window.event;\n\n    let mouseButton;\n\n    if (e.buttons) {\n        mouseButton = e.buttons;\n    } else if (e.button) {\n        mouseButton = e.button;\n    } else {\n        mouseButton = e.which;\n    }\n\n    // Get elements\n    const jssTable = getElement(e.target);\n\n    if (jssTable[0]) {\n        if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {\n            if (libraryBase.jspreadsheet.current) {\n                if (libraryBase.jspreadsheet.current.edition) {\n                    closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n                }\n                libraryBase.jspreadsheet.current.resetSelection();\n            }\n            libraryBase.jspreadsheet.current = jssTable[0].jssWorksheet;\n        }\n    } else {\n        if (libraryBase.jspreadsheet.current) {\n            if (libraryBase.jspreadsheet.current.edition) {\n                closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n            }\n\n            if (!e.target.classList.contains('jss_object')) {\n                resetSelection.call(libraryBase.jspreadsheet.current, true);\n                libraryBase.jspreadsheet.current = null;\n            }\n        }\n    }\n\n    if (libraryBase.jspreadsheet.current && mouseButton == 1) {\n        if (e.target.classList.contains('jss_selectall')) {\n            if (libraryBase.jspreadsheet.current) {\n                selectAll.call(libraryBase.jspreadsheet.current);\n            }\n        } else if (e.target.classList.contains('jss_corner')) {\n            if (libraryBase.jspreadsheet.current.options.editable != false) {\n                libraryBase.jspreadsheet.current.selectedCorner = true;\n            }\n        } else {\n            // Header found\n            if (jssTable[1] == 1) {\n                const columnId = e.target.getAttribute('data-x');\n                if (columnId) {\n                    // Update cursor\n                    const info = e.target.getBoundingClientRect();\n                    if (libraryBase.jspreadsheet.current.options.columnResize != false && info.width - e.offsetX < 6) {\n                        // Resize helper\n                        libraryBase.jspreadsheet.current.resizing = {\n                            mousePosition: e.pageX,\n                            column: columnId,\n                            width: info.width,\n                        };\n\n                        // Border indication\n                        libraryBase.jspreadsheet.current.headers[columnId].classList.add('resizing');\n                        for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {\n                            if (libraryBase.jspreadsheet.current.records[j][columnId]) {\n                                libraryBase.jspreadsheet.current.records[j][columnId].element.classList.add('resizing');\n                            }\n                        }\n                    } else if (libraryBase.jspreadsheet.current.options.columnDrag != false && info.height - e.offsetY < 6) {\n                        if (isColMerged.call(libraryBase.jspreadsheet.current, columnId).length) {\n                            console.error('Jspreadsheet: This column is part of a merged cell.');\n                        } else {\n                            // Reset selection\n                            libraryBase.jspreadsheet.current.resetSelection();\n                            // Drag helper\n                            libraryBase.jspreadsheet.current.dragging = {\n                                element: e.target,\n                                column: columnId,\n                                destination: columnId,\n                            };\n                            // Border indication\n                            libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging');\n                            for (let j = 0; j < libraryBase.jspreadsheet.current.records.length; j++) {\n                                if (libraryBase.jspreadsheet.current.records[j][columnId]) {\n                                    libraryBase.jspreadsheet.current.records[j][columnId].element.classList.add('dragging');\n                                }\n                            }\n                        }\n                    } else {\n                        let o, d;\n\n                        if (libraryBase.jspreadsheet.current.selectedHeader && (e.shiftKey || e.ctrlKey)) {\n                            o = libraryBase.jspreadsheet.current.selectedHeader;\n                            d = columnId;\n                        } else {\n                            // Press to rename\n                            if (\n                                libraryBase.jspreadsheet.current.selectedHeader == columnId &&\n                                libraryBase.jspreadsheet.current.options.allowRenameColumn != false\n                            ) {\n                                libraryBase.jspreadsheet.timeControl = setTimeout(function () {\n                                    libraryBase.jspreadsheet.current.setHeader(columnId);\n                                }, 800);\n                            }\n\n                            // Keep track of which header was selected first\n                            libraryBase.jspreadsheet.current.selectedHeader = columnId;\n\n                            // Update selection single column\n                            o = columnId;\n                            d = columnId;\n                        }\n\n                        // Update selection\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, o, 0, d, libraryBase.jspreadsheet.current.options.data.length - 1, e);\n                    }\n                } else {\n                    if (e.target.parentNode.classList.contains('jss_nested')) {\n                        let c1, c2;\n\n                        if (e.target.getAttribute('data-column')) {\n                            const column = e.target.getAttribute('data-column').split(',');\n                            c1 = parseInt(column[0]);\n                            c2 = parseInt(column[column.length - 1]);\n                        } else {\n                            c1 = 0;\n                            c2 = libraryBase.jspreadsheet.current.options.columns.length - 1;\n                        }\n                        updateSelectionFromCoords.call(\n                            libraryBase.jspreadsheet.current,\n                            c1,\n                            0,\n                            c2,\n                            libraryBase.jspreadsheet.current.options.data.length - 1,\n                            e\n                        );\n                    }\n                }\n            } else {\n                libraryBase.jspreadsheet.current.selectedHeader = false;\n            }\n\n            // Body found\n            if (jssTable[1] == 2) {\n                const rowId = parseInt(e.target.getAttribute('data-y'));\n\n                if (e.target.classList.contains('jss_row')) {\n                    const info = e.target.getBoundingClientRect();\n                    if (libraryBase.jspreadsheet.current.options.rowResize != false && info.height - e.offsetY < 6) {\n                        // Resize helper\n                        libraryBase.jspreadsheet.current.resizing = {\n                            element: e.target.parentNode,\n                            mousePosition: e.pageY,\n                            row: rowId,\n                            height: info.height,\n                        };\n                        // Border indication\n                        e.target.parentNode.classList.add('resizing');\n                    } else if (libraryBase.jspreadsheet.current.options.rowDrag != false && info.width - e.offsetX < 6) {\n                        if (isRowMerged.call(libraryBase.jspreadsheet.current, rowId).length) {\n                            console.error('Jspreadsheet: This row is part of a merged cell');\n                        } else if (libraryBase.jspreadsheet.current.options.search == true && libraryBase.jspreadsheet.current.results) {\n                            console.error('Jspreadsheet: Please clear your search before perform this action');\n                        } else {\n                            // Reset selection\n                            libraryBase.jspreadsheet.current.resetSelection();\n                            // Drag helper\n                            libraryBase.jspreadsheet.current.dragging = {\n                                element: e.target.parentNode,\n                                row: rowId,\n                                destination: rowId,\n                            };\n                            // Border indication\n                            e.target.parentNode.classList.add('dragging');\n                        }\n                    } else {\n                        let o, d;\n                        if (libraryBase.jspreadsheet.current.selectedRow != null && (e.shiftKey || e.ctrlKey)) {\n                            o = libraryBase.jspreadsheet.current.selectedRow;\n                            d = rowId;\n                        } else {\n                            // Keep track of which header was selected first\n                            libraryBase.jspreadsheet.current.selectedRow = rowId;\n\n                            // Update selection single column\n                            o = rowId;\n                            d = rowId;\n                        }\n\n                        // Update selection\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, null, o, null, d, e);\n                    }\n                } else {\n                    // Jclose\n                    if (e.target.classList.contains('jclose') && e.target.clientWidth - e.offsetX < 50 && e.offsetY < 50) {\n                        closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n                    } else {\n                        const getCellCoords = function (element) {\n                            const x = element.getAttribute('data-x');\n                            const y = element.getAttribute('data-y');\n                            if (x && y) {\n                                return [x, y];\n                            } else {\n                                if (element.parentNode) {\n                                    return getCellCoords(element.parentNode);\n                                }\n                            }\n                        };\n\n                        const position = getCellCoords(e.target);\n                        if (position) {\n                            const columnId = position[0];\n                            const rowId = position[1];\n                            // Close edition\n                            if (libraryBase.jspreadsheet.current.edition) {\n                                if (libraryBase.jspreadsheet.current.edition[2] != columnId || libraryBase.jspreadsheet.current.edition[3] != rowId) {\n                                    closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n                                }\n                            }\n\n                            if (!libraryBase.jspreadsheet.current.edition) {\n                                // Update cell selection\n                                if (e.shiftKey) {\n                                    updateSelectionFromCoords.call(\n                                        libraryBase.jspreadsheet.current,\n                                        libraryBase.jspreadsheet.current.selectedCell[0],\n                                        libraryBase.jspreadsheet.current.selectedCell[1],\n                                        columnId,\n                                        rowId,\n                                        e\n                                    );\n                                } else {\n                                    updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, columnId, rowId, undefined, undefined, e);\n                                }\n                            }\n\n                            // No full row selected\n                            libraryBase.jspreadsheet.current.selectedHeader = null;\n                            libraryBase.jspreadsheet.current.selectedRow = null;\n                        }\n                    }\n                }\n            } else {\n                libraryBase.jspreadsheet.current.selectedRow = false;\n            }\n\n            // Pagination\n            if (e.target.classList.contains('jss_page')) {\n                if (e.target.textContent == '<') {\n                    libraryBase.jspreadsheet.current.page(0);\n                } else if (e.target.textContent == '>') {\n                    libraryBase.jspreadsheet.current.page(e.target.getAttribute('title') - 1);\n                } else {\n                    libraryBase.jspreadsheet.current.page(e.target.textContent - 1);\n                }\n            }\n        }\n\n        if (libraryBase.jspreadsheet.current.edition) {\n            libraryBase.jspreadsheet.isMouseAction = false;\n        } else {\n            libraryBase.jspreadsheet.isMouseAction = true;\n        }\n    } else {\n        libraryBase.jspreadsheet.isMouseAction = false;\n    }\n};\n\n// Mouse move controls\nconst mouseMoveControls = function (e) {\n    e = e || window.event;\n\n    let mouseButton;\n\n    if (e.buttons) {\n        mouseButton = e.buttons;\n    } else if (e.button) {\n        mouseButton = e.button;\n    } else {\n        mouseButton = e.which;\n    }\n\n    if (!mouseButton) {\n        libraryBase.jspreadsheet.isMouseAction = false;\n    }\n\n    if (libraryBase.jspreadsheet.current) {\n        if (libraryBase.jspreadsheet.isMouseAction == true) {\n            // Resizing is ongoing\n            if (libraryBase.jspreadsheet.current.resizing) {\n                if (libraryBase.jspreadsheet.current.resizing.column) {\n                    const width = e.pageX - libraryBase.jspreadsheet.current.resizing.mousePosition;\n\n                    if (libraryBase.jspreadsheet.current.resizing.width + width > 0) {\n                        const tempWidth = libraryBase.jspreadsheet.current.resizing.width + width;\n                        libraryBase.jspreadsheet.current.cols[libraryBase.jspreadsheet.current.resizing.column].colElement.setAttribute('width', tempWidth);\n\n                        updateCornerPosition.call(libraryBase.jspreadsheet.current);\n                    }\n                } else {\n                    const height = e.pageY - libraryBase.jspreadsheet.current.resizing.mousePosition;\n\n                    if (libraryBase.jspreadsheet.current.resizing.height + height > 0) {\n                        const tempHeight = libraryBase.jspreadsheet.current.resizing.height + height;\n                        libraryBase.jspreadsheet.current.rows[libraryBase.jspreadsheet.current.resizing.row].element.setAttribute('height', tempHeight);\n\n                        updateCornerPosition.call(libraryBase.jspreadsheet.current);\n                    }\n                }\n            } else if (libraryBase.jspreadsheet.current.dragging) {\n                if (libraryBase.jspreadsheet.current.dragging.column) {\n                    const columnId = e.target.getAttribute('data-x');\n                    if (columnId) {\n                        if (isColMerged.call(libraryBase.jspreadsheet.current, columnId).length) {\n                            console.error('Jspreadsheet: This column is part of a merged cell.');\n                        } else {\n                            for (let i = 0; i < libraryBase.jspreadsheet.current.headers.length; i++) {\n                                libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-left');\n                                libraryBase.jspreadsheet.current.headers[i].classList.remove('dragging-right');\n                            }\n\n                            if (libraryBase.jspreadsheet.current.dragging.column == columnId) {\n                                libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);\n                            } else {\n                                if (e.target.clientWidth / 2 > e.offsetX) {\n                                    if (libraryBase.jspreadsheet.current.dragging.column < columnId) {\n                                        libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) - 1;\n                                    } else {\n                                        libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);\n                                    }\n                                    libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-left');\n                                } else {\n                                    if (libraryBase.jspreadsheet.current.dragging.column < columnId) {\n                                        libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId);\n                                    } else {\n                                        libraryBase.jspreadsheet.current.dragging.destination = parseInt(columnId) + 1;\n                                    }\n                                    libraryBase.jspreadsheet.current.headers[columnId].classList.add('dragging-right');\n                                }\n                            }\n                        }\n                    }\n                } else {\n                    const rowId = e.target.getAttribute('data-y');\n                    if (rowId) {\n                        if (isRowMerged.call(libraryBase.jspreadsheet.current, rowId).length) {\n                            console.error('Jspreadsheet: This row is part of a merged cell.');\n                        } else {\n                            const target = e.target.clientHeight / 2 > e.offsetY ? e.target.parentNode.nextSibling : e.target.parentNode;\n                            if (libraryBase.jspreadsheet.current.dragging.element != target) {\n                                e.target.parentNode.parentNode.insertBefore(libraryBase.jspreadsheet.current.dragging.element, target);\n                                libraryBase.jspreadsheet.current.dragging.destination = Array.prototype.indexOf.call(\n                                    libraryBase.jspreadsheet.current.dragging.element.parentNode.children,\n                                    libraryBase.jspreadsheet.current.dragging.element\n                                );\n                            }\n                        }\n                    }\n                }\n            }\n        } else {\n            const x = e.target.getAttribute('data-x');\n            const y = e.target.getAttribute('data-y');\n            const rect = e.target.getBoundingClientRect();\n\n            if (libraryBase.jspreadsheet.current.cursor) {\n                libraryBase.jspreadsheet.current.cursor.style.cursor = '';\n                libraryBase.jspreadsheet.current.cursor = null;\n            }\n\n            if (e.target.parentNode.parentNode && e.target.parentNode.parentNode.className) {\n                if (e.target.parentNode.parentNode.classList.contains('resizable')) {\n                    if (e.target && x && !y && rect.width - (e.clientX - rect.left) < 6) {\n                        libraryBase.jspreadsheet.current.cursor = e.target;\n                        libraryBase.jspreadsheet.current.cursor.style.cursor = 'col-resize';\n                    } else if (e.target && !x && y && rect.height - (e.clientY - rect.top) < 6) {\n                        libraryBase.jspreadsheet.current.cursor = e.target;\n                        libraryBase.jspreadsheet.current.cursor.style.cursor = 'row-resize';\n                    }\n                }\n\n                if (e.target.parentNode.parentNode.classList.contains('draggable')) {\n                    if (e.target && !x && y && rect.width - (e.clientX - rect.left) < 6) {\n                        libraryBase.jspreadsheet.current.cursor = e.target;\n                        libraryBase.jspreadsheet.current.cursor.style.cursor = 'move';\n                    } else if (e.target && x && !y && rect.height - (e.clientY - rect.top) < 6) {\n                        libraryBase.jspreadsheet.current.cursor = e.target;\n                        libraryBase.jspreadsheet.current.cursor.style.cursor = 'move';\n                    }\n                }\n            }\n        }\n    }\n};\n\n/**\n * Update copy selection\n *\n * @param int x, y\n * @return void\n */\nconst updateCopySelection = function (x3, y3) {\n    const obj = this;\n\n    // Remove selection\n    removeCopySelection.call(obj);\n\n    // Get elements first and last\n    const x1 = obj.selectedContainer[0];\n    const y1 = obj.selectedContainer[1];\n    const x2 = obj.selectedContainer[2];\n    const y2 = obj.selectedContainer[3];\n\n    if (x3 != null && y3 != null) {\n        let px, ux;\n\n        if (x3 - x2 > 0) {\n            px = parseInt(x2) + 1;\n            ux = parseInt(x3);\n        } else {\n            px = parseInt(x3);\n            ux = parseInt(x1) - 1;\n        }\n\n        let py, uy;\n\n        if (y3 - y2 > 0) {\n            py = parseInt(y2) + 1;\n            uy = parseInt(y3);\n        } else {\n            py = parseInt(y3);\n            uy = parseInt(y1) - 1;\n        }\n\n        if (ux - px <= uy - py) {\n            px = parseInt(x1);\n            ux = parseInt(x2);\n        } else {\n            py = parseInt(y1);\n            uy = parseInt(y2);\n        }\n\n        for (let j = py; j <= uy; j++) {\n            for (let i = px; i <= ux; i++) {\n                if (obj.records[j][i] && obj.rows[j].element.style.display != 'none' && obj.records[j][i].element.style.display != 'none') {\n                    obj.records[j][i].element.classList.add('selection');\n                    obj.records[py][i].element.classList.add('selection-top');\n                    obj.records[uy][i].element.classList.add('selection-bottom');\n                    obj.records[j][px].element.classList.add('selection-left');\n                    obj.records[j][ux].element.classList.add('selection-right');\n\n                    // Persist selected elements\n                    obj.selection.push(obj.records[j][i].element);\n                }\n            }\n        }\n    }\n};\n\nconst mouseOverControls = function (e) {\n    e = e || window.event;\n\n    let mouseButton;\n\n    if (e.buttons) {\n        mouseButton = e.buttons;\n    } else if (e.button) {\n        mouseButton = e.button;\n    } else {\n        mouseButton = e.which;\n    }\n\n    if (!mouseButton) {\n        libraryBase.jspreadsheet.isMouseAction = false;\n    }\n\n    if (libraryBase.jspreadsheet.current && libraryBase.jspreadsheet.isMouseAction == true) {\n        // Get elements\n        const jssTable = getElement(e.target);\n\n        if (jssTable[0]) {\n            // Avoid cross reference\n            if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {\n                if (libraryBase.jspreadsheet.current) {\n                    return false;\n                }\n            }\n\n            let columnId = e.target.getAttribute('data-x');\n            const rowId = e.target.getAttribute('data-y');\n            if (libraryBase.jspreadsheet.current.resizing || libraryBase.jspreadsheet.current.dragging) {\n                // ignore\n            } else {\n                // Header found\n                if (jssTable[1] == 1) {\n                    if (libraryBase.jspreadsheet.current.selectedHeader) {\n                        columnId = e.target.getAttribute('data-x');\n                        const o = libraryBase.jspreadsheet.current.selectedHeader;\n                        const d = columnId;\n                        // Update selection\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, o, 0, d, libraryBase.jspreadsheet.current.options.data.length - 1, e);\n                    }\n                }\n\n                // Body found\n                if (jssTable[1] == 2) {\n                    if (e.target.classList.contains('jss_row')) {\n                        if (libraryBase.jspreadsheet.current.selectedRow != null) {\n                            const o = libraryBase.jspreadsheet.current.selectedRow;\n                            const d = rowId;\n                            // Update selection\n                            updateSelectionFromCoords.call(\n                                libraryBase.jspreadsheet.current,\n                                0,\n                                o,\n                                libraryBase.jspreadsheet.current.options.data[0].length - 1,\n                                d,\n                                e\n                            );\n                        }\n                    } else {\n                        // Do not select edtion is in progress\n                        if (!libraryBase.jspreadsheet.current.edition) {\n                            if (columnId && rowId) {\n                                if (libraryBase.jspreadsheet.current.selectedCorner) {\n                                    updateCopySelection.call(libraryBase.jspreadsheet.current, columnId, rowId);\n                                } else {\n                                    if (libraryBase.jspreadsheet.current.selectedCell) {\n                                        updateSelectionFromCoords.call(\n                                            libraryBase.jspreadsheet.current,\n                                            libraryBase.jspreadsheet.current.selectedCell[0],\n                                            libraryBase.jspreadsheet.current.selectedCell[1],\n                                            columnId,\n                                            rowId,\n                                            e\n                                        );\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    // Clear any time control\n    if (libraryBase.jspreadsheet.timeControl) {\n        clearTimeout(libraryBase.jspreadsheet.timeControl);\n        libraryBase.jspreadsheet.timeControl = null;\n    }\n};\n\nconst doubleClickControls = function (e) {\n    // Jss is selected\n    if (libraryBase.jspreadsheet.current) {\n        // Corner action\n        if (e.target.classList.contains('jss_corner')) {\n            // Any selected cells\n            if (libraryBase.jspreadsheet.current.highlighted.length > 0) {\n                // Copy from this\n                const x1 = libraryBase.jspreadsheet.current.highlighted[0].element.getAttribute('data-x');\n                const y1 =\n                    parseInt(\n                        libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length - 1].element.getAttribute('data-y')\n                    ) + 1;\n                // Until this\n                const x2 = libraryBase.jspreadsheet.current.highlighted[libraryBase.jspreadsheet.current.highlighted.length - 1].element.getAttribute('data-x');\n                const y2 = libraryBase.jspreadsheet.current.records.length - 1;\n                // Execute copy\n                copyData.call(\n                    libraryBase.jspreadsheet.current,\n                    libraryBase.jspreadsheet.current.records[y1][x1].element,\n                    libraryBase.jspreadsheet.current.records[y2][x2].element\n                );\n            }\n        } else if (e.target.classList.contains('jss_column_filter')) {\n            // Column\n            const columnId = e.target.getAttribute('data-x');\n            // Open filter\n            openFilter.call(libraryBase.jspreadsheet.current, columnId);\n        } else {\n            // Get table\n            const jssTable = getElement(e.target);\n\n            // Double click over header\n            if (jssTable[1] == 1 && libraryBase.jspreadsheet.current.options.columnSorting != false) {\n                // Check valid column header coords\n                const columnId = e.target.getAttribute('data-x');\n                if (columnId) {\n                    libraryBase.jspreadsheet.current.orderBy(parseInt(columnId));\n                }\n            }\n\n            // Double click over body\n            if (jssTable[1] == 2 && libraryBase.jspreadsheet.current.options.editable != false) {\n                if (!libraryBase.jspreadsheet.current.edition) {\n                    const getCellCoords = function (element) {\n                        if (element.parentNode) {\n                            const x = element.getAttribute('data-x');\n                            const y = element.getAttribute('data-y');\n                            if (x && y) {\n                                return element;\n                            } else {\n                                return getCellCoords(element.parentNode);\n                            }\n                        }\n                    };\n                    const cell = getCellCoords(e.target);\n                    if (cell && cell.classList.contains('highlight')) {\n                        openEditor.call(libraryBase.jspreadsheet.current, cell, undefined, e);\n                    }\n                }\n            }\n        }\n    }\n};\n\nconst pasteControls = function (e) {\n    if (libraryBase.jspreadsheet.current && libraryBase.jspreadsheet.current.selectedCell) {\n        if (!libraryBase.jspreadsheet.current.edition) {\n            if (libraryBase.jspreadsheet.current.options.editable != false) {\n                if (e && e.clipboardData) {\n                    paste.call(\n                        libraryBase.jspreadsheet.current,\n                        libraryBase.jspreadsheet.current.selectedCell[0],\n                        libraryBase.jspreadsheet.current.selectedCell[1],\n                        e.clipboardData.getData('text')\n                    );\n                    e.preventDefault();\n                } else if (window.clipboardData) {\n                    paste.call(\n                        libraryBase.jspreadsheet.current,\n                        libraryBase.jspreadsheet.current.selectedCell[0],\n                        libraryBase.jspreadsheet.current.selectedCell[1],\n                        window.clipboardData.getData('text')\n                    );\n                }\n            }\n        }\n    }\n};\n\nconst getRole = function (element) {\n    if (element.classList.contains('jss_selectall')) {\n        return 'select-all';\n    }\n\n    if (element.classList.contains('jss_corner')) {\n        return 'fill-handle';\n    }\n\n    let tempElement = element;\n\n    while (!tempElement.classList.contains('jss_spreadsheet')) {\n        if (tempElement.classList.contains('jss_row')) {\n            return 'row';\n        }\n\n        if (tempElement.classList.contains('jss_nested')) {\n            return 'nested';\n        }\n\n        if (tempElement.classList.contains('jtabs-headers')) {\n            return 'tabs';\n        }\n\n        if (tempElement.classList.contains('jtoolbar')) {\n            return 'toolbar';\n        }\n\n        if (tempElement.classList.contains('jss_pagination')) {\n            return 'pagination';\n        }\n\n        if (tempElement.tagName === 'TBODY') {\n            return 'cell';\n        }\n\n        if (tempElement.tagName === 'TFOOT') {\n            return getElementIndex(element) === 0 ? 'grid' : 'footer';\n        }\n\n        if (tempElement.tagName === 'THEAD') {\n            return 'header';\n        }\n\n        tempElement = tempElement.parentElement;\n    }\n\n    return 'applications';\n};\n\nconst defaultContextMenu = function (worksheet, x, y, role) {\n    const items = [];\n\n    if (role === 'header') {\n        // Insert a new column\n        if (worksheet.options.allowInsertColumn != false) {\n            items.push({\n                title: jSuites.translate('Insert a new column before'),\n                onclick: function () {\n                    worksheet.insertColumn(1, parseInt(x), 1);\n                },\n            });\n        }\n\n        if (worksheet.options.allowInsertColumn != false) {\n            items.push({\n                title: jSuites.translate('Insert a new column after'),\n                onclick: function () {\n                    worksheet.insertColumn(1, parseInt(x), 0);\n                },\n            });\n        }\n\n        // Delete a column\n        if (worksheet.options.allowDeleteColumn != false) {\n            items.push({\n                title: jSuites.translate('Delete selected columns'),\n                onclick: function () {\n                    worksheet.deleteColumn(worksheet.getSelectedColumns().length ? undefined : parseInt(x));\n                },\n            });\n        }\n\n        // Rename column\n        if (worksheet.options.allowRenameColumn != false) {\n            items.push({\n                title: jSuites.translate('Rename this column'),\n                onclick: function () {\n                    const oldValue = worksheet.getHeader(x);\n\n                    const newValue = prompt(jSuites.translate('Column name'), oldValue);\n\n                    worksheet.setHeader(x, newValue);\n                },\n            });\n        }\n\n        // Sorting\n        if (worksheet.options.columnSorting != false) {\n            // Line\n            items.push({ type: 'line' });\n\n            items.push({\n                title: jSuites.translate('Order ascending'),\n                onclick: function () {\n                    worksheet.orderBy(x, 0);\n                },\n            });\n            items.push({\n                title: jSuites.translate('Order descending'),\n                onclick: function () {\n                    worksheet.orderBy(x, 1);\n                },\n            });\n        }\n    }\n\n    if (role === 'row' || role === 'cell') {\n        // Insert new row\n        if (worksheet.options.allowInsertRow != false) {\n            items.push({\n                title: jSuites.translate('Insert a new row before'),\n                onclick: function () {\n                    worksheet.insertRow(1, parseInt(y), 1);\n                },\n            });\n\n            items.push({\n                title: jSuites.translate('Insert a new row after'),\n                onclick: function () {\n                    worksheet.insertRow(1, parseInt(y));\n                },\n            });\n        }\n\n        if (worksheet.options.allowDeleteRow != false) {\n            items.push({\n                title: jSuites.translate('Delete selected rows'),\n                onclick: function () {\n                    worksheet.deleteRow(worksheet.getSelectedRows().length ? undefined : parseInt(y));\n                },\n            });\n        }\n    }\n\n    if (role === 'cell') {\n        if (worksheet.options.allowComments != false) {\n            items.push({ type: 'line' });\n\n            const title = worksheet.records[y][x].element.getAttribute('title') || '';\n\n            items.push({\n                title: jSuites.translate(title ? 'Edit comments' : 'Add comments'),\n                onclick: function () {\n                    const comment = prompt(jSuites.translate('Comments'), title);\n                    if (comment) {\n                        worksheet.setComments(getCellNameFromCoords(x, y), comment);\n                    }\n                },\n            });\n\n            if (title) {\n                items.push({\n                    title: jSuites.translate('Clear comments'),\n                    onclick: function () {\n                        worksheet.setComments(getCellNameFromCoords(x, y), '');\n                    },\n                });\n            }\n        }\n    }\n\n    // Line\n    if (items.length !== 0) {\n        items.push({ type: 'line' });\n    }\n\n    // Copy\n    if (role === 'header' || role === 'row' || role === 'cell') {\n        items.push({\n            title: jSuites.translate('Copy') + '...',\n            shortcut: 'Ctrl + C',\n            onclick: function () {\n                copy.call(worksheet, true);\n            },\n        });\n\n        // Paste\n        if (navigator && navigator.clipboard) {\n            items.push({\n                title: jSuites.translate('Paste') + '...',\n                shortcut: 'Ctrl + V',\n                onclick: function () {\n                    if (worksheet.selectedCell) {\n                        navigator.clipboard.readText().then(function (text) {\n                            if (text) {\n                                paste.call(worksheet, worksheet.selectedCell[0], worksheet.selectedCell[1], text);\n                            }\n                        });\n                    }\n                },\n            });\n        }\n    }\n\n    // Save\n    if (worksheet.parent.config.allowExport != false) {\n        items.push({\n            title: jSuites.translate('Save as') + '...',\n            shortcut: 'Ctrl + S',\n            onclick: function () {\n                worksheet.download();\n            },\n        });\n    }\n\n    // About\n    if (worksheet.parent.config.about != false) {\n        items.push({\n            title: jSuites.translate('About'),\n            onclick: function () {\n                if (typeof worksheet.parent.config.about === 'undefined' || worksheet.parent.config.about === true) {\n                    alert(version.print());\n                } else {\n                    alert(worksheet.parent.config.about);\n                }\n            },\n        });\n    }\n\n    return items;\n};\n\nconst getElementIndex = function (element) {\n    const parentChildren = element.parentElement.children;\n\n    for (let i = 0; i < parentChildren.length; i++) {\n        const currentElement = parentChildren[i];\n\n        if (element === currentElement) {\n            return i;\n        }\n    }\n\n    return -1;\n};\n\nconst contextMenuControls = function (e) {\n    e = e || window.event;\n\n    let mouseButton;\n\n    if ('buttons' in e) {\n        mouseButton = e.buttons;\n    } else {\n        mouseButton = e.which || e.button;\n    }\n\n    if (libraryBase.jspreadsheet.current) {\n        const spreadsheet = libraryBase.jspreadsheet.current.parent;\n\n        if (libraryBase.jspreadsheet.current.edition) {\n            e.preventDefault();\n        } else {\n            spreadsheet.contextMenu.contextmenu.close();\n\n            if (libraryBase.jspreadsheet.current) {\n                const role = getRole(e.target);\n\n                let x = null,\n                    y = null;\n\n                if (role === 'cell') {\n                    let cellElement = e.target;\n                    while (cellElement.tagName !== 'TD') {\n                        cellElement = cellElement.parentNode;\n                    }\n\n                    y = cellElement.getAttribute('data-y');\n                    x = cellElement.getAttribute('data-x');\n\n                    if (\n                        !libraryBase.jspreadsheet.current.selectedCell ||\n                        x < parseInt(libraryBase.jspreadsheet.current.selectedCell[0]) ||\n                        x > parseInt(libraryBase.jspreadsheet.current.selectedCell[2]) ||\n                        y < parseInt(libraryBase.jspreadsheet.current.selectedCell[1]) ||\n                        y > parseInt(libraryBase.jspreadsheet.current.selectedCell[3])\n                    ) {\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, x, y, x, y, e);\n                    }\n                } else if (role === 'row' || role === 'header') {\n                    if (role === 'row') {\n                        y = e.target.getAttribute('data-y');\n                    } else {\n                        x = e.target.getAttribute('data-x');\n                    }\n\n                    if (\n                        !libraryBase.jspreadsheet.current.selectedCell ||\n                        x < parseInt(libraryBase.jspreadsheet.current.selectedCell[0]) ||\n                        x > parseInt(libraryBase.jspreadsheet.current.selectedCell[2]) ||\n                        y < parseInt(libraryBase.jspreadsheet.current.selectedCell[1]) ||\n                        y > parseInt(libraryBase.jspreadsheet.current.selectedCell[3])\n                    ) {\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, x, y, x, y, e);\n                    }\n                } else if (role === 'nested') {\n                    const columns = e.target.getAttribute('data-column').split(',');\n\n                    x = getElementIndex(e.target) - 1;\n                    y = getElementIndex(e.target.parentElement);\n\n                    if (\n                        !libraryBase.jspreadsheet.current.selectedCell ||\n                        columns[0] != parseInt(libraryBase.jspreadsheet.current.selectedCell[0]) ||\n                        columns[columns.length - 1] != parseInt(libraryBase.jspreadsheet.current.selectedCell[2]) ||\n                        libraryBase.jspreadsheet.current.selectedCell[1] != null ||\n                        libraryBase.jspreadsheet.current.selectedCell[3] != null\n                    ) {\n                        updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, columns[0], null, columns[columns.length - 1], null, e);\n                    }\n                } else if (role === 'select-all') {\n                    selectAll.call(libraryBase.jspreadsheet.current);\n                } else if (role === 'tabs') {\n                    x = getElementIndex(e.target);\n                } else if (role === 'footer') {\n                    x = getElementIndex(e.target) - 1;\n                    y = getElementIndex(e.target.parentElement);\n                }\n\n                // Table found\n                let items = defaultContextMenu(libraryBase.jspreadsheet.current, parseInt(x), parseInt(y), role);\n\n                if (typeof spreadsheet.config.contextMenu === 'function') {\n                    const result = spreadsheet.config.contextMenu(libraryBase.jspreadsheet.current, x, y, e, items, role, x, y);\n\n                    if (result) {\n                        items = result;\n                    } else if (result === false) {\n                        return;\n                    }\n                }\n\n                if (typeof spreadsheet.plugins === 'object') {\n                    Object.entries(spreadsheet.plugins).forEach(function ([, plugin]) {\n                        if (typeof plugin.contextMenu === 'function') {\n                            const result = plugin.contextMenu(\n                                libraryBase.jspreadsheet.current,\n                                x !== null ? parseInt(x) : null,\n                                y !== null ? parseInt(y) : null,\n                                e,\n                                items,\n                                role,\n                                x !== null ? parseInt(x) : null,\n                                y !== null ? parseInt(y) : null\n                            );\n\n                            if (result) {\n                                items = result;\n                            }\n                        }\n                    });\n                }\n\n                // The id is depending on header and body\n                spreadsheet.contextMenu.contextmenu.open(e, items);\n                // Avoid the real one\n                e.preventDefault();\n            }\n        }\n    }\n};\n\nconst touchStartControls = function (e) {\n    const jssTable = getElement(e.target);\n\n    if (jssTable[0]) {\n        if (libraryBase.jspreadsheet.current != jssTable[0].jssWorksheet) {\n            if (libraryBase.jspreadsheet.current) {\n                libraryBase.jspreadsheet.current.resetSelection();\n            }\n            libraryBase.jspreadsheet.current = jssTable[0].jssWorksheet;\n        }\n    } else {\n        if (libraryBase.jspreadsheet.current) {\n            libraryBase.jspreadsheet.current.resetSelection();\n            libraryBase.jspreadsheet.current = null;\n        }\n    }\n\n    if (libraryBase.jspreadsheet.current) {\n        if (!libraryBase.jspreadsheet.current.edition) {\n            const columnId = e.target.getAttribute('data-x');\n            const rowId = e.target.getAttribute('data-y');\n\n            if (columnId && rowId) {\n                updateSelectionFromCoords.call(libraryBase.jspreadsheet.current, columnId, rowId, undefined, undefined, e);\n\n                libraryBase.jspreadsheet.timeControl = setTimeout(function () {\n                    // Keep temporary reference to the element\n                    if (libraryBase.jspreadsheet.current.options.columns[columnId].type == 'color') {\n                        libraryBase.jspreadsheet.tmpElement = null;\n                    } else {\n                        libraryBase.jspreadsheet.tmpElement = e.target;\n                    }\n                    openEditor.call(libraryBase.jspreadsheet.current, e.target, false, e);\n                }, 500);\n            }\n        }\n    }\n};\n\nconst touchEndControls = function (e) {\n    // Clear any time control\n    if (libraryBase.jspreadsheet.timeControl) {\n        clearTimeout(libraryBase.jspreadsheet.timeControl);\n        libraryBase.jspreadsheet.timeControl = null;\n        // Element\n        if (libraryBase.jspreadsheet.tmpElement && libraryBase.jspreadsheet.tmpElement.children[0].tagName == 'INPUT') {\n            libraryBase.jspreadsheet.tmpElement.children[0].focus();\n        }\n        libraryBase.jspreadsheet.tmpElement = null;\n    }\n};\n\nexport const cutControls = function (e) {\n    if (libraryBase.jspreadsheet.current) {\n        if (!libraryBase.jspreadsheet.current.edition) {\n            copy.call(libraryBase.jspreadsheet.current, true, undefined, undefined, undefined, undefined, true);\n            if (libraryBase.jspreadsheet.current.options.editable != false) {\n                libraryBase.jspreadsheet.current.setValue(\n                    libraryBase.jspreadsheet.current.highlighted.map(function (record) {\n                        return record.element;\n                    }),\n                    ''\n                );\n            }\n        }\n    }\n};\n\nconst copyControls = function (e) {\n    if (libraryBase.jspreadsheet.current && copyControls.enabled) {\n        if (!libraryBase.jspreadsheet.current.edition) {\n            copy.call(libraryBase.jspreadsheet.current, true);\n        }\n    }\n};\n\nconst isMac = function () {\n    return navigator.platform.toUpperCase().indexOf('MAC') >= 0;\n};\n\nconst isCtrl = function (e) {\n    if (isMac()) {\n        return e.metaKey;\n    } else {\n        return e.ctrlKey;\n    }\n};\n\nconst keyDownControls = function (e) {\n    if (libraryBase.jspreadsheet.current) {\n        if (libraryBase.jspreadsheet.current.edition) {\n            if (e.which == 27) {\n                // Escape\n                if (libraryBase.jspreadsheet.current.edition) {\n                    // Exit without saving\n                    closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], false);\n                }\n                e.preventDefault();\n            } else if (e.which == 13) {\n                // Enter\n                if (\n                    libraryBase.jspreadsheet.current.options.columns &&\n                    libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&\n                    libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type == 'calendar'\n                ) {\n                    closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n                } else if (\n                    libraryBase.jspreadsheet.current.options.columns &&\n                    libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&\n                    libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type == 'dropdown'\n                ) {\n                    // Do nothing\n                } else {\n                    // Alt enter -> do not close editor\n                    if (\n                        (libraryBase.jspreadsheet.current.options.wordWrap == true ||\n                            (libraryBase.jspreadsheet.current.options.columns &&\n                                libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&\n                                libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].wordWrap == true) ||\n                            (libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][\n                                libraryBase.jspreadsheet.current.edition[2]\n                            ] &&\n                                libraryBase.jspreadsheet.current.options.data[libraryBase.jspreadsheet.current.edition[3]][\n                                    libraryBase.jspreadsheet.current.edition[2]\n                                ].length > 200)) &&\n                        e.altKey\n                    ) {\n                        // Add new line to the editor\n                        const editorTextarea = libraryBase.jspreadsheet.current.edition[0].children[0];\n                        let editorValue = libraryBase.jspreadsheet.current.edition[0].children[0].value;\n                        const editorIndexOf = editorTextarea.selectionStart;\n                        editorValue = editorValue.slice(0, editorIndexOf) + '\\n' + editorValue.slice(editorIndexOf);\n                        editorTextarea.value = editorValue;\n                        editorTextarea.focus();\n                        editorTextarea.selectionStart = editorIndexOf + 1;\n                        editorTextarea.selectionEnd = editorIndexOf + 1;\n                    } else {\n                        libraryBase.jspreadsheet.current.edition[0].children[0].blur();\n                    }\n                }\n            } else if (e.which == 9) {\n                // Tab\n                if (\n                    libraryBase.jspreadsheet.current.options.columns &&\n                    libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]] &&\n                    ['calendar', 'html'].includes(libraryBase.jspreadsheet.current.options.columns[libraryBase.jspreadsheet.current.edition[2]].type)\n                ) {\n                    closeEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.edition[0], true);\n                } else {\n                    libraryBase.jspreadsheet.current.edition[0].children[0].blur();\n                }\n            }\n        }\n\n        if (!libraryBase.jspreadsheet.current.edition && libraryBase.jspreadsheet.current.selectedCell) {\n            // Which key\n            if (e.which == 37) {\n                left.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 39) {\n                right.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 38) {\n                up.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 40) {\n                down.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 36) {\n                first.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 35) {\n                last.call(libraryBase.jspreadsheet.current, e.shiftKey, e.ctrlKey);\n                e.preventDefault();\n            } else if (e.which == 46 || e.which == 8) {\n                // Delete\n                if (libraryBase.jspreadsheet.current.options.editable != false) {\n                    if (libraryBase.jspreadsheet.current.selectedRow != null) {\n                        if (libraryBase.jspreadsheet.current.options.allowDeleteRow != false) {\n                            if (confirm(jSuites.translate('Are you sure to delete the selected rows?'))) {\n                                libraryBase.jspreadsheet.current.deleteRow();\n                            }\n                        }\n                    } else if (libraryBase.jspreadsheet.current.selectedHeader) {\n                        if (libraryBase.jspreadsheet.current.options.allowDeleteColumn != false) {\n                            if (confirm(jSuites.translate('Are you sure to delete the selected columns?'))) {\n                                libraryBase.jspreadsheet.current.deleteColumn();\n                            }\n                        }\n                    } else {\n                        // Change value\n                        libraryBase.jspreadsheet.current.setValue(\n                            libraryBase.jspreadsheet.current.highlighted.map(function (record) {\n                                return record.element;\n                            }),\n                            ''\n                        );\n                    }\n                }\n            } else if (e.which == 13) {\n                // Move cursor\n                if (e.shiftKey) {\n                    up.call(libraryBase.jspreadsheet.current);\n                } else {\n                    if (libraryBase.jspreadsheet.current.options.allowInsertRow != false) {\n                        if (libraryBase.jspreadsheet.current.options.allowManualInsertRow != false) {\n                            if (libraryBase.jspreadsheet.current.selectedCell[1] == libraryBase.jspreadsheet.current.options.data.length - 1) {\n                                // New record in case selectedCell in the last row\n                                libraryBase.jspreadsheet.current.insertRow();\n                            }\n                        }\n                    }\n\n                    down.call(libraryBase.jspreadsheet.current);\n                }\n                e.preventDefault();\n            } else if (e.which == 9) {\n                // Tab\n                if (e.shiftKey) {\n                    left.call(libraryBase.jspreadsheet.current);\n                } else {\n                    if (libraryBase.jspreadsheet.current.options.allowInsertColumn != false) {\n                        if (libraryBase.jspreadsheet.current.options.allowManualInsertColumn != false) {\n                            if (libraryBase.jspreadsheet.current.selectedCell[0] == libraryBase.jspreadsheet.current.options.data[0].length - 1) {\n                                // New record in case selectedCell in the last column\n                                libraryBase.jspreadsheet.current.insertColumn();\n                            }\n                        }\n                    }\n\n                    right.call(libraryBase.jspreadsheet.current);\n                }\n                e.preventDefault();\n            } else {\n                if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {\n                    if (e.which == 65) {\n                        // Ctrl + A\n                        selectAll.call(libraryBase.jspreadsheet.current);\n                        e.preventDefault();\n                    } else if (e.which == 83) {\n                        // Ctrl + S\n                        libraryBase.jspreadsheet.current.download();\n                        e.preventDefault();\n                    } else if (e.which == 89) {\n                        // Ctrl + Y\n                        libraryBase.jspreadsheet.current.redo();\n                        e.preventDefault();\n                    } else if (e.which == 90) {\n                        // Ctrl + Z\n                        libraryBase.jspreadsheet.current.undo();\n                        e.preventDefault();\n                    } else if (e.which == 67) {\n                        // Ctrl + C\n                        copy.call(libraryBase.jspreadsheet.current, true);\n                        e.preventDefault();\n                    } else if (e.which == 88) {\n                        // Ctrl + X\n                        if (libraryBase.jspreadsheet.current.options.editable != false) {\n                            cutControls();\n                        } else {\n                            copyControls();\n                        }\n                        e.preventDefault();\n                    } else if (e.which == 86) {\n                        // Ctrl + V\n                        pasteControls();\n                    }\n                } else {\n                    if (libraryBase.jspreadsheet.current.selectedCell) {\n                        if (libraryBase.jspreadsheet.current.options.editable != false) {\n                            const rowId = libraryBase.jspreadsheet.current.selectedCell[1];\n                            const columnId = libraryBase.jspreadsheet.current.selectedCell[0];\n\n                            // Characters able to start a edition\n                            if (e.keyCode == 32) {\n                                // Space\n                                e.preventDefault();\n                                if (\n                                    libraryBase.jspreadsheet.current.options.columns[columnId].type == 'checkbox' ||\n                                    libraryBase.jspreadsheet.current.options.columns[columnId].type == 'radio'\n                                ) {\n                                    setCheckRadioValue.call(libraryBase.jspreadsheet.current);\n                                } else {\n                                    // Start edition\n                                    openEditor.call(\n                                        libraryBase.jspreadsheet.current,\n                                        libraryBase.jspreadsheet.current.records[rowId][columnId].element,\n                                        true,\n                                        e\n                                    );\n                                }\n                            } else if (e.keyCode == 113) {\n                                // Start edition with current content F2\n                                openEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[rowId][columnId].element, false, e);\n                            } else if ((e.key.length === 1 || e.key === 'Process') && !(e.altKey || isCtrl(e))) {\n                                // Start edition\n                                openEditor.call(libraryBase.jspreadsheet.current, libraryBase.jspreadsheet.current.records[rowId][columnId].element, true, e);\n                                // Prevent entries in the calendar\n                                if (\n                                    libraryBase.jspreadsheet.current.options.columns &&\n                                    libraryBase.jspreadsheet.current.options.columns[columnId] &&\n                                    libraryBase.jspreadsheet.current.options.columns[columnId].type == 'calendar'\n                                ) {\n                                    e.preventDefault();\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        } else {\n            if (e.target.classList.contains('jss_search')) {\n                if (libraryBase.jspreadsheet.timeControl) {\n                    clearTimeout(libraryBase.jspreadsheet.timeControl);\n                }\n\n                libraryBase.jspreadsheet.timeControl = setTimeout(function () {\n                    libraryBase.jspreadsheet.current.search(e.target.value);\n                }, 200);\n            }\n        }\n    }\n};\n\nexport const wheelControls = function (e) {\n    const obj = this;\n\n    if (obj.options.lazyLoading == true) {\n        if (libraryBase.jspreadsheet.timeControlLoading == null) {\n            libraryBase.jspreadsheet.timeControlLoading = setTimeout(function () {\n                if (obj.content.scrollTop + obj.content.clientHeight >= obj.content.scrollHeight - 10) {\n                    if (loadDown.call(obj)) {\n                        if (obj.content.scrollTop + obj.content.clientHeight > obj.content.scrollHeight - 10) {\n                            obj.content.scrollTop = obj.content.scrollTop - obj.content.clientHeight;\n                        }\n                        updateCornerPosition.call(obj);\n                    }\n                } else if (obj.content.scrollTop <= obj.content.clientHeight) {\n                    if (loadUp.call(obj)) {\n                        if (obj.content.scrollTop < 10) {\n                            obj.content.scrollTop = obj.content.scrollTop + obj.content.clientHeight;\n                        }\n                        updateCornerPosition.call(obj);\n                    }\n                }\n\n                libraryBase.jspreadsheet.timeControlLoading = null;\n            }, 100);\n        }\n    }\n};\n\nlet scrollLeft = 0;\n\nconst updateFreezePosition = function () {\n    const obj = this;\n\n    scrollLeft = obj.content.scrollLeft;\n    let width = 0;\n    if (scrollLeft > 50) {\n        for (let i = 0; i < obj.options.freezeColumns; i++) {\n            if (i > 0) {\n                // Must check if the previous column is hidden or not to determin whether the width shoule be added or not!\n                if (!obj.options.columns || !obj.options.columns[i - 1] || obj.options.columns[i - 1].type !== 'hidden') {\n                    let columnWidth;\n                    if (obj.options.columns && obj.options.columns[i - 1] && obj.options.columns[i - 1].width !== undefined) {\n                        columnWidth = parseInt(obj.options.columns[i - 1].width);\n                    } else {\n                        columnWidth = obj.options.defaultColWidth !== undefined ? parseInt(obj.options.defaultColWidth) : 100;\n                    }\n\n                    width += parseInt(columnWidth);\n                }\n            }\n            obj.headers[i].classList.add('jss_freezed');\n            obj.headers[i].style.left = width + 'px';\n            for (let j = 0; j < obj.rows.length; j++) {\n                if (obj.rows[j] && obj.records[j][i]) {\n                    const shifted = scrollLeft + (i > 0 ? obj.records[j][i - 1].element.style.width : 0) - 51 + 'px';\n                    obj.records[j][i].element.classList.add('jss_freezed');\n                    obj.records[j][i].element.style.left = shifted;\n                }\n            }\n        }\n    } else {\n        for (let i = 0; i < obj.options.freezeColumns; i++) {\n            obj.headers[i].classList.remove('jss_freezed');\n            obj.headers[i].style.left = '';\n            for (let j = 0; j < obj.rows.length; j++) {\n                if (obj.records[j][i]) {\n                    obj.records[j][i].element.classList.remove('jss_freezed');\n                    obj.records[j][i].element.style.left = '';\n                }\n            }\n        }\n    }\n\n    // Place the corner in the correct place\n    updateCornerPosition.call(obj);\n};\n\nexport const scrollControls = function (e) {\n    const obj = this;\n\n    wheelControls.call(obj);\n\n    if (obj.options.freezeColumns > 0 && obj.content.scrollLeft != scrollLeft) {\n        updateFreezePosition.call(obj);\n    }\n\n    // Close editor\n    if (obj.options.lazyLoading == true || obj.options.tableOverflow == true) {\n        if (obj.edition && e.target.className.substr(0, 9) != 'jdropdown') {\n            closeEditor.call(obj, obj.edition[0], true);\n        }\n    }\n};\n\nexport const setEvents = function (root) {\n    destroyEvents(root);\n    root.addEventListener('mouseup', mouseUpControls);\n    root.addEventListener('mousedown', mouseDownControls);\n    root.addEventListener('mousemove', mouseMoveControls);\n    root.addEventListener('mouseover', mouseOverControls);\n    root.addEventListener('dblclick', doubleClickControls);\n    root.addEventListener('paste', pasteControls);\n    root.addEventListener('contextmenu', contextMenuControls);\n    root.addEventListener('touchstart', touchStartControls);\n    root.addEventListener('touchend', touchEndControls);\n    root.addEventListener('touchcancel', touchEndControls);\n    root.addEventListener('touchmove', touchEndControls);\n    document.addEventListener('keydown', keyDownControls);\n};\n\nexport const destroyEvents = function (root) {\n    root.removeEventListener('mouseup', mouseUpControls);\n    root.removeEventListener('mousedown', mouseDownControls);\n    root.removeEventListener('mousemove', mouseMoveControls);\n    root.removeEventListener('mouseover', mouseOverControls);\n    root.removeEventListener('dblclick', doubleClickControls);\n    root.removeEventListener('paste', pasteControls);\n    root.removeEventListener('contextmenu', contextMenuControls);\n    root.removeEventListener('touchstart', touchStartControls);\n    root.removeEventListener('touchend', touchEndControls);\n    root.removeEventListener('touchcancel', touchEndControls);\n    document.removeEventListener('keydown', keyDownControls);\n};\n"
  },
  {
    "path": "src/utils/factory.js",
    "content": "import jSuites from 'jsuites';\n\nimport libraryBase from './libraryBase.js';\nimport { setEvents } from './events.js';\nimport { fullscreen, getWorksheetActive } from './internal.js';\nimport { hideToolbar, showToolbar, updateToolbar } from './toolbar.js';\nimport { buildWorksheet, createWorksheetObj, getNextDefaultWorksheetName } from './worksheets.js';\nimport dispatch from './dispatch.js';\nimport { createFromTable } from './helpers.js';\nimport { getSpreadsheetConfig, setConfig } from './config.js';\n\nconst factory = function () {};\n\nconst createWorksheets = async function (spreadsheet, options, el) {\n    // Create worksheets\n    let o = options.worksheets;\n    if (o) {\n        let tabsOptions = {\n            animation: true,\n            onbeforecreate: function (element, title) {\n                if (title) {\n                    return title;\n                } else {\n                    return getNextDefaultWorksheetName(spreadsheet);\n                }\n            },\n            oncreate: function (element, newTabContent) {\n                if (!spreadsheet.creationThroughJss) {\n                    const worksheetName = element.tabs.headers.children[element.tabs.headers.children.length - 2].innerHTML;\n\n                    createWorksheetObj.call(spreadsheet.worksheets[0], {\n                        minDimensions: [10, 15],\n                        worksheetName: worksheetName,\n                    });\n                } else {\n                    spreadsheet.creationThroughJss = false;\n                }\n\n                const newWorksheet = spreadsheet.worksheets[spreadsheet.worksheets.length - 1];\n\n                newWorksheet.element = newTabContent;\n\n                buildWorksheet.call(newWorksheet).then(function () {\n                    updateToolbar(newWorksheet);\n\n                    dispatch.call(newWorksheet, 'oncreateworksheet', newWorksheet, options, spreadsheet.worksheets.length - 1);\n                });\n            },\n            onchange: function (element, instance, tabIndex) {\n                if (spreadsheet.worksheets.length != 0 && spreadsheet.worksheets[tabIndex]) {\n                    updateToolbar(spreadsheet.worksheets[tabIndex]);\n                }\n            },\n        };\n\n        if (options.tabs == true) {\n            tabsOptions.allowCreate = true;\n        } else {\n            tabsOptions.hideHeaders = true;\n        }\n\n        tabsOptions.data = [];\n\n        let sheetNumber = 1;\n\n        for (let i = 0; i < o.length; i++) {\n            if (!o[i].worksheetName) {\n                o[i].worksheetName = 'Sheet' + sheetNumber++;\n            }\n\n            tabsOptions.data.push({\n                title: o[i].worksheetName,\n                content: '',\n            });\n        }\n\n        el.classList.add('jss_spreadsheet');\n        el.tabIndex = 0;\n\n        const tabs = jSuites.tabs(el, tabsOptions);\n\n        const spreadsheetStyles = options.style;\n        delete options.style;\n\n        for (let i = 0; i < o.length; i++) {\n            if (o[i].style) {\n                Object.entries(o[i].style).forEach(function ([cellName, value]) {\n                    if (typeof value === 'number') {\n                        o[i].style[cellName] = spreadsheetStyles[value];\n                    }\n                });\n            }\n\n            spreadsheet.worksheets.push({\n                parent: spreadsheet,\n                element: tabs.content.children[i],\n                options: o[i],\n                filters: [],\n                formula: [],\n                history: [],\n                selection: [],\n                historyIndex: -1,\n            });\n\n            await buildWorksheet.call(spreadsheet.worksheets[i]);\n        }\n    } else {\n        throw new Error('JSS: worksheets are not defined');\n    }\n};\n\nfactory.spreadsheet = async function (el, options, worksheets) {\n    if (el.tagName == 'TABLE') {\n        if (!options) {\n            options = {};\n        }\n\n        if (!options.worksheets) {\n            options.worksheets = [];\n        }\n\n        const tableOptions = createFromTable(el, options.worksheets[0]);\n\n        options.worksheets[0] = tableOptions;\n\n        const div = document.createElement('div');\n        el.parentNode.insertBefore(div, el);\n        el.remove();\n        el = div;\n    }\n\n    let spreadsheet = {\n        worksheets: worksheets,\n        config: options,\n        element: el,\n        el,\n    };\n\n    // Contextmenu container\n    spreadsheet.contextMenu = document.createElement('div');\n    spreadsheet.contextMenu.className = 'jss_contextmenu';\n\n    spreadsheet.getWorksheetActive = getWorksheetActive.bind(spreadsheet);\n    spreadsheet.fullscreen = fullscreen.bind(spreadsheet);\n    spreadsheet.showToolbar = showToolbar.bind(spreadsheet);\n    spreadsheet.hideToolbar = hideToolbar.bind(spreadsheet);\n    spreadsheet.getConfig = getSpreadsheetConfig.bind(spreadsheet);\n    spreadsheet.setConfig = setConfig.bind(spreadsheet);\n\n    spreadsheet.setPlugins = function (newPlugins) {\n        if (!spreadsheet.plugins) {\n            spreadsheet.plugins = {};\n        }\n\n        if (typeof newPlugins == 'object') {\n            Object.entries(newPlugins).forEach(function ([pluginName, plugin]) {\n                spreadsheet.plugins[pluginName] = plugin.call(libraryBase.jspreadsheet, spreadsheet, {}, spreadsheet.config);\n            });\n        }\n    };\n\n    spreadsheet.setPlugins(options.plugins);\n\n    // Create as worksheets\n    await createWorksheets(spreadsheet, options, el);\n\n    spreadsheet.element.appendChild(spreadsheet.contextMenu);\n\n    // Create element\n    jSuites.contextmenu(spreadsheet.contextMenu, {\n        onclick: function () {\n            spreadsheet.contextMenu.contextmenu.close(false);\n        },\n    });\n\n    // Fullscreen\n    if (spreadsheet.config.fullscreen == true) {\n        spreadsheet.element.classList.add('fullscreen');\n    }\n\n    showToolbar.call(spreadsheet);\n\n    // Build handlers\n    if (options.root) {\n        setEvents(options.root);\n    } else {\n        setEvents(document);\n    }\n\n    el.spreadsheet = spreadsheet;\n\n    return spreadsheet;\n};\n\nfactory.worksheet = function (spreadsheet, options, position) {\n    // Worksheet object\n    let w = {\n        // Parent of a worksheet is always the spreadsheet\n        parent: spreadsheet,\n        // Options for this worksheet\n        options: {},\n    };\n\n    // Create the worksheets object\n    if (typeof position === 'undefined') {\n        spreadsheet.worksheets.push(w);\n    } else {\n        spreadsheet.worksheets.splice(position, 0, w);\n    }\n    // Keep configuration used\n    Object.assign(w.options, options);\n\n    return w;\n};\n\nexport default factory;\n"
  },
  {
    "path": "src/utils/filter.js",
    "content": "import jSuites from 'jsuites';\nimport { updateResult } from './internal.js';\nimport { refreshSelection } from './selection.js';\n\n/**\n * Open the column filter\n */\nexport const openFilter = function (columnId) {\n    const obj = this;\n\n    if (!obj.options.filters) {\n        console.log('Jspreadsheet: filters not enabled.');\n    } else {\n        // Make sure is integer\n        columnId = parseInt(columnId);\n        // Reset selection\n        obj.resetSelection();\n        // Load options\n        let optionsFiltered = [];\n        if (obj.options.columns[columnId].type == 'checkbox') {\n            optionsFiltered.push({ id: 'true', name: 'True' });\n            optionsFiltered.push({ id: 'false', name: 'False' });\n        } else {\n            const options = [];\n            let hasBlanks = false;\n            for (let j = 0; j < obj.options.data.length; j++) {\n                const k = obj.options.data[j][columnId];\n                const v = obj.records[j][columnId].element.innerHTML;\n                if (k && v) {\n                    options[k] = v;\n                } else {\n                    hasBlanks = true;\n                }\n            }\n            const keys = Object.keys(options);\n            optionsFiltered = [];\n            for (let j = 0; j < keys.length; j++) {\n                optionsFiltered.push({ id: keys[j], name: options[keys[j]] });\n            }\n            // Has blank options\n            if (hasBlanks) {\n                optionsFiltered.push({ value: '', id: '', name: '(Blanks)' });\n            }\n        }\n\n        // Create dropdown\n        const div = document.createElement('div');\n        obj.filter.children[columnId + 1].innerHTML = '';\n        obj.filter.children[columnId + 1].appendChild(div);\n        obj.filter.children[columnId + 1].style.paddingLeft = '0px';\n        obj.filter.children[columnId + 1].style.paddingRight = '0px';\n        obj.filter.children[columnId + 1].style.overflow = 'initial';\n\n        const opt = {\n            data: optionsFiltered,\n            multiple: true,\n            autocomplete: true,\n            opened: true,\n            value: obj.filters[columnId] !== undefined ? obj.filters[columnId] : null,\n            width: '100%',\n            position: obj.options.tableOverflow == true || obj.parent.config.fullscreen == true ? true : false,\n            onclose: function (o) {\n                resetFilters.call(obj);\n                obj.filters[columnId] = o.dropdown.getValue(true);\n                obj.filter.children[columnId + 1].innerHTML = o.dropdown.getText();\n                obj.filter.children[columnId + 1].style.paddingLeft = '';\n                obj.filter.children[columnId + 1].style.paddingRight = '';\n                obj.filter.children[columnId + 1].style.overflow = '';\n                closeFilter.call(obj, columnId);\n                refreshSelection.call(obj);\n            },\n        };\n\n        // Dynamic dropdown\n        jSuites.dropdown(div, opt);\n    }\n};\n\nexport const closeFilter = function (columnId) {\n    const obj = this;\n\n    if (!columnId) {\n        for (let i = 0; i < obj.filter.children.length; i++) {\n            if (obj.filters[i]) {\n                columnId = i;\n            }\n        }\n    }\n\n    // Search filter\n    const search = function (query, x, y) {\n        for (let i = 0; i < query.length; i++) {\n            const value = '' + obj.options.data[y][x];\n            const label = '' + obj.records[y][x].element.innerHTML;\n            if (query[i] == value || query[i] == label) {\n                return true;\n            }\n        }\n        return false;\n    };\n\n    const query = obj.filters[columnId];\n    obj.results = [];\n    for (let j = 0; j < obj.options.data.length; j++) {\n        if (search(query, columnId, j)) {\n            obj.results.push(j);\n        }\n    }\n    if (!obj.results.length) {\n        obj.results = null;\n    }\n\n    updateResult.call(obj);\n};\n\nexport const resetFilters = function () {\n    const obj = this;\n\n    if (obj.options.filters) {\n        for (let i = 0; i < obj.filter.children.length; i++) {\n            obj.filter.children[i].innerHTML = '&nbsp;';\n            obj.filters[i] = null;\n        }\n    }\n\n    obj.results = null;\n    updateResult.call(obj);\n};\n"
  },
  {
    "path": "src/utils/footer.js",
    "content": "import { parseValue } from './internal.js';\n\nexport const setFooter = function (data) {\n    const obj = this;\n\n    if (data) {\n        obj.options.footers = data;\n    }\n\n    if (obj.options.footers) {\n        if (!obj.tfoot) {\n            obj.tfoot = document.createElement('tfoot');\n            obj.table.appendChild(obj.tfoot);\n        }\n\n        for (let j = 0; j < obj.options.footers.length; j++) {\n            let tr;\n\n            if (obj.tfoot.children[j]) {\n                tr = obj.tfoot.children[j];\n            } else {\n                tr = document.createElement('tr');\n                const td = document.createElement('td');\n                tr.appendChild(td);\n                obj.tfoot.appendChild(tr);\n            }\n            for (let i = 0; i < obj.headers.length; i++) {\n                if (!obj.options.footers[j][i]) {\n                    obj.options.footers[j][i] = '';\n                }\n\n                let td;\n\n                if (obj.tfoot.children[j].children[i + 1]) {\n                    td = obj.tfoot.children[j].children[i + 1];\n                } else {\n                    td = document.createElement('td');\n                    tr.appendChild(td);\n\n                    // Text align\n                    const colAlign = obj.options.columns[i].align || obj.options.defaultColAlign || 'center';\n                    td.style.textAlign = colAlign;\n                }\n                td.textContent = parseValue.call(obj, +obj.records.length + i, j, obj.options.footers[j][i]);\n\n                // Hide/Show with hideColumn()/showColumn()\n                td.style.display = obj.cols[i].colElement.style.display;\n            }\n        }\n    }\n};\n"
  },
  {
    "path": "src/utils/freeze.js",
    "content": "// Get width of all freezed cells together\nexport const getFreezeWidth = function () {\n    const obj = this;\n\n    let width = 0;\n    if (obj.options.freezeColumns > 0) {\n        for (let i = 0; i < obj.options.freezeColumns; i++) {\n            let columnWidth;\n            if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].width !== undefined) {\n                columnWidth = parseInt(obj.options.columns[i].width);\n            } else {\n                columnWidth = obj.options.defaultColWidth !== undefined ? parseInt(obj.options.defaultColWidth) : 100;\n            }\n\n            width += columnWidth;\n        }\n    }\n    return width;\n};\n"
  },
  {
    "path": "src/utils/headers.js",
    "content": "import { setHistory } from './history.js';\nimport dispatch from './dispatch.js';\nimport { getColumnName } from './helpers.js';\n\n/**\n * Get the column title\n *\n * @param column - column number (first column is: 0)\n * @param title - new column title\n */\nexport const getHeader = function (column) {\n    const obj = this;\n\n    return obj.headers[column].textContent;\n};\n\n/**\n * Get the headers\n *\n * @param asArray\n * @return mixed\n */\nexport const getHeaders = function (asArray) {\n    const obj = this;\n\n    const title = [];\n\n    for (let i = 0; i < obj.headers.length; i++) {\n        title.push(obj.getHeader(i));\n    }\n\n    return asArray ? title : title.join(obj.options.csvDelimiter);\n};\n\n/**\n * Set the column title\n *\n * @param column - column number (first column is: 0)\n * @param title - new column title\n */\nexport const setHeader = function (column, newValue) {\n    const obj = this;\n\n    if (obj.headers[column]) {\n        const oldValue = obj.headers[column].textContent;\n        const onchangeheaderOldValue = (obj.options.columns && obj.options.columns[column] && obj.options.columns[column].title) || '';\n\n        if (!newValue) {\n            newValue = getColumnName(column);\n        }\n\n        obj.headers[column].textContent = newValue;\n        // Keep the title property\n        obj.headers[column].setAttribute('title', newValue);\n        // Update title\n        if (!obj.options.columns) {\n            obj.options.columns = [];\n        }\n        if (!obj.options.columns[column]) {\n            obj.options.columns[column] = {};\n        }\n        obj.options.columns[column].title = newValue;\n\n        setHistory.call(obj, {\n            action: 'setHeader',\n            column: column,\n            oldValue: oldValue,\n            newValue: newValue,\n        });\n\n        // On onchange header\n        dispatch.call(obj, 'onchangeheader', obj, parseInt(column), newValue, onchangeheaderOldValue);\n    }\n};\n"
  },
  {
    "path": "src/utils/helpers.js",
    "content": "import { getColumnNameFromId } from './internalHelpers.js';\n\n/**\n * Get carret position for one element\n */\nexport const getCaretIndex = function (e) {\n    let d;\n\n    if (this.config.root) {\n        d = this.config.root;\n    } else {\n        d = window;\n    }\n    let pos = 0;\n    const s = d.getSelection();\n    if (s) {\n        if (s.rangeCount !== 0) {\n            const r = s.getRangeAt(0);\n            const p = r.cloneRange();\n            p.selectNodeContents(e);\n            p.setEnd(r.endContainer, r.endOffset);\n            pos = p.toString().length;\n        }\n    }\n    return pos;\n};\n\n/**\n * Invert keys and values\n */\nexport const invert = function (o) {\n    const d = [];\n    const k = Object.keys(o);\n    for (let i = 0; i < k.length; i++) {\n        d[o[k[i]]] = k[i];\n    }\n    return d;\n};\n\n/**\n * Get letter based on a number\n *\n * @param {number} columnNumber\n * @return string letter\n */\nexport const getColumnName = function (columnNumber) {\n    let dividend = columnNumber + 1;\n    let columnName = '';\n    let modulo;\n\n    while (dividend > 0) {\n        modulo = (dividend - 1) % 26;\n        columnName = String.fromCharCode(65 + modulo).toString() + columnName;\n        dividend = parseInt((dividend - modulo) / 26);\n    }\n\n    return columnName;\n};\n\n/**\n * Get column name from coords\n */\nexport const getCellNameFromCoords = function (x, y) {\n    return getColumnName(parseInt(x)) + (parseInt(y) + 1);\n};\n\nexport const getCoordsFromCellName = function (columnName) {\n    // Get the letters\n    const t = /^[a-zA-Z]+/.exec(columnName);\n\n    if (t) {\n        // Base 26 calculation\n        let code = 0;\n        for (let i = 0; i < t[0].length; i++) {\n            code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, t[0].length - 1 - i);\n        }\n        code--;\n        // Make sure jspreadsheet starts on zero\n        if (code < 0) {\n            code = 0;\n        }\n\n        // Number\n        let number = parseInt(/[0-9]+$/.exec(columnName)) || null;\n        if (number > 0) {\n            number--;\n        }\n\n        return [code, number];\n    }\n};\n\nexport const getCoordsFromRange = function (range) {\n    const [start, end] = range.split(':');\n\n    return [...getCoordsFromCellName(start), ...getCoordsFromCellName(end)];\n};\n\n/**\n * From stack overflow contributions\n */\nexport const parseCSV = function (str, delimiter) {\n    // user-supplied delimeter or default comma\n    delimiter = delimiter || ',';\n    // Remove last line break\n    str = str.replace(/\\r?\\n$|\\r$|\\n$/g, '');\n\n    const arr = [];\n    let quote = false; // true means we're inside a quoted field\n    // iterate over each character, keep track of current row and column (of the returned array)\n    let maxCol = 0;\n    let row = 0,\n        col = 0;\n    for (let c = 0; c < str.length; c++) {\n        const cc = str[c],\n            nc = str[c + 1];\n        arr[row] = arr[row] || [];\n        arr[row][col] = arr[row][col] || '';\n\n        // If the current character is a quotation mark, and we're inside a quoted field, and the next character is also a quotation mark, add a quotation mark to the current column and skip the next character\n        if (cc == '\"' && quote && nc == '\"') {\n            arr[row][col] += cc;\n            ++c;\n            continue;\n        }\n\n        // If it's just one quotation mark, begin/end quoted field\n        if (cc == '\"') {\n            quote = !quote;\n            continue;\n        }\n\n        // If it's a comma and we're not in a quoted field, move on to the next column\n        if (cc == delimiter && !quote) {\n            ++col;\n            continue;\n        }\n\n        // If it's a newline (CRLF) and we're not in a quoted field, skip the next character and move on to the next row and move to column 0 of that new row\n        if (cc == '\\r' && nc == '\\n' && !quote) {\n            ++row;\n            maxCol = Math.max(maxCol, col);\n            col = 0;\n            ++c;\n            continue;\n        }\n\n        // If it's a newline (LF or CR) and we're not in a quoted field, move on to the next row and move to column 0 of that new row\n        if (cc == '\\n' && !quote) {\n            ++row;\n            maxCol = Math.max(maxCol, col);\n            col = 0;\n            continue;\n        }\n        if (cc == '\\r' && !quote) {\n            ++row;\n            maxCol = Math.max(maxCol, col);\n            col = 0;\n            continue;\n        }\n\n        // Otherwise, append the current character to the current column\n        arr[row][col] += cc;\n    }\n\n    // fix array length\n    arr.forEach((row, i) => {\n        for (let i = row.length; i <= maxCol; i++) {\n            row.push('');\n        }\n    });\n    return arr;\n};\n\nexport const createFromTable = function (el, options) {\n    if (el.tagName != 'TABLE') {\n        console.log('Element is not a table');\n    } else {\n        // Configuration\n        if (!options) {\n            options = {};\n        }\n\n        options.columns = [];\n        options.data = [];\n\n        // Colgroup\n        const colgroup = el.querySelectorAll('colgroup > col');\n        if (colgroup.length) {\n            // Get column width\n            for (let i = 0; i < colgroup.length; i++) {\n                let width = colgroup[i].style.width;\n                if (!width) {\n                    width = colgroup[i].getAttribute('width');\n                }\n                // Set column width\n                if (width) {\n                    if (!options.columns[i]) {\n                        options.columns[i] = {};\n                    }\n                    options.columns[i].width = width;\n                }\n            }\n        }\n\n        // Parse header\n        const parseHeader = function (header, i) {\n            // Get width information\n            let info = header.getBoundingClientRect();\n            const width = info.width > 50 ? info.width : 50;\n\n            // Create column option\n            if (!options.columns[i]) {\n                options.columns[i] = {};\n            }\n            if (header.getAttribute('data-celltype')) {\n                options.columns[i].type = header.getAttribute('data-celltype');\n            } else {\n                options.columns[i].type = 'text';\n            }\n            options.columns[i].width = width + 'px';\n            options.columns[i].title = header.innerHTML;\n            if (header.style.textAlign) {\n                options.columns[i].align = header.style.textAlign;\n            }\n\n            if ((info = header.getAttribute('name'))) {\n                options.columns[i].name = info;\n            }\n            if ((info = header.getAttribute('id'))) {\n                options.columns[i].id = info;\n            }\n            if ((info = header.getAttribute('data-mask'))) {\n                options.columns[i].mask = info;\n            }\n        };\n\n        // Headers\n        const nested = [];\n        let headers = el.querySelectorAll(':scope > thead > tr');\n        if (headers.length) {\n            for (let j = 0; j < headers.length - 1; j++) {\n                const cells = [];\n                for (let i = 0; i < headers[j].children.length; i++) {\n                    const row = {\n                        title: headers[j].children[i].textContent,\n                        colspan: headers[j].children[i].getAttribute('colspan') || 1,\n                    };\n                    cells.push(row);\n                }\n                nested.push(cells);\n            }\n            // Get the last row in the thead\n            headers = headers[headers.length - 1].children;\n            // Go though the headers\n            for (let i = 0; i < headers.length; i++) {\n                parseHeader(headers[i], i);\n            }\n        }\n\n        // Content\n        let rowNumber = 0;\n        const mergeCells = {};\n        const rows = {};\n        const style = {};\n        const classes = {};\n\n        let content = el.querySelectorAll(':scope > tr, :scope > tbody > tr');\n        for (let j = 0; j < content.length; j++) {\n            options.data[rowNumber] = [];\n            if (options.parseTableFirstRowAsHeader == true && !headers.length && j == 0) {\n                for (let i = 0; i < content[j].children.length; i++) {\n                    parseHeader(content[j].children[i], i);\n                }\n            } else {\n                for (let i = 0; i < content[j].children.length; i++) {\n                    // WickedGrid formula compatibility\n                    let value = content[j].children[i].getAttribute('data-formula');\n                    if (value) {\n                        if (value.substr(0, 1) != '=') {\n                            value = '=' + value;\n                        }\n                    } else {\n                        value = content[j].children[i].innerHTML;\n                    }\n                    options.data[rowNumber].push(value);\n\n                    // Key\n                    const cellName = getColumnNameFromId([i, j]);\n\n                    // Classes\n                    const tmp = content[j].children[i].getAttribute('class');\n                    if (tmp) {\n                        classes[cellName] = tmp;\n                    }\n\n                    // Merged cells\n                    const mergedColspan = parseInt(content[j].children[i].getAttribute('colspan')) || 0;\n                    const mergedRowspan = parseInt(content[j].children[i].getAttribute('rowspan')) || 0;\n                    if (mergedColspan || mergedRowspan) {\n                        mergeCells[cellName] = [mergedColspan || 1, mergedRowspan || 1];\n                    }\n\n                    // Avoid problems with hidden cells\n                    if (content[j].children[i].style && content[j].children[i].style.display == 'none') {\n                        content[j].children[i].style.display = '';\n                    }\n                    // Get style\n                    const s = content[j].children[i].getAttribute('style');\n                    if (s) {\n                        style[cellName] = s;\n                    }\n                    // Bold\n                    if (content[j].children[i].classList.contains('styleBold')) {\n                        if (style[cellName]) {\n                            style[cellName] += '; font-weight:bold;';\n                        } else {\n                            style[cellName] = 'font-weight:bold;';\n                        }\n                    }\n                }\n\n                // Row Height\n                if (content[j].style && content[j].style.height) {\n                    rows[j] = { height: content[j].style.height };\n                }\n\n                // Index\n                rowNumber++;\n            }\n        }\n\n        // Nested\n        if (Object.keys(nested).length > 0) {\n            options.nestedHeaders = nested;\n        }\n        // Style\n        if (Object.keys(style).length > 0) {\n            options.style = style;\n        }\n        // Merged\n        if (Object.keys(mergeCells).length > 0) {\n            options.mergeCells = mergeCells;\n        }\n        // Row height\n        if (Object.keys(rows).length > 0) {\n            options.rows = rows;\n        }\n        // Classes\n        if (Object.keys(classes).length > 0) {\n            options.classes = classes;\n        }\n\n        content = el.querySelectorAll('tfoot tr');\n        if (content.length) {\n            const footers = [];\n            for (let j = 0; j < content.length; j++) {\n                let footer = [];\n                for (let i = 0; i < content[j].children.length; i++) {\n                    footer.push(content[j].children[i].textContent);\n                }\n                footers.push(footer);\n            }\n            if (Object.keys(footers).length > 0) {\n                options.footers = footers;\n            }\n        }\n        // TODO: data-hiddencolumns=\"3,4\"\n\n        // I guess in terms the better column type\n        if (options.parseTableAutoCellType == true) {\n            const pattern = [];\n            for (let i = 0; i < options.columns.length; i++) {\n                let test = true;\n                let testCalendar = true;\n                pattern[i] = [];\n                for (let j = 0; j < options.data.length; j++) {\n                    const value = options.data[j][i];\n                    if (!pattern[i][value]) {\n                        pattern[i][value] = 0;\n                    }\n                    pattern[i][value]++;\n                    if (value.length > 25) {\n                        test = false;\n                    }\n                    if (value.length == 10) {\n                        if (!(value.substr(4, 1) == '-' && value.substr(7, 1) == '-')) {\n                            testCalendar = false;\n                        }\n                    } else {\n                        testCalendar = false;\n                    }\n                }\n\n                const keys = Object.keys(pattern[i]).length;\n                if (testCalendar) {\n                    options.columns[i].type = 'calendar';\n                } else if (test == true && keys > 1 && keys <= parseInt(options.data.length * 0.1)) {\n                    options.columns[i].type = 'dropdown';\n                    options.columns[i].source = Object.keys(pattern[i]);\n                }\n            }\n        }\n\n        return options;\n    }\n};\n"
  },
  {
    "path": "src/utils/history.js",
    "content": "import dispatch from './dispatch.js';\nimport { injectArray } from './internalHelpers.js';\nimport { updateTableReferences } from './internal.js';\nimport { setMerge } from './merges.js';\nimport { updateOrder, updateOrderArrow } from './orderBy.js';\nimport { conditionalSelectionUpdate } from './selection.js';\n\n/**\n * Initializes a new history record for undo/redo\n *\n * @return null\n */\nexport const setHistory = function (changes) {\n    const obj = this;\n\n    if (obj.ignoreHistory != true) {\n        // Increment and get the current history index\n        const index = ++obj.historyIndex;\n\n        // Slice the array to discard undone changes\n        obj.history = obj.history = obj.history.slice(0, index + 1);\n\n        // Keep history\n        obj.history[index] = changes;\n    }\n};\n\n/**\n * Process row\n */\nconst historyProcessRow = function (type, historyRecord) {\n    const obj = this;\n\n    const rowIndex = !historyRecord.insertBefore ? historyRecord.rowNumber + 1 : +historyRecord.rowNumber;\n\n    if (obj.options.search == true) {\n        if (obj.results && obj.results.length != obj.rows.length) {\n            obj.resetSearch();\n        }\n    }\n\n    // Remove row\n    if (type == 1) {\n        const numOfRows = historyRecord.numOfRows;\n        // Remove nodes\n        for (let j = rowIndex; j < numOfRows + rowIndex; j++) {\n            obj.rows[j].element.parentNode.removeChild(obj.rows[j].element);\n        }\n        // Remove references\n        obj.records.splice(rowIndex, numOfRows);\n        obj.options.data.splice(rowIndex, numOfRows);\n        obj.rows.splice(rowIndex, numOfRows);\n\n        conditionalSelectionUpdate.call(obj, 1, rowIndex, numOfRows + rowIndex - 1);\n    } else {\n        // Insert data\n        const records = historyRecord.rowRecords.map((row) => {\n            return [...row];\n        });\n        obj.records = injectArray(obj.records, rowIndex, records);\n\n        const data = historyRecord.rowData.map((row) => {\n            return [...row];\n        });\n        obj.options.data = injectArray(obj.options.data, rowIndex, data);\n\n        obj.rows = injectArray(obj.rows, rowIndex, historyRecord.rowNode);\n        // Insert nodes\n        let index = 0;\n        for (let j = rowIndex; j < historyRecord.numOfRows + rowIndex; j++) {\n            obj.tbody.insertBefore(historyRecord.rowNode[index].element, obj.tbody.children[j]);\n            index++;\n        }\n    }\n\n    for (let j = rowIndex; j < obj.rows.length; j++) {\n        obj.rows[j].y = j;\n    }\n\n    for (let j = rowIndex; j < obj.records.length; j++) {\n        for (let i = 0; i < obj.records[j].length; i++) {\n            obj.records[j][i].y = j;\n        }\n    }\n\n    // Respect pagination\n    if (obj.options.pagination > 0) {\n        obj.page(obj.pageNumber);\n    }\n\n    updateTableReferences.call(obj);\n};\n\n/**\n * Process column\n */\nconst historyProcessColumn = function (type, historyRecord) {\n    const obj = this;\n\n    const columnIndex = !historyRecord.insertBefore ? historyRecord.columnNumber + 1 : historyRecord.columnNumber;\n\n    // Remove column\n    if (type == 1) {\n        const numOfColumns = historyRecord.numOfColumns;\n\n        obj.options.columns.splice(columnIndex, numOfColumns);\n        for (let i = columnIndex; i < numOfColumns + columnIndex; i++) {\n            obj.headers[i].parentNode.removeChild(obj.headers[i]);\n            obj.cols[i].colElement.parentNode.removeChild(obj.cols[i].colElement);\n        }\n        obj.headers.splice(columnIndex, numOfColumns);\n        obj.cols.splice(columnIndex, numOfColumns);\n        for (let j = 0; j < historyRecord.data.length; j++) {\n            for (let i = columnIndex; i < numOfColumns + columnIndex; i++) {\n                obj.records[j][i].element.parentNode.removeChild(obj.records[j][i].element);\n            }\n            obj.records[j].splice(columnIndex, numOfColumns);\n            obj.options.data[j].splice(columnIndex, numOfColumns);\n        }\n        // Process footers\n        if (obj.options.footers) {\n            for (let j = 0; j < obj.options.footers.length; j++) {\n                obj.options.footers[j].splice(columnIndex, numOfColumns);\n            }\n        }\n    } else {\n        // Insert data\n        obj.options.columns = injectArray(obj.options.columns, columnIndex, historyRecord.columns);\n        obj.headers = injectArray(obj.headers, columnIndex, historyRecord.headers);\n        obj.cols = injectArray(obj.cols, columnIndex, historyRecord.cols);\n\n        let index = 0;\n        for (let i = columnIndex; i < historyRecord.numOfColumns + columnIndex; i++) {\n            obj.headerContainer.insertBefore(historyRecord.headers[index], obj.headerContainer.children[i + 1]);\n            obj.colgroupContainer.insertBefore(historyRecord.cols[index].colElement, obj.colgroupContainer.children[i + 1]);\n            index++;\n        }\n\n        for (let j = 0; j < historyRecord.data.length; j++) {\n            obj.options.data[j] = injectArray(obj.options.data[j], columnIndex, historyRecord.data[j]);\n            obj.records[j] = injectArray(obj.records[j], columnIndex, historyRecord.records[j]);\n            let index = 0;\n            for (let i = columnIndex; i < historyRecord.numOfColumns + columnIndex; i++) {\n                obj.rows[j].element.insertBefore(historyRecord.records[j][index].element, obj.rows[j].element.children[i + 1]);\n                index++;\n            }\n        }\n        // Process footers\n        if (obj.options.footers) {\n            for (let j = 0; j < obj.options.footers.length; j++) {\n                obj.options.footers[j] = injectArray(obj.options.footers[j], columnIndex, historyRecord.footers[j]);\n            }\n        }\n    }\n\n    for (let i = columnIndex; i < obj.cols.length; i++) {\n        obj.cols[i].x = i;\n    }\n\n    for (let j = 0; j < obj.records.length; j++) {\n        for (let i = columnIndex; i < obj.records[j].length; i++) {\n            obj.records[j][i].x = i;\n        }\n    }\n\n    // Adjust nested headers\n    if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0 && obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {\n        for (let j = 0; j < obj.options.nestedHeaders.length; j++) {\n            let colspan;\n\n            if (type == 1) {\n                colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan) - historyRecord.numOfColumns;\n            } else {\n                colspan = parseInt(obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan) + historyRecord.numOfColumns;\n            }\n            obj.options.nestedHeaders[j][obj.options.nestedHeaders[j].length - 1].colspan = colspan;\n            obj.thead.children[j].children[obj.thead.children[j].children.length - 1].setAttribute('colspan', colspan);\n        }\n    }\n\n    updateTableReferences.call(obj);\n};\n\n/**\n * Undo last action\n */\nexport const undo = function () {\n    const obj = this;\n\n    // Ignore events and history\n    const ignoreEvents = obj.parent.ignoreEvents ? true : false;\n    const ignoreHistory = obj.ignoreHistory ? true : false;\n\n    obj.parent.ignoreEvents = true;\n    obj.ignoreHistory = true;\n\n    // Records\n    const records = [];\n\n    // Update cells\n    let historyRecord;\n\n    if (obj.historyIndex >= 0) {\n        // History\n        historyRecord = obj.history[obj.historyIndex--];\n\n        if (historyRecord.action == 'insertRow') {\n            historyProcessRow.call(obj, 1, historyRecord);\n        } else if (historyRecord.action == 'deleteRow') {\n            historyProcessRow.call(obj, 0, historyRecord);\n        } else if (historyRecord.action == 'insertColumn') {\n            historyProcessColumn.call(obj, 1, historyRecord);\n        } else if (historyRecord.action == 'deleteColumn') {\n            historyProcessColumn.call(obj, 0, historyRecord);\n        } else if (historyRecord.action == 'moveRow') {\n            obj.moveRow(historyRecord.newValue, historyRecord.oldValue);\n        } else if (historyRecord.action == 'moveColumn') {\n            obj.moveColumn(historyRecord.newValue, historyRecord.oldValue);\n        } else if (historyRecord.action == 'setMerge') {\n            obj.removeMerge(historyRecord.column, historyRecord.data);\n        } else if (historyRecord.action == 'setStyle') {\n            obj.setStyle(historyRecord.oldValue, null, null, 1);\n        } else if (historyRecord.action == 'setWidth') {\n            obj.setWidth(historyRecord.column, historyRecord.oldValue);\n        } else if (historyRecord.action == 'setHeight') {\n            obj.setHeight(historyRecord.row, historyRecord.oldValue);\n        } else if (historyRecord.action == 'setHeader') {\n            obj.setHeader(historyRecord.column, historyRecord.oldValue);\n        } else if (historyRecord.action == 'setComments') {\n            obj.setComments(historyRecord.oldValue);\n        } else if (historyRecord.action == 'orderBy') {\n            let rows = [];\n            for (let j = 0; j < historyRecord.rows.length; j++) {\n                rows[historyRecord.rows[j]] = j;\n            }\n            updateOrderArrow.call(obj, historyRecord.column, historyRecord.order ? 0 : 1);\n            updateOrder.call(obj, rows);\n        } else if (historyRecord.action == 'setValue') {\n            // Redo for changes in cells\n            for (let i = 0; i < historyRecord.records.length; i++) {\n                records.push({\n                    x: historyRecord.records[i].x,\n                    y: historyRecord.records[i].y,\n                    value: historyRecord.records[i].oldValue,\n                });\n\n                if (historyRecord.oldStyle) {\n                    obj.resetStyle(historyRecord.oldStyle);\n                }\n            }\n            // Update records\n            obj.setValue(records);\n\n            // Update selection\n            if (historyRecord.selection) {\n                obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);\n            }\n        }\n    }\n    obj.parent.ignoreEvents = ignoreEvents;\n    obj.ignoreHistory = ignoreHistory;\n\n    // Events\n    dispatch.call(obj, 'onundo', obj, historyRecord);\n};\n\n/**\n * Redo previously undone action\n */\nexport const redo = function () {\n    const obj = this;\n\n    // Ignore events and history\n    const ignoreEvents = obj.parent.ignoreEvents ? true : false;\n    const ignoreHistory = obj.ignoreHistory ? true : false;\n\n    obj.parent.ignoreEvents = true;\n    obj.ignoreHistory = true;\n\n    // Records\n    var records = [];\n\n    // Update cells\n    let historyRecord;\n\n    if (obj.historyIndex < obj.history.length - 1) {\n        // History\n        historyRecord = obj.history[++obj.historyIndex];\n\n        if (historyRecord.action == 'insertRow') {\n            historyProcessRow.call(obj, 0, historyRecord);\n        } else if (historyRecord.action == 'deleteRow') {\n            historyProcessRow.call(obj, 1, historyRecord);\n        } else if (historyRecord.action == 'insertColumn') {\n            historyProcessColumn.call(obj, 0, historyRecord);\n        } else if (historyRecord.action == 'deleteColumn') {\n            historyProcessColumn.call(obj, 1, historyRecord);\n        } else if (historyRecord.action == 'moveRow') {\n            obj.moveRow(historyRecord.oldValue, historyRecord.newValue);\n        } else if (historyRecord.action == 'moveColumn') {\n            obj.moveColumn(historyRecord.oldValue, historyRecord.newValue);\n        } else if (historyRecord.action == 'setMerge') {\n            setMerge.call(obj, historyRecord.column, historyRecord.colspan, historyRecord.rowspan, 1);\n        } else if (historyRecord.action == 'setStyle') {\n            obj.setStyle(historyRecord.newValue, null, null, 1);\n        } else if (historyRecord.action == 'setWidth') {\n            obj.setWidth(historyRecord.column, historyRecord.newValue);\n        } else if (historyRecord.action == 'setHeight') {\n            obj.setHeight(historyRecord.row, historyRecord.newValue);\n        } else if (historyRecord.action == 'setHeader') {\n            obj.setHeader(historyRecord.column, historyRecord.newValue);\n        } else if (historyRecord.action == 'setComments') {\n            obj.setComments(historyRecord.newValue);\n        } else if (historyRecord.action == 'orderBy') {\n            updateOrderArrow.call(obj, historyRecord.column, historyRecord.order);\n            updateOrder.call(obj, historyRecord.rows);\n        } else if (historyRecord.action == 'setValue') {\n            obj.setValue(historyRecord.records);\n            // Redo for changes in cells\n            for (let i = 0; i < historyRecord.records.length; i++) {\n                if (historyRecord.oldStyle) {\n                    obj.resetStyle(historyRecord.newStyle);\n                }\n            }\n            // Update selection\n            if (historyRecord.selection) {\n                obj.updateSelectionFromCoords(historyRecord.selection[0], historyRecord.selection[1], historyRecord.selection[2], historyRecord.selection[3]);\n            }\n        }\n    }\n    obj.parent.ignoreEvents = ignoreEvents;\n    obj.ignoreHistory = ignoreHistory;\n\n    // Events\n    dispatch.call(obj, 'onredo', obj, historyRecord);\n};\n"
  },
  {
    "path": "src/utils/internal.js",
    "content": "import jSuites from 'jsuites';\nimport formula from '@jspreadsheet/formula';\n\nimport dispatch from './dispatch.js';\nimport { refreshSelection, updateCornerPosition } from './selection.js';\nimport { getColumnName } from './helpers.js';\nimport { updateMeta } from './meta.js';\nimport { getFreezeWidth } from './freeze.js';\nimport { updatePagination } from './pagination.js';\nimport { setFooter } from './footer.js';\nimport { getColumnNameFromId, getIdFromColumnName } from './internalHelpers.js';\n\nexport const updateTable = function () {\n    const obj = this;\n\n    // Check for spare\n    if (obj.options.minSpareRows > 0) {\n        let numBlankRows = 0;\n        for (let j = obj.rows.length - 1; j >= 0; j--) {\n            let test = false;\n            for (let i = 0; i < obj.headers.length; i++) {\n                if (obj.options.data[j][i]) {\n                    test = true;\n                }\n            }\n            if (test) {\n                break;\n            } else {\n                numBlankRows++;\n            }\n        }\n\n        if (obj.options.minSpareRows - numBlankRows > 0) {\n            obj.insertRow(obj.options.minSpareRows - numBlankRows);\n        }\n    }\n\n    if (obj.options.minSpareCols > 0) {\n        let numBlankCols = 0;\n        for (let i = obj.headers.length - 1; i >= 0; i--) {\n            let test = false;\n            for (let j = 0; j < obj.rows.length; j++) {\n                if (obj.options.data[j][i]) {\n                    test = true;\n                }\n            }\n            if (test) {\n                break;\n            } else {\n                numBlankCols++;\n            }\n        }\n\n        if (obj.options.minSpareCols - numBlankCols > 0) {\n            obj.insertColumn(obj.options.minSpareCols - numBlankCols);\n        }\n    }\n\n    // Update footers\n    if (obj.options.footers) {\n        setFooter.call(obj);\n    }\n\n    if (obj.options.columns.length < obj.options.minDimensions[0]) {\n        obj.options.minDimensions[0] = obj.options.columns.length;\n    }\n\n    // Update corner position\n    setTimeout(function () {\n        updateCornerPosition.call(obj);\n    }, 0);\n};\n\n/**\n * Trying to extract a number from a string\n */\nconst parseNumber = function (value, columnNumber) {\n    const obj = this;\n\n    // Decimal point\n    const decimal = columnNumber && obj.options.columns[columnNumber].decimal ? obj.options.columns[columnNumber].decimal : '.';\n\n    // Parse both parts of the number\n    let number = '' + value;\n    number = number.split(decimal);\n    number[0] = number[0].match(/[+-]?[0-9]/g);\n    if (number[0]) {\n        number[0] = number[0].join('');\n    }\n    if (number[1]) {\n        number[1] = number[1].match(/[0-9]*/g).join('');\n    }\n\n    // Is a valid number\n    if (number[0] && Number.isInteger(Number(number[0]))) {\n        if (!number[1]) {\n            value = Number(number[0] + '.00');\n        } else {\n            value = Number(number[0] + '.' + number[1]);\n        }\n    } else {\n        value = null;\n    }\n\n    return value;\n};\n\n/**\n * Parse formulas\n */\nexport const executeFormula = function (expression, x, y) {\n    const obj = this;\n\n    const formulaResults = [];\n    const formulaLoopProtection = [];\n\n    // Execute formula with loop protection\n    const execute = function (expression, x, y) {\n        // Parent column identification\n        const parentId = getColumnNameFromId([x, y]);\n\n        // Code protection\n        if (formulaLoopProtection[parentId]) {\n            console.error('Reference loop detected');\n            return '#ERROR';\n        }\n\n        formulaLoopProtection[parentId] = true;\n\n        // Convert range tokens\n        const tokensUpdate = function (tokens) {\n            for (let index = 0; index < tokens.length; index++) {\n                const f = [];\n                const token = tokens[index].split(':');\n                const e1 = getIdFromColumnName(token[0], true);\n                const e2 = getIdFromColumnName(token[1], true);\n\n                let x1, x2;\n\n                if (e1[0] <= e2[0]) {\n                    x1 = e1[0];\n                    x2 = e2[0];\n                } else {\n                    x1 = e2[0];\n                    x2 = e1[0];\n                }\n\n                let y1, y2;\n\n                if (e1[1] <= e2[1]) {\n                    y1 = e1[1];\n                    y2 = e2[1];\n                } else {\n                    y1 = e2[1];\n                    y2 = e1[1];\n                }\n\n                for (let j = y1; j <= y2; j++) {\n                    for (let i = x1; i <= x2; i++) {\n                        f.push(getColumnNameFromId([i, j]));\n                    }\n                }\n\n                expression = expression.replace(tokens[index], f.join(','));\n            }\n        };\n\n        // Range with $ remove $\n        expression = expression.replace(/\\$?([A-Z]+)\\$?([0-9]+)/g, '$1$2');\n\n        let tokens = expression.match(/([A-Z]+[0-9]+):([A-Z]+[0-9]+)/g);\n        if (tokens && tokens.length) {\n            tokensUpdate(tokens);\n        }\n\n        // Get tokens\n        tokens = expression.match(/([A-Z]+[0-9]+)/g);\n\n        // Direct self-reference protection\n        if (tokens && tokens.indexOf(parentId) > -1) {\n            console.error('Self Reference detected');\n            return '#ERROR';\n        } else {\n            // Expressions to be used in the parsing\n            const formulaExpressions = {};\n\n            if (tokens) {\n                for (let i = 0; i < tokens.length; i++) {\n                    // Keep chain\n                    if (!obj.formula[tokens[i]]) {\n                        obj.formula[tokens[i]] = [];\n                    }\n                    // Is already in the register\n                    if (obj.formula[tokens[i]].indexOf(parentId) < 0) {\n                        obj.formula[tokens[i]].push(parentId);\n                    }\n\n                    // Do not calculate again\n                    if (eval('typeof(' + tokens[i] + ') == \"undefined\"')) {\n                        // Coords\n                        const position = getIdFromColumnName(tokens[i], 1);\n                        // Get value\n                        let value;\n\n                        if (typeof obj.options.data[position[1]] != 'undefined' && typeof obj.options.data[position[1]][position[0]] != 'undefined') {\n                            value = obj.options.data[position[1]][position[0]];\n                        } else {\n                            value = '';\n                        }\n                        // Get column data\n                        if (('' + value).substr(0, 1) == '=') {\n                            if (typeof formulaResults[tokens[i]] !== 'undefined') {\n                                value = formulaResults[tokens[i]];\n                            } else {\n                                value = execute(value, position[0], position[1]);\n                                formulaResults[tokens[i]] = value;\n                            }\n                        }\n                        // Type!\n                        if (('' + value).trim() == '') {\n                            // Null\n                            formulaExpressions[tokens[i]] = null;\n                        } else {\n                            if (value == Number(value) && obj.parent.config.autoCasting != false) {\n                                // Number\n                                formulaExpressions[tokens[i]] = Number(value);\n                            } else {\n                                // Trying any formatted number\n                                const number = parseNumber.call(obj, value, position[0]);\n                                if (obj.parent.config.autoCasting != false && number) {\n                                    formulaExpressions[tokens[i]] = number;\n                                } else {\n                                    formulaExpressions[tokens[i]] = '\"' + value + '\"';\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            const ret = dispatch.call(obj, 'onbeforeformula', obj, expression, x, y);\n            if (ret === false) {\n                return expression;\n            } else if (ret) {\n                expression = ret;\n            }\n\n            // Convert formula to javascript\n            let res;\n\n            try {\n                res = formula(expression.substr(1), formulaExpressions, x, y, obj);\n\n                if (typeof res === 'function') {\n                    res = '#ERROR';\n                }\n            } catch (e) {\n                res = '#ERROR';\n\n                if (obj.parent.config.debugFormulas === true) {\n                    console.log(expression.substr(1), formulaExpressions, e);\n                }\n            }\n\n            return res;\n        }\n    };\n\n    return execute(expression, x, y);\n};\n\nexport const parseValue = function (i, j, value, cell) {\n    const obj = this;\n\n    if (('' + value).substr(0, 1) == '=' && obj.parent.config.parseFormulas != false) {\n        value = executeFormula.call(obj, value, i, j);\n    }\n\n    // Column options\n    const options = obj.options.columns && obj.options.columns[i];\n    if (options && !isFormula(value)) {\n        // Mask options\n        let opt = null;\n        if ((opt = getMask(options))) {\n            if (value && value == Number(value)) {\n                value = Number(value);\n            }\n            // Process the decimals to match the mask\n            let masked = jSuites.mask.render(value, opt, true);\n            // Negative indication\n            if (cell) {\n                if (opt.mask) {\n                    const t = opt.mask.split(';');\n                    if (t[1]) {\n                        const t1 = t[1].match(new RegExp('\\\\[Red\\\\]', 'gi'));\n                        if (t1) {\n                            if (value < 0) {\n                                cell.classList.add('red');\n                            } else {\n                                cell.classList.remove('red');\n                            }\n                        }\n                        const t2 = t[1].match(new RegExp('\\\\(', 'gi'));\n                        if (t2) {\n                            if (value < 0) {\n                                masked = '(' + masked + ')';\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (masked) {\n                value = masked;\n            }\n        }\n    }\n\n    return value;\n};\n\n/**\n * Get dropdown value from key\n */\nconst getDropDownValue = function (column, key) {\n    const obj = this;\n\n    const value = [];\n\n    if (obj.options.columns && obj.options.columns[column] && obj.options.columns[column].source) {\n        // Create array from source\n        const combo = [];\n        const source = obj.options.columns[column].source;\n\n        for (let i = 0; i < source.length; i++) {\n            if (typeof source[i] == 'object') {\n                combo[source[i].id] = source[i].name;\n            } else {\n                combo[source[i]] = source[i];\n            }\n        }\n\n        // Guarantee single multiple compatibility\n        const keys = Array.isArray(key) ? key : ('' + key).split(';');\n\n        for (let i = 0; i < keys.length; i++) {\n            if (typeof keys[i] === 'object') {\n                value.push(combo[keys[i].id]);\n            } else {\n                if (combo[keys[i]]) {\n                    value.push(combo[keys[i]]);\n                }\n            }\n        }\n    } else {\n        console.error('Invalid column');\n    }\n\n    return value.length > 0 ? value.join('; ') : '';\n};\n\nconst validDate = function (date) {\n    date = '' + date;\n    if (date.substr(4, 1) == '-' && date.substr(7, 1) == '-') {\n        return true;\n    } else {\n        date = date.split('-');\n        if (date[0].length == 4 && date[0] == Number(date[0]) && date[1].length == 2 && date[1] == Number(date[1])) {\n            return true;\n        }\n    }\n    return false;\n};\n\n/**\n * Strip tags\n */\nconst stripScript = function (a) {\n    const b = new Option();\n    b.innerHTML = a;\n    let c = null;\n    for (a = b.getElementsByTagName('script'); (c = a[0]); ) c.parentNode.removeChild(c);\n    return b.innerHTML;\n};\n\nexport const createCell = function (i, j, value) {\n    const obj = this;\n\n    // Create cell and properties\n    let td = document.createElement('td');\n    td.setAttribute('data-x', i);\n    td.setAttribute('data-y', j);\n\n    if (obj.headers[i].style.display === 'none') {\n        td.style.display = 'none';\n    }\n    // Security\n    if (('' + value).substr(0, 1) == '=' && obj.options.secureFormulas == true) {\n        const val = secureFormula(value);\n        if (val != value) {\n            // Update the data container\n            value = val;\n        }\n    }\n\n    // Custom column\n    if (obj.options.columns && obj.options.columns[i] && typeof obj.options.columns[i].type === 'object') {\n        if (obj.parent.config.parseHTML === true) {\n            td.innerHTML = value;\n        } else {\n            td.textContent = value;\n        }\n        if (typeof obj.options.columns[i].type.createCell == 'function') {\n            obj.options.columns[i].type.createCell(td, value, parseInt(i), parseInt(j), obj, obj.options.columns[i]);\n        }\n    } else {\n        // Hidden column\n        if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'hidden') {\n            td.style.display = 'none';\n            td.textContent = value;\n        } else if (obj.options.columns && obj.options.columns[i] && (obj.options.columns[i].type == 'checkbox' || obj.options.columns[i].type == 'radio')) {\n            // Create input\n            const element = document.createElement('input');\n            element.type = obj.options.columns[i].type;\n            element.name = 'c' + i;\n            element.checked = value == 1 || value == true || value == 'true' ? true : false;\n            element.onclick = function () {\n                obj.setValue(td, this.checked);\n            };\n\n            if (obj.options.columns[i].readOnly == true || obj.options.editable == false) {\n                element.setAttribute('disabled', 'disabled');\n            }\n\n            // Append to the table\n            td.appendChild(element);\n            // Make sure the values are correct\n            obj.options.data[j][i] = element.checked;\n        } else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'calendar') {\n            // Try formatted date\n            let formatted = null;\n            if (!validDate(value)) {\n                const tmp = jSuites.calendar.extractDateFromString(\n                    value,\n                    (obj.options.columns[i].options && obj.options.columns[i].options.format) || 'YYYY-MM-DD'\n                );\n                if (tmp) {\n                    formatted = tmp;\n                }\n            }\n            // Create calendar cell\n            td.textContent = jSuites.calendar.getDateString(\n                formatted ? formatted : value,\n                obj.options.columns[i].options && obj.options.columns[i].options.format\n            );\n        } else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'dropdown') {\n            // Create dropdown cell\n            td.classList.add('jss_dropdown');\n            td.textContent = getDropDownValue.call(obj, i, value);\n        } else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'color') {\n            if (obj.options.columns[i].render == 'square') {\n                const color = document.createElement('div');\n                color.className = 'color';\n                color.style.backgroundColor = value;\n                td.appendChild(color);\n            } else {\n                td.style.color = value;\n                td.textContent = value;\n            }\n        } else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'image') {\n            if (value && value.substr(0, 10) == 'data:image') {\n                const img = document.createElement('img');\n                img.src = value;\n                td.appendChild(img);\n            }\n        } else {\n            if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'html') {\n                td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));\n            } else {\n                if (obj.parent.config.parseHTML === true) {\n                    td.innerHTML = stripScript(parseValue.call(this, i, j, value, td));\n                } else {\n                    td.textContent = parseValue.call(this, i, j, value, td);\n                }\n            }\n        }\n    }\n\n    // Readonly\n    if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].readOnly == true) {\n        td.className = 'readonly';\n    }\n\n    // Text align\n    const colAlign = (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].align) || obj.options.defaultColAlign || 'center';\n    td.style.textAlign = colAlign;\n\n    // Wrap option\n    if (\n        (!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].wordWrap != false) &&\n        (obj.options.wordWrap == true ||\n            (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].wordWrap == true) ||\n            td.innerHTML.length > 200)\n    ) {\n        td.style.whiteSpace = 'pre-wrap';\n    }\n\n    // Overflow\n    if (i > 0) {\n        if (this.options.textOverflow == true) {\n            if (value || td.innerHTML) {\n                obj.records[j][i - 1].element.style.overflow = 'hidden';\n            } else {\n                if (i == obj.options.columns.length - 1) {\n                    td.style.overflow = 'hidden';\n                }\n            }\n        }\n    }\n\n    dispatch.call(obj, 'oncreatecell', obj, td, i, j, value);\n\n    return td;\n};\n\n/**\n * Update cell content\n *\n * @param object cell\n * @return void\n */\nexport const updateCell = function (x, y, value, force) {\n    const obj = this;\n\n    let record;\n\n    // Changing value depending on the column type\n    if (obj.records[y][x].element.classList.contains('readonly') == true && !force) {\n        // Do nothing\n        record = {\n            x: x,\n            y: y,\n            col: x,\n            row: y,\n        };\n    } else {\n        // Security\n        if (('' + value).substr(0, 1) == '=' && obj.options.secureFormulas == true) {\n            const val = secureFormula(value);\n            if (val != value) {\n                // Update the data container\n                value = val;\n            }\n        }\n\n        // On change\n        const val = dispatch.call(obj, 'onbeforechange', obj, obj.records[y][x].element, x, y, value);\n\n        // If you return something this will overwrite the value\n        if (val != undefined) {\n            value = val;\n        }\n\n        if (\n            obj.options.columns &&\n            obj.options.columns[x] &&\n            typeof obj.options.columns[x].type === 'object' &&\n            typeof obj.options.columns[x].type.updateCell === 'function'\n        ) {\n            const result = obj.options.columns[x].type.updateCell(obj.records[y][x].element, value, parseInt(x), parseInt(y), obj, obj.options.columns[x]);\n\n            if (result !== undefined) {\n                value = result;\n            }\n        }\n\n        // History format\n        record = {\n            x: x,\n            y: y,\n            col: x,\n            row: y,\n            value: value,\n            oldValue: obj.options.data[y][x],\n        };\n\n        let editor = obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].type === 'object' ? obj.options.columns[x].type : null;\n        if (editor) {\n            // Update data and cell\n            obj.options.data[y][x] = value;\n            if (typeof editor.setValue === 'function') {\n                editor.setValue(obj.records[y][x].element, value);\n            }\n        } else {\n            // Native functions\n            if (obj.options.columns && obj.options.columns[x] && (obj.options.columns[x].type == 'checkbox' || obj.options.columns[x].type == 'radio')) {\n                // Unchecked all options\n                if (obj.options.columns[x].type == 'radio') {\n                    for (let j = 0; j < obj.options.data.length; j++) {\n                        obj.options.data[j][x] = false;\n                    }\n                }\n\n                // Update data and cell\n                obj.records[y][x].element.children[0].checked = value == 1 || value == true || value == 'true' || value == 'TRUE' ? true : false;\n                obj.options.data[y][x] = obj.records[y][x].element.children[0].checked;\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'dropdown') {\n                // Update data and cell\n                obj.options.data[y][x] = value;\n                obj.records[y][x].element.textContent = getDropDownValue.call(obj, x, value);\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'calendar') {\n                // Try formatted date\n                let formatted = null;\n                if (!validDate(value)) {\n                    const tmp = jSuites.calendar.extractDateFromString(\n                        value,\n                        (obj.options.columns[x].options && obj.options.columns[x].options.format) || 'YYYY-MM-DD'\n                    );\n                    if (tmp) {\n                        formatted = tmp;\n                    }\n                }\n                // Update data and cell\n                obj.options.data[y][x] = value;\n                obj.records[y][x].element.textContent = jSuites.calendar.getDateString(\n                    formatted ? formatted : value,\n                    obj.options.columns[x].options && obj.options.columns[x].options.format\n                );\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'color') {\n                // Update color\n                obj.options.data[y][x] = value;\n                // Render\n                if (obj.options.columns[x].render == 'square') {\n                    const color = document.createElement('div');\n                    color.className = 'color';\n                    color.style.backgroundColor = value;\n                    obj.records[y][x].element.textContent = '';\n                    obj.records[y][x].element.appendChild(color);\n                } else {\n                    obj.records[y][x].element.style.color = value;\n                    obj.records[y][x].element.textContent = value;\n                }\n            } else if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'image') {\n                value = '' + value;\n                obj.options.data[y][x] = value;\n                obj.records[y][x].element.innerHTML = '';\n                if (value && value.substr(0, 10) == 'data:image') {\n                    const img = document.createElement('img');\n                    img.src = value;\n                    obj.records[y][x].element.appendChild(img);\n                }\n            } else {\n                // Update data and cell\n                obj.options.data[y][x] = value;\n                // Label\n                if (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].type == 'html') {\n                    obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value));\n                } else {\n                    if (obj.parent.config.parseHTML === true) {\n                        obj.records[y][x].element.innerHTML = stripScript(parseValue.call(obj, x, y, value, obj.records[y][x].element));\n                    } else {\n                        obj.records[y][x].element.textContent = parseValue.call(obj, x, y, value, obj.records[y][x].element);\n                    }\n                }\n                // Handle big text inside a cell\n                if (\n                    (!obj.options.columns || !obj.options.columns[x] || obj.options.columns[x].wordWrap != false) &&\n                    (obj.options.wordWrap == true ||\n                        (obj.options.columns && obj.options.columns[x] && obj.options.columns[x].wordWrap == true) ||\n                        obj.records[y][x].element.innerHTML.length > 200)\n                ) {\n                    obj.records[y][x].element.style.whiteSpace = 'pre-wrap';\n                } else {\n                    obj.records[y][x].element.style.whiteSpace = '';\n                }\n            }\n        }\n\n        // Overflow\n        if (x > 0) {\n            if (value) {\n                obj.records[y][x - 1].element.style.overflow = 'hidden';\n            } else {\n                obj.records[y][x - 1].element.style.overflow = '';\n            }\n        }\n\n        if (obj.options.columns && obj.options.columns[x] && typeof obj.options.columns[x].render === 'function') {\n            obj.options.columns[x].render(\n                obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null,\n                value,\n                parseInt(x),\n                parseInt(y),\n                obj,\n                obj.options.columns[x]\n            );\n        }\n\n        // On change\n        dispatch.call(obj, 'onchange', obj, obj.records[y] && obj.records[y][x] ? obj.records[y][x].element : null, x, y, value, record.oldValue);\n    }\n\n    return record;\n};\n\n/**\n * The value is a formula\n */\nexport const isFormula = function (value) {\n    const v = ('' + value)[0];\n    return v == '=' || v == '#' ? true : false;\n};\n\n/**\n * Get the mask in the jSuites.mask format\n */\nexport const getMask = function (o) {\n    if (o.format || o.mask || o.locale) {\n        const opt = {};\n        if (o.mask) {\n            opt.mask = o.mask;\n        } else if (o.format) {\n            opt.mask = o.format;\n        } else {\n            opt.locale = o.locale;\n            opt.options = o.options;\n        }\n\n        if (o.decimal) {\n            if (!opt.options) {\n                opt.options = {};\n            }\n            opt.options = { decimal: o.decimal };\n        }\n        return opt;\n    }\n\n    return null;\n};\n\n/**\n * Secure formula\n */\nconst secureFormula = function (oldValue) {\n    let newValue = '';\n    let inside = 0;\n\n    for (let i = 0; i < oldValue.length; i++) {\n        if (oldValue[i] == '\"') {\n            if (inside == 0) {\n                inside = 1;\n            } else {\n                inside = 0;\n            }\n        }\n\n        if (inside == 1) {\n            newValue += oldValue[i];\n        } else {\n            newValue += oldValue[i].toUpperCase();\n        }\n    }\n\n    return newValue;\n};\n\n/**\n * Update all related cells in the chain\n */\nlet chainLoopProtection = [];\n\nexport const updateFormulaChain = function (x, y, records) {\n    const obj = this;\n\n    const cellId = getColumnNameFromId([x, y]);\n    if (obj.formula[cellId] && obj.formula[cellId].length > 0) {\n        if (chainLoopProtection[cellId]) {\n            obj.records[y][x].element.innerHTML = '#ERROR';\n            obj.formula[cellId] = '';\n        } else {\n            // Protection\n            chainLoopProtection[cellId] = true;\n\n            for (let i = 0; i < obj.formula[cellId].length; i++) {\n                const cell = getIdFromColumnName(obj.formula[cellId][i], true);\n                // Update cell\n                const value = '' + obj.options.data[cell[1]][cell[0]];\n                if (value.substr(0, 1) == '=') {\n                    records.push(updateCell.call(obj, cell[0], cell[1], value, true));\n                } else {\n                    // No longer a formula, remove from the chain\n                    Object.keys(obj.formula)[i] = null;\n                }\n                updateFormulaChain.call(obj, cell[0], cell[1], records);\n            }\n        }\n    }\n\n    chainLoopProtection = [];\n};\n\n/**\n * Update formula\n */\nexport const updateFormula = function (formula, referencesToUpdate) {\n    const testLetter = /[A-Z]/;\n    const testNumber = /[0-9]/;\n\n    let newFormula = '';\n    let letter = null;\n    let number = null;\n    let token = '';\n\n    for (let index = 0; index < formula.length; index++) {\n        if (testLetter.exec(formula[index])) {\n            letter = 1;\n            number = 0;\n            token += formula[index];\n        } else if (testNumber.exec(formula[index])) {\n            number = letter ? 1 : 0;\n            token += formula[index];\n        } else {\n            if (letter && number) {\n                token = referencesToUpdate[token] ? referencesToUpdate[token] : token;\n            }\n            newFormula += token;\n            newFormula += formula[index];\n            letter = 0;\n            number = 0;\n            token = '';\n        }\n    }\n\n    if (token) {\n        if (letter && number) {\n            token = referencesToUpdate[token] ? referencesToUpdate[token] : token;\n        }\n        newFormula += token;\n    }\n\n    return newFormula;\n};\n\n/**\n * Update formulas\n */\nconst updateFormulas = function (referencesToUpdate) {\n    const obj = this;\n\n    // Update formulas\n    for (let j = 0; j < obj.options.data.length; j++) {\n        for (let i = 0; i < obj.options.data[0].length; i++) {\n            const value = '' + obj.options.data[j][i];\n            // Is formula\n            if (value.substr(0, 1) == '=') {\n                // Replace tokens\n                const newFormula = updateFormula(value, referencesToUpdate);\n                if (newFormula != value) {\n                    obj.options.data[j][i] = newFormula;\n                }\n            }\n        }\n    }\n\n    // Update formula chain\n    const formula = [];\n    const keys = Object.keys(obj.formula);\n    for (let j = 0; j < keys.length; j++) {\n        // Current key and values\n        let key = keys[j];\n        const value = obj.formula[key];\n        // Update key\n        if (referencesToUpdate[key]) {\n            key = referencesToUpdate[key];\n        }\n        // Update values\n        formula[key] = [];\n        for (let i = 0; i < value.length; i++) {\n            let letter = value[i];\n            if (referencesToUpdate[letter]) {\n                letter = referencesToUpdate[letter];\n            }\n            formula[key].push(letter);\n        }\n    }\n    obj.formula = formula;\n};\n\n/**\n * Update cell references\n *\n * @return void\n */\nexport const updateTableReferences = function () {\n    const obj = this;\n    if (obj.skipUpdateTableReferences) {\n        return;\n    }\n\n    // Update headers\n    for (let i = 0; i < obj.headers.length; i++) {\n        const x = obj.headers[i].getAttribute('data-x');\n\n        if (x != i) {\n            // Update coords\n            obj.headers[i].setAttribute('data-x', i);\n            // Title\n            if (!obj.headers[i].getAttribute('title')) {\n                obj.headers[i].innerHTML = getColumnName(i);\n            }\n        }\n    }\n\n    // Update all rows\n    for (let j = 0; j < obj.rows.length; j++) {\n        if (obj.rows[j]) {\n            const y = obj.rows[j].element.getAttribute('data-y');\n\n            if (y != j) {\n                // Update coords\n                obj.rows[j].element.setAttribute('data-y', j);\n                obj.rows[j].element.children[0].setAttribute('data-y', j);\n                // Row number\n                obj.rows[j].element.children[0].innerHTML = j + 1;\n            }\n        }\n    }\n\n    // Regular cells affected by this change\n    const affectedTokens = [];\n    const mergeCellUpdates = [];\n\n    // Update cell\n    const updatePosition = function (x, y, i, j) {\n        if (x != i) {\n            obj.records[j][i].element.setAttribute('data-x', i);\n        }\n        if (y != j) {\n            obj.records[j][i].element.setAttribute('data-y', j);\n        }\n\n        // Other updates\n        if (x != i || y != j) {\n            const columnIdFrom = getColumnNameFromId([x, y]);\n            const columnIdTo = getColumnNameFromId([i, j]);\n            affectedTokens[columnIdFrom] = columnIdTo;\n        }\n    };\n\n    for (let j = 0; j < obj.records.length; j++) {\n        for (let i = 0; i < obj.records[0].length; i++) {\n            if (obj.records[j][i]) {\n                // Current values\n                const x = obj.records[j][i].element.getAttribute('data-x');\n                const y = obj.records[j][i].element.getAttribute('data-y');\n\n                // Update column\n                if (obj.records[j][i].element.getAttribute('data-merged')) {\n                    const columnIdFrom = getColumnNameFromId([x, y]);\n                    const columnIdTo = getColumnNameFromId([i, j]);\n                    if (mergeCellUpdates[columnIdFrom] == null) {\n                        if (columnIdFrom == columnIdTo) {\n                            mergeCellUpdates[columnIdFrom] = false;\n                        } else {\n                            const totalX = parseInt(i - x);\n                            const totalY = parseInt(j - y);\n                            mergeCellUpdates[columnIdFrom] = [columnIdTo, totalX, totalY];\n                        }\n                    }\n                } else {\n                    updatePosition(x, y, i, j);\n                }\n            }\n        }\n    }\n\n    // Update merged if applicable\n    const keys = Object.keys(mergeCellUpdates);\n    if (keys.length) {\n        for (let i = 0; i < keys.length; i++) {\n            if (mergeCellUpdates[keys[i]]) {\n                const info = getIdFromColumnName(keys[i], true);\n                let x = info[0];\n                let y = info[1];\n                updatePosition(x, y, x + mergeCellUpdates[keys[i]][1], y + mergeCellUpdates[keys[i]][2]);\n\n                const columnIdFrom = keys[i];\n                const columnIdTo = mergeCellUpdates[keys[i]][0];\n                for (let j = 0; j < obj.options.mergeCells[columnIdFrom][2].length; j++) {\n                    x = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-x'));\n                    y = parseInt(obj.options.mergeCells[columnIdFrom][2][j].getAttribute('data-y'));\n                    obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-x', x + mergeCellUpdates[keys[i]][1]);\n                    obj.options.mergeCells[columnIdFrom][2][j].setAttribute('data-y', y + mergeCellUpdates[keys[i]][2]);\n                }\n\n                obj.options.mergeCells[columnIdTo] = obj.options.mergeCells[columnIdFrom];\n                delete obj.options.mergeCells[columnIdFrom];\n            }\n        }\n    }\n\n    // Update formulas\n    updateFormulas.call(obj, affectedTokens);\n\n    // Update meta data\n    updateMeta.call(obj, affectedTokens);\n\n    // Refresh selection\n    refreshSelection.call(obj);\n\n    // Update table with custom configuration if applicable\n    updateTable.call(obj);\n};\n\n/**\n * Update scroll position based on the selection\n */\nexport const updateScroll = function (direction) {\n    const obj = this;\n\n    // Jspreadsheet Container information\n    const contentRect = obj.content.getBoundingClientRect();\n    const x1 = contentRect.left;\n    const y1 = contentRect.top;\n    const w1 = contentRect.width;\n    const h1 = contentRect.height;\n\n    // Direction Left or Up\n    const reference = obj.records[obj.selectedCell[3]][obj.selectedCell[2]].element;\n\n    // Reference\n    const referenceRect = reference.getBoundingClientRect();\n    const x2 = referenceRect.left;\n    const y2 = referenceRect.top;\n    const w2 = referenceRect.width;\n    const h2 = referenceRect.height;\n\n    let x, y;\n\n    // Direction\n    if (direction == 0 || direction == 1) {\n        x = x2 - x1 + obj.content.scrollLeft;\n        y = y2 - y1 + obj.content.scrollTop - 2;\n    } else {\n        x = x2 - x1 + obj.content.scrollLeft + w2;\n        y = y2 - y1 + obj.content.scrollTop + h2;\n    }\n\n    // Top position check\n    if (y > obj.content.scrollTop + 30 && y < obj.content.scrollTop + h1) {\n        // In the viewport\n    } else {\n        // Out of viewport\n        if (y < obj.content.scrollTop + 30) {\n            obj.content.scrollTop = y - h2;\n        } else {\n            obj.content.scrollTop = y - (h1 - 2);\n        }\n    }\n\n    // Freeze columns?\n    const freezed = getFreezeWidth.call(obj);\n\n    // Left position check - TODO: change that to the bottom border of the element\n    if (x > obj.content.scrollLeft + freezed && x < obj.content.scrollLeft + w1) {\n        // In the viewport\n    } else {\n        // Out of viewport\n        if (x < obj.content.scrollLeft + 30) {\n            obj.content.scrollLeft = x;\n            if (obj.content.scrollLeft < 50) {\n                obj.content.scrollLeft = 0;\n            }\n        } else if (x < obj.content.scrollLeft + freezed) {\n            obj.content.scrollLeft = x - freezed - 1;\n        } else {\n            obj.content.scrollLeft = x - (w1 - 20);\n        }\n    }\n};\n\nexport const updateResult = function () {\n    const obj = this;\n\n    let total = 0;\n    let index = 0;\n\n    // Page 1\n    if (obj.options.lazyLoading == true) {\n        total = 100;\n    } else if (obj.options.pagination > 0) {\n        total = obj.options.pagination;\n    } else {\n        if (obj.results) {\n            total = obj.results.length;\n        } else {\n            total = obj.rows.length;\n        }\n    }\n\n    // Reset current nodes\n    while (obj.tbody.firstChild) {\n        obj.tbody.removeChild(obj.tbody.firstChild);\n    }\n\n    // Hide all records from the table\n    for (let j = 0; j < obj.rows.length; j++) {\n        if (!obj.results || obj.results.indexOf(j) > -1) {\n            if (index < total) {\n                obj.tbody.appendChild(obj.rows[j].element);\n                index++;\n            }\n            obj.rows[j].element.style.display = '';\n        } else {\n            obj.rows[j].element.style.display = 'none';\n        }\n    }\n\n    // Update pagination\n    if (obj.options.pagination > 0) {\n        updatePagination.call(obj);\n    }\n\n    updateCornerPosition.call(obj);\n\n    dispatch.call(obj, 'onupdateresult', obj, obj.results);\n\n    return total;\n};\n\n/**\n * Get the cell object\n *\n * @param object cell\n * @return string value\n */\nexport const getCell = function (x, y) {\n    const obj = this;\n\n    if (typeof x === 'string') {\n        // Convert in case name is excel liked ex. A10, BB92\n        const cell = getIdFromColumnName(x, true);\n\n        x = cell[0];\n        y = cell[1];\n    }\n\n    return obj.records[y][x].element;\n};\n\n/**\n * Get the cell object from coords\n *\n * @param object cell\n * @return string value\n */\nexport const getCellFromCoords = function (x, y) {\n    const obj = this;\n\n    return obj.records[y][x].element;\n};\n\n/**\n * Get label\n *\n * @param object cell\n * @return string value\n */\nexport const getLabel = function (x, y) {\n    const obj = this;\n\n    if (typeof x === 'string') {\n        // Convert in case name is excel liked ex. A10, BB92\n        const cell = getIdFromColumnName(x, true);\n\n        x = cell[0];\n        y = cell[1];\n    }\n\n    return obj.records[y][x].element.innerHTML;\n};\n\n/**\n * Activate/Disable fullscreen\n * use programmatically : table.fullscreen(); or table.fullscreen(true); or table.fullscreen(false);\n * @Param {boolean} activate\n */\nexport const fullscreen = function (activate) {\n    const spreadsheet = this;\n\n    // If activate not defined, get reverse options.fullscreen\n    if (activate == null) {\n        activate = !spreadsheet.config.fullscreen;\n    }\n\n    // If change\n    if (spreadsheet.config.fullscreen != activate) {\n        spreadsheet.config.fullscreen = activate;\n\n        // Test LazyLoading conflict\n        if (activate == true) {\n            spreadsheet.element.classList.add('fullscreen');\n        } else {\n            spreadsheet.element.classList.remove('fullscreen');\n        }\n    }\n};\n\n/**\n * Show index column\n */\nexport const showIndex = function () {\n    const obj = this;\n\n    obj.table.classList.remove('jss_hidden_index');\n};\n\n/**\n * Hide index column\n */\nexport const hideIndex = function () {\n    const obj = this;\n\n    obj.table.classList.add('jss_hidden_index');\n};\n\n/**\n * Create a nested header object\n */\nexport const createNestedHeader = function (nestedInformation) {\n    const obj = this;\n\n    const tr = document.createElement('tr');\n    tr.classList.add('jss_nested');\n    const td = document.createElement('td');\n    td.classList.add('jss_selectall');\n\n    tr.appendChild(td);\n    // Element\n    nestedInformation.element = tr;\n\n    let headerIndex = 0;\n    for (let i = 0; i < nestedInformation.length; i++) {\n        // Default values\n        if (!nestedInformation[i].colspan) {\n            nestedInformation[i].colspan = 1;\n        }\n        if (!nestedInformation[i].title) {\n            nestedInformation[i].title = '';\n        }\n        if (!nestedInformation[i].id) {\n            nestedInformation[i].id = '';\n        }\n\n        // Number of columns\n        let numberOfColumns = nestedInformation[i].colspan;\n\n        // Classes container\n        const column = [];\n        // Header classes for this cell\n        for (let x = 0; x < numberOfColumns; x++) {\n            if (obj.options.columns[headerIndex] && obj.options.columns[headerIndex].type == 'hidden') {\n                numberOfColumns++;\n            }\n            column.push(headerIndex);\n            headerIndex++;\n        }\n\n        // Created the nested cell\n        const td = document.createElement('td');\n        td.setAttribute('data-column', column.join(','));\n        td.setAttribute('colspan', nestedInformation[i].colspan);\n        td.setAttribute('align', nestedInformation[i].align || 'center');\n        td.setAttribute('id', nestedInformation[i].id);\n        td.textContent = nestedInformation[i].title;\n        tr.appendChild(td);\n    }\n\n    return tr;\n};\n\nexport const getWorksheetActive = function () {\n    const spreadsheet = this.parent ? this.parent : this;\n\n    return spreadsheet.element.tabs ? spreadsheet.element.tabs.getActive() : 0;\n};\n\nexport const getWorksheetInstance = function (index) {\n    const spreadsheet = this;\n\n    const worksheetIndex = typeof index !== 'undefined' ? index : getWorksheetActive.call(spreadsheet);\n\n    return spreadsheet.worksheets[worksheetIndex];\n};\n"
  },
  {
    "path": "src/utils/internalHelpers.js",
    "content": "import { getColumnName } from './helpers.js';\n\n/**\n * Helper injectArray\n */\nexport const injectArray = function (o, idx, arr) {\n    if (idx <= o.length) {\n        return o.slice(0, idx).concat(arr).concat(o.slice(idx));\n    }\n\n    const array = o.slice(0, o.length);\n\n    while (idx > array.length) {\n        array.push(undefined);\n    }\n\n    return array.concat(arr);\n};\n\n/**\n * Convert excel like column to jss id\n *\n * @param string id\n * @return string id\n */\nexport const getIdFromColumnName = function (id, arr) {\n    // Get the letters\n    const t = /^[a-zA-Z]+/.exec(id);\n\n    if (t) {\n        // Base 26 calculation\n        let code = 0;\n        for (let i = 0; i < t[0].length; i++) {\n            code += parseInt(t[0].charCodeAt(i) - 64) * Math.pow(26, t[0].length - 1 - i);\n        }\n        code--;\n        // Make sure jss starts on zero\n        if (code < 0) {\n            code = 0;\n        }\n\n        // Number\n        let number = parseInt(/[0-9]+$/.exec(id));\n        if (number > 0) {\n            number--;\n        }\n\n        if (arr == true) {\n            id = [code, number];\n        } else {\n            id = code + '-' + number;\n        }\n    }\n\n    return id;\n};\n\n/**\n * Convert jss id to excel like column name\n *\n * @param string id\n * @return string id\n */\nexport const getColumnNameFromId = function (cellId) {\n    if (!Array.isArray(cellId)) {\n        cellId = cellId.split('-');\n    }\n\n    return getColumnName(parseInt(cellId[0])) + (parseInt(cellId[1]) + 1);\n};\n"
  },
  {
    "path": "src/utils/keys.js",
    "content": "import { updateScroll } from './internal.js';\nimport { loadDown, loadPage, loadUp, loadValidation } from './lazyLoading.js';\n\nconst upGet = function (x, y) {\n    const obj = this;\n\n    x = parseInt(x);\n    y = parseInt(y);\n    for (let j = y - 1; j >= 0; j--) {\n        if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {\n            if (obj.records[j][x].element.getAttribute('data-merged')) {\n                if (obj.records[j][x].element == obj.records[y][x].element) {\n                    continue;\n                }\n            }\n            y = j;\n            break;\n        }\n    }\n\n    return y;\n};\n\nconst upVisible = function (group, direction) {\n    const obj = this;\n\n    let x, y;\n\n    if (group == 0) {\n        x = parseInt(obj.selectedCell[0]);\n        y = parseInt(obj.selectedCell[1]);\n    } else {\n        x = parseInt(obj.selectedCell[2]);\n        y = parseInt(obj.selectedCell[3]);\n    }\n\n    if (direction == 0) {\n        for (let j = 0; j < y; j++) {\n            if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {\n                y = j;\n                break;\n            }\n        }\n    } else {\n        y = upGet.call(obj, x, y);\n    }\n\n    if (group == 0) {\n        obj.selectedCell[0] = x;\n        obj.selectedCell[1] = y;\n    } else {\n        obj.selectedCell[2] = x;\n        obj.selectedCell[3] = y;\n    }\n};\n\nexport const up = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (obj.selectedCell[3] > 0) {\n            upVisible.call(obj, 1, ctrlKey ? 0 : 1);\n        }\n    } else {\n        if (obj.selectedCell[1] > 0) {\n            upVisible.call(obj, 0, ctrlKey ? 0 : 1);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    // Update selection\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n\n    // Change page\n    if (obj.options.lazyLoading == true) {\n        if (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0) {\n            loadPage.call(obj, 0);\n            obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n        } else {\n            if (loadValidation.call(obj)) {\n                obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n            } else {\n                const item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));\n                if (obj.selectedCell[1] - item < 30) {\n                    loadUp.call(obj);\n                    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n                }\n            }\n        }\n    } else if (obj.options.pagination > 0) {\n        const pageNumber = obj.whichPage(obj.selectedCell[3]);\n        if (pageNumber != obj.pageNumber) {\n            obj.page(pageNumber);\n        }\n    }\n\n    updateScroll.call(obj, 1);\n};\n\nexport const rightGet = function (x, y) {\n    const obj = this;\n\n    x = parseInt(x);\n    y = parseInt(y);\n\n    for (let i = x + 1; i < obj.headers.length; i++) {\n        if (obj.records[y][i].element.style.display != 'none') {\n            if (obj.records[y][i].element.getAttribute('data-merged')) {\n                if (obj.records[y][i].element == obj.records[y][x].element) {\n                    continue;\n                }\n            }\n            x = i;\n            break;\n        }\n    }\n\n    return x;\n};\n\nconst rightVisible = function (group, direction) {\n    const obj = this;\n\n    let x, y;\n\n    if (group == 0) {\n        x = parseInt(obj.selectedCell[0]);\n        y = parseInt(obj.selectedCell[1]);\n    } else {\n        x = parseInt(obj.selectedCell[2]);\n        y = parseInt(obj.selectedCell[3]);\n    }\n\n    if (direction == 0) {\n        for (let i = obj.headers.length - 1; i > x; i--) {\n            if (obj.records[y][i].element.style.display != 'none') {\n                x = i;\n                break;\n            }\n        }\n    } else {\n        x = rightGet.call(obj, x, y);\n    }\n\n    if (group == 0) {\n        obj.selectedCell[0] = x;\n        obj.selectedCell[1] = y;\n    } else {\n        obj.selectedCell[2] = x;\n        obj.selectedCell[3] = y;\n    }\n};\n\nexport const right = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (obj.selectedCell[2] < obj.headers.length - 1) {\n            rightVisible.call(obj, 1, ctrlKey ? 0 : 1);\n        }\n    } else {\n        if (obj.selectedCell[0] < obj.headers.length - 1) {\n            rightVisible.call(obj, 0, ctrlKey ? 0 : 1);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n    updateScroll.call(obj, 2);\n};\n\nexport const downGet = function (x, y) {\n    const obj = this;\n\n    x = parseInt(x);\n    y = parseInt(y);\n    for (let j = y + 1; j < obj.rows.length; j++) {\n        if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {\n            if (obj.records[j][x].element.getAttribute('data-merged')) {\n                if (obj.records[j][x].element == obj.records[y][x].element) {\n                    continue;\n                }\n            }\n            y = j;\n            break;\n        }\n    }\n\n    return y;\n};\n\nconst downVisible = function (group, direction) {\n    const obj = this;\n\n    let x, y;\n\n    if (group == 0) {\n        x = parseInt(obj.selectedCell[0]);\n        y = parseInt(obj.selectedCell[1]);\n    } else {\n        x = parseInt(obj.selectedCell[2]);\n        y = parseInt(obj.selectedCell[3]);\n    }\n\n    if (direction == 0) {\n        for (let j = obj.rows.length - 1; j > y; j--) {\n            if (obj.records[j][x].element.style.display != 'none' && obj.rows[j].element.style.display != 'none') {\n                y = j;\n                break;\n            }\n        }\n    } else {\n        y = downGet.call(obj, x, y);\n    }\n\n    if (group == 0) {\n        obj.selectedCell[0] = x;\n        obj.selectedCell[1] = y;\n    } else {\n        obj.selectedCell[2] = x;\n        obj.selectedCell[3] = y;\n    }\n};\n\nexport const down = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (obj.selectedCell[3] < obj.records.length - 1) {\n            downVisible.call(obj, 1, ctrlKey ? 0 : 1);\n        }\n    } else {\n        if (obj.selectedCell[1] < obj.records.length - 1) {\n            downVisible.call(obj, 0, ctrlKey ? 0 : 1);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n\n    // Change page\n    if (obj.options.lazyLoading == true) {\n        if (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1) {\n            loadPage.call(obj, -1);\n            obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n        } else {\n            if (loadValidation.call(obj)) {\n                obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n            } else {\n                const item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));\n                if (item - obj.selectedCell[3] < 30) {\n                    loadDown.call(obj);\n                    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n                }\n            }\n        }\n    } else if (obj.options.pagination > 0) {\n        const pageNumber = obj.whichPage(obj.selectedCell[3]);\n        if (pageNumber != obj.pageNumber) {\n            obj.page(pageNumber);\n        }\n    }\n\n    updateScroll.call(obj, 3);\n};\n\nconst leftGet = function (x, y) {\n    const obj = this;\n\n    x = parseInt(x);\n    y = parseInt(y);\n    for (let i = x - 1; i >= 0; i--) {\n        if (obj.records[y][i].element.style.display != 'none') {\n            if (obj.records[y][i].element.getAttribute('data-merged')) {\n                if (obj.records[y][i].element == obj.records[y][x].element) {\n                    continue;\n                }\n            }\n            x = i;\n            break;\n        }\n    }\n\n    return x;\n};\n\nconst leftVisible = function (group, direction) {\n    const obj = this;\n\n    let x, y;\n\n    if (group == 0) {\n        x = parseInt(obj.selectedCell[0]);\n        y = parseInt(obj.selectedCell[1]);\n    } else {\n        x = parseInt(obj.selectedCell[2]);\n        y = parseInt(obj.selectedCell[3]);\n    }\n\n    if (direction == 0) {\n        for (let i = 0; i < x; i++) {\n            if (obj.records[y][i].element.style.display != 'none') {\n                x = i;\n                break;\n            }\n        }\n    } else {\n        x = leftGet.call(obj, x, y);\n    }\n\n    if (group == 0) {\n        obj.selectedCell[0] = x;\n        obj.selectedCell[1] = y;\n    } else {\n        obj.selectedCell[2] = x;\n        obj.selectedCell[3] = y;\n    }\n};\n\nexport const left = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (obj.selectedCell[2] > 0) {\n            leftVisible.call(obj, 1, ctrlKey ? 0 : 1);\n        }\n    } else {\n        if (obj.selectedCell[0] > 0) {\n            leftVisible.call(obj, 0, ctrlKey ? 0 : 1);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n    updateScroll.call(obj, 0);\n};\n\nexport const first = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (ctrlKey) {\n            obj.selectedCell[3] = 0;\n        } else {\n            leftVisible.call(obj, 1, 0);\n        }\n    } else {\n        if (ctrlKey) {\n            obj.selectedCell[1] = 0;\n        } else {\n            leftVisible.call(obj, 0, 0);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    // Change page\n    if (obj.options.lazyLoading == true && (obj.selectedCell[1] == 0 || obj.selectedCell[3] == 0)) {\n        loadPage.call(obj, 0);\n    } else if (obj.options.pagination > 0) {\n        const pageNumber = obj.whichPage(obj.selectedCell[3]);\n        if (pageNumber != obj.pageNumber) {\n            obj.page(pageNumber);\n        }\n    }\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n    updateScroll.call(obj, 1);\n};\n\nexport const last = function (shiftKey, ctrlKey) {\n    const obj = this;\n\n    if (shiftKey) {\n        if (ctrlKey) {\n            obj.selectedCell[3] = obj.records.length - 1;\n        } else {\n            rightVisible.call(obj, 1, 0);\n        }\n    } else {\n        if (ctrlKey) {\n            obj.selectedCell[1] = obj.records.length - 1;\n        } else {\n            rightVisible.call(obj, 0, 0);\n        }\n        obj.selectedCell[2] = obj.selectedCell[0];\n        obj.selectedCell[3] = obj.selectedCell[1];\n    }\n\n    // Change page\n    if (obj.options.lazyLoading == true && (obj.selectedCell[1] == obj.records.length - 1 || obj.selectedCell[3] == obj.records.length - 1)) {\n        loadPage.call(obj, -1);\n    } else if (obj.options.pagination > 0) {\n        const pageNumber = obj.whichPage(obj.selectedCell[3]);\n        if (pageNumber != obj.pageNumber) {\n            obj.page(pageNumber);\n        }\n    }\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n    updateScroll.call(obj, 3);\n};\n"
  },
  {
    "path": "src/utils/lazyLoading.js",
    "content": "/**\n * Go to a page in a lazyLoading\n */\nexport const loadPage = function (pageNumber) {\n    const obj = this;\n\n    // Search\n    let results;\n\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        results = obj.results;\n    } else {\n        results = obj.rows;\n    }\n\n    // Per page\n    const quantityPerPage = 100;\n\n    // pageNumber\n    if (pageNumber == null || pageNumber == -1) {\n        // Last page\n        pageNumber = Math.ceil(results.length / quantityPerPage) - 1;\n    }\n\n    let startRow = pageNumber * quantityPerPage;\n    let finalRow = pageNumber * quantityPerPage + quantityPerPage;\n    if (finalRow > results.length) {\n        finalRow = results.length;\n    }\n    startRow = finalRow - 100;\n    if (startRow < 0) {\n        startRow = 0;\n    }\n\n    // Appeding items\n    for (let j = startRow; j < finalRow; j++) {\n        if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n            obj.tbody.appendChild(obj.rows[results[j]].element);\n        } else {\n            obj.tbody.appendChild(obj.rows[j].element);\n        }\n\n        if (obj.tbody.children.length > quantityPerPage) {\n            obj.tbody.removeChild(obj.tbody.firstChild);\n        }\n    }\n};\n\nexport const loadValidation = function () {\n    const obj = this;\n\n    if (obj.selectedCell) {\n        const currentPage = parseInt(obj.tbody.firstChild.getAttribute('data-y')) / 100;\n        const selectedPage = parseInt(obj.selectedCell[3] / 100);\n        const totalPages = parseInt(obj.rows.length / 100);\n\n        if (currentPage != selectedPage && selectedPage <= totalPages) {\n            if (!Array.prototype.indexOf.call(obj.tbody.children, obj.rows[obj.selectedCell[3]].element)) {\n                obj.loadPage(selectedPage);\n                return true;\n            }\n        }\n    }\n\n    return false;\n};\n\nexport const loadUp = function () {\n    const obj = this;\n\n    // Search\n    let results;\n\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        results = obj.results;\n    } else {\n        results = obj.rows;\n    }\n    let test = 0;\n    if (results.length > 100) {\n        // Get the first element in the page\n        let item = parseInt(obj.tbody.firstChild.getAttribute('data-y'));\n        if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n            item = results.indexOf(item);\n        }\n        if (item > 0) {\n            for (let j = 0; j < 30; j++) {\n                item = item - 1;\n                if (item > -1) {\n                    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n                        obj.tbody.insertBefore(obj.rows[results[item]].element, obj.tbody.firstChild);\n                    } else {\n                        obj.tbody.insertBefore(obj.rows[item].element, obj.tbody.firstChild);\n                    }\n                    if (obj.tbody.children.length > 100) {\n                        obj.tbody.removeChild(obj.tbody.lastChild);\n                        test = 1;\n                    }\n                }\n            }\n        }\n    }\n    return test;\n};\n\nexport const loadDown = function () {\n    const obj = this;\n\n    // Search\n    let results;\n\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        results = obj.results;\n    } else {\n        results = obj.rows;\n    }\n    let test = 0;\n    if (results.length > 100) {\n        // Get the last element in the page\n        let item = parseInt(obj.tbody.lastChild.getAttribute('data-y'));\n        if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n            item = results.indexOf(item);\n        }\n        if (item < obj.rows.length - 1) {\n            for (let j = 0; j <= 30; j++) {\n                if (item < results.length) {\n                    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n                        obj.tbody.appendChild(obj.rows[results[item]].element);\n                    } else {\n                        obj.tbody.appendChild(obj.rows[item].element);\n                    }\n                    if (obj.tbody.children.length > 100) {\n                        obj.tbody.removeChild(obj.tbody.firstChild);\n                        test = 1;\n                    }\n                }\n                item = item + 1;\n            }\n        }\n    }\n\n    return test;\n};\n"
  },
  {
    "path": "src/utils/libraryBase.js",
    "content": "const lib = {\n    jspreadsheet: {},\n};\n\nexport default lib;\n"
  },
  {
    "path": "src/utils/merges.js",
    "content": "import jSuites from 'jsuites';\n\nimport { getColumnNameFromId, getIdFromColumnName } from './internalHelpers.js';\nimport { updateCell } from './internal.js';\nimport { setHistory } from './history.js';\nimport dispatch from './dispatch.js';\nimport { updateSelection } from './selection.js';\n\n/**\n * Is column merged\n */\nexport const isColMerged = function (x, insertBefore) {\n    const obj = this;\n\n    const cols = [];\n    // Remove any merged cells\n    if (obj.options.mergeCells) {\n        const keys = Object.keys(obj.options.mergeCells);\n        for (let i = 0; i < keys.length; i++) {\n            const info = getIdFromColumnName(keys[i], true);\n            const colspan = obj.options.mergeCells[keys[i]][0];\n            const x1 = info[0];\n            const x2 = info[0] + (colspan > 1 ? colspan - 1 : 0);\n\n            if (insertBefore == null) {\n                if (x1 <= x && x2 >= x) {\n                    cols.push(keys[i]);\n                }\n            } else {\n                if (insertBefore) {\n                    if (x1 < x && x2 >= x) {\n                        cols.push(keys[i]);\n                    }\n                } else {\n                    if (x1 <= x && x2 > x) {\n                        cols.push(keys[i]);\n                    }\n                }\n            }\n        }\n    }\n\n    return cols;\n};\n\n/**\n * Is rows merged\n */\nexport const isRowMerged = function (y, insertBefore) {\n    const obj = this;\n\n    const rows = [];\n    // Remove any merged cells\n    if (obj.options.mergeCells) {\n        const keys = Object.keys(obj.options.mergeCells);\n        for (let i = 0; i < keys.length; i++) {\n            const info = getIdFromColumnName(keys[i], true);\n            const rowspan = obj.options.mergeCells[keys[i]][1];\n            const y1 = info[1];\n            const y2 = info[1] + (rowspan > 1 ? rowspan - 1 : 0);\n\n            if (insertBefore == null) {\n                if (y1 <= y && y2 >= y) {\n                    rows.push(keys[i]);\n                }\n            } else {\n                if (insertBefore) {\n                    if (y1 < y && y2 >= y) {\n                        rows.push(keys[i]);\n                    }\n                } else {\n                    if (y1 <= y && y2 > y) {\n                        rows.push(keys[i]);\n                    }\n                }\n            }\n        }\n    }\n\n    return rows;\n};\n\n/**\n * Merge cells\n * @param cellName\n * @param colspan\n * @param rowspan\n * @param ignoreHistoryAndEvents\n */\nexport const getMerge = function (cellName) {\n    const obj = this;\n\n    let data = {};\n    if (cellName) {\n        if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {\n            data = [obj.options.mergeCells[cellName][0], obj.options.mergeCells[cellName][1]];\n        } else {\n            data = null;\n        }\n    } else {\n        if (obj.options.mergeCells) {\n            var mergedCells = obj.options.mergeCells;\n            const keys = Object.keys(obj.options.mergeCells);\n            for (let i = 0; i < keys.length; i++) {\n                data[keys[i]] = [obj.options.mergeCells[keys[i]][0], obj.options.mergeCells[keys[i]][1]];\n            }\n        }\n    }\n\n    return data;\n};\n\n/**\n * Merge cells\n * @param cellName\n * @param colspan\n * @param rowspan\n * @param ignoreHistoryAndEvents\n */\nexport const setMerge = function (cellName, colspan, rowspan, ignoreHistoryAndEvents) {\n    const obj = this;\n\n    let test = false;\n\n    if (!cellName) {\n        if (!obj.highlighted.length) {\n            alert(jSuites.translate('No cells selected'));\n            return null;\n        } else {\n            const x1 = parseInt(obj.highlighted[0].getAttribute('data-x'));\n            const y1 = parseInt(obj.highlighted[0].getAttribute('data-y'));\n            const x2 = parseInt(obj.highlighted[obj.highlighted.length - 1].getAttribute('data-x'));\n            const y2 = parseInt(obj.highlighted[obj.highlighted.length - 1].getAttribute('data-y'));\n            cellName = getColumnNameFromId([x1, y1]);\n            colspan = x2 - x1 + 1;\n            rowspan = y2 - y1 + 1;\n        }\n    } else if (typeof cellName !== 'string') {\n        return null;\n    }\n\n    const cell = getIdFromColumnName(cellName, true);\n\n    if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {\n        if (obj.records[cell[1]][cell[0]].element.getAttribute('data-merged')) {\n            test = 'Cell already merged';\n        }\n    } else if ((!colspan || colspan < 2) && (!rowspan || rowspan < 2)) {\n        test = 'Invalid merged properties';\n    } else {\n        var cells = [];\n        for (let j = cell[1]; j < cell[1] + rowspan; j++) {\n            for (let i = cell[0]; i < cell[0] + colspan; i++) {\n                var columnName = getColumnNameFromId([i, j]);\n                if (obj.records[j][i].element.getAttribute('data-merged')) {\n                    test = 'There is a conflict with another merged cell';\n                }\n            }\n        }\n    }\n\n    if (test) {\n        alert(jSuites.translate(test));\n    } else {\n        // Add property\n        if (colspan > 1) {\n            obj.records[cell[1]][cell[0]].element.setAttribute('colspan', colspan);\n        } else {\n            colspan = 1;\n        }\n        if (rowspan > 1) {\n            obj.records[cell[1]][cell[0]].element.setAttribute('rowspan', rowspan);\n        } else {\n            rowspan = 1;\n        }\n        // Keep links to the existing nodes\n        if (!obj.options.mergeCells) {\n            obj.options.mergeCells = {};\n        }\n\n        obj.options.mergeCells[cellName] = [colspan, rowspan, []];\n        // Mark cell as merged\n        obj.records[cell[1]][cell[0]].element.setAttribute('data-merged', 'true');\n        // Overflow\n        obj.records[cell[1]][cell[0]].element.style.overflow = 'hidden';\n        // History data\n        const data = [];\n        // Adjust the nodes\n        for (let y = cell[1]; y < cell[1] + rowspan; y++) {\n            for (let x = cell[0]; x < cell[0] + colspan; x++) {\n                if (!(cell[0] == x && cell[1] == y)) {\n                    data.push(obj.options.data[y][x]);\n                    updateCell.call(obj, x, y, '', true);\n                    obj.options.mergeCells[cellName][2].push(obj.records[y][x].element);\n                    obj.records[y][x].element.style.display = 'none';\n                    obj.records[y][x].element = obj.records[cell[1]][cell[0]].element;\n                }\n            }\n        }\n        // In the initialization is not necessary keep the history\n        updateSelection.call(obj, obj.records[cell[1]][cell[0]].element);\n\n        if (!ignoreHistoryAndEvents) {\n            setHistory.call(obj, {\n                action: 'setMerge',\n                column: cellName,\n                colspan: colspan,\n                rowspan: rowspan,\n                data: data,\n            });\n\n            dispatch.call(obj, 'onmerge', obj, { [cellName]: [colspan, rowspan] });\n        }\n    }\n};\n\n/**\n * Remove merge by cellname\n * @param cellName\n */\nexport const removeMerge = function (cellName, data, keepOptions) {\n    const obj = this;\n\n    if (obj.options.mergeCells && obj.options.mergeCells[cellName]) {\n        const beforeMerges = { [cellName]: obj.options.mergeCells[cellName] };\n        const cell = getIdFromColumnName(cellName, true);\n        obj.records[cell[1]][cell[0]].element.removeAttribute('colspan');\n        obj.records[cell[1]][cell[0]].element.removeAttribute('rowspan');\n        obj.records[cell[1]][cell[0]].element.removeAttribute('data-merged');\n        const info = obj.options.mergeCells[cellName];\n\n        let index = 0;\n\n        let j, i;\n\n        for (j = 0; j < info[1]; j++) {\n            for (i = 0; i < info[0]; i++) {\n                if (j > 0 || i > 0) {\n                    obj.records[cell[1] + j][cell[0] + i].element = info[2][index];\n                    obj.records[cell[1] + j][cell[0] + i].element.style.display = '';\n                    // Recover data\n                    if (data && data[index]) {\n                        updateCell.call(obj, cell[0] + i, cell[1] + j, data[index]);\n                    }\n                    index++;\n                }\n            }\n        }\n\n        // Update selection\n        updateSelection.call(obj, obj.records[cell[1]][cell[0]].element, obj.records[cell[1] + j - 1][cell[0] + i - 1].element);\n\n        if (!keepOptions) {\n            delete obj.options.mergeCells[cellName];\n        }\n\n        dispatch.call(obj, 'onunmerge', obj, cellName, beforeMerges);\n    }\n};\n\n/**\n * Remove all merged cells\n */\nexport const destroyMerge = function (keepOptions) {\n    const obj = this;\n\n    // Remove any merged cells\n    if (obj.options.mergeCells) {\n        var mergedCells = obj.options.mergeCells;\n        const keys = Object.keys(obj.options.mergeCells);\n        for (let i = 0; i < keys.length; i++) {\n            removeMerge.call(obj, keys[i], null, keepOptions);\n        }\n    }\n};\n"
  },
  {
    "path": "src/utils/meta.js",
    "content": "import dispatch from './dispatch.js';\n\n/**\n * Get meta information from cell(s)\n *\n * @return integer\n */\nexport const getMeta = function (cell, key) {\n    const obj = this;\n\n    if (!cell) {\n        return obj.options.meta;\n    } else {\n        if (key) {\n            return obj.options.meta && obj.options.meta[cell] && obj.options.meta[cell][key] ? obj.options.meta[cell][key] : null;\n        } else {\n            return obj.options.meta && obj.options.meta[cell] ? obj.options.meta[cell] : null;\n        }\n    }\n};\n\n/**\n * Update meta information\n *\n * @return integer\n */\nexport const updateMeta = function (affectedCells) {\n    const obj = this;\n\n    if (obj.options.meta) {\n        const newMeta = {};\n        const keys = Object.keys(obj.options.meta);\n        for (let i = 0; i < keys.length; i++) {\n            if (affectedCells[keys[i]]) {\n                newMeta[affectedCells[keys[i]]] = obj.options.meta[keys[i]];\n            } else {\n                newMeta[keys[i]] = obj.options.meta[keys[i]];\n            }\n        }\n        // Update meta information\n        obj.options.meta = newMeta;\n    }\n};\n\n/**\n * Set meta information to cell(s)\n *\n * @return integer\n */\nexport const setMeta = function (o, k, v) {\n    const obj = this;\n\n    if (!obj.options.meta) {\n        obj.options.meta = {};\n    }\n\n    if (k && v) {\n        // Set data value\n        if (!obj.options.meta[o]) {\n            obj.options.meta[o] = {};\n        }\n        obj.options.meta[o][k] = v;\n\n        dispatch.call(obj, 'onchangemeta', obj, { [o]: { [k]: v } });\n    } else {\n        // Apply that for all cells\n        const keys = Object.keys(o);\n        for (let i = 0; i < keys.length; i++) {\n            if (!obj.options.meta[keys[i]]) {\n                obj.options.meta[keys[i]] = {};\n            }\n\n            const prop = Object.keys(o[keys[i]]);\n            for (let j = 0; j < prop.length; j++) {\n                obj.options.meta[keys[i]][prop[j]] = o[keys[i]][prop[j]];\n            }\n        }\n\n        dispatch.call(obj, 'onchangemeta', obj, o);\n    }\n};\n"
  },
  {
    "path": "src/utils/orderBy.js",
    "content": "import jSuites from 'jsuites';\nimport { setHistory } from './history.js';\nimport dispatch from './dispatch.js';\nimport { updateTableReferences } from './internal.js';\nimport { loadPage } from './lazyLoading.js';\nimport { closeFilter } from './filter.js';\n\n/**\n * Update order arrow\n */\nexport const updateOrderArrow = function (column, order) {\n    const obj = this;\n\n    // Remove order\n    for (let i = 0; i < obj.headers.length; i++) {\n        obj.headers[i].classList.remove('arrow-up');\n        obj.headers[i].classList.remove('arrow-down');\n    }\n\n    // No order specified then toggle order\n    if (order) {\n        obj.headers[column].classList.add('arrow-up');\n    } else {\n        obj.headers[column].classList.add('arrow-down');\n    }\n};\n\n/**\n * Update rows position\n */\nexport const updateOrder = function (rows) {\n    const obj = this;\n\n    // History\n    let data = [];\n    for (let j = 0; j < rows.length; j++) {\n        data[j] = obj.options.data[rows[j]];\n    }\n    obj.options.data = data;\n\n    data = [];\n    for (let j = 0; j < rows.length; j++) {\n        data[j] = obj.records[rows[j]];\n\n        for (let i = 0; i < data[j].length; i++) {\n            data[j][i].y = j;\n        }\n    }\n    obj.records = data;\n\n    data = [];\n    for (let j = 0; j < rows.length; j++) {\n        data[j] = obj.rows[rows[j]];\n        data[j].y = j;\n    }\n    obj.rows = data;\n\n    // Update references\n    updateTableReferences.call(obj);\n\n    // Redo search\n    if (obj.results && obj.results.length) {\n        if (obj.searchInput.value) {\n            obj.search(obj.searchInput.value);\n        } else {\n            closeFilter.call(obj);\n        }\n    } else {\n        // Create page\n        obj.results = null;\n        obj.pageNumber = 0;\n\n        if (obj.options.pagination > 0) {\n            obj.page(0);\n        } else if (obj.options.lazyLoading == true) {\n            loadPage.call(obj, 0);\n        } else {\n            for (let j = 0; j < obj.rows.length; j++) {\n                obj.tbody.appendChild(obj.rows[j].element);\n            }\n        }\n    }\n};\n\n/**\n * Sort data and reload table\n */\nexport const orderBy = function (column, order) {\n    const obj = this;\n\n    if (column >= 0) {\n        // Merged cells\n        if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n            if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                return false;\n            } else {\n                // Remove merged cells\n                obj.destroyMerge();\n            }\n        }\n\n        // Direction\n        if (order == null) {\n            order = obj.headers[column].classList.contains('arrow-down') ? 1 : 0;\n        } else {\n            order = order ? 1 : 0;\n        }\n\n        // Test order\n        let temp = [];\n        if (\n            obj.options.columns &&\n            obj.options.columns[column] &&\n            (obj.options.columns[column].type == 'number' ||\n                obj.options.columns[column].type == 'numeric' ||\n                obj.options.columns[column].type == 'percentage' ||\n                obj.options.columns[column].type == 'autonumber' ||\n                obj.options.columns[column].type == 'color')\n        ) {\n            for (let j = 0; j < obj.options.data.length; j++) {\n                temp[j] = [j, Number(obj.options.data[j][column])];\n            }\n        } else if (\n            obj.options.columns &&\n            obj.options.columns[column] &&\n            (obj.options.columns[column].type == 'calendar' || obj.options.columns[column].type == 'checkbox' || obj.options.columns[column].type == 'radio')\n        ) {\n            for (let j = 0; j < obj.options.data.length; j++) {\n                temp[j] = [j, obj.options.data[j][column]];\n            }\n        } else {\n            for (let j = 0; j < obj.options.data.length; j++) {\n                temp[j] = [j, obj.records[j][column].element.textContent.toLowerCase()];\n            }\n        }\n\n        // Default sorting method\n        if (typeof obj.parent.config.sorting !== 'function') {\n            obj.parent.config.sorting = function (direction) {\n                return function (a, b) {\n                    const valueA = a[1];\n                    const valueB = b[1];\n\n                    if (!direction) {\n                        return valueA === '' && valueB !== '' ? 1 : valueA !== '' && valueB === '' ? -1 : valueA > valueB ? 1 : valueA < valueB ? -1 : 0;\n                    } else {\n                        return valueA === '' && valueB !== '' ? 1 : valueA !== '' && valueB === '' ? -1 : valueA > valueB ? -1 : valueA < valueB ? 1 : 0;\n                    }\n                };\n            };\n        }\n\n        temp = temp.sort(obj.parent.config.sorting(order));\n\n        // Save history\n        const newValue = [];\n        for (let j = 0; j < temp.length; j++) {\n            newValue[j] = temp[j][0];\n        }\n\n        // Save history\n        setHistory.call(obj, {\n            action: 'orderBy',\n            rows: newValue,\n            column: column,\n            order: order,\n        });\n\n        // Update order\n        updateOrderArrow.call(obj, column, order);\n        updateOrder.call(obj, newValue);\n\n        // On sort event\n        dispatch.call(\n            obj,\n            'onsort',\n            obj,\n            column,\n            order,\n            newValue.map((row) => row)\n        );\n\n        return true;\n    }\n};\n"
  },
  {
    "path": "src/utils/pagination.js",
    "content": "import jSuites from 'jsuites';\n\nimport dispatch from './dispatch.js';\nimport { updateCornerPosition } from './selection.js';\n\n/**\n * Which page the row is\n */\nexport const whichPage = function (row) {\n    const obj = this;\n\n    // Search\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        row = obj.results.indexOf(row);\n    }\n\n    return Math.ceil((parseInt(row) + 1) / parseInt(obj.options.pagination)) - 1;\n};\n\n/**\n * Update the pagination\n */\nexport const updatePagination = function () {\n    const obj = this;\n\n    // Reset container\n    obj.pagination.children[0].innerHTML = '';\n    obj.pagination.children[1].innerHTML = '';\n\n    // Start pagination\n    if (obj.options.pagination) {\n        // Searchable\n        let results;\n\n        if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n            results = obj.results.length;\n        } else {\n            results = obj.rows.length;\n        }\n\n        if (!results) {\n            // No records found\n            obj.pagination.children[0].innerHTML = jSuites.translate('No records found');\n        } else {\n            // Pagination container\n            const quantyOfPages = Math.ceil(results / obj.options.pagination);\n\n            let startNumber, finalNumber;\n\n            if (obj.pageNumber < 6) {\n                startNumber = 1;\n                finalNumber = quantyOfPages < 10 ? quantyOfPages : 10;\n            } else if (quantyOfPages - obj.pageNumber < 5) {\n                startNumber = quantyOfPages - 9;\n                finalNumber = quantyOfPages;\n                if (startNumber < 1) {\n                    startNumber = 1;\n                }\n            } else {\n                startNumber = obj.pageNumber - 4;\n                finalNumber = obj.pageNumber + 5;\n            }\n\n            // First\n            if (startNumber > 1) {\n                const paginationItem = document.createElement('div');\n                paginationItem.className = 'jss_page';\n                paginationItem.innerHTML = '<';\n                paginationItem.title = 1;\n                obj.pagination.children[1].appendChild(paginationItem);\n            }\n\n            // Get page links\n            for (let i = startNumber; i <= finalNumber; i++) {\n                const paginationItem = document.createElement('div');\n                paginationItem.className = 'jss_page';\n                paginationItem.innerHTML = i;\n                obj.pagination.children[1].appendChild(paginationItem);\n\n                if (obj.pageNumber == i - 1) {\n                    paginationItem.classList.add('jss_page_selected');\n                }\n            }\n\n            // Last\n            if (finalNumber < quantyOfPages) {\n                const paginationItem = document.createElement('div');\n                paginationItem.className = 'jss_page';\n                paginationItem.innerHTML = '>';\n                paginationItem.title = quantyOfPages;\n                obj.pagination.children[1].appendChild(paginationItem);\n            }\n\n            // Text\n            const format = function (format) {\n                const args = Array.prototype.slice.call(arguments, 1);\n                return format.replace(/{(\\d+)}/g, function (match, number) {\n                    return typeof args[number] != 'undefined' ? args[number] : match;\n                });\n            };\n\n            obj.pagination.children[0].innerHTML = format(jSuites.translate('Showing page {0} of {1} entries'), obj.pageNumber + 1, quantyOfPages);\n        }\n    }\n};\n\n/**\n * Go to page\n */\nexport const page = function (pageNumber) {\n    const obj = this;\n\n    const oldPage = obj.pageNumber;\n\n    // Search\n    let results;\n\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        results = obj.results;\n    } else {\n        results = obj.rows;\n    }\n\n    // Per page\n    const quantityPerPage = parseInt(obj.options.pagination);\n\n    // pageNumber\n    if (pageNumber == null || pageNumber == -1) {\n        // Last page\n        pageNumber = Math.ceil(results.length / quantityPerPage) - 1;\n    }\n\n    // Page number\n    obj.pageNumber = pageNumber;\n\n    let startRow = pageNumber * quantityPerPage;\n    let finalRow = pageNumber * quantityPerPage + quantityPerPage;\n    if (finalRow > results.length) {\n        finalRow = results.length;\n    }\n    if (startRow < 0) {\n        startRow = 0;\n    }\n\n    // Reset container\n    while (obj.tbody.firstChild) {\n        obj.tbody.removeChild(obj.tbody.firstChild);\n    }\n\n    // Appeding items\n    for (let j = startRow; j < finalRow; j++) {\n        if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n            obj.tbody.appendChild(obj.rows[results[j]].element);\n        } else {\n            obj.tbody.appendChild(obj.rows[j].element);\n        }\n    }\n\n    if (obj.options.pagination > 0) {\n        updatePagination.call(obj);\n    }\n\n    // Update corner position\n    updateCornerPosition.call(obj);\n\n    // Events\n    dispatch.call(obj, 'onchangepage', obj, pageNumber, oldPage, obj.options.pagination);\n};\n\nexport const quantiyOfPages = function () {\n    const obj = this;\n\n    let results;\n    if ((obj.options.search == true || obj.options.filters == true) && obj.results) {\n        results = obj.results.length;\n    } else {\n        results = obj.rows.length;\n    }\n\n    return Math.ceil(results / obj.options.pagination);\n};\n"
  },
  {
    "path": "src/utils/rows.js",
    "content": "import jSuites from 'jsuites';\nimport { getNumberOfColumns } from './columns.js';\nimport { createCell, updateTableReferences } from './internal.js';\nimport dispatch from './dispatch.js';\nimport { isRowMerged } from './merges.js';\nimport { conditionalSelectionUpdate, getSelectedRows, updateCornerPosition } from './selection.js';\nimport { setHistory } from './history.js';\nimport { getColumnNameFromId } from './internalHelpers.js';\n\n/**\n * Create row\n */\nexport const createRow = function (j, data) {\n    const obj = this;\n\n    // Create container\n    if (!obj.records[j]) {\n        obj.records[j] = [];\n    }\n    // Default data\n    if (!data) {\n        data = obj.options.data[j];\n    }\n    // New line of data to be append in the table\n    const row = {\n        element: document.createElement('tr'),\n        y: j,\n    };\n\n    obj.rows[j] = row;\n\n    row.element.setAttribute('data-y', j);\n    // Index\n    let index = null;\n\n    // Set default row height\n    if (obj.options.defaultRowHeight) {\n        row.element.style.height = obj.options.defaultRowHeight + 'px';\n    }\n\n    // Definitions\n    if (obj.options.rows && obj.options.rows[j]) {\n        if (obj.options.rows[j].height) {\n            row.element.style.height = obj.options.rows[j].height;\n        }\n        if (obj.options.rows[j].title) {\n            index = obj.options.rows[j].title;\n        }\n    }\n    if (!index) {\n        index = parseInt(j + 1);\n    }\n    // Row number label\n    const td = document.createElement('td');\n    td.innerHTML = index;\n    td.setAttribute('data-y', j);\n    td.className = 'jss_row';\n    row.element.appendChild(td);\n\n    const numberOfColumns = getNumberOfColumns.call(obj);\n\n    // Data columns\n    for (let i = 0; i < numberOfColumns; i++) {\n        // New column of data to be append in the line\n        obj.records[j][i] = {\n            element: createCell.call(this, i, j, data[i]),\n            x: i,\n            y: j,\n        };\n        // Add column to the row\n        row.element.appendChild(obj.records[j][i].element);\n\n        if (obj.options.columns && obj.options.columns[i] && typeof obj.options.columns[i].render === 'function') {\n            obj.options.columns[i].render(obj.records[j][i].element, data[i], parseInt(i), parseInt(j), obj, obj.options.columns[i]);\n        }\n    }\n\n    // Add row to the table body\n    return row;\n};\n\n/**\n * Insert a new row\n *\n * @param mixed - number of blank lines to be insert or a single array with the data of the new row\n * @param rowNumber\n * @param insertBefore\n * @return void\n */\nexport const insertRow = function (mixed, rowNumber, insertBefore) {\n    const obj = this;\n\n    // Configuration\n    if (obj.options.allowInsertRow != false) {\n        // Records\n        var records = [];\n\n        // Data to be insert\n        let data = [];\n\n        // The insert could be lead by number of rows or the array of data\n        let numOfRows;\n\n        if (!Array.isArray(mixed)) {\n            numOfRows = typeof mixed !== 'undefined' ? mixed : 1;\n        } else {\n            numOfRows = 1;\n\n            if (mixed) {\n                data = mixed;\n            }\n        }\n\n        // Direction\n        insertBefore = insertBefore ? true : false;\n\n        // Current column number\n        const lastRow = obj.options.data.length - 1;\n\n        if (rowNumber == undefined || rowNumber >= parseInt(lastRow) || rowNumber < 0) {\n            rowNumber = lastRow;\n        }\n\n        const onbeforeinsertrowRecords = [];\n\n        for (let row = 0; row < numOfRows; row++) {\n            const newRow = [];\n\n            for (let col = 0; col < obj.options.columns.length; col++) {\n                newRow[col] = data[col] ? data[col] : '';\n            }\n\n            onbeforeinsertrowRecords.push({\n                row: row + rowNumber + (insertBefore ? 0 : 1),\n                data: newRow,\n            });\n        }\n\n        // Onbeforeinsertrow\n        if (dispatch.call(obj, 'onbeforeinsertrow', obj, onbeforeinsertrowRecords) === false) {\n            return false;\n        }\n\n        // Merged cells\n        if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n            if (isRowMerged.call(obj, rowNumber, insertBefore).length) {\n                if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                    return false;\n                } else {\n                    obj.destroyMerge();\n                }\n            }\n        }\n\n        // Clear any search\n        if (obj.options.search == true) {\n            if (obj.results && obj.results.length != obj.rows.length) {\n                if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {\n                    obj.resetSearch();\n                } else {\n                    return false;\n                }\n            }\n\n            obj.results = null;\n        }\n\n        // Insertbefore\n        const rowIndex = !insertBefore ? rowNumber + 1 : rowNumber;\n\n        // Keep the current data\n        const currentRecords = obj.records.splice(rowIndex);\n        const currentData = obj.options.data.splice(rowIndex);\n        const currentRows = obj.rows.splice(rowIndex);\n\n        // Adding lines\n        const rowRecords = [];\n        const rowData = [];\n        const rowNode = [];\n\n        for (let row = rowIndex; row < numOfRows + rowIndex; row++) {\n            // Push data to the data container\n            obj.options.data[row] = [];\n            for (let col = 0; col < obj.options.columns.length; col++) {\n                obj.options.data[row][col] = data[col] ? data[col] : '';\n            }\n            // Create row\n            const newRow = createRow.call(obj, row, obj.options.data[row]);\n            // Append node\n            if (currentRows[0]) {\n                if (Array.prototype.indexOf.call(obj.tbody.children, currentRows[0].element) >= 0) {\n                    obj.tbody.insertBefore(newRow.element, currentRows[0].element);\n                }\n            } else {\n                if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[rowNumber].element) >= 0) {\n                    obj.tbody.appendChild(newRow.element);\n                }\n            }\n            // Record History\n            rowRecords.push([...obj.records[row]]);\n            rowData.push([...obj.options.data[row]]);\n            rowNode.push(newRow);\n        }\n\n        // Copy the data back to the main data\n        Array.prototype.push.apply(obj.records, currentRecords);\n        Array.prototype.push.apply(obj.options.data, currentData);\n        Array.prototype.push.apply(obj.rows, currentRows);\n\n        for (let j = rowIndex; j < obj.rows.length; j++) {\n            obj.rows[j].y = j;\n        }\n\n        for (let j = rowIndex; j < obj.records.length; j++) {\n            for (let i = 0; i < obj.records[j].length; i++) {\n                obj.records[j][i].y = j;\n            }\n        }\n\n        // Respect pagination\n        if (obj.options.pagination > 0) {\n            obj.page(obj.pageNumber);\n        }\n\n        // Keep history\n        setHistory.call(obj, {\n            action: 'insertRow',\n            rowNumber: rowNumber,\n            numOfRows: numOfRows,\n            insertBefore: insertBefore,\n            rowRecords: rowRecords,\n            rowData: rowData,\n            rowNode: rowNode,\n        });\n\n        // Remove table references\n        updateTableReferences.call(obj);\n\n        // Events\n        dispatch.call(obj, 'oninsertrow', obj, onbeforeinsertrowRecords);\n    }\n};\n\n/**\n * Move row\n *\n * @return void\n */\nexport const moveRow = function (o, d, ignoreDom) {\n    const obj = this;\n\n    if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n        let insertBefore;\n\n        if (o > d) {\n            insertBefore = 1;\n        } else {\n            insertBefore = 0;\n        }\n\n        if (isRowMerged.call(obj, o).length || isRowMerged.call(obj, d, insertBefore).length) {\n            if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                return false;\n            } else {\n                obj.destroyMerge();\n            }\n        }\n    }\n\n    if (obj.options.search == true) {\n        if (obj.results && obj.results.length != obj.rows.length) {\n            if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {\n                obj.resetSearch();\n            } else {\n                return false;\n            }\n        }\n\n        obj.results = null;\n    }\n\n    if (!ignoreDom) {\n        if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[d].element) >= 0) {\n            if (o > d) {\n                obj.tbody.insertBefore(obj.rows[o].element, obj.rows[d].element);\n            } else {\n                obj.tbody.insertBefore(obj.rows[o].element, obj.rows[d].element.nextSibling);\n            }\n        } else {\n            obj.tbody.removeChild(obj.rows[o].element);\n        }\n    }\n\n    // Place references in the correct position\n    obj.rows.splice(d, 0, obj.rows.splice(o, 1)[0]);\n    obj.records.splice(d, 0, obj.records.splice(o, 1)[0]);\n    obj.options.data.splice(d, 0, obj.options.data.splice(o, 1)[0]);\n\n    const firstAffectedIndex = Math.min(o, d);\n    const lastAffectedIndex = Math.max(o, d);\n\n    for (let j = firstAffectedIndex; j <= lastAffectedIndex; j++) {\n        obj.rows[j].y = j;\n    }\n\n    for (let j = firstAffectedIndex; j <= lastAffectedIndex; j++) {\n        for (let i = 0; i < obj.records[j].length; i++) {\n            obj.records[j][i].y = j;\n        }\n    }\n\n    // Respect pagination\n    if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {\n        obj.page(obj.pageNumber);\n    }\n\n    // Keeping history of changes\n    setHistory.call(obj, {\n        action: 'moveRow',\n        oldValue: o,\n        newValue: d,\n    });\n\n    // Update table references\n    updateTableReferences.call(obj);\n\n    // Events\n    dispatch.call(obj, 'onmoverow', obj, parseInt(o), parseInt(d), 1);\n};\n\n/**\n * Delete a row by number\n *\n * @param integer rowNumber - row number to be excluded\n * @param integer numOfRows - number of lines\n * @return void\n */\nexport const deleteRow = function (rowNumber, numOfRows) {\n    const obj = this;\n\n    // Global Configuration\n    if (obj.options.allowDeleteRow != false) {\n        if (obj.options.allowDeletingAllRows == true || obj.options.data.length > 1) {\n            // Delete row definitions\n            if (rowNumber == undefined) {\n                const number = getSelectedRows.call(obj);\n\n                if (number.length === 0) {\n                    rowNumber = obj.options.data.length - 1;\n                    numOfRows = 1;\n                } else {\n                    rowNumber = number[0];\n                    numOfRows = number.length;\n                }\n            }\n\n            // Last column\n            let lastRow = obj.options.data.length - 1;\n\n            if (rowNumber == undefined || rowNumber > lastRow || rowNumber < 0) {\n                rowNumber = lastRow;\n            }\n\n            if (!numOfRows) {\n                numOfRows = 1;\n            }\n\n            // Do not delete more than the number of records\n            if (rowNumber + numOfRows >= obj.options.data.length) {\n                numOfRows = obj.options.data.length - rowNumber;\n            }\n\n            // Onbeforedeleterow\n            const onbeforedeleterowRecords = [];\n            for (let i = 0; i < numOfRows; i++) {\n                onbeforedeleterowRecords.push(i + rowNumber);\n            }\n\n            if (dispatch.call(obj, 'onbeforedeleterow', obj, onbeforedeleterowRecords) === false) {\n                return false;\n            }\n\n            if (parseInt(rowNumber) > -1) {\n                // Merged cells\n                let mergeExists = false;\n                if (obj.options.mergeCells && Object.keys(obj.options.mergeCells).length > 0) {\n                    for (let row = rowNumber; row < rowNumber + numOfRows; row++) {\n                        if (isRowMerged.call(obj, row, false).length) {\n                            mergeExists = true;\n                        }\n                    }\n                }\n                if (mergeExists) {\n                    if (!confirm(jSuites.translate('This action will destroy any existing merged cells. Are you sure?'))) {\n                        return false;\n                    } else {\n                        obj.destroyMerge();\n                    }\n                }\n\n                // Clear any search\n                if (obj.options.search == true) {\n                    if (obj.results && obj.results.length != obj.rows.length) {\n                        if (confirm(jSuites.translate('This action will clear your search results. Are you sure?'))) {\n                            obj.resetSearch();\n                        } else {\n                            return false;\n                        }\n                    }\n\n                    obj.results = null;\n                }\n\n                // If delete all rows, and set allowDeletingAllRows false, will stay one row\n                if (obj.options.allowDeletingAllRows != true && lastRow + 1 === numOfRows) {\n                    numOfRows--;\n                    console.error('Jspreadsheet: It is not possible to delete the last row');\n                }\n\n                // Remove node\n                for (let row = rowNumber; row < rowNumber + numOfRows; row++) {\n                    if (Array.prototype.indexOf.call(obj.tbody.children, obj.rows[row].element) >= 0) {\n                        obj.rows[row].element.className = '';\n                        obj.rows[row].element.parentNode.removeChild(obj.rows[row].element);\n                    }\n                }\n\n                // Remove data\n                const rowRecords = obj.records.splice(rowNumber, numOfRows);\n                const rowData = obj.options.data.splice(rowNumber, numOfRows);\n                const rowNode = obj.rows.splice(rowNumber, numOfRows);\n\n                for (let j = rowNumber; j < obj.rows.length; j++) {\n                    obj.rows[j].y = j;\n                }\n\n                for (let j = rowNumber; j < obj.records.length; j++) {\n                    for (let i = 0; i < obj.records[j].length; i++) {\n                        obj.records[j][i].y = j;\n                    }\n                }\n\n                // Respect pagination\n                if (obj.options.pagination > 0 && obj.tbody.children.length != obj.options.pagination) {\n                    obj.page(obj.pageNumber);\n                }\n\n                // Remove selection\n                conditionalSelectionUpdate.call(obj, 1, rowNumber, rowNumber + numOfRows - 1);\n\n                // Keep history\n                setHistory.call(obj, {\n                    action: 'deleteRow',\n                    rowNumber: rowNumber,\n                    numOfRows: numOfRows,\n                    insertBefore: 1,\n                    rowRecords: rowRecords,\n                    rowData: rowData,\n                    rowNode: rowNode,\n                });\n\n                // Remove table references\n                updateTableReferences.call(obj);\n\n                // Events\n                dispatch.call(obj, 'ondeleterow', obj, onbeforedeleterowRecords);\n            }\n        } else {\n            console.error('Jspreadsheet: It is not possible to delete the last row');\n        }\n    }\n};\n\n/**\n * Get the row height\n *\n * @param row - row number (first row is: 0)\n * @return height - current row height\n */\nexport const getHeight = function (row) {\n    const obj = this;\n\n    let data;\n\n    if (typeof row === 'undefined') {\n        // Get height of all rows\n        data = [];\n        for (let j = 0; j < obj.rows.length; j++) {\n            const h = obj.rows[j].element.style.height;\n            if (h) {\n                data[j] = h;\n            }\n        }\n    } else {\n        // In case the row is an object\n        if (typeof row == 'object') {\n            row = row.getAttribute('data-y');\n        }\n\n        data = obj.rows[row].element.style.height;\n    }\n\n    return data;\n};\n\n/**\n * Set the row height\n *\n * @param row - row number (first row is: 0)\n * @param height - new row height\n * @param oldHeight - old row height\n */\nexport const setHeight = function (row, height, oldHeight) {\n    const obj = this;\n\n    if (height > 0) {\n        // Oldwidth\n        if (!oldHeight) {\n            oldHeight = obj.rows[row].element.getAttribute('height');\n\n            if (!oldHeight) {\n                const rect = obj.rows[row].element.getBoundingClientRect();\n                oldHeight = rect.height;\n            }\n        }\n\n        // Integer\n        height = parseInt(height);\n\n        // Set width\n        obj.rows[row].element.style.height = height + 'px';\n\n        if (!obj.options.rows) {\n            obj.options.rows = [];\n        }\n\n        // Keep options updated\n        if (!obj.options.rows[row]) {\n            obj.options.rows[row] = {};\n        }\n        obj.options.rows[row].height = height;\n\n        // Keeping history of changes\n        setHistory.call(obj, {\n            action: 'setHeight',\n            row: row,\n            oldValue: oldHeight,\n            newValue: height,\n        });\n\n        // On resize column\n        dispatch.call(obj, 'onresizerow', obj, row, height, oldHeight);\n\n        // Update corner position\n        updateCornerPosition.call(obj);\n    }\n};\n\n/**\n * Show row\n */\nexport const showRow = function (rowNumber) {\n    const obj = this;\n\n    if (!Array.isArray(rowNumber)) {\n        rowNumber = [rowNumber];\n    }\n\n    rowNumber.forEach(function (rowIndex) {\n        obj.rows[rowIndex].element.style.display = '';\n    });\n};\n\n/**\n * Hide row\n */\nexport const hideRow = function (rowNumber) {\n    const obj = this;\n\n    if (!Array.isArray(rowNumber)) {\n        rowNumber = [rowNumber];\n    }\n\n    rowNumber.forEach(function (rowIndex) {\n        obj.rows[rowIndex].element.style.display = 'none';\n    });\n};\n\n/**\n * Get a row data by rowNumber\n */\nexport const getRowData = function (rowNumber, processed) {\n    const obj = this;\n\n    if (processed) {\n        return obj.records[rowNumber].map(function (record) {\n            return record.element.innerHTML;\n        });\n    } else {\n        return obj.options.data[rowNumber];\n    }\n};\n\n/**\n * Set a row data by rowNumber\n */\nexport const setRowData = function (rowNumber, data, force) {\n    const obj = this;\n\n    for (let i = 0; i < obj.headers.length; i++) {\n        // Update cell\n        const columnName = getColumnNameFromId([i, rowNumber]);\n        // Set value\n        if (data[i] != null) {\n            obj.setValue(columnName, data[i], force);\n        }\n    }\n};\n"
  },
  {
    "path": "src/utils/search.js",
    "content": "import { resetFilters } from './filter.js';\nimport { getIdFromColumnName } from './internalHelpers.js';\nimport { updateResult } from './internal.js';\nimport { isRowMerged } from './merges.js';\n\n/**\n * Search\n */\nexport const search = function (query) {\n    const obj = this;\n\n    // Reset any filter\n    if (obj.options.filters) {\n        resetFilters.call(obj);\n    }\n\n    // Reset selection\n    obj.resetSelection();\n\n    // Total of results\n    obj.pageNumber = 0;\n    obj.results = [];\n\n    if (query) {\n        if (obj.searchInput.value !== query) {\n            obj.searchInput.value = query;\n        }\n\n        // Search filter\n        const search = function (item, query, index) {\n            for (let i = 0; i < item.length; i++) {\n                if (('' + item[i]).toLowerCase().search(query) >= 0 || ('' + obj.records[index][i].element.innerHTML).toLowerCase().search(query) >= 0) {\n                    return true;\n                }\n            }\n            return false;\n        };\n\n        // Result\n        const addToResult = function (k) {\n            if (obj.results.indexOf(k) == -1) {\n                obj.results.push(k);\n            }\n        };\n\n        let parsedQuery = query.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&');\n        parsedQuery = new RegExp(parsedQuery, 'i');\n\n        // Filter\n        obj.options.data.forEach(function (v, k) {\n            if (search(v, parsedQuery, k)) {\n                // Merged rows found\n                const rows = isRowMerged.call(obj, k);\n                if (rows.length) {\n                    for (let i = 0; i < rows.length; i++) {\n                        const row = getIdFromColumnName(rows[i], true);\n                        for (let j = 0; j < obj.options.mergeCells[rows[i]][1]; j++) {\n                            addToResult(row[1] + j);\n                        }\n                    }\n                } else {\n                    // Normal row found\n                    addToResult(k);\n                }\n            }\n        });\n    } else {\n        obj.results = null;\n    }\n\n    updateResult.call(obj);\n};\n\n/**\n * Reset search\n */\nexport const resetSearch = function () {\n    const obj = this;\n\n    obj.searchInput.value = '';\n    obj.search('');\n    obj.results = null;\n};\n"
  },
  {
    "path": "src/utils/selection.js",
    "content": "import dispatch from './dispatch.js';\nimport { getFreezeWidth } from './freeze.js';\nimport { getCellNameFromCoords } from './helpers.js';\nimport { setHistory } from './history.js';\nimport { updateCell, updateFormula, updateFormulaChain, updateTable } from './internal.js';\nimport { getColumnNameFromId, getIdFromColumnName } from './internalHelpers.js';\nimport { updateToolbar } from './toolbar.js';\n\nexport const updateCornerPosition = function () {\n    const obj = this;\n\n    // If any selected cells\n    if (!obj.highlighted || !obj.highlighted.length) {\n        obj.corner.style.top = '-2000px';\n        obj.corner.style.left = '-2000px';\n    } else {\n        // Get last cell\n        const last = obj.highlighted[obj.highlighted.length - 1].element;\n        const lastX = last.getAttribute('data-x');\n\n        const contentRect = obj.content.getBoundingClientRect();\n        const x1 = contentRect.left;\n        const y1 = contentRect.top;\n\n        const lastRect = last.getBoundingClientRect();\n        const x2 = lastRect.left;\n        const y2 = lastRect.top;\n        const w2 = lastRect.width;\n        const h2 = lastRect.height;\n\n        const x = x2 - x1 + obj.content.scrollLeft + w2 - 4;\n        const y = y2 - y1 + obj.content.scrollTop + h2 - 4;\n\n        // Place the corner in the correct place\n        obj.corner.style.top = y + 'px';\n        obj.corner.style.left = x + 'px';\n\n        if (obj.options.freezeColumns) {\n            const width = getFreezeWidth.call(obj);\n            // Only check if the last column is not part of the merged cells\n            if (lastX > obj.options.freezeColumns - 1 && x2 - x1 + w2 < width) {\n                obj.corner.style.display = 'none';\n            } else {\n                if (obj.options.selectionCopy != false) {\n                    obj.corner.style.display = '';\n                }\n            }\n        } else {\n            if (obj.options.selectionCopy != false) {\n                obj.corner.style.display = '';\n            }\n        }\n    }\n\n    updateToolbar(obj);\n};\n\nexport const resetSelection = function (blur) {\n    const obj = this;\n\n    let previousStatus;\n\n    // Remove style\n    if (!obj.highlighted || !obj.highlighted.length) {\n        previousStatus = 0;\n    } else {\n        previousStatus = 1;\n\n        for (let i = 0; i < obj.highlighted.length; i++) {\n            obj.highlighted[i].element.classList.remove('highlight');\n            obj.highlighted[i].element.classList.remove('highlight-left');\n            obj.highlighted[i].element.classList.remove('highlight-right');\n            obj.highlighted[i].element.classList.remove('highlight-top');\n            obj.highlighted[i].element.classList.remove('highlight-bottom');\n            obj.highlighted[i].element.classList.remove('highlight-selected');\n\n            const px = parseInt(obj.highlighted[i].element.getAttribute('data-x'));\n            const py = parseInt(obj.highlighted[i].element.getAttribute('data-y'));\n\n            // Check for merged cells\n            let ux, uy;\n\n            if (obj.highlighted[i].element.getAttribute('data-merged')) {\n                const colspan = parseInt(obj.highlighted[i].element.getAttribute('colspan'));\n                const rowspan = parseInt(obj.highlighted[i].element.getAttribute('rowspan'));\n                ux = colspan > 0 ? px + (colspan - 1) : px;\n                uy = rowspan > 0 ? py + (rowspan - 1) : py;\n            } else {\n                ux = px;\n                uy = py;\n            }\n\n            // Remove selected from headers\n            for (let j = px; j <= ux; j++) {\n                if (obj.headers[j]) {\n                    obj.headers[j].classList.remove('selected');\n                }\n            }\n\n            // Remove selected from rows\n            for (let j = py; j <= uy; j++) {\n                if (obj.rows[j]) {\n                    obj.rows[j].element.classList.remove('selected');\n                }\n            }\n        }\n    }\n\n    // Reset highlighted cells\n    obj.highlighted = [];\n\n    // Reset\n    obj.selectedCell = null;\n\n    // Hide corner\n    obj.corner.style.top = '-2000px';\n    obj.corner.style.left = '-2000px';\n\n    if (blur == true && previousStatus == 1) {\n        dispatch.call(obj, 'onblur', obj);\n    }\n\n    return previousStatus;\n};\n\n/**\n * Update selection based on two cells\n */\nexport const updateSelection = function (el1, el2, origin) {\n    const obj = this;\n\n    const x1 = el1.getAttribute('data-x');\n    const y1 = el1.getAttribute('data-y');\n\n    let x2, y2;\n    if (el2) {\n        x2 = el2.getAttribute('data-x');\n        y2 = el2.getAttribute('data-y');\n    } else {\n        x2 = x1;\n        y2 = y1;\n    }\n\n    updateSelectionFromCoords.call(obj, x1, y1, x2, y2, origin);\n};\n\nexport const removeCopyingSelection = function () {\n    const copying = document.querySelectorAll('.jss_worksheet .copying');\n    for (let i = 0; i < copying.length; i++) {\n        copying[i].classList.remove('copying');\n        copying[i].classList.remove('copying-left');\n        copying[i].classList.remove('copying-right');\n        copying[i].classList.remove('copying-top');\n        copying[i].classList.remove('copying-bottom');\n    }\n};\n\nexport const updateSelectionFromCoords = function (x1, y1, x2, y2, origin) {\n    const obj = this;\n\n    // select column\n    if (y1 == null) {\n        y1 = 0;\n        y2 = obj.rows.length - 1;\n\n        if (x1 == null) {\n            return;\n        }\n    } else if (x1 == null) {\n        // select row\n        x1 = 0;\n        x2 = obj.options.data[0].length - 1;\n    }\n\n    // Same element\n    if (x2 == null) {\n        x2 = x1;\n    }\n    if (y2 == null) {\n        y2 = y1;\n    }\n\n    // Selection must be within the existing data\n    if (x1 >= obj.headers.length) {\n        x1 = obj.headers.length - 1;\n    }\n    if (y1 >= obj.rows.length) {\n        y1 = obj.rows.length - 1;\n    }\n    if (x2 >= obj.headers.length) {\n        x2 = obj.headers.length - 1;\n    }\n    if (y2 >= obj.rows.length) {\n        y2 = obj.rows.length - 1;\n    }\n\n    // Limits\n    let borderLeft = null;\n    let borderRight = null;\n    let borderTop = null;\n    let borderBottom = null;\n\n    // Origin & Destination\n    let px, ux;\n\n    if (parseInt(x1) < parseInt(x2)) {\n        px = parseInt(x1);\n        ux = parseInt(x2);\n    } else {\n        px = parseInt(x2);\n        ux = parseInt(x1);\n    }\n\n    let py, uy;\n\n    if (parseInt(y1) < parseInt(y2)) {\n        py = parseInt(y1);\n        uy = parseInt(y2);\n    } else {\n        py = parseInt(y2);\n        uy = parseInt(y1);\n    }\n\n    // Verify merged columns\n    for (let i = px; i <= ux; i++) {\n        for (let j = py; j <= uy; j++) {\n            if (obj.records[j][i] && obj.records[j][i].element.getAttribute('data-merged')) {\n                const x = parseInt(obj.records[j][i].element.getAttribute('data-x'));\n                const y = parseInt(obj.records[j][i].element.getAttribute('data-y'));\n                const colspan = parseInt(obj.records[j][i].element.getAttribute('colspan'));\n                const rowspan = parseInt(obj.records[j][i].element.getAttribute('rowspan'));\n\n                if (colspan > 1) {\n                    if (x < px) {\n                        px = x;\n                    }\n                    if (x + colspan > ux) {\n                        ux = x + colspan - 1;\n                    }\n                }\n\n                if (rowspan) {\n                    if (y < py) {\n                        py = y;\n                    }\n                    if (y + rowspan > uy) {\n                        uy = y + rowspan - 1;\n                    }\n                }\n            }\n        }\n    }\n\n    // Vertical limits\n    for (let j = py; j <= uy; j++) {\n        if (obj.rows[j].element.style.display != 'none') {\n            if (borderTop == null) {\n                borderTop = j;\n            }\n            borderBottom = j;\n        }\n    }\n\n    for (let i = px; i <= ux; i++) {\n        for (let j = py; j <= uy; j++) {\n            // Horizontal limits\n            if (!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].type != 'hidden') {\n                if (borderLeft == null) {\n                    borderLeft = i;\n                }\n                borderRight = i;\n            }\n        }\n    }\n\n    // Create borders\n    if (!borderLeft) {\n        borderLeft = 0;\n    }\n    if (!borderRight) {\n        borderRight = 0;\n    }\n\n    const ret = dispatch.call(obj, 'onbeforeselection', obj, borderLeft, borderTop, borderRight, borderBottom, origin);\n    if (ret === false) {\n        return false;\n    }\n\n    // Reset Selection\n    const previousState = obj.resetSelection();\n\n    // Keep selected cell\n    obj.selectedCell = [x1, y1, x2, y2];\n\n    // Add selected cell\n    if (obj.records[y1][x1]) {\n        obj.records[y1][x1].element.classList.add('highlight-selected');\n    }\n\n    // Redefining styles\n    for (let i = px; i <= ux; i++) {\n        for (let j = py; j <= uy; j++) {\n            if (obj.rows[j].element.style.display != 'none' && obj.records[j][i].element.style.display != 'none') {\n                obj.records[j][i].element.classList.add('highlight');\n                obj.highlighted.push(obj.records[j][i]);\n            }\n        }\n    }\n\n    for (let i = borderLeft; i <= borderRight; i++) {\n        if (\n            (!obj.options.columns || !obj.options.columns[i] || obj.options.columns[i].type != 'hidden') &&\n            obj.cols[i].colElement.style &&\n            obj.cols[i].colElement.style.display != 'none'\n        ) {\n            // Top border\n            if (obj.records[borderTop] && obj.records[borderTop][i]) {\n                obj.records[borderTop][i].element.classList.add('highlight-top');\n            }\n            // Bottom border\n            if (obj.records[borderBottom] && obj.records[borderBottom][i]) {\n                obj.records[borderBottom][i].element.classList.add('highlight-bottom');\n            }\n            // Add selected from headers\n            obj.headers[i].classList.add('selected');\n        }\n    }\n\n    for (let j = borderTop; j <= borderBottom; j++) {\n        if (obj.rows[j] && obj.rows[j].element.style.display != 'none') {\n            // Left border\n            obj.records[j][borderLeft].element.classList.add('highlight-left');\n            // Right border\n            obj.records[j][borderRight].element.classList.add('highlight-right');\n            // Add selected from rows\n            obj.rows[j].element.classList.add('selected');\n        }\n    }\n\n    obj.selectedContainer = [borderLeft, borderTop, borderRight, borderBottom];\n\n    // Handle events\n    if (previousState == 0) {\n        dispatch.call(obj, 'onfocus', obj);\n\n        removeCopyingSelection();\n    }\n\n    dispatch.call(obj, 'onselection', obj, borderLeft, borderTop, borderRight, borderBottom, origin);\n\n    // Find corner cell\n    updateCornerPosition.call(obj);\n};\n\n/**\n * Get selected column numbers\n *\n * @return array\n */\nexport const getSelectedColumns = function (visibleOnly) {\n    const obj = this;\n\n    if (!obj.selectedCell) {\n        return [];\n    }\n\n    const result = [];\n\n    for (let i = Math.min(obj.selectedCell[0], obj.selectedCell[2]); i <= Math.max(obj.selectedCell[0], obj.selectedCell[2]); i++) {\n        if (!visibleOnly || obj.headers[i].style.display != 'none') {\n            result.push(i);\n        }\n    }\n\n    return result;\n};\n\n/**\n * Refresh current selection\n */\nexport const refreshSelection = function () {\n    const obj = this;\n\n    if (obj.selectedCell) {\n        obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n    }\n};\n\n/**\n * Remove copy selection\n *\n * @return void\n */\nexport const removeCopySelection = function () {\n    const obj = this;\n\n    // Remove current selection\n    for (let i = 0; i < obj.selection.length; i++) {\n        obj.selection[i].classList.remove('selection');\n        obj.selection[i].classList.remove('selection-left');\n        obj.selection[i].classList.remove('selection-right');\n        obj.selection[i].classList.remove('selection-top');\n        obj.selection[i].classList.remove('selection-bottom');\n    }\n\n    obj.selection = [];\n};\n\nconst doubleDigitFormat = function (v) {\n    v = '' + v;\n    if (v.length == 1) {\n        v = '0' + v;\n    }\n    return v;\n};\n\n/**\n * Helper function to copy data using the corner icon\n */\nexport const copyData = function (o, d) {\n    const obj = this;\n\n    // Get data from all selected cells\n    const data = obj.getData(true, false);\n\n    // Selected cells\n    const h = obj.selectedContainer;\n\n    // Cells\n    const x1 = parseInt(o.getAttribute('data-x'));\n    const y1 = parseInt(o.getAttribute('data-y'));\n    const x2 = parseInt(d.getAttribute('data-x'));\n    const y2 = parseInt(d.getAttribute('data-y'));\n\n    // Records\n    const records = [];\n    let breakControl = false;\n\n    let rowNumber, colNumber;\n\n    if (h[0] == x1) {\n        // Vertical copy\n        if (y1 < h[1]) {\n            rowNumber = y1 - h[1];\n        } else {\n            rowNumber = 1;\n        }\n        colNumber = 0;\n    } else {\n        if (x1 < h[0]) {\n            colNumber = x1 - h[0];\n        } else {\n            colNumber = 1;\n        }\n        rowNumber = 0;\n    }\n\n    // Copy data procedure\n    let posx = 0;\n    let posy = 0;\n\n    for (let j = y1; j <= y2; j++) {\n        // Skip hidden rows\n        if (obj.rows[j] && obj.rows[j].element.style.display == 'none') {\n            continue;\n        }\n\n        // Controls\n        if (data[posy] == undefined) {\n            posy = 0;\n        }\n        posx = 0;\n\n        // Data columns\n        if (h[0] != x1) {\n            if (x1 < h[0]) {\n                colNumber = x1 - h[0];\n            } else {\n                colNumber = 1;\n            }\n        }\n        // Data columns\n        for (let i = x1; i <= x2; i++) {\n            // Update non-readonly\n            if (\n                obj.records[j][i] &&\n                !obj.records[j][i].element.classList.contains('readonly') &&\n                obj.records[j][i].element.style.display != 'none' &&\n                breakControl == false\n            ) {\n                // Stop if contains value\n                if (!obj.selection.length) {\n                    if (obj.options.data[j][i] != '') {\n                        breakControl = true;\n                        continue;\n                    }\n                }\n\n                // Column\n                if (data[posy] == undefined) {\n                    posx = 0;\n                } else if (data[posy][posx] == undefined) {\n                    posx = 0;\n                }\n\n                // Value\n                let value = data[posy][posx];\n\n                if (value && !data[1] && obj.parent.config.autoIncrement != false) {\n                    if (\n                        obj.options.columns &&\n                        obj.options.columns[i] &&\n                        (!obj.options.columns[i].type || obj.options.columns[i].type == 'text' || obj.options.columns[i].type == 'number')\n                    ) {\n                        if (('' + value).substr(0, 1) == '=') {\n                            const tokens = value.match(/([A-Z]+[0-9]+)/g);\n\n                            if (tokens) {\n                                const affectedTokens = [];\n                                for (let index = 0; index < tokens.length; index++) {\n                                    const position = getIdFromColumnName(tokens[index], 1);\n                                    position[0] += colNumber;\n                                    position[1] += rowNumber;\n                                    if (position[1] < 0) {\n                                        position[1] = 0;\n                                    }\n                                    const token = getColumnNameFromId([position[0], position[1]]);\n\n                                    if (token != tokens[index]) {\n                                        affectedTokens[tokens[index]] = token;\n                                    }\n                                }\n                                // Update formula\n                                if (affectedTokens) {\n                                    value = updateFormula(value, affectedTokens);\n                                }\n                            }\n                        } else {\n                            if (value == Number(value)) {\n                                value = Number(value) + rowNumber;\n                            }\n                        }\n                    } else if (obj.options.columns && obj.options.columns[i] && obj.options.columns[i].type == 'calendar') {\n                        const date = new Date(value);\n                        date.setDate(date.getDate() + rowNumber);\n                        value =\n                            date.getFullYear() +\n                            '-' +\n                            doubleDigitFormat(parseInt(date.getMonth() + 1)) +\n                            '-' +\n                            doubleDigitFormat(date.getDate()) +\n                            ' ' +\n                            '00:00:00';\n                    }\n                }\n\n                records.push(updateCell.call(obj, i, j, value));\n\n                // Update all formulas in the chain\n                updateFormulaChain.call(obj, i, j, records);\n            }\n            posx++;\n            if (h[0] != x1) {\n                colNumber++;\n            }\n        }\n        posy++;\n        rowNumber++;\n    }\n\n    // Update history\n    setHistory.call(obj, {\n        action: 'setValue',\n        records: records,\n        selection: obj.selectedCell,\n    });\n\n    // Update table with custom configuration if applicable\n    updateTable.call(obj);\n\n    // On after changes\n    const onafterchangesRecords = records.map(function (record) {\n        return {\n            x: record.x,\n            y: record.y,\n            value: record.value,\n            oldValue: record.oldValue,\n        };\n    });\n\n    dispatch.call(obj, 'onafterchanges', obj, onafterchangesRecords);\n};\n\nexport const hash = function (str) {\n    let hash = 0,\n        i,\n        chr;\n\n    if (!str || str.length === 0) {\n        return hash;\n    } else {\n        for (i = 0; i < str.length; i++) {\n            chr = str.charCodeAt(i);\n            hash = (hash << 5) - hash + chr;\n            hash |= 0;\n        }\n    }\n    return hash;\n};\n\n/**\n * Move coords to A1 in case overlaps with an excluded cell\n */\nexport const conditionalSelectionUpdate = function (type, o, d) {\n    const obj = this;\n\n    if (type == 1) {\n        if (obj.selectedCell && ((o >= obj.selectedCell[1] && o <= obj.selectedCell[3]) || (d >= obj.selectedCell[1] && d <= obj.selectedCell[3]))) {\n            obj.resetSelection();\n            return;\n        }\n    } else {\n        if (obj.selectedCell && ((o >= obj.selectedCell[0] && o <= obj.selectedCell[2]) || (d >= obj.selectedCell[0] && d <= obj.selectedCell[2]))) {\n            obj.resetSelection();\n            return;\n        }\n    }\n};\n\n/**\n * Get selected rows numbers\n *\n * @return array\n */\nexport const getSelectedRows = function (visibleOnly) {\n    const obj = this;\n\n    if (!obj.selectedCell) {\n        return [];\n    }\n\n    const result = [];\n\n    for (let i = Math.min(obj.selectedCell[1], obj.selectedCell[3]); i <= Math.max(obj.selectedCell[1], obj.selectedCell[3]); i++) {\n        if (!visibleOnly || obj.rows[i].element.style.display != 'none') {\n            result.push(i);\n        }\n    }\n\n    return result;\n};\n\nexport const selectAll = function () {\n    const obj = this;\n\n    if (!obj.selectedCell) {\n        obj.selectedCell = [];\n    }\n\n    obj.selectedCell[0] = 0;\n    obj.selectedCell[1] = 0;\n    obj.selectedCell[2] = obj.headers.length - 1;\n    obj.selectedCell[3] = obj.records.length - 1;\n\n    obj.updateSelectionFromCoords(obj.selectedCell[0], obj.selectedCell[1], obj.selectedCell[2], obj.selectedCell[3]);\n};\n\nexport const getSelection = function () {\n    const obj = this;\n\n    if (!obj.selectedCell) {\n        return null;\n    }\n\n    return [\n        Math.min(obj.selectedCell[0], obj.selectedCell[2]),\n        Math.min(obj.selectedCell[1], obj.selectedCell[3]),\n        Math.max(obj.selectedCell[0], obj.selectedCell[2]),\n        Math.max(obj.selectedCell[1], obj.selectedCell[3]),\n    ];\n};\n\nexport const getSelected = function (columnNameOnly) {\n    const obj = this;\n\n    const selectedRange = getSelection.call(obj);\n\n    if (!selectedRange) {\n        return [];\n    }\n\n    const cells = [];\n\n    for (let y = selectedRange[1]; y <= selectedRange[3]; y++) {\n        for (let x = selectedRange[0]; x <= selectedRange[2]; x++) {\n            if (columnNameOnly) {\n                cells.push(getCellNameFromCoords(x, y));\n            } else {\n                cells.push(obj.records[y][x]);\n            }\n        }\n    }\n\n    return cells;\n};\n\nexport const getRange = function () {\n    const obj = this;\n\n    const selectedRange = getSelection.call(obj);\n\n    if (!selectedRange) {\n        return '';\n    }\n\n    const start = getCellNameFromCoords(selectedRange[0], selectedRange[1]);\n    const end = getCellNameFromCoords(selectedRange[2], selectedRange[3]);\n\n    if (start === end) {\n        return obj.options.worksheetName + '!' + start;\n    }\n\n    return obj.options.worksheetName + '!' + start + ':' + end;\n};\n\nexport const isSelected = function (x, y) {\n    const obj = this;\n\n    const selection = getSelection.call(obj);\n\n    return x >= selection[0] && x <= selection[2] && y >= selection[1] && y <= selection[3];\n};\n\nexport const getHighlighted = function () {\n    const obj = this;\n\n    const selection = getSelection.call(obj);\n\n    if (selection) {\n        return [selection];\n    }\n\n    return [];\n};\n"
  },
  {
    "path": "src/utils/style.js",
    "content": "import dispatch from './dispatch.js';\nimport { getColumnNameFromId, getIdFromColumnName } from './internalHelpers.js';\nimport { setHistory } from './history.js';\n\n/**\n * Get style information from cell(s)\n *\n * @return integer\n */\nexport const getStyle = function (cell, key) {\n    const obj = this;\n\n    // Cell\n    if (!cell) {\n        // Control vars\n        const data = {};\n\n        // Column and row length\n        const x = obj.options.data[0].length;\n        const y = obj.options.data.length;\n\n        // Go through the columns to get the data\n        for (let j = 0; j < y; j++) {\n            for (let i = 0; i < x; i++) {\n                // Value\n                const v = key ? obj.records[j][i].element.style[key] : obj.records[j][i].element.getAttribute('style');\n\n                // Any meta data for this column?\n                if (v) {\n                    // Column name\n                    const k = getColumnNameFromId([i, j]);\n                    // Value\n                    data[k] = v;\n                }\n            }\n        }\n\n        return data;\n    } else {\n        cell = getIdFromColumnName(cell, true);\n\n        return key ? obj.records[cell[1]][cell[0]].element.style[key] : obj.records[cell[1]][cell[0]].element.getAttribute('style');\n    }\n};\n\n/**\n * Set meta information to cell(s)\n *\n * @return integer\n */\nexport const setStyle = function (o, k, v, force, ignoreHistoryAndEvents) {\n    const obj = this;\n\n    const newValue = {};\n    const oldValue = {};\n\n    // Apply style\n    const applyStyle = function (cellId, key, value) {\n        // Position\n        const cell = getIdFromColumnName(cellId, true);\n\n        if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]] && (obj.records[cell[1]][cell[0]].element.classList.contains('readonly') == false || force)) {\n            // Current value\n            const currentValue = obj.records[cell[1]][cell[0]].element.style[key];\n\n            // Change layout\n            if (currentValue == value && !force) {\n                value = '';\n                obj.records[cell[1]][cell[0]].element.style[key] = '';\n            } else {\n                obj.records[cell[1]][cell[0]].element.style[key] = value;\n            }\n\n            // History\n            if (!oldValue[cellId]) {\n                oldValue[cellId] = [];\n            }\n            if (!newValue[cellId]) {\n                newValue[cellId] = [];\n            }\n\n            oldValue[cellId].push([key + ':' + currentValue]);\n            newValue[cellId].push([key + ':' + value]);\n        }\n    };\n\n    if (k && v) {\n        // Get object from string\n        if (typeof o == 'string') {\n            applyStyle(o, k, v);\n        }\n    } else {\n        const keys = Object.keys(o);\n        for (let i = 0; i < keys.length; i++) {\n            let style = o[keys[i]];\n            if (typeof style == 'string') {\n                style = style.split(';');\n            }\n            for (let j = 0; j < style.length; j++) {\n                if (typeof style[j] == 'string') {\n                    style[j] = style[j].split(':');\n                }\n                // Apply value\n                if (style[j][0].trim()) {\n                    applyStyle(keys[i], style[j][0].trim(), style[j][1]);\n                }\n            }\n        }\n    }\n\n    let keys = Object.keys(oldValue);\n    for (let i = 0; i < keys.length; i++) {\n        oldValue[keys[i]] = oldValue[keys[i]].join(';');\n    }\n    keys = Object.keys(newValue);\n    for (let i = 0; i < keys.length; i++) {\n        newValue[keys[i]] = newValue[keys[i]].join(';');\n    }\n\n    if (!ignoreHistoryAndEvents) {\n        // Keeping history of changes\n        setHistory.call(obj, {\n            action: 'setStyle',\n            oldValue: oldValue,\n            newValue: newValue,\n        });\n    }\n\n    dispatch.call(obj, 'onchangestyle', obj, newValue);\n};\n\nexport const resetStyle = function (o, ignoreHistoryAndEvents) {\n    const obj = this;\n\n    const keys = Object.keys(o);\n    for (let i = 0; i < keys.length; i++) {\n        // Position\n        const cell = getIdFromColumnName(keys[i], true);\n        if (obj.records[cell[1]] && obj.records[cell[1]][cell[0]]) {\n            obj.records[cell[1]][cell[0]].element.setAttribute('style', '');\n        }\n    }\n    obj.setStyle(o, null, null, null, ignoreHistoryAndEvents);\n};\n"
  },
  {
    "path": "src/utils/toolbar.js",
    "content": "import jSuites from 'jsuites';\nimport { getCellNameFromCoords } from './helpers.js';\nimport { getWorksheetInstance } from './internal.js';\n\nconst setItemStatus = function (toolbarItem, worksheet) {\n    if (worksheet.options.editable != false) {\n        toolbarItem.classList.remove('jtoolbar-disabled');\n    } else {\n        toolbarItem.classList.add('jtoolbar-disabled');\n    }\n};\n\nexport const getDefault = function () {\n    const items = [];\n    const spreadsheet = this;\n\n    const getActive = function () {\n        return getWorksheetInstance.call(spreadsheet);\n    };\n\n    items.push({\n        content: 'undo',\n        onclick: function () {\n            const worksheet = getActive();\n\n            worksheet.undo();\n        },\n    });\n\n    items.push({\n        content: 'redo',\n        onclick: function () {\n            const worksheet = getActive();\n\n            worksheet.redo();\n        },\n    });\n\n    items.push({\n        content: 'save',\n        onclick: function () {\n            const worksheet = getActive();\n\n            if (worksheet) {\n                worksheet.download();\n            }\n        },\n    });\n\n    items.push({\n        type: 'divisor',\n    });\n\n    items.push({\n        type: 'select',\n        width: '120px',\n        options: ['Default', 'Verdana', 'Arial', 'Courier New'],\n        render: function (e) {\n            return '<span style=\"font-family:' + e + '\">' + e + '</span>';\n        },\n        onchange: function (a, b, c, d, e) {\n            const worksheet = getActive();\n\n            let cells = worksheet.getSelected(true);\n            if (cells) {\n                let value = !e ? '' : d;\n\n                worksheet.setStyle(\n                    Object.fromEntries(\n                        cells.map(function (cellName) {\n                            return [cellName, 'font-family: ' + value];\n                        })\n                    )\n                );\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'select',\n        width: '48px',\n        content: 'format_size',\n        options: ['x-small', 'small', 'medium', 'large', 'x-large'],\n        render: function (e) {\n            return '<span style=\"font-size:' + e + '\">' + e + '</span>';\n        },\n        onchange: function (a, b, c, value) {\n            const worksheet = getActive();\n\n            let cells = worksheet.getSelected(true);\n            if (cells) {\n                worksheet.setStyle(\n                    Object.fromEntries(\n                        cells.map(function (cellName) {\n                            return [cellName, 'font-size: ' + value];\n                        })\n                    )\n                );\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'select',\n        options: ['left', 'center', 'right', 'justify'],\n        render: function (e) {\n            return '<i class=\"material-icons\">format_align_' + e + '</i>';\n        },\n        onchange: function (a, b, c, value) {\n            const worksheet = getActive();\n\n            let cells = worksheet.getSelected(true);\n            if (cells) {\n                worksheet.setStyle(\n                    Object.fromEntries(\n                        cells.map(function (cellName) {\n                            return [cellName, 'text-align: ' + value];\n                        })\n                    )\n                );\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        content: 'format_bold',\n        onclick: function () {\n            const worksheet = getActive();\n\n            let cells = worksheet.getSelected(true);\n            if (cells) {\n                worksheet.setStyle(\n                    Object.fromEntries(\n                        cells.map(function (cellName) {\n                            return [cellName, 'font-weight:bold'];\n                        })\n                    )\n                );\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'color',\n        content: 'format_color_text',\n        k: 'color',\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'color',\n        content: 'format_color_fill',\n        k: 'background-color',\n        updateState: function (a, b, toolbarItem, d) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    let verticalAlign = ['top', 'middle', 'bottom'];\n\n    items.push({\n        type: 'select',\n        options: ['vertical_align_top', 'vertical_align_center', 'vertical_align_bottom'],\n        render: function (e) {\n            return '<i class=\"material-icons\">' + e + '</i>';\n        },\n        value: 1,\n        onchange: function (a, b, c, d, value) {\n            const worksheet = getActive();\n\n            let cells = worksheet.getSelected(true);\n            if (cells) {\n                worksheet.setStyle(\n                    Object.fromEntries(\n                        cells.map(function (cellName) {\n                            return [cellName, 'vertical-align: ' + verticalAlign[value]];\n                        })\n                    )\n                );\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        content: 'web',\n        tooltip: jSuites.translate('Merge the selected cells'),\n        onclick: function () {\n            const worksheet = getActive();\n\n            if (worksheet.selectedCell && confirm(jSuites.translate('The merged cells will retain the value of the top-left cell only. Are you sure?'))) {\n                const selectedRange = [\n                    Math.min(worksheet.selectedCell[0], worksheet.selectedCell[2]),\n                    Math.min(worksheet.selectedCell[1], worksheet.selectedCell[3]),\n                    Math.max(worksheet.selectedCell[0], worksheet.selectedCell[2]),\n                    Math.max(worksheet.selectedCell[1], worksheet.selectedCell[3]),\n                ];\n\n                let cell = getCellNameFromCoords(selectedRange[0], selectedRange[1]);\n                if (worksheet.records[selectedRange[1]][selectedRange[0]].element.getAttribute('data-merged')) {\n                    worksheet.removeMerge(cell);\n                } else {\n                    let colspan = selectedRange[2] - selectedRange[0] + 1;\n                    let rowspan = selectedRange[3] - selectedRange[1] + 1;\n\n                    if (colspan !== 1 || rowspan !== 1) {\n                        worksheet.setMerge(cell, colspan, rowspan);\n                    }\n                }\n            }\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'select',\n        options: [\n            'border_all',\n            'border_outer',\n            'border_inner',\n            'border_horizontal',\n            'border_vertical',\n            'border_left',\n            'border_top',\n            'border_right',\n            'border_bottom',\n            'border_clear',\n        ],\n        columns: 5,\n        render: function (e) {\n            return '<i class=\"material-icons\">' + e + '</i>';\n        },\n        right: true,\n        onchange: function (a, b, c, d) {\n            const worksheet = getActive();\n\n            if (worksheet.selectedCell) {\n                const selectedRange = [\n                    Math.min(worksheet.selectedCell[0], worksheet.selectedCell[2]),\n                    Math.min(worksheet.selectedCell[1], worksheet.selectedCell[3]),\n                    Math.max(worksheet.selectedCell[0], worksheet.selectedCell[2]),\n                    Math.max(worksheet.selectedCell[1], worksheet.selectedCell[3]),\n                ];\n\n                let type = d;\n\n                if (selectedRange) {\n                    // Default options\n                    let thickness = b.thickness || 1;\n                    let color = b.color || 'black';\n                    const borderStyle = b.style || 'solid';\n\n                    if (borderStyle === 'double') {\n                        thickness += 2;\n                    }\n\n                    let style = {};\n\n                    // Matrix\n                    let px = selectedRange[0];\n                    let py = selectedRange[1];\n                    let ux = selectedRange[2];\n                    let uy = selectedRange[3];\n\n                    const setBorder = function (columnName, i, j) {\n                        let border = ['', '', '', ''];\n\n                        if (\n                            ((type === 'border_top' || type === 'border_outer') && j === py) ||\n                            ((type === 'border_inner' || type === 'border_horizontal') && j > py) ||\n                            type === 'border_all'\n                        ) {\n                            border[0] = 'border-top: ' + thickness + 'px ' + borderStyle + ' ' + color;\n                        } else {\n                            border[0] = 'border-top: ';\n                        }\n\n                        if ((type === 'border_all' || type === 'border_right' || type === 'border_outer') && i === ux) {\n                            border[1] = 'border-right: ' + thickness + 'px ' + borderStyle + ' ' + color;\n                        } else {\n                            border[1] = 'border-right: ';\n                        }\n\n                        if ((type === 'border_all' || type === 'border_bottom' || type === 'border_outer') && j === uy) {\n                            border[2] = 'border-bottom: ' + thickness + 'px ' + borderStyle + ' ' + color;\n                        } else {\n                            border[2] = 'border-bottom: ';\n                        }\n\n                        if (\n                            ((type === 'border_left' || type === 'border_outer') && i === px) ||\n                            ((type === 'border_inner' || type === 'border_vertical') && i > px) ||\n                            type === 'border_all'\n                        ) {\n                            border[3] = 'border-left: ' + thickness + 'px ' + borderStyle + ' ' + color;\n                        } else {\n                            border[3] = 'border-left: ';\n                        }\n\n                        style[columnName] = border.join(';');\n                    };\n\n                    for (let j = selectedRange[1]; j <= selectedRange[3]; j++) {\n                        // Row - py - uy\n                        for (let i = selectedRange[0]; i <= selectedRange[2]; i++) {\n                            // Col - px - ux\n                            setBorder(getCellNameFromCoords(i, j), i, j);\n\n                            if (worksheet.records[j][i].element.getAttribute('data-merged')) {\n                                setBorder(getCellNameFromCoords(selectedRange[0], selectedRange[1]), i, j);\n                            }\n                        }\n                    }\n\n                    if (Object.keys(style)) {\n                        worksheet.setStyle(style);\n                    }\n                }\n            }\n        },\n        onload: function (a, b) {\n            // Border color\n            let container = document.createElement('div');\n            let div = document.createElement('div');\n            container.appendChild(div);\n\n            let colorPicker = jSuites.color(div, {\n                closeOnChange: false,\n                onchange: function (o, v) {\n                    o.parentNode.children[1].style.color = v;\n                    b.color = v;\n                },\n            });\n\n            let i = document.createElement('i');\n            i.classList.add('material-icons');\n            i.innerHTML = 'color_lens';\n            i.onclick = function () {\n                colorPicker.open();\n            };\n            container.appendChild(i);\n            a.children[1].appendChild(container);\n\n            div = document.createElement('div');\n            jSuites.picker(div, {\n                type: 'select',\n                data: [1, 2, 3, 4, 5],\n                render: function (e) {\n                    return '<div style=\"height: ' + e + 'px; width: 30px; background-color: black;\"></div>';\n                },\n                onchange: function (a, k, c, d) {\n                    b.thickness = d;\n                },\n                width: '50px',\n            });\n            a.children[1].appendChild(div);\n\n            const borderStylePicker = document.createElement('div');\n            jSuites.picker(borderStylePicker, {\n                type: 'select',\n                data: ['solid', 'dotted', 'dashed', 'double'],\n                render: function (e) {\n                    if (e === 'double') {\n                        return '<div style=\"width: 30px; border-top: 3px ' + e + ' black;\"></div>';\n                    }\n                    return '<div style=\"width: 30px; border-top: 2px ' + e + ' black;\"></div>';\n                },\n                onchange: function (a, k, c, d) {\n                    b.style = d;\n                },\n                width: '50px',\n            });\n            a.children[1].appendChild(borderStylePicker);\n\n            div = document.createElement('div');\n            div.style.flex = '1';\n            a.children[1].appendChild(div);\n        },\n        updateState: function (a, b, toolbarItem) {\n            setItemStatus(toolbarItem, getActive());\n        },\n    });\n\n    items.push({\n        type: 'divisor',\n    });\n\n    items.push({\n        content: 'fullscreen',\n        tooltip: 'Toggle Fullscreen',\n        onclick: function (a, b, c) {\n            if (c.children[0].textContent === 'fullscreen') {\n                spreadsheet.fullscreen(true);\n                c.children[0].textContent = 'fullscreen_exit';\n            } else {\n                spreadsheet.fullscreen(false);\n                c.children[0].textContent = 'fullscreen';\n            }\n        },\n        updateState: function (a, b, c, d) {\n            if (d.parent.config.fullscreen === true) {\n                c.children[0].textContent = 'fullscreen_exit';\n            } else {\n                c.children[0].textContent = 'fullscreen';\n            }\n        },\n    });\n\n    return items;\n};\n\nconst adjustToolbarSettingsForJSuites = function (toolbar) {\n    const spreadsheet = this;\n\n    const items = toolbar.items;\n\n    for (let i = 0; i < items.length; i++) {\n        // Tooltip\n        if (items[i].tooltip) {\n            items[i].title = items[i].tooltip;\n\n            delete items[i].tooltip;\n        }\n\n        if (items[i].type == 'select') {\n            if (items[i].options) {\n                items[i].data = items[i].options;\n                delete items[i].options;\n            } else {\n                items[i].data = items[i].v;\n                delete items[i].v;\n\n                if (items[i].k && !items[i].onchange) {\n                    items[i].onchange = function (el, config, value) {\n                        const worksheet = getWorksheetInstance.call(spreadsheet);\n\n                        const cells = worksheet.getSelected(true);\n\n                        worksheet.setStyle(\n                            Object.fromEntries(\n                                cells.map(function (cellName) {\n                                    return [cellName, items[i].k + ': ' + value];\n                                })\n                            )\n                        );\n                    };\n                }\n            }\n        } else if (items[i].type == 'color') {\n            items[i].type = 'i';\n\n            items[i].onclick = function (a, b, c) {\n                if (!c.color) {\n                    jSuites.color(c, {\n                        onchange: function (o, v) {\n                            const worksheet = getWorksheetInstance.call(spreadsheet);\n\n                            const cells = worksheet.getSelected(true);\n\n                            worksheet.setStyle(\n                                Object.fromEntries(\n                                    cells.map(function (cellName) {\n                                        return [cellName, items[i].k + ': ' + v];\n                                    })\n                                )\n                            );\n                        },\n                        onopen: function (o) {\n                            o.color.select('');\n                        },\n                    });\n\n                    c.color.open();\n                }\n            };\n        }\n    }\n};\n\n/**\n * Create toolbar\n */\nexport const createToolbar = function (toolbar) {\n    const spreadsheet = this;\n\n    const toolbarElement = document.createElement('div');\n    toolbarElement.classList.add('jss_toolbar');\n\n    adjustToolbarSettingsForJSuites.call(spreadsheet, toolbar);\n\n    if (typeof spreadsheet.plugins === 'object') {\n        Object.entries(spreadsheet.plugins).forEach(function ([, plugin]) {\n            if (typeof plugin.toolbar === 'function') {\n                const result = plugin.toolbar(toolbar);\n\n                if (result) {\n                    toolbar = result;\n                }\n            }\n        });\n    }\n\n    jSuites.toolbar(toolbarElement, toolbar);\n\n    return toolbarElement;\n};\n\nexport const updateToolbar = function (worksheet) {\n    if (worksheet.parent.toolbar) {\n        worksheet.parent.toolbar.toolbar.update(worksheet);\n    }\n};\n\nexport const showToolbar = function () {\n    const spreadsheet = this;\n\n    if (spreadsheet.config.toolbar && !spreadsheet.toolbar) {\n        let toolbar;\n\n        if (Array.isArray(spreadsheet.config.toolbar)) {\n            toolbar = {\n                items: spreadsheet.config.toolbar,\n            };\n        } else if (typeof spreadsheet.config.toolbar === 'object') {\n            toolbar = spreadsheet.config.toolbar;\n        } else {\n            toolbar = {\n                items: getDefault.call(spreadsheet),\n            };\n\n            if (typeof spreadsheet.config.toolbar === 'function') {\n                toolbar = spreadsheet.config.toolbar(toolbar);\n            }\n        }\n\n        spreadsheet.toolbar = spreadsheet.element.insertBefore(createToolbar.call(spreadsheet, toolbar), spreadsheet.element.children[1]);\n    }\n};\n\nexport const hideToolbar = function () {\n    const spreadsheet = this;\n\n    if (spreadsheet.toolbar) {\n        spreadsheet.toolbar.parentNode.removeChild(spreadsheet.toolbar);\n\n        delete spreadsheet.toolbar;\n    }\n};\n"
  },
  {
    "path": "src/utils/version.js",
    "content": "// Basic version information\nexport default {\n    version: '5.0.0',\n    host: 'https://bossanova.uk/jspreadsheet',\n    license: 'MIT',\n    print: function () {\n        return [['Jspreadsheet CE', this.version, this.host, this.license].join('\\r\\n')];\n    },\n};\n"
  },
  {
    "path": "src/utils/worksheets.js",
    "content": "import jSuites from 'jsuites';\n\nimport libraryBase from './libraryBase.js';\n\nimport { parseCSV } from './helpers.js';\nimport {\n    createCellHeader,\n    deleteColumn,\n    getColumnData,\n    getNumberOfColumns,\n    getWidth,\n    hideColumn,\n    insertColumn,\n    moveColumn,\n    setColumnData,\n    setWidth,\n    showColumn,\n} from './columns.js';\nimport { getData, getDataFromRange, getValue, getValueFromCoords, setData, setValue, setValueFromCoords } from './data.js';\nimport { cutControls, scrollControls, wheelControls } from './events.js';\nimport {\n    getHighlighted,\n    getRange,\n    getSelected,\n    getSelectedColumns,\n    getSelectedRows,\n    getSelection,\n    isSelected,\n    resetSelection,\n    selectAll,\n    updateSelectionFromCoords,\n} from './selection.js';\nimport { deleteRow, getHeight, getRowData, hideRow, insertRow, moveRow, setHeight, setRowData, showRow } from './rows.js';\nimport { destroyMerge, getMerge, removeMerge, setMerge } from './merges.js';\nimport { resetSearch, search } from './search.js';\nimport { getHeader, getHeaders, setHeader } from './headers.js';\nimport { getStyle, resetStyle, setStyle } from './style.js';\nimport { page, quantiyOfPages, whichPage } from './pagination.js';\nimport { download } from './download.js';\nimport { down, first, last, left, right, up } from './keys.js';\nimport { createNestedHeader, executeFormula, getCell, getCellFromCoords, getLabel, getWorksheetActive, hideIndex, showIndex } from './internal.js';\nimport { getComments, setComments } from './comments.js';\nimport { orderBy } from './orderBy.js';\nimport { getWorksheetConfig, setConfig } from './config.js';\nimport { getMeta, setMeta } from './meta.js';\nimport { closeEditor, openEditor } from './editor.js';\nimport dispatch from './dispatch.js';\nimport { getIdFromColumnName } from './internalHelpers.js';\nimport { copy, paste } from './copyPaste.js';\nimport { isReadOnly, setReadOnly } from './cells.js';\nimport { openFilter, resetFilters } from './filter.js';\nimport { redo, undo } from './history.js';\n\nconst setWorksheetFunctions = function (worksheet) {\n    for (let i = 0; i < worksheetPublicMethodsLength; i++) {\n        const [methodName, method] = worksheetPublicMethods[i];\n\n        worksheet[methodName] = method.bind(worksheet);\n    }\n};\n\nconst createTable = function () {\n    let obj = this;\n\n    setWorksheetFunctions(obj);\n\n    // Elements\n    obj.table = document.createElement('table');\n    obj.thead = document.createElement('thead');\n    obj.tbody = document.createElement('tbody');\n\n    // Create headers controllers\n    obj.headers = [];\n    obj.cols = [];\n\n    // Create table container\n    obj.content = document.createElement('div');\n    obj.content.classList.add('jss_content');\n    obj.content.onscroll = function (e) {\n        scrollControls.call(obj, e);\n    };\n    obj.content.onwheel = function (e) {\n        wheelControls.call(obj, e);\n    };\n\n    // Search\n    const searchContainer = document.createElement('div');\n\n    const searchLabel = document.createElement('label');\n    searchLabel.innerHTML = jSuites.translate('Search') + ': ';\n    searchContainer.appendChild(searchLabel);\n\n    obj.searchInput = document.createElement('input');\n    obj.searchInput.classList.add('jss_search');\n    searchLabel.appendChild(obj.searchInput);\n    obj.searchInput.onfocus = function () {\n        obj.resetSelection();\n    };\n\n    // Pagination select option\n    const paginationUpdateContainer = document.createElement('div');\n\n    if (obj.options.pagination > 0 && obj.options.paginationOptions && obj.options.paginationOptions.length > 0) {\n        obj.paginationDropdown = document.createElement('select');\n        obj.paginationDropdown.classList.add('jss_pagination_dropdown');\n        obj.paginationDropdown.onchange = function () {\n            obj.options.pagination = parseInt(this.value);\n            obj.page(0);\n        };\n\n        for (let i = 0; i < obj.options.paginationOptions.length; i++) {\n            const temp = document.createElement('option');\n            temp.value = obj.options.paginationOptions[i];\n            temp.innerHTML = obj.options.paginationOptions[i];\n            obj.paginationDropdown.appendChild(temp);\n        }\n\n        // Set initial pagination value\n        obj.paginationDropdown.value = obj.options.pagination;\n\n        paginationUpdateContainer.appendChild(document.createTextNode(jSuites.translate('Show ')));\n        paginationUpdateContainer.appendChild(obj.paginationDropdown);\n        paginationUpdateContainer.appendChild(document.createTextNode(jSuites.translate('entries')));\n    }\n\n    // Filter and pagination container\n    const filter = document.createElement('div');\n    filter.classList.add('jss_filter');\n    filter.appendChild(paginationUpdateContainer);\n    filter.appendChild(searchContainer);\n\n    // Colsgroup\n    obj.colgroupContainer = document.createElement('colgroup');\n    let tempCol = document.createElement('col');\n    tempCol.setAttribute('width', '50');\n    obj.colgroupContainer.appendChild(tempCol);\n\n    // Nested\n    if (obj.options.nestedHeaders && obj.options.nestedHeaders.length > 0 && obj.options.nestedHeaders[0] && obj.options.nestedHeaders[0][0]) {\n        for (let j = 0; j < obj.options.nestedHeaders.length; j++) {\n            obj.thead.appendChild(createNestedHeader.call(obj, obj.options.nestedHeaders[j]));\n        }\n    }\n\n    // Row\n    obj.headerContainer = document.createElement('tr');\n    tempCol = document.createElement('td');\n    tempCol.classList.add('jss_selectall');\n    obj.headerContainer.appendChild(tempCol);\n\n    const numberOfColumns = getNumberOfColumns.call(obj);\n\n    for (let i = 0; i < numberOfColumns; i++) {\n        // Create header\n        createCellHeader.call(obj, i);\n        // Append cell to the container\n        obj.headerContainer.appendChild(obj.headers[i]);\n        obj.colgroupContainer.appendChild(obj.cols[i].colElement);\n    }\n\n    obj.thead.appendChild(obj.headerContainer);\n\n    // Filters\n    if (obj.options.filters == true) {\n        obj.filter = document.createElement('tr');\n        const td = document.createElement('td');\n        obj.filter.appendChild(td);\n\n        for (let i = 0; i < obj.options.columns.length; i++) {\n            const td = document.createElement('td');\n            td.innerHTML = '&nbsp;';\n            td.setAttribute('data-x', i);\n            td.className = 'jss_column_filter';\n            if (obj.options.columns[i].type == 'hidden') {\n                td.style.display = 'none';\n            }\n            obj.filter.appendChild(td);\n        }\n\n        obj.thead.appendChild(obj.filter);\n    }\n\n    // Content table\n    obj.table = document.createElement('table');\n    obj.table.classList.add('jss_worksheet');\n    obj.table.setAttribute('cellpadding', '0');\n    obj.table.setAttribute('cellspacing', '0');\n    obj.table.setAttribute('unselectable', 'yes');\n    //obj.table.setAttribute('onselectstart', 'return false');\n    obj.table.appendChild(obj.colgroupContainer);\n    obj.table.appendChild(obj.thead);\n    obj.table.appendChild(obj.tbody);\n\n    if (!obj.options.textOverflow) {\n        obj.table.classList.add('jss_overflow');\n    }\n\n    // Spreadsheet corner\n    obj.corner = document.createElement('div');\n    obj.corner.className = 'jss_corner';\n    obj.corner.setAttribute('unselectable', 'on');\n    obj.corner.setAttribute('onselectstart', 'return false');\n\n    if (obj.options.selectionCopy == false) {\n        obj.corner.style.display = 'none';\n    }\n\n    // Textarea helper\n    obj.textarea = document.createElement('textarea');\n    obj.textarea.className = 'jss_textarea';\n    obj.textarea.id = 'jss_textarea';\n    obj.textarea.tabIndex = '-1';\n    obj.textarea.ariaHidden = 'true';\n\n    // Powered by Jspreadsheet\n    const ads = document.createElement('a');\n    ads.setAttribute('href', 'https://bossanova.uk/jspreadsheet/');\n    obj.ads = document.createElement('div');\n    obj.ads.className = 'jss_about';\n\n    const span = document.createElement('span');\n    span.innerHTML = 'Jspreadsheet CE';\n    ads.appendChild(span);\n    obj.ads.appendChild(ads);\n\n    // Create table container TODO: frozen columns\n    const container = document.createElement('div');\n    container.classList.add('jss_table');\n\n    // Pagination\n    obj.pagination = document.createElement('div');\n    obj.pagination.classList.add('jss_pagination');\n    const paginationInfo = document.createElement('div');\n    const paginationPages = document.createElement('div');\n    obj.pagination.appendChild(paginationInfo);\n    obj.pagination.appendChild(paginationPages);\n\n    // Hide pagination if not in use\n    if (!obj.options.pagination) {\n        obj.pagination.style.display = 'none';\n    }\n\n    // Append containers to the table\n    if (obj.options.search == true) {\n        obj.element.appendChild(filter);\n    }\n\n    // Elements\n    obj.content.appendChild(obj.table);\n    obj.content.appendChild(obj.corner);\n    obj.content.appendChild(obj.textarea);\n\n    obj.element.appendChild(obj.content);\n    obj.element.appendChild(obj.pagination);\n    obj.element.appendChild(obj.ads);\n    obj.element.classList.add('jss_container');\n\n    obj.element.jssWorksheet = obj;\n    obj.element.jspreadsheet = obj;\n\n    // Overflow\n    if (obj.options.tableOverflow == true) {\n        if (obj.options.tableHeight) {\n            obj.content.style['overflow-y'] = 'auto';\n            obj.content.style['box-shadow'] = 'rgb(221 221 221) 2px 2px 5px 0.1px';\n            obj.content.style.maxHeight = typeof obj.options.tableHeight === 'string' ? obj.options.tableHeight : obj.options.tableHeight + 'px';\n        }\n        if (obj.options.tableWidth) {\n            obj.content.style['overflow-x'] = 'auto';\n            obj.content.style.width = typeof obj.options.tableWidth === 'string' ? obj.options.tableWidth : obj.options.tableWidth + 'px';\n        }\n    }\n\n    // With toolbars\n    if (obj.options.tableOverflow != true && obj.parent.config.toolbar) {\n        obj.element.classList.add('with-toolbar');\n    }\n\n    // Actions\n    if (obj.options.columnDrag != false) {\n        obj.thead.classList.add('draggable');\n    }\n    if (obj.options.columnResize != false) {\n        obj.thead.classList.add('resizable');\n    }\n    if (obj.options.rowDrag != false) {\n        obj.tbody.classList.add('draggable');\n    }\n    if (obj.options.rowResize != false) {\n        obj.tbody.classList.add('resizable');\n    }\n\n    // Load data\n    obj.setData.call(obj);\n\n    // Style\n    if (obj.options.style) {\n        obj.setStyle(obj.options.style, null, null, 1, 1);\n\n        delete obj.options.style;\n    }\n\n    Object.defineProperty(obj.options, 'style', {\n        enumerable: true,\n        configurable: true,\n        get() {\n            return obj.getStyle();\n        },\n    });\n\n    if (obj.options.comments) {\n        obj.setComments(obj.options.comments);\n    }\n\n    // Classes\n    if (obj.options.classes) {\n        const k = Object.keys(obj.options.classes);\n        for (let i = 0; i < k.length; i++) {\n            const cell = getIdFromColumnName(k[i], true);\n            obj.records[cell[1]][cell[0]].element.classList.add(obj.options.classes[k[i]]);\n        }\n    }\n};\n\n/**\n * Prepare the jspreadsheet table\n *\n * @Param config\n */\nconst prepareTable = function () {\n    const obj = this;\n\n    // Lazy loading\n    if (obj.options.lazyLoading == true && obj.options.tableOverflow != true && obj.parent.config.fullscreen != true) {\n        console.error('Jspreadsheet: The lazyloading only works when tableOverflow = yes or fullscreen = yes');\n        obj.options.lazyLoading = false;\n    }\n\n    if (!obj.options.columns) {\n        obj.options.columns = [];\n    }\n\n    // Number of columns\n    let size = obj.options.columns.length;\n    let keys;\n\n    if (obj.options.data && typeof obj.options.data[0] !== 'undefined') {\n        if (!Array.isArray(obj.options.data[0])) {\n            // Data keys\n            keys = Object.keys(obj.options.data[0]);\n\n            if (keys.length > size) {\n                size = keys.length;\n            }\n        } else {\n            const numOfColumns = obj.options.data[0].length;\n\n            if (numOfColumns > size) {\n                size = numOfColumns;\n            }\n        }\n    }\n\n    // Minimal dimensions\n    if (!obj.options.minDimensions) {\n        obj.options.minDimensions = [0, 0];\n    }\n\n    if (obj.options.minDimensions[0] > size) {\n        size = obj.options.minDimensions[0];\n    }\n\n    // Requests\n    const multiple = [];\n\n    // Preparations\n    for (let i = 0; i < size; i++) {\n        // Default column description\n        if (!obj.options.columns[i]) {\n            obj.options.columns[i] = {};\n        }\n        if (!obj.options.columns[i].name && keys && keys[i]) {\n            obj.options.columns[i].name = keys[i];\n        }\n\n        // Pre-load initial source for json dropdown\n        if (obj.options.columns[i].type == 'dropdown') {\n            // if remote content\n            if (obj.options.columns[i].url) {\n                multiple.push({\n                    url: obj.options.columns[i].url,\n                    index: i,\n                    method: 'GET',\n                    dataType: 'json',\n                    success: function (data) {\n                        if (!obj.options.columns[this.index].source) {\n                            obj.options.columns[this.index].source = [];\n                        }\n\n                        for (let i = 0; i < data.length; i++) {\n                            obj.options.columns[this.index].source.push(data[i]);\n                        }\n                    },\n                });\n            }\n        }\n    }\n\n    // Create the table when is ready\n    if (!multiple.length) {\n        createTable.call(obj);\n    } else {\n        jSuites.ajax(multiple, function () {\n            createTable.call(obj);\n        });\n    }\n};\n\nexport const getNextDefaultWorksheetName = function (spreadsheet) {\n    const defaultWorksheetNameRegex = /^Sheet(\\d+)$/;\n\n    let largestWorksheetNumber = 0;\n\n    spreadsheet.worksheets.forEach(function (worksheet) {\n        const regexResult = defaultWorksheetNameRegex.exec(worksheet.options.worksheetName);\n        if (regexResult) {\n            largestWorksheetNumber = Math.max(largestWorksheetNumber, parseInt(regexResult[1]));\n        }\n    });\n\n    return 'Sheet' + (largestWorksheetNumber + 1);\n};\n\nexport const buildWorksheet = async function () {\n    const obj = this;\n    const el = obj.element;\n\n    const spreadsheet = obj.parent;\n\n    if (typeof spreadsheet.plugins === 'object') {\n        Object.entries(spreadsheet.plugins).forEach(function ([, plugin]) {\n            if (typeof plugin.beforeinit === 'function') {\n                plugin.beforeinit(obj);\n            }\n        });\n    }\n\n    libraryBase.jspreadsheet.current = obj;\n\n    const promises = [];\n\n    // Load the table data based on an CSV file\n    if (obj.options.csv) {\n        const promise = new Promise((resolve) => {\n            // Load CSV file\n            jSuites.ajax({\n                url: obj.options.csv,\n                method: 'GET',\n                dataType: 'text',\n                success: function (result) {\n                    // Convert data\n                    const newData = parseCSV(result, obj.options.csvDelimiter);\n\n                    // Headers\n                    if (obj.options.csvHeaders == true && newData.length > 0) {\n                        const headers = newData.shift();\n\n                        if (headers.length > 0) {\n                            if (!obj.options.columns) {\n                                obj.options.columns = [];\n                            }\n\n                            for (let i = 0; i < headers.length; i++) {\n                                if (!obj.options.columns[i]) {\n                                    obj.options.columns[i] = {};\n                                }\n                                // Precedence over pre-configurated titles\n                                if (typeof obj.options.columns[i].title === 'undefined') {\n                                    obj.options.columns[i].title = headers[i];\n                                }\n                            }\n                        }\n                    }\n                    // Data\n                    obj.options.data = newData;\n                    // Prepare table\n                    prepareTable.call(obj);\n\n                    resolve();\n                },\n            });\n        });\n\n        promises.push(promise);\n    } else if (obj.options.url) {\n        const promise = new Promise((resolve) => {\n            jSuites.ajax({\n                url: obj.options.url,\n                method: 'GET',\n                dataType: 'json',\n                success: function (result) {\n                    // Data\n                    obj.options.data = result.data ? result.data : result;\n                    // Prepare table\n                    prepareTable.call(obj);\n\n                    resolve();\n                },\n            });\n        });\n\n        promises.push(promise);\n    } else {\n        // Prepare table\n        prepareTable.call(obj);\n    }\n\n    await Promise.all(promises);\n\n    if (typeof spreadsheet.plugins === 'object') {\n        Object.entries(spreadsheet.plugins).forEach(function ([, plugin]) {\n            if (typeof plugin.init === 'function') {\n                plugin.init(obj);\n            }\n        });\n    }\n};\n\nexport const createWorksheetObj = function (options) {\n    const obj = this;\n\n    const spreadsheet = obj.parent;\n\n    if (!options.worksheetName) {\n        options.worksheetName = getNextDefaultWorksheetName(obj.parent);\n    }\n\n    const newWorksheet = {\n        parent: spreadsheet,\n        options: options,\n        filters: [],\n        formula: [],\n        history: [],\n        selection: [],\n        historyIndex: -1,\n    };\n\n    spreadsheet.config.worksheets.push(newWorksheet.options);\n    spreadsheet.worksheets.push(newWorksheet);\n\n    return newWorksheet;\n};\n\nexport const createWorksheet = function (options) {\n    const obj = this;\n    const spreadsheet = obj.parent;\n\n    spreadsheet.creationThroughJss = true;\n\n    createWorksheetObj.call(obj, options);\n\n    spreadsheet.element.tabs.create(options.worksheetName);\n};\n\nexport const openWorksheet = function (position) {\n    const obj = this;\n    const spreadsheet = obj.parent;\n\n    spreadsheet.element.tabs.open(position);\n};\n\nexport const deleteWorksheet = function (position) {\n    const obj = this;\n\n    obj.parent.element.tabs.remove(position);\n\n    const removedWorksheet = obj.parent.worksheets.splice(position, 1)[0];\n\n    dispatch.call(obj.parent, 'ondeleteworksheet', removedWorksheet, position);\n};\n\nconst worksheetPublicMethods = [\n    ['selectAll', selectAll],\n    [\n        'updateSelectionFromCoords',\n        function (x1, y1, x2, y2) {\n            return updateSelectionFromCoords.call(this, x1, y1, x2, y2);\n        },\n    ],\n    [\n        'resetSelection',\n        function () {\n            return resetSelection.call(this);\n        },\n    ],\n    ['getSelection', getSelection],\n    ['getSelected', getSelected],\n    ['getSelectedColumns', getSelectedColumns],\n    ['getSelectedRows', getSelectedRows],\n    ['getData', getData],\n    ['setData', setData],\n    ['getValue', getValue],\n    ['getValueFromCoords', getValueFromCoords],\n    ['setValue', setValue],\n    ['setValueFromCoords', setValueFromCoords],\n    ['getWidth', getWidth],\n    [\n        'setWidth',\n        function (column, width) {\n            return setWidth.call(this, column, width);\n        },\n    ],\n    ['insertRow', insertRow],\n    [\n        'moveRow',\n        function (rowNumber, newPositionNumber) {\n            return moveRow.call(this, rowNumber, newPositionNumber);\n        },\n    ],\n    ['deleteRow', deleteRow],\n    ['hideRow', hideRow],\n    ['showRow', showRow],\n    ['getRowData', getRowData],\n    ['setRowData', setRowData],\n    ['getHeight', getHeight],\n    [\n        'setHeight',\n        function (row, height) {\n            return setHeight.call(this, row, height);\n        },\n    ],\n    ['getMerge', getMerge],\n    [\n        'setMerge',\n        function (cellName, colspan, rowspan) {\n            return setMerge.call(this, cellName, colspan, rowspan);\n        },\n    ],\n    [\n        'destroyMerge',\n        function () {\n            return destroyMerge.call(this);\n        },\n    ],\n    [\n        'removeMerge',\n        function (cellName, data) {\n            return removeMerge.call(this, cellName, data);\n        },\n    ],\n    ['search', search],\n    ['resetSearch', resetSearch],\n    ['getHeader', getHeader],\n    ['getHeaders', getHeaders],\n    ['setHeader', setHeader],\n    ['getStyle', getStyle],\n    [\n        'setStyle',\n        function (cell, property, value, forceOverwrite) {\n            return setStyle.call(this, cell, property, value, forceOverwrite);\n        },\n    ],\n    ['resetStyle', resetStyle],\n    ['insertColumn', insertColumn],\n    ['moveColumn', moveColumn],\n    ['deleteColumn', deleteColumn],\n    ['getColumnData', getColumnData],\n    ['setColumnData', setColumnData],\n    ['whichPage', whichPage],\n    ['page', page],\n    ['download', download],\n    ['getComments', getComments],\n    ['setComments', setComments],\n    ['orderBy', orderBy],\n    ['undo', undo],\n    ['redo', redo],\n    ['getCell', getCell],\n    ['getCellFromCoords', getCellFromCoords],\n    ['getLabel', getLabel],\n    ['getConfig', getWorksheetConfig],\n    ['setConfig', setConfig],\n    [\n        'getMeta',\n        function (cell) {\n            return getMeta.call(this, cell);\n        },\n    ],\n    ['setMeta', setMeta],\n    ['showColumn', showColumn],\n    ['hideColumn', hideColumn],\n    ['showIndex', showIndex],\n    ['hideIndex', hideIndex],\n    ['getWorksheetActive', getWorksheetActive],\n    ['openEditor', openEditor],\n    ['closeEditor', closeEditor],\n    ['createWorksheet', createWorksheet],\n    ['openWorksheet', openWorksheet],\n    ['deleteWorksheet', deleteWorksheet],\n    [\n        'copy',\n        function (cut) {\n            if (cut) {\n                cutControls();\n            } else {\n                copy.call(this, true);\n            }\n        },\n    ],\n    ['paste', paste],\n    ['executeFormula', executeFormula],\n    ['getDataFromRange', getDataFromRange],\n    ['quantiyOfPages', quantiyOfPages],\n    ['getRange', getRange],\n    ['isSelected', isSelected],\n    ['setReadOnly', setReadOnly],\n    ['isReadOnly', isReadOnly],\n    ['getHighlighted', getHighlighted],\n    ['dispatch', dispatch],\n    ['down', down],\n    ['first', first],\n    ['last', last],\n    ['left', left],\n    ['right', right],\n    ['up', up],\n    ['openFilter', openFilter],\n    ['resetFilters', resetFilters],\n];\n\nconst worksheetPublicMethodsLength = worksheetPublicMethods.length;\n"
  },
  {
    "path": "src/webcomponent.js",
    "content": "class Jspreadsheet extends HTMLElement {\n    constructor() {\n        super();\n    }\n\n    init() {\n        // Shadow root\n        const shadowRoot = this.attachShadow({ mode: 'open' });\n\n        // Style\n        const css = document.createElement('link');\n        css.rel = 'stylesheet';\n        css.type = 'text/css';\n        css.href = 'https://cdn.jsdelivr.net/npm/jspreadsheet-ce/dist/jspreadsheet.min.css';\n        shadowRoot.appendChild(css);\n\n        const cssJsuites = document.createElement('link');\n        cssJsuites.rel = 'stylesheet';\n        cssJsuites.type = 'text/css';\n        cssJsuites.href = 'https://cdn.jsdelivr.net/npm/jsuites/dist/jsuites.min.css';\n        shadowRoot.appendChild(cssJsuites);\n\n        const cssMaterial = document.createElement('link');\n        cssMaterial.rel = 'stylesheet';\n        cssMaterial.type = 'text/css';\n        cssMaterial.href = 'https://fonts.googleapis.com/css?family=Material+Icons';\n        shadowRoot.appendChild(cssMaterial);\n\n        // JSS container\n        const container = document.createElement('div');\n        shadowRoot.appendChild(container);\n\n        // Properties\n        const toolbar = this.getAttribute('toolbar') == 'true' ? true : false;\n\n        // Create jexcel element\n        this.el = jspreadsheet(container, {\n            tabs: true,\n            toolbar: toolbar,\n            root: shadowRoot,\n            worksheets: [\n                {\n                    filters: true,\n                    minDimensions: [6, 6],\n                },\n            ],\n        });\n    }\n\n    connectedCallback() {\n        this.init(this);\n    }\n\n    disconnectedCallback() {}\n\n    attributeChangedCallback() {}\n}\n\nwindow.customElements.define('j-spreadsheet-ce', Jspreadsheet);\n"
  },
  {
    "path": "test/calculations.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Calculations', () => {\n    it('Testing formula chain', () => {\n        let test = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['1', ''],\n                        ['', ''],\n                        ['', ''],\n                        ['', ''],\n                        ['', ''],\n                    ],\n                },\n            ],\n        });\n\n        test[0].setValue('B5', '=B3+A1');\n        test[0].setValue('B3', '=A1+1');\n        test[0].setValue('A1', '2');\n\n        expect(test[0].getValue('B5', true)).to.equal('5');\n    });\n\n    describe('Test updating formulas when adding new rows', () => {\n        it('1', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '=SUM(A2:C2)'],\n                            ['4', '5', '6', '=SUM(A2:C2)'],\n                            ['7', '8', '9', '=SUM(A2:C2)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertRow(1, 1, true);\n\n            expect(test[0].getValue('D1')).to.equal('=SUM(A3:C3)');\n            expect(test[0].getValue('D2')).to.equal('');\n            expect(test[0].getValue('D3')).to.equal('=SUM(A3:C3)');\n            expect(test[0].getValue('D4')).to.equal('=SUM(A3:C3)');\n        });\n\n        it('2', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '=SUM(A2:C3)'],\n                            ['4', '5', '6', '=SUM(A2:C3)'],\n                            ['7', '8', '9', '=SUM(A2:C3)'],\n                            ['10', '11', '12', '=SUM(A2:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertRow(1, 1, true);\n\n            expect(test[0].getValue('D1')).to.equal('=SUM(A3:C4)');\n            expect(test[0].getValue('D2')).to.equal('');\n            expect(test[0].getValue('D3')).to.equal('=SUM(A3:C4)');\n            expect(test[0].getValue('D4')).to.equal('=SUM(A3:C4)');\n            expect(test[0].getValue('D5')).to.equal('=SUM(A3:C4)');\n        });\n\n        it('3', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '=SUM(A2:C3)'],\n                            ['4', '5', '6', '=SUM(A2:C3)'],\n                            ['7', '8', '9', '=SUM(A2:C3)'],\n                            ['10', '11', '12', '=SUM(A2:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertRow(1, 1, false);\n\n            expect(test[0].getValue('D1')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D2')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D3')).to.equal('');\n            expect(test[0].getValue('D4')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D5')).to.equal('=SUM(A2:C4)');\n        });\n\n        it('4', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '=SUM(A2:C3)'],\n                            ['4', '5', '6', '=SUM(A2:C3)'],\n                            ['7', '8', '9', '=SUM(A2:C3)'],\n                            ['10', '11', '12', '=SUM(A2:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertRow(1, 2, true);\n\n            expect(test[0].getValue('D1')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D2')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D3')).to.equal('');\n            expect(test[0].getValue('D4')).to.equal('=SUM(A2:C4)');\n            expect(test[0].getValue('D5')).to.equal('=SUM(A2:C4)');\n        });\n\n        it('5', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '=SUM(A2:C3)'],\n                            ['4', '5', '6', '=SUM(A2:C3)'],\n                            ['7', '8', '9', '=SUM(A2:C3)'],\n                            ['10', '11', '12', '=SUM(A2:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertRow(1, 2, false);\n\n            expect(test[0].getValue('D1')).to.equal('=SUM(A2:C3)');\n            expect(test[0].getValue('D2')).to.equal('=SUM(A2:C3)');\n            expect(test[0].getValue('D3')).to.equal('=SUM(A2:C3)');\n            expect(test[0].getValue('D4')).to.equal('');\n            expect(test[0].getValue('D5')).to.equal('=SUM(A2:C3)');\n        });\n    });\n\n    describe('Test updating formulas when adding new columns', () => {\n        it('1', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3'],\n                            ['4', '5', '6'],\n                            ['7', '8', '9'],\n                            ['=SUM(B1:B3)', '=SUM(B1:B3)', '=SUM(B1:B3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertColumn(1, 1, true);\n\n            expect(test[0].getValue('A4')).to.equal('=SUM(C1:C3)');\n            expect(test[0].getValue('B4')).to.equal('');\n            expect(test[0].getValue('C4')).to.equal('=SUM(C1:C3)');\n            expect(test[0].getValue('D4')).to.equal('=SUM(C1:C3)');\n        });\n\n        it('2', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '4'],\n                            ['5', '6', '7', '8'],\n                            ['9', '10', '11', '12'],\n                            ['=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertColumn(1, 1, true);\n\n            expect(test[0].getValue('A4')).to.equal('=SUM(C1:D3)');\n            expect(test[0].getValue('B4')).to.equal('');\n            expect(test[0].getValue('C4')).to.equal('=SUM(C1:D3)');\n            expect(test[0].getValue('D4')).to.equal('=SUM(C1:D3)');\n            expect(test[0].getValue('E4')).to.equal('=SUM(C1:D3)');\n        });\n\n        it('3', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '4'],\n                            ['5', '6', '7', '8'],\n                            ['9', '10', '11', '12'],\n                            ['=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertColumn(1, 1, false);\n\n            expect(test[0].getValue('A4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('B4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('C4')).to.equal('');\n            expect(test[0].getValue('D4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('E4')).to.equal('=SUM(B1:D3)');\n        });\n\n        it('4', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '4'],\n                            ['5', '6', '7', '8'],\n                            ['9', '10', '11', '12'],\n                            ['=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertColumn(1, 2, true);\n\n            expect(test[0].getValue('A4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('B4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('C4')).to.equal('');\n            expect(test[0].getValue('D4')).to.equal('=SUM(B1:D3)');\n            expect(test[0].getValue('E4')).to.equal('=SUM(B1:D3)');\n        });\n\n        it('5', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['1', '2', '3', '4'],\n                            ['5', '6', '7', '8'],\n                            ['9', '10', '11', '12'],\n                            ['=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)', '=SUM(B1:C3)'],\n                        ],\n                        worksheetName: 'sheet1',\n                    },\n                ],\n            });\n\n            test[0].insertColumn(1, 2, false);\n\n            expect(test[0].getValue('A4')).to.equal('=SUM(B1:C3)');\n            expect(test[0].getValue('B4')).to.equal('=SUM(B1:C3)');\n            expect(test[0].getValue('C4')).to.equal('=SUM(B1:C3)');\n            expect(test[0].getValue('D4')).to.equal('');\n            expect(test[0].getValue('E4')).to.equal('=SUM(B1:C3)');\n        });\n\n        it('6', () => {\n            let test = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], ['=SUM(A1:A4)']],\n                        minDimensions: [5, 5],\n                    },\n                ],\n            });\n\n            test[0].deleteRow(1);\n\n            expect(test[0].getValue('A5')).to.equal('=SUM(A1:A3)');\n        });\n    });\n});\n"
  },
  {
    "path": "test/columns.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use the columns method', () => {\n    it('insertColumn and column is inserted in the position 0', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [9, 9, 9, 9, 9, 9],\n                        [9, 9, 9, 9, 9, 9],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n\n        // Insert [3, 3] column in the first column\n        instance[0].insertColumn([3, 3], 0, 1);\n\n        expect(rows[0].children[1].innerHTML).to.include(3);\n\n        // Insert [2, 2] column in the first column\n        instance[0].insertColumn([2, 2], 0, 1);\n\n        expect(rows[0].children[1].innerHTML).to.include(2);\n    });\n\n    it('insertColumn and column is inserted in the position 1', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [9, 9, 9, 9, 9, 9],\n                        [9, 9, 9, 9, 9, 9],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[2].innerHTML).to.include(9);\n\n        // Insert [3, 3] column in the second column\n        instance[0].insertColumn([3, 3], 0);\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(3);\n\n        // Insert [2, 2] column in the second column\n        instance[0].insertColumn([2, 2], 0);\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n    });\n\n    it('deleteColumn and column is removed in the given index', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 9, 9, 9],\n                        [3, 6, 9, 9, 9, 9],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(3);\n        expect(rows[0].children[2].innerHTML).to.include(6);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n\n        // Delete first column\n        instance[0].deleteColumn(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(6);\n        expect(rows[0].children[2].innerHTML).to.include(9);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n\n        // Delete first column\n        instance[0].deleteColumn(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(9);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n    });\n\n    it('deleteColumn and multiple column are removed starting from the given index', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(3);\n        expect(rows[0].children[2].innerHTML).to.include(6);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n\n        // Delete first two columns\n        instance[0].deleteColumn(0, 2);\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(12);\n        expect(rows[0].children[3].innerHTML).to.include(15);\n\n        // Delete first two columns\n        instance[0].deleteColumn(0, 2);\n\n        expect(rows[0].children[1].innerHTML).to.include(15);\n        expect(rows[0].children[2].innerHTML).to.include(18);\n    });\n\n    it('hideColumn and showColumn', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('thead');\n        let headers = table.children[0].children;\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(window.getComputedStyle(headers[1]).display).not.to.include('none');\n\n        // Hides the first column 'A'\n        instance[0].hideColumn(0);\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(window.getComputedStyle(headers[1]).display).to.include('none');\n        expect(headers[2].innerHTML).to.include('B');\n        expect(window.getComputedStyle(headers[2]).display).not.to.include('none');\n\n        // Shows the column that was hidden\n        instance[0].showColumn(0);\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(window.getComputedStyle(headers[1]).display).not.to.include('none');\n        expect(headers[2].innerHTML).to.include('B');\n        expect(window.getComputedStyle(headers[2]).display).not.to.include('none');\n    });\n\n    it('insertColumn history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [9, 9, 9, 9, 9, 9],\n                        [9, 9, 9, 9, 9, 9],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n\n        // Insert [3, 3] column in the first column\n        instance[0].insertColumn([3, 3], 0, 1);\n\n        expect(rows[0].children[1].innerHTML).to.include(3);\n\n        instance[0].undo();\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n\n        instance[0].redo();\n\n        expect(rows[0].children[1].innerHTML).to.include(3);\n    });\n\n    it('deleteColumn history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3, 4],\n                        [5, 6, 7, 8],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        // Delete first column\n        instance[0].deleteColumn(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(2);\n        expect(rows[0].children[2].innerHTML).to.include(3);\n        expect(rows[0].children[3].innerHTML).to.include(4);\n\n        instance[0].undo(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        instance[0].redo(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(2);\n        expect(rows[0].children[2].innerHTML).to.include(3);\n        expect(rows[0].children[3].innerHTML).to.include(4);\n    });\n});\n"
  },
  {
    "path": "test/comments.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Comment tests', () => {\n    it('Set comment', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Cheese', '2019-02-12'],\n                        ['CA', 'Apples', '2019-03-01'],\n                        ['CA', 'Carrots', '2018-11-10'],\n                        ['BR', 'Oranges', '2019-01-12'],\n                    ],\n                    columns: [{ width: '300px' }, { width: '200px' }, { width: '200px' }],\n                    allowComments: true,\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        instance[0].setComments('C2', 'Test');\n\n        expect(rows[1].children[3].getAttribute('title')).to.equal('Test');\n\n        instance[0].setComments('C2', '');\n\n        expect(rows[1].children[3].getAttribute('title')).to.equal('');\n    });\n\n    it('Get comment', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Cheese', '2019-02-12'],\n                        ['CA', 'Apples', '2019-03-01'],\n                        ['CA', 'Carrots', '2018-11-10'],\n                        ['BR', 'Oranges', '2019-01-12'],\n                    ],\n                    columns: [{ width: '300px' }, { width: '200px' }, { width: '200px' }],\n                    allowComments: true,\n                },\n            ],\n        });\n\n        instance[0].setComments('B3', 'something');\n\n        expect(instance[0].getComments('B3')).to.equal('something');\n    });\n\n    it('setComments history', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Cheese', '2019-02-12'],\n                        ['CA', 'Apples', '2019-03-01'],\n                        ['CA', 'Carrots', '2018-11-10'],\n                        ['BR', 'Oranges', '2019-01-12'],\n                    ],\n                    columns: [{ width: '300px' }, { width: '200px' }, { width: '200px' }],\n                    allowComments: true,\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        instance[0].setComments('C2', 'Test');\n\n        expect(rows[1].children[3].getAttribute('title')).to.equal('Test');\n\n        instance[0].undo();\n\n        expect(rows[1].children[3].getAttribute('title')).to.equal('');\n\n        instance[0].redo();\n\n        expect(rows[1].children[3].getAttribute('title')).to.equal('Test');\n    });\n});\n"
  },
  {
    "path": "test/data.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use the data method', () => {\n    it('getData and it returns the data properly', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [3, 2, 1],\n                        [4, 5, 6],\n                        [6, 5, 4],\n                        [9, 12, 15],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const data = instance[0].getData();\n\n        expect(data.length).to.eq(7);\n        expect(data[0].length).to.eq(7);\n\n        expect(data[0][0]).to.eq(1);\n        expect(data[0][1]).to.eq(2);\n        expect(data[0][2]).to.eq(3);\n        expect(data[1][0]).to.eq(3);\n        expect(data[1][1]).to.eq(2);\n        expect(data[1][2]).to.eq(1);\n        expect(data[2][0]).to.eq(4);\n        expect(data[2][1]).to.eq(5);\n        expect(data[2][2]).to.eq(6);\n        expect(data[3][0]).to.eq(6);\n        expect(data[3][1]).to.eq(5);\n        expect(data[3][2]).to.eq(4);\n        expect(data[4][0]).to.eq(9);\n        expect(data[4][1]).to.eq(12);\n        expect(data[4][2]).to.eq(15);\n    });\n\n    it('setData and it sets data properly', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        instance[0].setData([\n            ['Hello', 'World'],\n            ['Testing', 'CE'],\n        ]);\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[1].innerHTML).not.to.include('World');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n        expect(firstRow.children[2].innerHTML).not.to.include('Hello');\n\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[1].innerHTML).not.to.include('CE');\n        expect(secondRow.children[2].innerHTML).to.include('CE');\n        expect(secondRow.children[2].innerHTML).not.to.include('Testing');\n    });\n\n    it('setValue and it sets the value of a cell', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('CE');\n\n        instance[0].setValue('A1', 'New Value');\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].setValue('A1', 'olleH');\n        instance[0].setValue('B1', 'dlroW');\n\n        expect(firstRow.children[1].innerHTML).to.include('olleH');\n        expect(firstRow.children[2].innerHTML).to.include('dlroW');\n\n        instance[0].setValue('B2', 'TESTING');\n\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('TESTING');\n    });\n\n    it('getValue and it gets the value from the cell', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        expect(instance[0].getValue('A1')).to.include('Hello');\n        expect(instance[0].getValue('B1')).to.include('World');\n        expect(instance[0].getValue('A2')).to.include('Testing');\n        expect(instance[0].getValue('B2')).to.include('CE');\n    });\n\n    it('getValueFromCoords and it gets the not processed cell value', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['=1+1', '=2+2'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        expect(instance[0].getValueFromCoords(0, 0)).to.include('=1+1');\n        expect(instance[0].getValueFromCoords(1, 0)).to.include('=2+2');\n        expect(instance[0].getValueFromCoords(0, 1)).to.include('Testing');\n        expect(instance[0].getValueFromCoords(1, 1)).to.include('CE');\n    });\n\n    it('getValueFromCoords and it gets the processed cell value', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['=1+1', '=2+2'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        expect(instance[0].getValueFromCoords(0, 0, true)).to.include('2');\n        expect(instance[0].getValueFromCoords(1, 0, true)).to.include('4');\n        expect(instance[0].getValueFromCoords(0, 1, true)).to.include('Testing');\n        expect(instance[0].getValueFromCoords(1, 1, true)).to.include('CE');\n    });\n\n    it('setValueFromCoords and it sets the value of a cell', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('CE');\n\n        instance[0].setValueFromCoords(0, 0, 'New Value');\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].setValueFromCoords(0, 0, 'olleH');\n        instance[0].setValueFromCoords(1, 0, 'dlroW');\n\n        expect(firstRow.children[1].innerHTML).to.include('olleH');\n        expect(firstRow.children[2].innerHTML).to.include('dlroW');\n\n        instance[0].setValueFromCoords(1, 1, 'TESTING');\n\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('TESTING');\n    });\n\n    it('setValueFromCoords history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n\n        instance[0].setValueFromCoords(0, 0, 'New Value');\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n\n        instance[0].undo();\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n\n        instance[0].redo();\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n    });\n});\n"
  },
  {
    "path": "test/footer.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use footers', () => {\n    it('Start the worksheet with a footer', () => {\n        jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    freezeColumns: 2,\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    footers: [\n                        ['a', 'b', 'c'],\n                        [1, 2, 3],\n                    ],\n                },\n            ],\n        });\n\n        const footerTag = root.querySelector('tfoot');\n\n        const firstRow = footerTag.children[0];\n\n        expect(firstRow.children[1].innerHTML).to.equal('a');\n        expect(firstRow.children[2].innerHTML).to.equal('b');\n        expect(firstRow.children[3].innerHTML).to.equal('c');\n\n        const secondRow = footerTag.children[1];\n\n        expect(secondRow.children[1].innerHTML).to.equal('1');\n        expect(secondRow.children[2].innerHTML).to.equal('2');\n        expect(secondRow.children[3].innerHTML).to.equal('3');\n    });\n});\n"
  },
  {
    "path": "test/headers.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use the headers method', () => {\n    it('setHeader and header title is changed', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('thead');\n        let headers = table.children[0].children;\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(headers[2].innerHTML).to.include('B');\n\n        instance[0].setHeader(0, 'Produtos');\n\n        expect(headers[1].innerHTML).to.include('Produtos');\n        expect(headers[2].innerHTML).to.include('B');\n\n        instance[0].setHeader(1, 'Quantidade');\n\n        expect(headers[1].innerHTML).to.include('Produtos');\n        expect(headers[2].innerHTML).to.include('Quantidade');\n    });\n\n    it('getHeader and header title is retrieved', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('thead');\n        let headers = table.children[0].children;\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(headers[2].innerHTML).to.include('B');\n\n        expect(instance[0].getHeader(0)).to.include('A');\n        expect(instance[0].getHeader(1)).to.include('B');\n        expect(instance[0].getHeader(2)).to.include('C');\n    });\n\n    it('getHeaders and header titles are retrieved', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('thead');\n        let headers = table.children[0].children;\n\n        expect(headers[1].innerHTML).to.include('A');\n        expect(headers[2].innerHTML).to.include('B');\n\n        let h = instance[0].getHeaders();\n\n        expect(h).to.include('A');\n        expect(h).to.include('B');\n        expect(h).to.include('C');\n        expect(h).to.include('D');\n        expect(h).to.include('E');\n        expect(h).to.include('F');\n    });\n\n    it('setHeader history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [3, 6, 9, 12, 15, 18],\n                        [3, 6, 9, 12, 15, 18],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('thead');\n        let headers = table.children[0].children;\n\n        expect(headers[1].innerHTML).to.equal('A');\n        expect(headers[2].innerHTML).to.equal('B');\n\n        instance[0].setHeader(0, 'Products');\n\n        expect(headers[1].innerHTML).to.equal('Products');\n        expect(headers[2].innerHTML).to.equal('B');\n\n        instance[0].undo();\n\n        expect(headers[1].innerHTML).to.equal('A');\n        expect(headers[2].innerHTML).to.equal('B');\n\n        instance[0].redo();\n\n        expect(headers[1].innerHTML).to.equal('Products');\n        expect(headers[2].innerHTML).to.equal('B');\n    });\n});\n"
  },
  {
    "path": "test/instance.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Create a jspreadsheet instance', () => {\n    it('and the dimensions are applied correctly', () => {\n        jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [10, 10],\n                    data: [[10, 20, 30]],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n\n        // check that the amount of rows displayed is 10\n        expect(rows.length).to.eq(10);\n\n        // check that the amount of columns displayed is 10 + 1 (one from the row header)\n        expect(firstRow.children.length).to.eq(10 + 1);\n    });\n\n    it('and the data is displayed correctly', () => {\n        jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [10, 10],\n                    data: [\n                        [10, 20, 30],\n                        [40, 50, 60],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        // check that [A1, B1, C1] received the data value\n        expect(firstRow.children[1].innerHTML).to.include(10);\n        expect(firstRow.children[1].innerHTML).not.to.include(20);\n        expect(firstRow.children[1].innerHTML).not.to.include(30);\n        expect(firstRow.children[2].innerHTML).to.include(20);\n        expect(firstRow.children[2].innerHTML).not.to.include(10);\n        expect(firstRow.children[2].innerHTML).not.to.include(30);\n        expect(firstRow.children[3].innerHTML).to.include(30);\n        expect(firstRow.children[3].innerHTML).not.to.include(10);\n        expect(firstRow.children[3].innerHTML).not.to.include(20);\n\n        // check that [A2, B2, C2] received the data value\n        expect(secondRow.children[1].innerHTML).to.include(40);\n        expect(secondRow.children[1].innerHTML).not.to.include(50);\n        expect(secondRow.children[1].innerHTML).not.to.include(60);\n        expect(secondRow.children[2].innerHTML).to.include(50);\n        expect(secondRow.children[2].innerHTML).not.to.include(40);\n        expect(secondRow.children[2].innerHTML).not.to.include(60);\n        expect(secondRow.children[3].innerHTML).to.include(60);\n        expect(secondRow.children[3].innerHTML).not.to.include(40);\n        expect(secondRow.children[3].innerHTML).not.to.include(50);\n    });\n\n    it('and the worksheet names are displayed correctly', () => {\n        jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                { minDimensions: [10, 10], worksheetName: 'Countries' },\n                { minDimensions: [10, 10], worksheetName: 'Employees' },\n            ],\n        });\n\n        const headerWorksheets = root.querySelector('.jtabs-headers');\n\n        expect(headerWorksheets.children[0].innerHTML).to.include('Countries');\n        expect(headerWorksheets.children[0].innerHTML).not.to.include('Employees');\n        expect(headerWorksheets.children[1].innerHTML).to.include('Employees');\n        expect(headerWorksheets.children[1].innerHTML).not.to.include('Countries');\n    });\n});\n"
  },
  {
    "path": "test/merges.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Merge tests', () => {\n    describe('Get merge', () => {\n        it('Worksheet started with a merge', () => {\n            const instance = jspreadsheet(root, {\n                toolbar: true,\n                worksheets: [\n                    {\n                        data: [\n                            ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                            ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                            ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                            ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                        ],\n                        minDimensions: [10, 15],\n                        columns: [\n                            {\n                                width: '300px',\n                            },\n                            {\n                                width: '80px',\n                            },\n                            {\n                                width: '100px',\n                            },\n                            {\n                                width: '150px',\n                            },\n                        ],\n                        mergeCells: {\n                            C1: [1, 2],\n                        },\n                    },\n                ],\n            });\n\n            expect(instance[0].getMerge('C1')).to.eql([1, 2]);\n            expect(instance[0].getMerge('C2')).to.equal(null);\n\n            expect(instance[0].getMerge()).to.eql({ C1: [1, 2] });\n        });\n\n        it('Worksheet started without merges', () => {\n            const instance = jspreadsheet(root, {\n                toolbar: true,\n                worksheets: [\n                    {\n                        data: [\n                            ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                            ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                            ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                            ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                        ],\n                        minDimensions: [10, 15],\n                        columns: [\n                            {\n                                width: '300px',\n                            },\n                            {\n                                width: '80px',\n                            },\n                            {\n                                width: '100px',\n                            },\n                            {\n                                width: '150px',\n                            },\n                        ],\n                    },\n                ],\n            });\n\n            expect(instance[0].getMerge('C1')).to.equal(null);\n\n            expect(instance[0].getMerge()).to.eql({});\n        });\n    });\n\n    it('Set merge', () => {\n        const instance = jspreadsheet(root, {\n            toolbar: true,\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                    minDimensions: [10, 15],\n                    columns: [\n                        {\n                            width: '300px',\n                        },\n                        {\n                            width: '80px',\n                        },\n                        {\n                            width: '100px',\n                        },\n                        {\n                            width: '150px',\n                        },\n                    ],\n                },\n            ],\n        });\n\n        instance[0].setMerge('A3', 2, 3);\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        expect(rows[2].children[1].getAttribute('colspan')).to.equal('2');\n        expect(rows[2].children[1].getAttribute('rowspan')).to.equal('3');\n    });\n\n    it('Remove merge', () => {\n        const instance = jspreadsheet(root, {\n            toolbar: true,\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                    minDimensions: [10, 15],\n                    columns: [\n                        {\n                            width: '300px',\n                        },\n                        {\n                            width: '80px',\n                        },\n                        {\n                            width: '100px',\n                        },\n                        {\n                            width: '150px',\n                        },\n                    ],\n                    mergeCells: {\n                        A1: [2, 2],\n                        E5: [3, 2],\n                    },\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        instance[0].removeMerge('A1');\n\n        expect(rows[0].children[1].getAttribute('colspan')).to.equal(null);\n        expect(rows[0].children[1].getAttribute('rowspan')).to.equal(null);\n\n        expect(rows[4].children[5].getAttribute('colspan')).to.equal('3');\n        expect(rows[4].children[5].getAttribute('rowspan')).to.equal('2');\n    });\n\n    it('Remove all merge', () => {\n        const instance = jspreadsheet(root, {\n            toolbar: true,\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                    minDimensions: [10, 15],\n                    columns: [\n                        {\n                            width: '300px',\n                        },\n                        {\n                            width: '80px',\n                        },\n                        {\n                            width: '100px',\n                        },\n                        {\n                            width: '150px',\n                        },\n                    ],\n                    mergeCells: {\n                        A1: [2, 2],\n                        E5: [3, 2],\n                    },\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        instance[0].destroyMerge();\n\n        expect(rows[0].children[1].getAttribute('colspan')).to.equal(null);\n        expect(rows[0].children[1].getAttribute('rowspan')).to.equal(null);\n\n        expect(rows[4].children[5].getAttribute('colspan')).to.equal(null);\n        expect(rows[4].children[5].getAttribute('rowspan')).to.equal(null);\n    });\n\n    it('setMerge history', () => {\n        const instance = jspreadsheet(root, {\n            toolbar: true,\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                    minDimensions: [10, 15],\n                    columns: [\n                        {\n                            width: '300px',\n                        },\n                        {\n                            width: '80px',\n                        },\n                        {\n                            width: '100px',\n                        },\n                        {\n                            width: '150px',\n                        },\n                    ],\n                },\n            ],\n        });\n\n        instance[0].setMerge('A3', 2, 3);\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n\n        expect(rows[2].children[1].getAttribute('colspan')).to.equal('2');\n        expect(rows[2].children[1].getAttribute('rowspan')).to.equal('3');\n\n        instance[0].undo();\n\n        expect(rows[0].children[1].getAttribute('colspan')).to.equal(null);\n        expect(rows[0].children[1].getAttribute('rowspan')).to.equal(null);\n\n        instance[0].redo();\n\n        expect(rows[2].children[1].getAttribute('colspan')).to.equal('2');\n        expect(rows[2].children[1].getAttribute('rowspan')).to.equal('3');\n    });\n\n    it('removeMerge event', () => {\n        let eventCalled = false;\n        let eventCellName = null;\n        let eventInstance = null;\n        let eventBeforeMerges = null;\n\n        const instance = jspreadsheet(root, {\n            toolbar: true,\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                    minDimensions: [10, 15],\n                    columns: [\n                        {\n                            width: '300px',\n                        },\n                        {\n                            width: '80px',\n                        },\n                        {\n                            width: '100px',\n                        },\n                        {\n                            width: '150px',\n                        },\n                    ],\n                    mergeCells: {\n                        A1: [2, 2],\n                        E5: [3, 2],\n                    },\n                },\n            ],\n            onunmerge: (worksheetInstance, cellName, beforeMerges) => {\n                eventCalled = true;\n                eventCellName = cellName;\n                eventInstance = worksheetInstance;\n                eventBeforeMerges = beforeMerges;\n            },\n        });\n\n        instance[0].removeMerge('A1');\n        instance[0].getMerge();\n\n        expect(eventCalled).to.equal(true);\n        expect(eventCellName).to.equal('A1');\n        expect(eventInstance).to.equal(instance[0]);\n        expect(eventBeforeMerges['A1'][0]).to.eql(2);\n        expect(eventBeforeMerges['A1'][1]).to.eql(2);\n        expect(Object.keys(eventBeforeMerges).length).to.eql(1);\n\n        expect(instance[0].getMerge('A1')).to.equal(null);\n    });\n});\n"
  },
  {
    "path": "test/meta.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Meta tests', () => {\n    describe('Set meta information', () => {\n        it('Set meta information using an object', () => {\n            const instance = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['US', 'Apples', 'Yes', '2019-02-12'],\n                            ['UK', 'Carrots', 'Yes', '2019-03-01'],\n                            ['CA', 'Oranges', 'No', '2018-11-10'],\n                            ['BR', 'Coconuts', 'Yes', '2019-01-12'],\n                        ],\n                    },\n                ],\n            });\n\n            instance[0].setMeta({ B1: { id: '1', y: '2019' }, C2: { test: '2' } });\n\n            expect(instance[0].options.meta).to.eql({\n                B1: { id: '1', y: '2019' },\n                C2: { test: '2' },\n            });\n\n            instance[0].setMeta({ C2: { something: '35' } });\n\n            expect(instance[0].options.meta).to.eql({\n                B1: { id: '1', y: '2019' },\n                C2: { test: '2', something: '35' },\n            });\n        });\n\n        it('Set meta information using strings', () => {\n            const instance = jspreadsheet(root, {\n                worksheets: [\n                    {\n                        data: [\n                            ['US', 'Apples', 'Yes', '2019-02-12'],\n                            ['UK', 'Carrots', 'Yes', '2019-03-01'],\n                            ['CA', 'Oranges', 'No', '2018-11-10'],\n                            ['BR', 'Coconuts', 'Yes', '2019-01-12'],\n                        ],\n                    },\n                ],\n            });\n\n            instance[0].setMeta('A1', 'myMeta', 'this is just a test');\n            instance[0].setMeta('A1', 'otherMetaInformation', 'other test');\n            instance[0].setMeta('D2', 'info', 'test');\n\n            expect(instance[0].options.meta).to.eql({\n                A1: {\n                    myMeta: 'this is just a test',\n                    otherMetaInformation: 'other test',\n                },\n                D2: { info: 'test' },\n            });\n\n            instance[0].setMeta('D2', 'myMetaData', 'something');\n\n            expect(instance[0].options.meta).to.eql({\n                A1: {\n                    myMeta: 'this is just a test',\n                    otherMetaInformation: 'other test',\n                },\n                D2: { info: 'test', myMetaData: 'something' },\n            });\n        });\n    });\n\n    it('Get meta information', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['US', 'Apples', 'Yes', '2019-02-12'],\n                        ['UK', 'Carrots', 'Yes', '2019-03-01'],\n                        ['CA', 'Oranges', 'No', '2018-11-10'],\n                        ['BR', 'Coconuts', 'Yes', '2019-01-12'],\n                    ],\n                    meta: {\n                        A1: {\n                            myMeta: 'this is just a test',\n                            otherMetaInformation: 'other test',\n                        },\n                        D2: { info: 'test' },\n                    },\n                },\n            ],\n        });\n\n        expect(instance[0].getMeta()).to.eql({\n            A1: { myMeta: 'this is just a test', otherMetaInformation: 'other test' },\n            D2: { info: 'test' },\n        });\n\n        expect(instance[0].getMeta('A1')).to.eql({\n            myMeta: 'this is just a test',\n            otherMetaInformation: 'other test',\n        });\n\n        expect(instance[0].getMeta('D2')).to.eql({ info: 'test' });\n\n        expect(instance[0].getMeta('A2')).to.equal(null);\n    });\n});\n"
  },
  {
    "path": "test/orderBy.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Sorting tests', () => {\n    it('Default sorting', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n                        ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n                    ],\n                },\n            ],\n        });\n\n        instance[0].orderBy(5);\n\n        expect(instance[0].options.data).to.eql([\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E4*F4'],\n        ]);\n\n        instance[0].orderBy(5);\n\n        expect(instance[0].options.data).to.eql([\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E1*F1'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E2*F2'],\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E3*F3'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n        ]);\n    });\n\n    it('Custom sorting', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n                        ['Peugeot', 1800, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n                        ['test', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n                        ['Honda CRV', 1900, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n                    ],\n                    sorting: function (direction) {\n                        return function (a, b) {\n                            let valueA = a[1];\n                            let valueB = b[1];\n\n                            if (valueA === 'test') {\n                                return direction ? 1 : -1;\n                            }\n\n                            if (valueB === 'test') {\n                                return direction ? -1 : 1;\n                            }\n\n                            // Consider blank rows in the sorting\n                            if (!direction) {\n                                return valueA > valueB ? 1 : valueA < valueB ? -1 : 0;\n                            } else {\n                                return valueA > valueB ? -1 : valueA < valueB ? 1 : 0;\n                            }\n                        };\n                    },\n                },\n            ],\n        });\n\n        instance[0].orderBy(0, 1);\n\n        expect(instance[0].options.data).to.eql([\n            ['test', 2009, 3000, '2004-01-01', '214.00', '3', '=E1*F1'],\n            ['Peugeot', 1800, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E3*F3'],\n            ['Honda CRV', 1900, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n        ]);\n    });\n\n    it('orderBy history', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n                        ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n                    ],\n                },\n            ],\n        });\n\n        instance[0].orderBy(5);\n\n        expect(instance[0].options.data).to.eql([\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E4*F4'],\n        ]);\n\n        instance[0].undo(5);\n\n        expect(instance[0].options.data).to.eql([\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E4*F4'],\n        ]);\n\n        instance[0].redo(5);\n\n        expect(instance[0].options.data).to.eql([\n            ['Mazda', 2001, 2000, '2006-01-01', '453.00', '2', '=E1*F1'],\n            ['Honda CRV', 2010, 6000, '2003-01-01', '56.11', '2', '=E2*F2'],\n            ['Honda Fit', 2009, 3000, '2004-01-01', '214.00', '3', '=E3*F3'],\n            ['Peugeot', 2010, 5000, '2005-01-01', '23.00', '5', '=E4*F4'],\n        ]);\n    });\n});\n"
  },
  {
    "path": "test/pagination.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use pagination', () => {\n    it('Start the worksheet with pagination', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3, 4, 5],\n                        [6, 7, 8, 9, 10],\n                        [11, 12, 13, 14, 15],\n                        [16, 17, 18, 19, 20],\n                        [21, 22, 23, 24, 25],\n                        [26, 27, 28, 29, 30],\n                        [31, 32, 33, 34, 35],\n                        [36, 37, 38, 39, 40],\n                        [41, 42, 43, 44, 45],\n                        [46, 47, 48, 49, 50],\n                    ],\n                    pagination: 3,\n                },\n            ],\n        });\n\n        expect(instance[0].quantiyOfPages()).to.equal(4);\n\n        const bodyTag = root.querySelector('tbody');\n\n        expect(bodyTag.children.length).to.equal(3);\n\n        expect(bodyTag.children[0].getAttribute('data-y')).to.equal('0');\n        expect(bodyTag.children[1].getAttribute('data-y')).to.equal('1');\n        expect(bodyTag.children[2].getAttribute('data-y')).to.equal('2');\n    });\n\n    it('page method', () => {\n        const instance = jspreadsheet(root, {\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3, 4, 5],\n                        [6, 7, 8, 9, 10],\n                        [11, 12, 13, 14, 15],\n                        [16, 17, 18, 19, 20],\n                        [21, 22, 23, 24, 25],\n                        [26, 27, 28, 29, 30],\n                        [31, 32, 33, 34, 35],\n                        [36, 37, 38, 39, 40],\n                        [41, 42, 43, 44, 45],\n                        [46, 47, 48, 49, 50],\n                    ],\n                    pagination: 3,\n                },\n            ],\n        });\n\n        instance[0].page(2);\n\n        expect(instance[0].quantiyOfPages()).to.equal(4);\n\n        const bodyTag = root.querySelector('tbody');\n\n        expect(bodyTag.children.length).to.equal(3);\n\n        expect(bodyTag.children[0].getAttribute('data-y')).to.equal('6');\n        expect(bodyTag.children[1].getAttribute('data-y')).to.equal('7');\n        expect(bodyTag.children[2].getAttribute('data-y')).to.equal('8');\n    });\n});\n"
  },
  {
    "path": "test/paste.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\nglobal.document.execCommand = function execCommandMock() {};\n\nconst fixtureData = () => [\n    ['Mazda', 2001, 2000, 1],\n    ['Peugeot', 2010, 5000, '=B2+C2'],\n    ['Honda Fit', 2009, 3000, '=B3+C3'],\n    ['Honda CRV', 2010, 6000, '=B4+C4'],\n];\n\ndescribe('Paste', () => {\n    it('no expand', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\t0-2\\t0-3\\n1-0\\t1-1\\t1-2\\t1-3\\n2-0\\t2-1\\t2-2\\t2-3\\n3-0\\t3-1\\t3-2\\t3-3';\n        sheet.updateSelectionFromCoords(0, 0, 0, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['0-0', '0-1', '0-2', '0-3'],\n            ['1-0', '1-1', '1-2', '1-3'],\n            ['2-0', '2-1', '2-2', '2-3'],\n            ['3-0', '3-1', '3-2', '3-3'],\n        ]);\n    });\n\n    it('expand', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\t0-2\\t0-3\\n1-0\\t1-1\\t1-2\\t1-3\\n2-0\\t2-1\\t2-2\\t2-3\\n3-0\\t3-1\\t3-2\\t3-3';\n        sheet.updateSelectionFromCoords(3, 3, 3, 3);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['Mazda', 2001, 2000, 1, '', '', ''],\n            ['Peugeot', 2010, 5000, '=B2+C2', '', '', ''],\n            ['Honda Fit', 2009, 3000, '=B3+C3', '', '', ''],\n            ['Honda CRV', 2010, 6000, '0-0', '0-1', '0-2', '0-3'],\n            ['', '', '', '1-0', '1-1', '1-2', '1-3'],\n            ['', '', '', '2-0', '2-1', '2-2', '2-3'],\n            ['', '', '', '3-0', '3-1', '3-2', '3-3'],\n        ]);\n    });\n\n    it('repeat horizontal', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1';\n        sheet.updateSelectionFromCoords(0, 0, 4, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['0-0', '0-1', '0-0', '0-1'],\n            ['Peugeot', 2010, 5000, '=B2+C2'],\n            ['Honda Fit', 2009, 3000, '=B3+C3'],\n            ['Honda CRV', 2010, 6000, '=B4+C4'],\n        ]);\n    });\n\n    it('repeat vertical', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\n1-0';\n        sheet.updateSelectionFromCoords(0, 0, 0, 4);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['0-0', 2001, 2000, 1],\n            ['1-0', 2010, 5000, '=B2+C2'],\n            ['0-0', 2009, 3000, '=B3+C3'],\n            ['1-0', 2010, 6000, '=B4+C4'],\n        ]);\n    });\n\n    it('repeat rectangle', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\n1-0\\t1-1';\n        sheet.updateSelectionFromCoords(1, 0, 1, 3);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['Mazda', '0-0', '0-1', 1],\n            ['Peugeot', '1-0', '1-1', '=B2+C2'],\n            ['Honda Fit', '0-0', '0-1', '=B3+C3'],\n            ['Honda CRV', '1-0', '1-1', '=B4+C4'],\n        ]);\n    });\n\n    it('skip hidden column', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    columns: [\n                        { type: 'text' },\n                        { type: 'text' },\n                        { type: 'hidden' }, // paste is skipped.\n                        { type: 'text' },\n                    ],\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\n1-0\\t1-1';\n        sheet.updateSelectionFromCoords(1, 0, 1, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['Mazda', '0-0', 2000, '0-1'],\n            ['Peugeot', '1-0', 5000, '1-1'],\n            ['Honda Fit', 2009, 3000, '=B3+C3'],\n            ['Honda CRV', 2010, 6000, '=B4+C4'],\n        ]);\n    });\n\n    it('skip hidden row', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\n1-0\\t1-1';\n        sheet.hideRow(1);\n        sheet.updateSelectionFromCoords(1, 0, 1, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['Mazda', '0-0', '0-1', 1],\n            ['Peugeot', 2010, 5000, '=B2+C2'],\n            ['Honda Fit', '1-0', '1-1', '=B3+C3'],\n            ['Honda CRV', 2010, 6000, '=B4+C4'],\n        ]);\n    });\n\n    it('see https://github.com/jspreadsheet/ce/pull/1717#issuecomment-2576060698', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    minDimensions: [4, 4],\n                    data: [\n                        [1, 2],\n                        [3, 4],\n                    ],\n                },\n            ],\n        })[0];\n\n        sheet.updateSelectionFromCoords(0, 0, 1, 1);\n        sheet.copy();\n        sheet.hideRow(0);\n        sheet.hideColumn(0);\n        sheet.updateSelectionFromCoords(2, 2, 2, 2);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], sheet.data);\n\n        expect(sheet.getData()).to.eql([\n            [1, 2, '', ''],\n            [3, 4, '', ''],\n            ['', '', '1', '2'],\n            ['', '', '3', '4'],\n        ]);\n    });\n\n    it('see https://github.com/jspreadsheet/ce/pull/1717#issuecomment-2580087207', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    minDimensions: [10, 4],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                },\n            ],\n        })[0];\n\n        sheet.updateSelectionFromCoords(0, 0, 2, 1);\n        sheet.copy();\n        sheet.hideColumn(8);\n        sheet.hideColumn(9);\n        sheet.hideRow(3);\n        sheet.updateSelectionFromCoords(7, 2, 7, 2);\n        expect(sheet.getData()).to.eql([\n            [1, 2, 3, '', '', '', '', '', '', ''],\n            [4, 5, 6, '', '', '', '', '', '', ''],\n            ['', '', '', '', '', '', '', '', '', ''],\n            ['', '', '', '', '', '', '', '', '', ''],\n        ]);\n\n        sheet.paste(7, 2, sheet.data);\n\n        expect(sheet.getData()).to.eql([\n            [1, 2, 3, '', '', '', '', '', '', '', '', ''],\n            [4, 5, 6, '', '', '', '', '', '', '', '', ''],\n            ['', '', '', '', '', '', '', '1', '', '', '2', '3'],\n            ['', '', '', '', '', '', '', '', '', '', '', ''],\n            ['', '', '', '', '', '', '', '4', '', '', '5', '6'],\n        ]);\n    });\n\n    it('large data paste', () => {\n        let count = {};\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n            onevent: (event) => {\n                count[event] = (count[event] ?? 0) + 1;\n            },\n        })[0];\n\n        const pasteText = new Array(1000)\n            .fill(0)\n            .map((v, i) =>\n                new Array(20)\n                    .fill(0)\n                    .map((v2, i2) => `${i}-${i2}`)\n                    .join('\\t')\n            )\n            .join('\\n');\n        sheet.updateSelectionFromCoords(3, 3, 3, 3);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n        expect(count.onbeforechange).to.eql(20000);\n        expect(count.onbeforeinsertcolumn).to.eql(1);\n        expect(count.onbeforeinsertrow).to.eql(1);\n        expect(count.onchange).to.eql(20000);\n        expect(count.oninsertcolumn).to.eql(1);\n        expect(count.oninsertrow).to.eql(1);\n        expect(count.onselection).to.eql(3);\n    }).timeout(10 * 1000);\n\n    it('expand with hidden cells', () => {\n        let count = {};\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    columns: [\n                        { type: 'text' },\n                        { type: 'text' },\n                        { type: 'hidden' }, // paste is skipped.\n                        { type: 'text' },\n                    ],\n                    data: fixtureData(),\n                },\n            ],\n            onevent: (event) => {\n                count[event] = (count[event] ?? 0) + 1;\n            },\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\t0-2\\t0-3\\n1-0\\t1-1\\t1-2\\t1-3\\n2-0\\t2-1\\t2-2\\t2-3\\n3-0\\t3-1\\t3-2\\t3-3';\n        sheet.hideRow(2);\n        sheet.updateSelectionFromCoords(1, 1, 1, 1);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n        expect(sheet.getData()).to.eql([\n            ['Mazda', 2001, 2000, 1, '', ''],\n            ['Peugeot', '0-0', 5000, '0-1', '0-2', '0-3'],\n            ['Honda Fit', 2009, 3000, '=B3+C3', '', ''],\n            ['Honda CRV', '1-0', 6000, '1-1', '1-2', '1-3'],\n            ['', '2-0', '', '2-1', '2-2', '2-3'],\n            ['', '3-0', '', '3-1', '3-2', '3-3'],\n        ]);\n        expect(count.onbeforeinsertcolumn).to.eql(1);\n        expect(count.onbeforeinsertrow).to.eql(1);\n    }).timeout(10 * 1000);\n\n    it('copy and paste with style', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        sheet.setStyle('A1', 'color', 'red');\n        sheet.updateSelectionFromCoords(0, 0, 1, 1);\n        sheet.copy();\n        sheet.paste(2, 2, sheet.data);\n        expect(sheet.getData()).to.eql([\n            ['Mazda', 2001, 2000, 1],\n            ['Peugeot', 2010, 5000, '=B2+C2'],\n            ['Honda Fit', 2009, 'Mazda', '2001'],\n            ['Honda CRV', 2010, 'Peugeot', '2010'],\n        ]);\n        expect(sheet.getStyle('A1', 'color')).to.eql('red');\n        expect(sheet.getStyle('C3', 'color')).to.eql('red');\n    });\n\n    it('copy and repeat paste with style', () => {\n        global.document.execCommand = function execCommandMock() {};\n\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        sheet.setStyle('A1', 'color', 'red');\n        sheet.updateSelectionFromCoords(0, 0, 1, 0);\n        sheet.copy();\n        sheet.updateSelectionFromCoords(0, 2, 4, 4);\n        sheet.paste(0, 2, sheet.data);\n        expect(sheet.getData()).to.eql([\n            ['Mazda', 2001, 2000, 1],\n            ['Peugeot', 2010, 5000, '=B2+C2'],\n            ['Mazda', '2001', 'Mazda', '2001'],\n            ['Mazda', '2001', 'Mazda', '2001'],\n        ]);\n        //\n        expect(sheet.getStyle('A1', 'color')).to.eql('red');\n        //\n        expect(sheet.getStyle('A3', 'color')).to.eql('red');\n        expect(sheet.getStyle('B3', 'color')).to.eql('');\n        expect(sheet.getStyle('C3', 'color')).to.eql('red');\n        expect(sheet.getStyle('D3', 'color')).to.eql('');\n        //\n        expect(sheet.getStyle('A4', 'color')).to.eql('red');\n        expect(sheet.getStyle('B4', 'color')).to.eql('');\n        expect(sheet.getStyle('C4', 'color')).to.eql('red');\n        expect(sheet.getStyle('D4', 'color')).to.eql('');\n    });\n\n    it('copy and paste to another sheet', async () => {\n        global.document.execCommand = function execCommandMock() {};\n\n        let isLoaded = false;\n        let sheets = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    data: fixtureData(),\n                    worksheetName: 'Sheet1',\n                },\n                {\n                    data: fixtureData(),\n                    worksheetName: 'Sheet2',\n                },\n            ],\n            onload: () => {\n                isLoaded = true;\n            },\n        });\n\n        const awaitLoop = (resolve) => {\n            setTimeout(() => {\n                if (isLoaded) {\n                    resolve();\n                } else {\n                    resolve(awaitLoop);\n                }\n            }, 100);\n        };\n        // NOTE: jpreadsheet constructor is acutally async. So it waits for load events in await.\n        await new Promise(awaitLoop);\n\n        const from = sheets[0];\n        from.setStyle('A1', 'color', 'red');\n        from.updateSelectionFromCoords(0, 0, 1, 0);\n        from.copy();\n        const to = sheets[1];\n        to.updateSelectionFromCoords(0, 2, 4, 4);\n        to.paste(0, 2, from.data);\n        expect(to.getData()).to.eql([\n            ['Mazda', 2001, 2000, 1],\n            ['Peugeot', 2010, 5000, '=B2+C2'],\n            ['Mazda', '2001', 'Mazda', '2001'],\n            ['Mazda', '2001', 'Mazda', '2001'],\n        ]);\n    });\n\n    it('fix - u0000 is pasted, when the last cell ends in a tab', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t0-1\\t0-2\\t0-3\\n1-0\\t1-1\\t1-2\\t1-3\\n2-0\\t2-1\\t2-2\\t2-3\\n3-0\\t3-1\\t3-2\\t';\n        sheet.updateSelectionFromCoords(0, 0, 0, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['0-0', '0-1', '0-2', '0-3'],\n            ['1-0', '1-1', '1-2', '1-3'],\n            ['2-0', '2-1', '2-2', '2-3'],\n            ['3-0', '3-1', '3-2', ''],\n        ]);\n    });\n\n    it('paste lacked columns data', () => {\n        let sheet = jspreadsheet(root, {\n            worksheets: [\n                {\n                    data: fixtureData(),\n                },\n            ],\n        })[0];\n\n        const pasteText = '0-0\\t\\n' + '1-0\\t1-1\\t1-2\\t1-3\\n' + '2-0\\n' + '3-0\\t3-1\\t3-2\\t';\n        sheet.updateSelectionFromCoords(0, 0, 0, 0);\n        sheet.paste(sheet.selectedCell[0], sheet.selectedCell[1], pasteText);\n\n        expect(sheet.getData()).to.eql([\n            ['0-0', '', '', ''],\n            ['1-0', '1-1', '1-2', '1-3'],\n            ['2-0', '', '', ''],\n            ['3-0', '3-1', '3-2', ''],\n        ]);\n    });\n});\n"
  },
  {
    "path": "test/redo.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use the redo method', () => {\n    it('.undo', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('CE');\n\n        instance[0].setValueFromCoords(0, 0, 'New Value');\n        instance[0].setValueFromCoords(1, 0, 'TESTING');\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('TESTING');\n\n        instance[0].undo();\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].undo();\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n    });\n\n    it('.redo after undo something', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Hello', 'World'],\n                        ['Testing', 'CE'],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        const table = root.querySelector('tbody');\n        const rows = table.children;\n        const firstRow = rows[0];\n        const secondRow = rows[1];\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n        expect(secondRow.children[1].innerHTML).to.include('Testing');\n        expect(secondRow.children[2].innerHTML).to.include('CE');\n\n        instance[0].setValueFromCoords(0, 0, 'New Value');\n        instance[0].setValueFromCoords(1, 0, 'TESTING');\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('TESTING');\n\n        instance[0].undo();\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].undo();\n\n        expect(firstRow.children[1].innerHTML).to.include('Hello');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].redo();\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('World');\n\n        instance[0].redo();\n\n        expect(firstRow.children[1].innerHTML).to.include('New Value');\n        expect(firstRow.children[2].innerHTML).to.include('TESTING');\n    });\n});\n"
  },
  {
    "path": "test/rows.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use the rows method', () => {\n    it('deleteRow and a row is removed', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n        let firstRow = rows[0];\n\n        // Check that first row has the value of [1, 2, 3]\n        expect(firstRow.children[1].innerHTML).to.include(1);\n        expect(firstRow.children[2].innerHTML).to.include(2);\n        expect(firstRow.children[3].innerHTML).to.include(3);\n\n        instance[0].deleteRow(0);\n\n        table = root.querySelector('tbody');\n        rows = table.children;\n        firstRow = rows[0];\n\n        // Check that the value of the first row now is [4, 5, 6] since the first one got removed\n        expect(firstRow.children[1].innerHTML).to.include(4);\n        expect(firstRow.children[2].innerHTML).to.include(5);\n        expect(firstRow.children[3].innerHTML).to.include(6);\n    });\n\n    it('insertRow and a row is added', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n        let firstRow = rows[0];\n\n        // Check that first row has the value of [1, 2, 3]\n        expect(firstRow.children[1].innerHTML).to.include(1);\n        expect(firstRow.children[2].innerHTML).to.include(2);\n        expect(firstRow.children[3].innerHTML).to.include(3);\n\n        instance[0].insertRow([9, 9, 9], 0, 1);\n\n        table = root.querySelector('tbody');\n        rows = table.children;\n        firstRow = rows[0];\n\n        // Check that the value of the first row now is [9, 9, 9]\n        expect(firstRow.children[1].innerHTML).to.include(9);\n        expect(firstRow.children[2].innerHTML).to.include(9);\n        expect(firstRow.children[3].innerHTML).to.include(9);\n    });\n\n    it('moveRow and the row is moved', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n        let firstRow = rows[0];\n        let secondRow = rows[1];\n        let A1 = firstRow.children[1];\n        let A2 = secondRow.children[1];\n\n        expect(A1.innerHTML).to.include(1);\n        expect(A2.innerHTML).to.include(4);\n\n        instance[0].moveRow(0, 1);\n\n        table = root.querySelector('tbody');\n        rows = table.children;\n        firstRow = rows[0];\n        secondRow = rows[1];\n        A1 = firstRow.children[1];\n        A2 = secondRow.children[1];\n\n        expect(A1.innerHTML).to.include(4);\n        expect(A2.innerHTML).to.include(1);\n    });\n\n    it('deleteRow history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        instance[0].deleteRow(0);\n\n        expect(rows[0].children[1].innerHTML).to.include(4);\n        expect(rows[0].children[2].innerHTML).to.include(5);\n        expect(rows[0].children[3].innerHTML).to.include(6);\n\n        instance[0].undo();\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        instance[0].redo();\n\n        expect(rows[0].children[1].innerHTML).to.include(4);\n        expect(rows[0].children[2].innerHTML).to.include(5);\n        expect(rows[0].children[3].innerHTML).to.include(6);\n    });\n\n    it('insertRow history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                    worksheetName: 'Countries',\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n        let rows = table.children;\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        instance[0].insertRow([9, 9, 9], 0, 1);\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(9);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n\n        instance[0].undo();\n\n        expect(rows[0].children[1].innerHTML).to.include(1);\n        expect(rows[0].children[2].innerHTML).to.include(2);\n        expect(rows[0].children[3].innerHTML).to.include(3);\n\n        instance[0].redo();\n\n        expect(rows[0].children[1].innerHTML).to.include(9);\n        expect(rows[0].children[2].innerHTML).to.include(9);\n        expect(rows[0].children[3].innerHTML).to.include(9);\n    });\n\n    it('moveRow history', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    minDimensions: [7, 7],\n                    data: [\n                        [1, 2, 3],\n                        [4, 5, 6],\n                    ],\n                },\n            ],\n        });\n\n        let table = root.querySelector('tbody');\n\n        expect(table.children[0].children[1].innerHTML).to.include(1);\n        expect(table.children[1].children[1].innerHTML).to.include(4);\n\n        instance[0].moveRow(0, 1);\n\n        expect(table.children[0].children[1].innerHTML).to.include(4);\n        expect(table.children[1].children[1].innerHTML).to.include(1);\n\n        instance[0].undo();\n\n        expect(table.children[0].children[1].innerHTML).to.include(1);\n        expect(table.children[1].children[1].innerHTML).to.include(4);\n\n        instance[0].redo();\n\n        expect(table.children[0].children[1].innerHTML).to.include(4);\n        expect(table.children[1].children[1].innerHTML).to.include(1);\n    });\n});\n"
  },
  {
    "path": "test/search.js",
    "content": "const { expect } = require('chai');\n\nconst jspreadsheet = require('../dist/index.js');\n\ndescribe('Use search', () => {\n    it('search and resetSearch methods', () => {\n        const instance = jspreadsheet(root, {\n            tabs: true,\n            worksheets: [\n                {\n                    search: true,\n                    minDimensions: [7, 7],\n                    data: [\n                        ['Mazda', 2001, 2000, '2006-01-01 12:00:00'],\n                        ['Peugeot', 2010, 5000, '2005-01-01 13:00:00'],\n                        ['Honda Fit', 2009, 3000, '2004-01-01 14:01:00'],\n                        ['Honda CRV', 2010, 6000, '2003-01-01 23:30:00'],\n                    ],\n                },\n            ],\n        });\n\n        instance[0].search('Honda');\n\n        expect(instance[0].searchInput.value).to.equal('Honda');\n\n        const bodyTag = root.querySelector('tbody');\n\n        expect(bodyTag.children.length).to.equal(2);\n\n        expect(bodyTag.children[0].getAttribute('data-y')).to.equal('2');\n        expect(bodyTag.children[1].getAttribute('data-y')).to.equal('3');\n\n        instance[0].resetSearch();\n\n        expect(instance[0].searchInput.value).to.equal('');\n\n        expect(bodyTag.children[0].getAttribute('data-y')).to.equal('0');\n        expect(bodyTag.children[1].getAttribute('data-y')).to.equal('1');\n    });\n});\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require('path');\nconst MiniCssExtractPlugin = require('mini-css-extract-plugin');\n\nclass MyPlugin {\n    apply(compiler) {\n        compiler.hooks.emit.tap('MyPlugin', (compilation) => {\n            // Get the bundled file name\n            const fileName = Object.keys(compilation.assets)[0];\n\n            // Get the bundled file content\n            const fileContent = compilation.assets[fileName].source();\n\n            const header = `if (! jSuites && typeof(require) === 'function') {\n    var jSuites = require('jsuites');\n}\n\nif (! formula && typeof(require) === 'function') {\n    var formula = require('@jspreadsheet/formula');\n}\n\n;(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n    typeof define === 'function' && define.amd ? define(factory) :\n    global.jspreadsheet = factory();\n}(this, (function () {`;\n\n            const footer = `    return jspreadsheet;\n})));`;\n\n            // Updated file content with custom content added\n            const updatedFileContent = header + '\\n\\n' + fileContent + '\\n\\n' + footer;\n\n            // Replace the bundled file content with updated content\n            compilation.assets[fileName] = {\n                source: () => updatedFileContent,\n                size: () => updatedFileContent.length,\n            };\n        });\n    }\n}\n\nlet isProduction = process.env.NODE_ENV === 'production';\n\nconst webpack = {\n    target: ['web', 'es5'],\n    entry: isProduction ? './src/index' : './src/test.js',\n    mode: isProduction ? 'production' : 'development',\n    externals: {},\n    output: {\n        filename: 'index.js',\n        path: path.resolve(__dirname, 'dist'),\n        library: 'jspreadsheet',\n        libraryExport: 'default',\n    },\n    optimization: {\n        minimize: true,\n    },\n    devServer: {\n        static: {\n            directory: path.join(__dirname, '/public'),\n        },\n        headers: {\n            'Access-Control-Allow-Origin': '*',\n            'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',\n            'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',\n        },\n        port: 8000,\n        devMiddleware: {\n            publicPath: 'https://localhost:3000/',\n        },\n        hot: 'only',\n    },\n    plugins: [],\n    module: {\n        rules: [\n            isProduction\n                ? {\n                      test: /\\.js$/,\n                      use: [\n                          {\n                              loader: path.resolve('build.cjs'),\n                              options: {},\n                          },\n                      ],\n                  }\n                : null,\n            {\n                test: /\\.css$/,\n                use: [isProduction ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'],\n            },\n        ],\n    },\n    stats: {\n        warnings: false,\n    },\n};\n\nif (isProduction) {\n    webpack.plugins.push(new MyPlugin());\n    webpack.plugins.push(\n        new MiniCssExtractPlugin({\n            filename: 'jspreadsheet.css',\n        })\n    );\n}\n\nmodule.exports = webpack;\n"
  }
]