diff --git a/.config/webpack/base.js b/.config/webpack/base.js
index dd7dde6766..9f3305d906 100644
--- a/.config/webpack/base.js
+++ b/.config/webpack/base.js
@@ -2,6 +2,7 @@ const path = require('path');
const fs = require('fs');
const { BannerPlugin } = require('webpack');
+const licenseComment = fs.readFileSync(path.resolve(__dirname, '../source-license-header.js'), 'utf8').trim();
let licensePreamble = fs.readFileSync(path.resolve(__dirname, '../../LICENSE.txt'), 'utf8');
licensePreamble += '\n\nVersion: ' + process.env.HT_VERSION;
@@ -42,6 +43,14 @@ module.exports.create = function create(processedFile) {
}
]
},
+ {
+ test: /\.js$/,
+ loader: 'string-replace-loader',
+ options: {
+ search: licenseComment,
+ replace: '',
+ }
+ }
]
},
plugins: [
diff --git a/.eslintignore b/.eslintignore
index c0a76472bd..1bec93b8cd 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,6 +1,9 @@
# Common files
node_modules
+# example files
+docs/examples/
+
# 3rd party
src/interpreter/plugin/3rdparty
diff --git a/docs/.vuepress/components/ScriptLoader.vue b/docs/.vuepress/components/ScriptLoader.vue
new file mode 100644
index 0000000000..c77d3eb329
--- /dev/null
+++ b/docs/.vuepress/components/ScriptLoader.vue
@@ -0,0 +1,39 @@
+
+
+
+
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index d966d28666..599773844d 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -2,9 +2,9 @@ const highlight = require('./highlight');
const regexPlugin = require('markdown-it-regex').default;
const footnotePlugin = require('markdown-it-footnote');
const searchBoxPlugin = require('./plugins/search-box');
+const examples = require('./plugins/examples/examples');
const HyperFormula = require('../../dist/hyperformula.full');
-const fs = require('fs');
-const path = require('path');
+const includeCodeSnippet = require('./plugins/markdown-it-include-code-snippet');
const searchPattern = new RegExp('^/api', 'i');
@@ -12,6 +12,12 @@ module.exports = {
title: 'HyperFormula (v' + HyperFormula.version + ')',
description: 'HyperFormula is an open-source, high-performance calculation engine for spreadsheets and web applications.',
head: [
+ // Import HF (required for the examples)
+ [ 'script', { src: 'https://cdn.jsdelivr.net/npm/hyperformula/dist/hyperformula.full.min.js' } ],
+ [ 'script', { src: 'https://cdn.jsdelivr.net/npm/hyperformula@2.7.1/dist/languages/enUS.js' } ],
+ [ 'script', { src: 'https://cdn.jsdelivr.net/npm/hyperformula@2.7.1/dist/languages/frFR.js' } ],
+ // Import moment (required for the examples)
+ [ 'script', { src: 'https://cdn.jsdelivr.net/npm/moment/moment.min.js' } ],
// Google Tag Manager, an extra element within the `ssr.html` file.
['script', {}, `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
@@ -41,11 +47,11 @@ module.exports = {
`],
[
'script',
- {
- id: 'Sentry.io',
- src: 'https://js.sentry-cdn.com/50617701901516ce348cb7b252564a60.min.js',
- crossorigin: 'anonymous',
- },
+ {
+ id: 'Sentry.io',
+ src: 'https://js.sentry-cdn.com/50617701901516ce348cb7b252564a60.min.js',
+ crossorigin: 'anonymous',
+ },
],
// Favicon
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/favicon/apple-touch-icon.png' }],
@@ -57,14 +63,7 @@ module.exports = {
base: '/',
plugins: [
searchBoxPlugin,
- // [
- // 'vuepress-plugin-clean-urls',
- // {
- // normalSuffix: '',
- // indexSuffix: '/',
- // notFoundPath: '/404.html',
- // },
- // ],
+ ['container', examples()],
{
extendPageData ($page) {
// inject current HF version as {{ $page.version }} variable
@@ -109,6 +108,7 @@ module.exports = {
replace: () => `'${HyperFormula.releaseDate}'`
})
md.use(footnotePlugin)
+ md.use(includeCodeSnippet)
}
},
// TODO: It doesn't work. It's seems that this option is bugged. Documentation says that this option is configurable,
diff --git a/docs/.vuepress/plugins/examples/code-builder.js b/docs/.vuepress/plugins/examples/code-builder.js
new file mode 100644
index 0000000000..9c48be9ae4
--- /dev/null
+++ b/docs/.vuepress/plugins/examples/code-builder.js
@@ -0,0 +1,32 @@
+const { transformSync } = require('@babel/core');
+
+const babelConfig = {
+ presets: [
+ '@babel/preset-env',
+ '@babel/preset-react',
+ '@babel/preset-typescript',
+ ],
+ plugins: [
+ '@babel/plugin-transform-modules-commonjs',
+ '@babel/plugin-syntax-class-properties',
+ ['@babel/plugin-proposal-decorators', { legacy: true }],
+ ['@babel/plugin-proposal-class-properties', { loose: true }],
+ ['@babel/plugin-proposal-private-methods', { loose: true }],
+ ['@babel/plugin-proposal-private-property-in-object', { loose: true }]
+ ],
+ targets: {
+ ie: 9
+ }
+};
+
+const buildCode = (filename, contentJs, relativePath = '') => {
+ try {
+ return transformSync(contentJs, { ...babelConfig, filename }).code;
+ } catch (error) {
+ // eslint-disable-next-line
+ console.error(`Babel error when building ${relativePath}`);
+ throw error;
+ }
+};
+
+module.exports = { buildCode };
diff --git a/docs/.vuepress/plugins/examples/examples.js b/docs/.vuepress/plugins/examples/examples.js
new file mode 100644
index 0000000000..18a19c1b5b
--- /dev/null
+++ b/docs/.vuepress/plugins/examples/examples.js
@@ -0,0 +1,119 @@
+const Token = require('markdown-it/lib/token');
+const { buildCode } = require('./code-builder');
+const { stackblitz } = require('./stackblitz');
+
+/**
+ * Matches into: `example #ID .class :preset --css 2 --html 0 --js 1 --ts 3 --no-edit`.
+ *
+ * @type {RegExp}
+ */
+const EXAMPLE_REGEX = /^(example)\s*(#\S*|)\s*(\.\S*|)\s*(:\S*|)\s*([\S|\s]*)$/;
+
+const parseCode = (content) => {
+ if (!content) return '';
+
+ return content
+ // Remove the all "/* start:skip-in-preview */" and "/* end:skip-in-preview */" comments
+ .replace(/\/\*(\s+)?(start|end):skip-in-preview(\s+)?\*\/\r?\n/gm, '')
+ // Remove the all "/* start:skip-in-sandbox */" and "/* end:skip-in-sandbox */" comments
+ .replace(/\/\*(\s+)?(start|end):skip-in-sandbox(\s+)?\*\/\r?\n/gm, '')
+ // Remove the code between "/* start:skip-in-compilation */" and "/* end:skip-in-compilation */" expressions
+ .replace(/\/\*(\s+)?start:skip-in-compilation(\s+)?\*\/\r?\n.*?\/\*(\s+)?end:skip-in-compilation(\s+)?\*\/\r?\n/msg, '')
+ // Remove /* end-file */
+ .replace(/\/\* end-file \*\//gm, '');
+};
+
+const parseCodeSandbox = (content) => {
+ if (!content) return '';
+
+ return content
+ // Remove the code between "/* start:skip-in-sandbox */" and "/* end:skip-in-sandbox */" expressions
+ .replace(/\/\*(\s+)?start:skip-in-sandbox(\s+)?\*\/\r?\n.*?\/\*(\s+)?end:skip-in-sandbox(\s+)?\*\/\r?\n/msg, '')
+ // Remove the all "/* start:skip-in-preview */" and "/* end:skip-in-preview */" comments
+ .replace(/\/\*(\s+)?(start|end):skip-in-preview(\s+)?\*\/\r?\n/gm, '')
+ // Remove the all "/* start:skip-in-compilation */" and "/* end:skip-in-compilation */" comments
+ .replace(/\/\*(\s+)?(start|end):skip-in-compilation(\s+)?\*\/\r?\n/gm, '');
+};
+
+module.exports = function() {
+ return {
+ type: 'example',
+ render(tokens, index, _opts, env) {
+ const token = tokens[index];
+ const m = token.info.trim().match(EXAMPLE_REGEX);
+
+ if (token.nesting !== 1 || !m) {
+ return '';
+ }
+
+ let [, , id, klass, preset, args] = m;
+
+ id = id ? id.substring(1) : 'example1';
+ klass = klass ? klass.substring(1) : '';
+ preset = preset ? preset.substring(1) : 'hot';
+ args = args || '';
+
+ const htmlPos = args.match(/--html (\d*)/)?.[1];
+ const htmlIndex = htmlPos ? index + Number.parseInt(htmlPos, 10) : 0;
+ const htmlToken = htmlPos ? tokens[htmlIndex] : undefined;
+ const htmlContent = htmlToken
+ ? htmlToken.content
+ : `
`;
+ const htmlContentRoot = `${htmlContent}
`;
+
+ const cssPos = args.match(/--css (\d*)/)?.[1];
+ const cssIndex = cssPos ? index + Number.parseInt(cssPos, 10) : 0;
+ const cssToken = cssPos ? tokens[cssIndex] : undefined;
+ const cssContent = cssToken ? cssToken.content : '';
+
+ const jsPos = args.match(/--js (\d*)/)?.[1] || 1;
+ const jsIndex = jsPos ? index + Number.parseInt(jsPos, 10) : 0;
+ const jsToken = jsPos ? tokens[jsIndex] : undefined;
+
+ const tsPos = args.match(/--ts (\d*)/)?.[1];
+ const tsIndex = tsPos ? index + Number.parseInt(tsPos, 10) : 0;
+ const tsToken = tsPos ? tokens[tsIndex] : undefined;
+
+ // Parse code
+ const codeToCompile = parseCode(jsToken?.content);
+ const tsCodeToCompileSandbox = parseCodeSandbox(tsToken?.content);
+
+ [htmlIndex, jsIndex, tsIndex, cssIndex].filter(x => !!x).sort().reverse().forEach((x) => {
+ tokens.splice(x, 1);
+ });
+
+ const newTokens = [
+ new Token('container_div_open', 'div', 1),
+ new Token('container_div_close', 'div', -1),
+ new Token('container_div_open', 'div', 1),
+ new Token('container_div_close', 'div', -1),
+ ];
+
+ tokens.splice(index + 1, 0, ...newTokens);
+
+ const builtCode = buildCode(
+ id + '.js',
+ codeToCompile,
+ env.relativePath
+ );
+ const encodedCode = encodeURI(builtCode);
+
+ return `
+
+
+
${htmlContentRoot}
+
+
+
+ ${stackblitz(
+ id,
+ htmlContent,
+ tsCodeToCompileSandbox,
+ cssContent,
+ 'ts'
+ )}
+
+ `;
+ },
+ };
+};
diff --git a/docs/.vuepress/plugins/examples/stackblitz.js b/docs/.vuepress/plugins/examples/stackblitz.js
new file mode 100644
index 0000000000..7b891637c8
--- /dev/null
+++ b/docs/.vuepress/plugins/examples/stackblitz.js
@@ -0,0 +1,77 @@
+const buildJavascriptBody = ({ id, html, js, css, hyperformulaVersion, lang }) => {
+ return {
+ files: {
+ 'package.json': {
+ content: `{
+ "name": "hyperformula-demo",
+ "version": "1.0.0",
+ "main": "index.html",
+ "dependencies": {
+ "hyperformula": "${hyperformulaVersion}",
+ "moment": "latest"
+ }
+}`
+ },
+ 'index.html': {
+ content: `
+
+
+
+
+ HyperFormula demo
+
+
+
+ ${html || ``}
+
+`
+ },
+ 'styles.css': {
+ content: css
+ },
+ [`index.${lang}`]: {
+ content: `import './styles.css'
+${js}`
+ },
+ }
+ };
+};
+
+const stackblitz = (id, html, js, css, lang) => {
+ const hyperformulaVersion = 'latest';
+ const body = buildJavascriptBody({ id, html, js, css, hyperformulaVersion, lang });
+ const template = lang === 'ts' ? 'typescript' : 'node';
+
+ const projects = body?.files
+ ? Object.entries(body?.files).map(([key, value]) => (
+ ``
+ )) : [];
+
+ return `
+
+ `;
+};
+
+module.exports = { stackblitz };
diff --git a/docs/.vuepress/plugins/markdown-it-include-code-snippet/index.js b/docs/.vuepress/plugins/markdown-it-include-code-snippet/index.js
new file mode 100644
index 0000000000..c35bd4c956
--- /dev/null
+++ b/docs/.vuepress/plugins/markdown-it-include-code-snippet/index.js
@@ -0,0 +1,105 @@
+const fs = require('fs');
+
+const fileExists = filePath => fs.existsSync(filePath);
+
+const readFileContent = filePath =>
+ (fileExists(filePath)
+ ? fs.readFileSync(filePath).toString()
+ : `Not Found: ${filePath}`);
+
+const parseOptions = (optionsString) => {
+ const parsedOptions = {};
+
+ optionsString
+ .trim()
+ .split(' ')
+ .forEach((pair) => {
+ const [option, value] = pair.split('=');
+
+ parsedOptions[option] = value;
+ });
+
+ return parsedOptions;
+};
+
+module.exports = function includeCodeSnippet(markdown, options) {
+ const rootDirectory = options && options.root ? options.root : process.cwd();
+
+ const createDataObject = (state, position, maximum) => {
+ const start = position + 6;
+ const end = state.skipSpacesBack(maximum, position) - 1;
+ const [optionsString, fullpathWithAtSym] = state.src
+ .slice(start, end)
+ .trim()
+ .split('](');
+
+ const fullpath = fullpathWithAtSym.replace(/^@/, rootDirectory).trim();
+ const pathParts = fullpath.split('/');
+ const fileParts = pathParts[pathParts.length - 1].split('.');
+
+ return {
+ file: {
+ resolve: fullpath,
+ path: pathParts.slice(0, pathParts.length - 1).join('/'),
+ name: fileParts.slice(0, fileParts.length - 1).join('.'),
+ ext: fileParts[fileParts.length - 1],
+ },
+ options: parseOptions(optionsString),
+ content: readFileContent(fullpath),
+ fileExists: fileExists(fullpath),
+ };
+ };
+
+ // eslint-disable-next-line no-shadow
+ const mapOptions = ({ options }) => ({
+ hasHighlight: options.highlight || false,
+ get meta() {
+ return this.hasHighlight ? options.highlight : '';
+ },
+ });
+
+ /**
+ * Custom parser function for handling code snippets in markdown.
+ *
+ * @param {object} state - The current state object of the markdown-it parser.
+ * @param {number} startLine - The line number where the code snippet starts.
+ * @param {number} endLine - The line number where the code snippet ends.
+ * @param {boolean} silent - Flag indicating whether the parser should run in silent mode.
+ * @returns {boolean} - Returns true if the code snippet was successfully parsed, false otherwise.
+ */
+ function customParser(state, startLine, endLine, silent) {
+ const fenceMarker = '@[code]';
+ const position = state.bMarks[startLine] + state.tShift[startLine];
+ const maximum = state.eMarks[startLine];
+
+ // Early return for indented blocks
+ if (state.sCount[startLine] - state.blkIndent >= 4) return false;
+
+ if (!state.src.startsWith(fenceMarker, position)) {
+ // Early return if fence marker is not present
+ return false;
+ }
+
+ if (silent) {
+ // Handle silent mode
+ return true;
+ }
+
+ const dataObject = createDataObject(state, position, maximum);
+ const optionsMapping = mapOptions(dataObject);
+
+ const token = state.push('fence', 'code', 0);
+
+ token.info =
+ (dataObject.options.lang || dataObject.file.ext) + optionsMapping.meta;
+ token.content = dataObject.content;
+ token.markup = '```';
+ token.map = [startLine, startLine + 1];
+
+ state.line = startLine + 1;
+
+ return true;
+ }
+
+ markdown.block.ruler.before('fence', 'snippet', customParser);
+};
diff --git a/docs/.vuepress/public/favicon/site.webmanifest b/docs/.vuepress/public/favicon/site.webmanifest
index c2700e2936..191b296147 100644
--- a/docs/.vuepress/public/favicon/site.webmanifest
+++ b/docs/.vuepress/public/favicon/site.webmanifest
@@ -3,12 +3,12 @@
"short_name": "HyperFormula",
"icons": [
{
- "src": "/android-chrome-192x192.png",
+ "src": "/favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
- "src": "/android-chrome-512x512.png",
+ "src": "/favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl
index 0679d6cdbe..f2969329b3 100644
--- a/docs/.vuepress/styles/index.styl
+++ b/docs/.vuepress/styles/index.styl
@@ -87,4 +87,60 @@ table {
line-height: 1.7;
text-align: left;
}
-}
\ No newline at end of file
+}
+
+.example-controls {
+ display: flex
+ flex-wrap: wrap
+ gap: 8px
+ background: $editorColor
+ border-radius: $radius
+ padding: 10px
+ margin 0
+
+ .hidden {
+ display: none
+ }
+
+ button {
+ border: 1px solid $borderColor
+ border-radius: $radius
+ padding: 12px 20px 11px 17px !important
+ display: flex
+ align-items: center
+ gap: 8px
+ transition: background 0.3s ease, box-shadow 0.3s ease
+ cursor: pointer
+ background: white
+ font-size: 14px
+
+ &:hover {
+ background: #f3f3f3
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)
+ }
+
+ i, svg {
+ opacity: .7
+ }
+
+ svg {
+ path {
+ fill: black
+ }
+ }
+
+ &:disabled {
+ i {
+ opacity: 0.3 !important
+ }
+ }
+
+ @media (hover: hover) {
+ &:hover {
+ i, svg {
+ opacity 1
+ }
+ }
+ }
+ }
+}
diff --git a/docs/.vuepress/styles/palette.styl b/docs/.vuepress/styles/palette.styl
index 4b7e0fa3d3..4659740559 100644
--- a/docs/.vuepress/styles/palette.styl
+++ b/docs/.vuepress/styles/palette.styl
@@ -7,6 +7,7 @@ $arrowBgColor = #ccc
$badgeTipColor = #1147f1
$badgeWarningColor = darken(#ffe564, 35%)
$badgeErrorColor = #DA5961
+$editorColor = #efeff3
// layout
$navbarHeight = 3.6rem
@@ -18,3 +19,6 @@ $homePageWidth = 960px
$MQNarrow = 959px
$MQMobile = 719px
$MQMobileNarrow = 419px
+
+// other
+$radius = 8px
diff --git a/docs/code-examples-generator.sh b/docs/code-examples-generator.sh
new file mode 100644
index 0000000000..e9e36c492b
--- /dev/null
+++ b/docs/code-examples-generator.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/bash
+
+# This script generates JS/JSX examples from TS/TSX files and formats them using ESLint.
+# Usage:
+# ./code-examples-generator.sh path/to/file.ts - generates single example path/to/file.js
+# ./code-examples-generator.sh --generateAll - generates all examples in the content/guides directory
+# ./code-examples-generator.sh --formatAllTsExamples - runs the autoformatter on all TS and TSX example files in the content/guides directory
+
+ALL_EXAMPLES_DIR="docs/examples"
+ESLINT_CONFIG="docs/examples/eslintrc.examples.js"
+
+format() {
+ eslint --fix --no-ignore -c "$ESLINT_CONFIG" "$1" > /dev/null
+}
+
+build_output_filename() {
+ local input_filename
+ local output_filename
+ input_filename="$1"
+
+ if [[ "$input_filename" == *.ts ]]; then
+ output_filename="${input_filename%.*}.js"
+ elif [[ "$input_filename" == *.tsx ]]; then
+ output_filename="${input_filename%.*}.jsx"
+ else
+ echo "Invalid file extension: $input_filename. Must be .ts or .tsx" >&2
+ return 1
+ fi
+
+ echo "$output_filename"
+}
+
+generate() {
+ local input_filename
+ local output_filename
+ input_filename="$1"
+ output_filename=$(build_output_filename "$input_filename")
+
+ if [[ "$input_filename" == *.ts ]]; then
+ tsc --target esnext --skipLibCheck "$input_filename" > /dev/null
+ elif [[ "$input_filename" == *.tsx ]]; then
+ tsc --target esnext --jsx preserve --skipLibCheck "$input_filename" > /dev/null
+ else
+ echo "Invalid file extension: $input_filename. Must be .ts or .tsx" >&2
+ return 1
+ fi
+
+ if [ ! -f "$output_filename" ]; then
+ echo "Failed to generate $output_filename from $input_filename" >&2
+ return 1
+ fi
+}
+
+format_single_file() {
+ format "$1"
+ echo "Formatted $1"
+}
+
+generate_single_example() {
+ local input_filename
+ local output_filename
+ input_filename="$1"
+ output_filename=$(build_output_filename "$input_filename")
+
+ generate "$input_filename"
+ format "$output_filename"
+ echo "Generated $output_filename"
+}
+
+go_through_all_examples_concurrently() {
+ local task
+ local jobs_limit
+ task="$1"
+ jobs_limit=16
+
+ echo "Running $jobs_limit jobs in parallel..."
+
+ find "$ALL_EXAMPLES_DIR" -type f \( -name "*.ts" -o -name "*.tsx" \) -print0 | while read -d $'\0' source_input_filename; do
+ while test "$(jobs | wc -l)" -ge "$jobs_limit"; do
+ sleep 1
+ done
+
+ if [ "$task" == "formatAllTsExamples" ]; then
+ format_single_file "$source_input_filename" &
+ else
+ generate_single_example "$source_input_filename" &
+ fi
+
+ done
+
+ wait
+ echo "Waiting for the result of all jobs..."
+ sleep 30
+ echo "All jobs finished"
+}
+
+if [ -z "$1" ]; then
+ echo "Provide a path to the TS/TSX file or use one of the commands: --generateAll, --formatAllTsExamples"
+elif [ "$1" == "--generateAll" ]; then
+ echo "Generating all examples..."
+ go_through_all_examples_concurrently "generateAll"
+elif [ "$1" == "--formatAllTsExamples" ]; then
+ echo "Formatting all TS/TSX example files..."
+ go_through_all_examples_concurrently "formatAllTsExamples"
+else
+ echo "Generating single example..."
+ generate_single_example "$1"
+fi
diff --git a/docs/examples/advanced-usage/example1.css b/docs/examples/advanced-usage/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/advanced-usage/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/advanced-usage/example1.html b/docs/examples/advanced-usage/example1.html
new file mode 100644
index 0000000000..2973be171d
--- /dev/null
+++ b/docs/examples/advanced-usage/example1.html
@@ -0,0 +1,49 @@
+
+
+
+ 🏆
+
+
+
+
+
Team A
+
+
+
+
+
+
+
+ ID |
+ Score |
+
+
+
+
+
+
+
Team B
+
+
+
+
+
+
+
+ ID |
+ Score |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/advanced-usage/example1.js b/docs/examples/advanced-usage/example1.js
new file mode 100644
index 0000000000..564a58c6fd
--- /dev/null
+++ b/docs/examples/advanced-usage/example1.js
@@ -0,0 +1,129 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+// first column represents players' IDs
+// second column represents players' scores
+const playersAData = [
+ ['1', '2'],
+ ['2', '3'],
+ ['3', '5'],
+ ['4', '7'],
+ ['5', '13'],
+ ['6', '17'],
+];
+
+const playersBData = [
+ ['7', '19'],
+ ['8', '31'],
+ ['9', '61'],
+ ['10', '89'],
+ ['11', '107'],
+ ['12', '127'],
+];
+
+// in a cell A1 a formula checks which team is a winning one
+// in cells A2 and A3 formulas calculate the average score of players
+const formulasData = [
+ ['=IF(Formulas!A2>Formulas!A3,"TeamA","TeamB")'],
+ ['=AVERAGE(TeamA!B1:B6)'],
+ ['=AVERAGE(TeamB!B1:B6)'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+const sheetInfo = {
+ teamA: { sheetName: 'TeamA' },
+ teamB: { sheetName: 'TeamB' },
+ formulas: { sheetName: 'Formulas' },
+};
+
+// add 'TeamA' sheet
+hf.addSheet(sheetInfo.teamA.sheetName);
+// insert playersA content into targeted 'TeamA' sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.teamA.sheetName), playersAData);
+// add 'TeamB' sheet
+hf.addSheet(sheetInfo.teamB.sheetName);
+// insert playersB content into targeted 'TeamB' sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.teamB.sheetName), playersBData);
+// add a sheet named 'Formulas'
+hf.addSheet(sheetInfo.formulas.sheetName);
+// add formulas to that sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.formulas.sheetName), formulasData);
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {string} sheetName Sheet name.
+ */
+function renderTable(sheetName) {
+ const sheetId = hf.getSheetId(sheetName);
+ const tbodyDOM = document.querySelector(
+ `.example #${sheetName}-container tbody`
+ );
+
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && !cellHasFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `${cellValue} | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Render the result block
+ */
+function renderResult() {
+ const resultOutputDOM = document.querySelector('.example #output');
+ const cellAddress = hf.simpleCellAddressFromString(
+ `${sheetInfo.formulas.sheetName}!A1`,
+ hf.getSheetId(sheetInfo.formulas.sheetName)
+ );
+
+ resultOutputDOM.innerHTML = `
+ ${hf.getCellValue(cellAddress)} won!
+ `;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+
+ runButton.addEventListener('click', () => {
+ renderResult();
+ });
+}
+
+// Bind the button events.
+bindEvents();
+
+// Render the preview tables.
+for (const [_, tableInfo] of Object.entries(sheetInfo)) {
+ renderTable(tableInfo.sheetName);
+}
diff --git a/docs/examples/advanced-usage/example1.ts b/docs/examples/advanced-usage/example1.ts
new file mode 100644
index 0000000000..fe2519e418
--- /dev/null
+++ b/docs/examples/advanced-usage/example1.ts
@@ -0,0 +1,131 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+// first column represents players' IDs
+// second column represents players' scores
+const playersAData = [
+ ['1', '2'],
+ ['2', '3'],
+ ['3', '5'],
+ ['4', '7'],
+ ['5', '13'],
+ ['6', '17'],
+];
+
+const playersBData = [
+ ['7', '19'],
+ ['8', '31'],
+ ['9', '61'],
+ ['10', '89'],
+ ['11', '107'],
+ ['12', '127'],
+];
+
+// in a cell A1 a formula checks which team is a winning one
+// in cells A2 and A3 formulas calculate the average score of players
+const formulasData = [
+ ['=IF(Formulas!A2>Formulas!A3,"TeamA","TeamB")'],
+ ['=AVERAGE(TeamA!B1:B6)'],
+ ['=AVERAGE(TeamB!B1:B6)'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+const sheetInfo = {
+ teamA: { sheetName: 'TeamA' },
+ teamB: { sheetName: 'TeamB' },
+ formulas: { sheetName: 'Formulas' },
+};
+
+// add 'TeamA' sheet
+hf.addSheet(sheetInfo.teamA.sheetName);
+// insert playersA content into targeted 'TeamA' sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.teamA.sheetName), playersAData);
+
+// add 'TeamB' sheet
+hf.addSheet(sheetInfo.teamB.sheetName);
+// insert playersB content into targeted 'TeamB' sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.teamB.sheetName), playersBData);
+
+// add a sheet named 'Formulas'
+hf.addSheet(sheetInfo.formulas.sheetName);
+// add formulas to that sheet
+hf.setSheetContent(hf.getSheetId(sheetInfo.formulas.sheetName), formulasData);
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {string} sheetName Sheet name.
+ */
+function renderTable(sheetName) {
+ const sheetId = hf.getSheetId(sheetName);
+ const tbodyDOM = document.querySelector(
+ `.example #${sheetName}-container tbody`
+ );
+
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && !cellHasFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `${cellValue} | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Render the result block
+ */
+function renderResult() {
+ const resultOutputDOM = document.querySelector('.example #output');
+ const cellAddress = hf.simpleCellAddressFromString(
+ `${sheetInfo.formulas.sheetName}!A1`,
+ hf.getSheetId(sheetInfo.formulas.sheetName)
+ );
+
+ resultOutputDOM.innerHTML = `
+ ${hf.getCellValue(cellAddress)} won!
+ `;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+
+ runButton.addEventListener('click', () => {
+ renderResult();
+ });
+}
+
+// Bind the button events.
+bindEvents();
+
+// Render the preview tables.
+for (const [_, tableInfo] of Object.entries(sheetInfo)) {
+ renderTable(tableInfo.sheetName);
+}
diff --git a/docs/examples/basic-operations/example1.css b/docs/examples/basic-operations/example1.css
new file mode 100644
index 0000000000..ef0730e924
--- /dev/null
+++ b/docs/examples/basic-operations/example1.css
@@ -0,0 +1,228 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+/* basic-operations form */
+
+.example #inputs {
+ display: none;
+}
+
+.example #inputs input,
+.example #toolbar select,
+.example #inputs button {
+ height: 38px;
+}
+
+.example #inputs input.inline,
+.example #inputs select.inline {
+ border-bottom-right-radius: 0;
+ border-right: 0;
+ border-top-right-radius: 0;
+ margin: 0;
+ width: 10em;
+ float: left;
+}
+
+.example #inputs button.inline {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ margin: 0;
+}
+
+.example #inputs input.inline.middle {
+ border-radius: 0;
+ margin: 0;
+ width: 10em;
+ float: left;
+}
+
+.example #inputs input::placeholder {
+ opacity: 0.55;
+}
+
+.example #inputs input:disabled {
+ background-color: #f7f7f7;
+}
+
+.example #inputs.error input {
+ border: 1px solid red;
+}
diff --git a/docs/examples/basic-operations/example1.html b/docs/examples/basic-operations/example1.html
new file mode 100644
index 0000000000..f009af48ac
--- /dev/null
+++ b/docs/examples/basic-operations/example1.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
diff --git a/docs/examples/basic-operations/example1.js b/docs/examples/basic-operations/example1.js
new file mode 100644
index 0000000000..a0ecad379e
--- /dev/null
+++ b/docs/examples/basic-operations/example1.js
@@ -0,0 +1,461 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+const ANIMATION_ENABLED = true;
+
+/**
+ * Return sample data for the provided number of rows and columns.
+ *
+ * @param {number} rows Amount of rows to create.
+ * @param {number} columns Amount of columns to create.
+ * @returns {string[][]}
+ */
+function getSampleData(rows, columns) {
+ const data = [];
+
+ for (let r = 0; r < rows; r++) {
+ data.push([]);
+
+ for (let c = 0; c < columns; c++) {
+ data[r].push(`${Math.floor(Math.random() * 999) + 1}`);
+ }
+ }
+
+ return data;
+}
+
+/**
+ * A simple state object for the demo.
+ *
+ * @type {object}
+ */
+const state = {
+ currentSheet: null,
+};
+
+/**
+ * Input configuration and definition.
+ *
+ * @type {object}
+ */
+const inputConfig = {
+ 'add-sheet': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Sheet name',
+ },
+ ],
+ buttonText: 'Add Sheet',
+ disclaimer:
+ 'For the sake of this demo, the new sheets will be filled with random data.',
+ },
+ 'remove-sheet': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Sheet name',
+ },
+ ],
+ buttonText: 'Remove Sheet',
+ },
+ 'add-rows': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Add Rows',
+ },
+ 'add-columns': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Add Columns',
+ },
+ 'remove-rows': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Remove Rows',
+ },
+ 'remove-columns': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Remove Columns',
+ },
+ 'get-value': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Cell Address',
+ },
+ {
+ type: 'text',
+ disabled: 'disabled',
+ placeholder: '',
+ },
+ ],
+ disclaimer: 'Cell addresses format examples: A1, B4, C6.',
+ buttonText: 'Get Value',
+ },
+ 'set-value': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Cell Address',
+ },
+ {
+ type: 'text',
+ placeholder: 'Value',
+ },
+ ],
+ disclaimer: 'Cell addresses format examples: A1, B4, C6.',
+ buttonText: 'Set Value',
+ },
+};
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+state.currentSheet = 'InitialSheet';
+
+const sheetName = hf.addSheet(state.currentSheet);
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setSheetContent(sheetId, getSampleData(5, 5));
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const sheetId = hf.getSheetId(state.currentSheet);
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const isEmpty = hf.isCellEmpty(cellAddress);
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = cellHasFormula;
+ let cellValue = '';
+
+ if (isEmpty) {
+ cellValue = '';
+ } else if (!showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Updates the sheet dropdown.
+ */
+function updateSheetDropdown() {
+ const sheetNames = hf.getSheetNames();
+ const sheetDropdownDOM = document.querySelector('.example #sheet-select');
+ let dropdownContent = '';
+
+ sheetDropdownDOM.innerHTML = '';
+ sheetNames.forEach((sheetName) => {
+ const isCurrent = sheetName === state.currentSheet;
+
+ dropdownContent += ``;
+ });
+ sheetDropdownDOM.innerHTML = dropdownContent;
+}
+
+/**
+ * Update the form to the provided action.
+ *
+ * @param {string} action Action chosen from the dropdown.
+ */
+function updateForm(action) {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const submitButtonDOM = document.querySelector('.example #inputs button');
+ const allInputsDOM = document.querySelectorAll('.example #inputs input');
+ const disclaimerDOM = document.querySelector('.example #disclaimer');
+
+ // Hide all inputs
+ allInputsDOM.forEach((input) => {
+ input.style.display = 'none';
+ input.value = '';
+ input.disabled = false;
+ });
+ inputConfig[action].inputs.forEach((inputCfg, index) => {
+ const inputDOM = document.querySelector(`.example #input-${index + 1}`);
+
+ // Show only those needed
+ inputDOM.style.display = 'block';
+
+ for (const [attribute, value] of Object.entries(inputCfg)) {
+ inputDOM.setAttribute(attribute, value);
+ }
+ });
+ submitButtonDOM.innerText = inputConfig[action].buttonText;
+
+ if (inputConfig[action].disclaimer) {
+ disclaimerDOM.innerHTML = inputConfig[action].disclaimer;
+ disclaimerDOM.parentElement.style.display = 'block';
+ } else {
+ disclaimerDOM.innerHTML = ' ';
+ }
+
+ inputsDOM.style.display = 'block';
+}
+
+/**
+ * Add the error overlay.
+ *
+ * @param {string} message Error message.
+ */
+function renderError(message) {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const errorDOM = document.querySelector('.example #error-message');
+
+ if (inputsDOM.className.indexOf('error') === -1) {
+ inputsDOM.className += ' error';
+ }
+
+ errorDOM.innerText = message;
+ errorDOM.parentElement.style.display = 'block';
+}
+
+/**
+ * Clear the error overlay.
+ */
+function clearError() {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const errorDOM = document.querySelector('.example #error-message');
+
+ inputsDOM.className = inputsDOM.className.replace(' error', '');
+ errorDOM.innerText = '';
+ errorDOM.parentElement.style.display = 'none';
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const sheetDropdown = document.querySelector('.example #sheet-select');
+ const actionDropdown = document.querySelector('.example #action-select');
+ const submitButton = document.querySelector('.example #inputs button');
+
+ sheetDropdown.addEventListener('change', (event) => {
+ state.currentSheet = event.target.value;
+ clearError();
+ renderTable();
+ });
+ actionDropdown.addEventListener('change', (event) => {
+ clearError();
+ updateForm(event.target.value);
+ });
+ submitButton.addEventListener('click', (event) => {
+ const action = document.querySelector('.example #action-select').value;
+
+ doAction(action);
+ });
+}
+
+/**
+ * Perform the wanted action.
+ *
+ * @param {string} action Action to perform.
+ */
+function doAction(action) {
+ let cellAddress = null;
+ const inputValues = [
+ document.querySelector('.example #input-1').value || void 0,
+ document.querySelector('.example #input-2').value || void 0,
+ ];
+
+ clearError();
+
+ switch (action) {
+ case 'add-sheet':
+ state.currentSheet = hf.addSheet(inputValues[0]);
+ handleError(() => {
+ hf.setSheetContent(
+ hf.getSheetId(state.currentSheet),
+ getSampleData(5, 5)
+ );
+ });
+ updateSheetDropdown();
+ renderTable();
+
+ break;
+ case 'remove-sheet':
+ handleError(() => {
+ hf.removeSheet(hf.getSheetId(inputValues[0]));
+ });
+
+ if (state.currentSheet === inputValues[0]) {
+ state.currentSheet = hf.getSheetNames()[0];
+ renderTable();
+ }
+
+ updateSheetDropdown();
+
+ break;
+ case 'add-rows':
+ handleError(() => {
+ hf.addRows(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+ renderTable();
+
+ break;
+ case 'add-columns':
+ handleError(() => {
+ hf.addColumns(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+ renderTable();
+
+ break;
+ case 'remove-rows':
+ handleError(() => {
+ hf.removeRows(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+ renderTable();
+
+ break;
+ case 'remove-columns':
+ handleError(() => {
+ hf.removeColumns(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+ renderTable();
+
+ break;
+ case 'get-value':
+ const resultDOM = document.querySelector('.example #input-2');
+
+ cellAddress = handleError(() => {
+ return hf.simpleCellAddressFromString(
+ inputValues[0],
+ hf.getSheetId(state.currentSheet)
+ );
+ }, 'Invalid cell address format.');
+
+ if (cellAddress !== null) {
+ resultDOM.value = handleError(() => {
+ return hf.getCellValue(cellAddress);
+ });
+ }
+
+ break;
+ case 'set-value':
+ cellAddress = handleError(() => {
+ return hf.simpleCellAddressFromString(
+ inputValues[0],
+ hf.getSheetId(state.currentSheet)
+ );
+ }, 'Invalid cell address format.');
+
+ if (cellAddress !== null) {
+ handleError(() => {
+ hf.setCellContents(cellAddress, inputValues[1]);
+ });
+ }
+
+ renderTable();
+
+ break;
+ default:
+ }
+}
+
+/**
+ * Handle the HF errors.
+ *
+ * @param {Function} tryFunc Function to handle.
+ * @param {string} [message] Optional forced error message.
+ */
+function handleError(tryFunc, message = null) {
+ let result = null;
+
+ try {
+ result = tryFunc();
+ } catch (e) {
+ if (e instanceof Error) {
+ renderError(message || e.message);
+ } else {
+ renderError('Something went wrong');
+ }
+ }
+
+ return result;
+}
+
+// // Bind the UI events.
+bindEvents();
+// Render the table.
+renderTable();
+// Refresh the sheet dropdown list
+updateSheetDropdown();
+document.querySelector('.example .message-box').style.display = 'block';
diff --git a/docs/examples/basic-operations/example1.ts b/docs/examples/basic-operations/example1.ts
new file mode 100644
index 0000000000..ca2b9e24f4
--- /dev/null
+++ b/docs/examples/basic-operations/example1.ts
@@ -0,0 +1,481 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Return sample data for the provided number of rows and columns.
+ *
+ * @param {number} rows Amount of rows to create.
+ * @param {number} columns Amount of columns to create.
+ * @returns {string[][]}
+ */
+function getSampleData(rows, columns) {
+ const data = [];
+
+ for (let r = 0; r < rows; r++) {
+ data.push([]);
+
+ for (let c = 0; c < columns; c++) {
+ data[r].push(`${Math.floor(Math.random() * 999) + 1}`);
+ }
+ }
+
+ return data;
+}
+
+/**
+ * A simple state object for the demo.
+ *
+ * @type {object}
+ */
+const state = {
+ currentSheet: null,
+};
+
+/**
+ * Input configuration and definition.
+ *
+ * @type {object}
+ */
+const inputConfig = {
+ 'add-sheet': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Sheet name',
+ },
+ ],
+ buttonText: 'Add Sheet',
+ disclaimer:
+ 'For the sake of this demo, the new sheets will be filled with random data.',
+ },
+ 'remove-sheet': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Sheet name',
+ },
+ ],
+ buttonText: 'Remove Sheet',
+ },
+ 'add-rows': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Add Rows',
+ },
+ 'add-columns': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Add Columns',
+ },
+ 'remove-rows': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Remove Rows',
+ },
+ 'remove-columns': {
+ inputs: [
+ {
+ type: 'number',
+ placeholder: 'Index',
+ },
+ {
+ type: 'number',
+ placeholder: 'Amount',
+ },
+ ],
+ buttonText: 'Remove Columns',
+ },
+ 'get-value': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Cell Address',
+ },
+ {
+ type: 'text',
+ disabled: 'disabled',
+ placeholder: '',
+ },
+ ],
+ disclaimer: 'Cell addresses format examples: A1, B4, C6.',
+ buttonText: 'Get Value',
+ },
+ 'set-value': {
+ inputs: [
+ {
+ type: 'text',
+ placeholder: 'Cell Address',
+ },
+ {
+ type: 'text',
+ placeholder: 'Value',
+ },
+ ],
+ disclaimer: 'Cell addresses format examples: A1, B4, C6.',
+ buttonText: 'Set Value',
+ },
+};
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+state.currentSheet = 'InitialSheet';
+
+const sheetName = hf.addSheet(state.currentSheet);
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setSheetContent(sheetId, getSampleData(5, 5));
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const sheetId = hf.getSheetId(state.currentSheet);
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const isEmpty = hf.isCellEmpty(cellAddress);
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = cellHasFormula;
+ let cellValue = '';
+
+ if (isEmpty) {
+ cellValue = '';
+ } else if (!showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Updates the sheet dropdown.
+ */
+function updateSheetDropdown() {
+ const sheetNames = hf.getSheetNames();
+ const sheetDropdownDOM = document.querySelector('.example #sheet-select');
+ let dropdownContent = '';
+
+ sheetDropdownDOM.innerHTML = '';
+
+ sheetNames.forEach((sheetName) => {
+ const isCurrent = sheetName === state.currentSheet;
+
+ dropdownContent += ``;
+ });
+
+ sheetDropdownDOM.innerHTML = dropdownContent;
+}
+
+/**
+ * Update the form to the provided action.
+ *
+ * @param {string} action Action chosen from the dropdown.
+ */
+function updateForm(action) {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const submitButtonDOM = document.querySelector('.example #inputs button');
+ const allInputsDOM = document.querySelectorAll('.example #inputs input');
+ const disclaimerDOM = document.querySelector('.example #disclaimer');
+
+ // Hide all inputs
+ allInputsDOM.forEach((input) => {
+ input.style.display = 'none';
+ input.value = '';
+ input.disabled = false;
+ });
+
+ inputConfig[action].inputs.forEach((inputCfg, index) => {
+ const inputDOM = document.querySelector(`.example #input-${index + 1}`);
+
+ // Show only those needed
+ inputDOM.style.display = 'block';
+
+ for (const [attribute, value] of Object.entries(inputCfg)) {
+ inputDOM.setAttribute(attribute, value);
+ }
+ });
+
+ submitButtonDOM.innerText = inputConfig[action].buttonText;
+
+ if (inputConfig[action].disclaimer) {
+ disclaimerDOM.innerHTML = inputConfig[action].disclaimer;
+ disclaimerDOM.parentElement.style.display = 'block';
+ } else {
+ disclaimerDOM.innerHTML = ' ';
+ }
+
+ inputsDOM.style.display = 'block';
+}
+
+/**
+ * Add the error overlay.
+ *
+ * @param {string} message Error message.
+ */
+function renderError(message) {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const errorDOM = document.querySelector('.example #error-message');
+
+ if (inputsDOM.className.indexOf('error') === -1) {
+ inputsDOM.className += ' error';
+ }
+
+ errorDOM.innerText = message;
+ errorDOM.parentElement.style.display = 'block';
+}
+
+/**
+ * Clear the error overlay.
+ */
+function clearError() {
+ const inputsDOM = document.querySelector('.example #inputs');
+ const errorDOM = document.querySelector('.example #error-message');
+
+ inputsDOM.className = inputsDOM.className.replace(' error', '');
+
+ errorDOM.innerText = '';
+ errorDOM.parentElement.style.display = 'none';
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const sheetDropdown = document.querySelector('.example #sheet-select');
+ const actionDropdown = document.querySelector('.example #action-select');
+ const submitButton = document.querySelector('.example #inputs button');
+
+ sheetDropdown.addEventListener('change', (event) => {
+ state.currentSheet = event.target.value;
+
+ clearError();
+
+ renderTable();
+ });
+
+ actionDropdown.addEventListener('change', (event) => {
+ clearError();
+
+ updateForm(event.target.value);
+ });
+
+ submitButton.addEventListener('click', (event) => {
+ const action = document.querySelector('.example #action-select').value;
+
+ doAction(action);
+ });
+}
+
+/**
+ * Perform the wanted action.
+ *
+ * @param {string} action Action to perform.
+ */
+function doAction(action) {
+ let cellAddress = null;
+ const inputValues = [
+ document.querySelector('.example #input-1').value || void 0,
+ document.querySelector('.example #input-2').value || void 0,
+ ];
+
+ clearError();
+
+ switch (action) {
+ case 'add-sheet':
+ state.currentSheet = hf.addSheet(inputValues[0]);
+
+ handleError(() => {
+ hf.setSheetContent(
+ hf.getSheetId(state.currentSheet),
+ getSampleData(5, 5)
+ );
+ });
+
+ updateSheetDropdown();
+ renderTable();
+
+ break;
+ case 'remove-sheet':
+ handleError(() => {
+ hf.removeSheet(hf.getSheetId(inputValues[0]));
+ });
+
+ if (state.currentSheet === inputValues[0]) {
+ state.currentSheet = hf.getSheetNames()[0];
+
+ renderTable();
+ }
+
+ updateSheetDropdown();
+
+ break;
+ case 'add-rows':
+ handleError(() => {
+ hf.addRows(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+
+ renderTable();
+
+ break;
+ case 'add-columns':
+ handleError(() => {
+ hf.addColumns(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+
+ renderTable();
+
+ break;
+ case 'remove-rows':
+ handleError(() => {
+ hf.removeRows(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+
+ renderTable();
+
+ break;
+ case 'remove-columns':
+ handleError(() => {
+ hf.removeColumns(hf.getSheetId(state.currentSheet), [
+ parseInt(inputValues[0], 10),
+ parseInt(inputValues[1], 10),
+ ]);
+ });
+
+ renderTable();
+
+ break;
+ case 'get-value':
+ const resultDOM = document.querySelector('.example #input-2');
+
+ cellAddress = handleError(() => {
+ return hf.simpleCellAddressFromString(
+ inputValues[0],
+ hf.getSheetId(state.currentSheet)
+ );
+ }, 'Invalid cell address format.');
+
+ if (cellAddress !== null) {
+ resultDOM.value = handleError(() => {
+ return hf.getCellValue(cellAddress);
+ });
+ }
+
+ break;
+ case 'set-value':
+ cellAddress = handleError(() => {
+ return hf.simpleCellAddressFromString(
+ inputValues[0],
+ hf.getSheetId(state.currentSheet)
+ );
+ }, 'Invalid cell address format.');
+
+ if (cellAddress !== null) {
+ handleError(() => {
+ hf.setCellContents(cellAddress, inputValues[1]);
+ });
+ }
+
+ renderTable();
+
+ break;
+ default:
+ }
+}
+
+/**
+ * Handle the HF errors.
+ *
+ * @param {Function} tryFunc Function to handle.
+ * @param {string} [message] Optional forced error message.
+ */
+function handleError(tryFunc, message = null) {
+ let result = null;
+
+ try {
+ result = tryFunc();
+ } catch (e) {
+ if (e instanceof Error) {
+ renderError(message || e.message);
+ } else {
+ renderError('Something went wrong');
+ }
+ }
+
+ return result;
+}
+
+// // Bind the UI events.
+bindEvents();
+
+// Render the table.
+renderTable();
+
+// Refresh the sheet dropdown list
+updateSheetDropdown();
+
+document.querySelector('.example .message-box').style.display = 'block';
diff --git a/docs/examples/basic-usage/example1.css b/docs/examples/basic-usage/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/basic-usage/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/basic-usage/example1.html b/docs/examples/basic-usage/example1.html
new file mode 100644
index 0000000000..df9c6a2a33
--- /dev/null
+++ b/docs/examples/basic-usage/example1.html
@@ -0,0 +1,17 @@
+
+
+
+ result:
+
+
+
\ No newline at end of file
diff --git a/docs/examples/basic-usage/example1.js b/docs/examples/basic-usage/example1.js
new file mode 100644
index 0000000000..4484b6bba7
--- /dev/null
+++ b/docs/examples/basic-usage/example1.js
@@ -0,0 +1,87 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+const tableData = [['10', '20', '=SUM(A1,B1)']];
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ precisionRounding: 10,
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const theadDOM = document.querySelector('.example thead');
+ const tbodyDOM = document.querySelector('.example tbody');
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTheadHTML = '';
+ let newTbodyHTML = '';
+
+ for (let row = -1; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ if (row === -1) {
+ newTheadHTML += ` | `;
+
+ continue;
+ }
+
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && !cellHasFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+ }
+
+ tbodyDOM.innerHTML = `${newTbodyHTML}
`;
+ theadDOM.innerHTML = newTheadHTML;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const calculateButton = document.querySelector('.example #calculate');
+ const formulaPreview = document.querySelector('.example #address-output');
+ const calculationResult = document.querySelector('.example #result-output');
+ const cellAddress = { sheet: sheetId, row: 0, col: 2 };
+
+ formulaPreview.innerText = hf.simpleCellAddressToString(cellAddress, sheetId);
+ calculateButton.addEventListener('click', () => {
+ calculationResult.innerText = hf.getCellValue(cellAddress);
+ });
+}
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/basic-usage/example1.ts b/docs/examples/basic-usage/example1.ts
new file mode 100644
index 0000000000..3901624e55
--- /dev/null
+++ b/docs/examples/basic-usage/example1.ts
@@ -0,0 +1,90 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+const tableData = [['10', '20', '=SUM(A1,B1)']];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ precisionRounding: 10,
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const theadDOM = document.querySelector('.example thead');
+ const tbodyDOM = document.querySelector('.example tbody');
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTheadHTML = '';
+ let newTbodyHTML = '';
+
+ for (let row = -1; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ if (row === -1) {
+ newTheadHTML += ` | `;
+
+ continue;
+ }
+
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && !cellHasFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+ }
+
+ tbodyDOM.innerHTML = `${newTbodyHTML}
`;
+ theadDOM.innerHTML = newTheadHTML;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const calculateButton = document.querySelector('.example #calculate');
+ const formulaPreview = document.querySelector('.example #address-output');
+ const calculationResult = document.querySelector('.example #result-output');
+ const cellAddress = { sheet: sheetId, row: 0, col: 2 };
+
+ formulaPreview.innerText = hf.simpleCellAddressToString(cellAddress, sheetId);
+
+ calculateButton.addEventListener('click', () => {
+ calculationResult.innerText = hf.getCellValue(cellAddress);
+ });
+}
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/batch-operations/example1.css b/docs/examples/batch-operations/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/batch-operations/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/batch-operations/example1.html b/docs/examples/batch-operations/example1.html
new file mode 100644
index 0000000000..febd5ed821
--- /dev/null
+++ b/docs/examples/batch-operations/example1.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Year_1 |
+ Year_2 |
+ Average |
+ Sum |
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/batch-operations/example1.js b/docs/examples/batch-operations/example1.js
new file mode 100644
index 0000000000..72a98595a3
--- /dev/null
+++ b/docs/examples/batch-operations/example1.js
@@ -0,0 +1,164 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=AVERAGE(B1:C1)', '=SUM(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=AVERAGE(B2:C2)', '=SUM(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=AVERAGE(B3:C3)', '=SUM(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=AVERAGE(B4:C4)', '=SUM(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=AVERAGE(B5:C5)',
+ '=SUM(B5:C5)',
+ ],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SUM(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SUM(main!$C$1:main!$C$5)');
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SUM(Year_1)', '=SUM(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+ TOTAL |
+
+ ${
+ calculated
+ ? hf.calculateFormula(totals[0], sheetId).toFixed(2)
+ : totals[0]
+ }
+ |
+
+ ${
+ calculated
+ ? hf.calculateFormula(totals[1], sheetId).toFixed(2)
+ : totals[1]
+ }
+ |
+ |
+
`;
+ newTbodyHTML += totalRowsHTML;
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+let IS_CALCULATED = false;
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+ const calculatedCheckbox = document.querySelector('.example #isCalculated');
+
+ runButton.addEventListener('click', () => {
+ runBatchOperations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTableData();
+ });
+ calculatedCheckbox.addEventListener('change', (e) => {
+ if (e.target.checked) {
+ renderTable(true);
+ } else {
+ renderTable();
+ }
+
+ IS_CALCULATED = e.target.checked;
+ });
+}
+
+/**
+ * Reset the data for the table.
+ */
+function resetTableData() {
+ hf.setSheetContent(sheetId, tableData);
+ renderTable(IS_CALCULATED);
+}
+
+/**
+ * Run batch operations.
+ */
+function runBatchOperations() {
+ hf.batch(() => {
+ hf.setCellContents({ col: 1, row: 0, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 1, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 2, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 4, sheet: sheetId }, [['=B4']]);
+ });
+ renderTable(IS_CALCULATED);
+}
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/batch-operations/example1.ts b/docs/examples/batch-operations/example1.ts
new file mode 100644
index 0000000000..d6501f5fd7
--- /dev/null
+++ b/docs/examples/batch-operations/example1.ts
@@ -0,0 +1,171 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=AVERAGE(B1:C1)', '=SUM(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=AVERAGE(B2:C2)', '=SUM(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=AVERAGE(B3:C3)', '=SUM(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=AVERAGE(B4:C4)', '=SUM(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=AVERAGE(B5:C5)',
+ '=SUM(B5:C5)',
+ ],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SUM(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SUM(main!$C$1:main!$C$5)');
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SUM(Year_1)', '=SUM(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+ TOTAL |
+
+ ${
+ calculated
+ ? hf.calculateFormula(totals[0], sheetId).toFixed(2)
+ : totals[0]
+ }
+ |
+
+ ${
+ calculated
+ ? hf.calculateFormula(totals[1], sheetId).toFixed(2)
+ : totals[1]
+ }
+ |
+ |
+
`;
+
+ newTbodyHTML += totalRowsHTML;
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+let IS_CALCULATED = false;
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+ const calculatedCheckbox = document.querySelector('.example #isCalculated');
+
+ runButton.addEventListener('click', () => {
+ runBatchOperations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTableData();
+ });
+
+ calculatedCheckbox.addEventListener('change', (e) => {
+ if (e.target.checked) {
+ renderTable(true);
+ } else {
+ renderTable();
+ }
+
+ IS_CALCULATED = e.target.checked;
+ });
+}
+
+/**
+ * Reset the data for the table.
+ */
+function resetTableData() {
+ hf.setSheetContent(sheetId, tableData);
+ renderTable(IS_CALCULATED);
+}
+
+/**
+ * Run batch operations.
+ */
+function runBatchOperations() {
+ hf.batch(() => {
+ hf.setCellContents({ col: 1, row: 0, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 1, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 2, sheet: sheetId }, [['=B4']]);
+ hf.setCellContents({ col: 1, row: 4, sheet: sheetId }, [['=B4']]);
+ });
+
+ renderTable(IS_CALCULATED);
+}
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/clipboard-operations/example1.css b/docs/examples/clipboard-operations/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/clipboard-operations/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/clipboard-operations/example1.html b/docs/examples/clipboard-operations/example1.html
new file mode 100644
index 0000000000..ba9e9668bf
--- /dev/null
+++ b/docs/examples/clipboard-operations/example1.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Surname |
+ Both |
+
+
+
+
+
diff --git a/docs/examples/clipboard-operations/example1.js b/docs/examples/clipboard-operations/example1.js
new file mode 100644
index 0000000000..6cd3f1f1d1
--- /dev/null
+++ b/docs/examples/clipboard-operations/example1.js
@@ -0,0 +1,130 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg', 'Black', '=CONCATENATE(A1, " ",B1)'],
+ ['Anne', 'Carpenter', '=CONCATENATE(A2, " ", B2)'],
+ ['Chris', 'Aklips', '=CONCATENATE(A3, " ",B3)'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+/**
+ * Reinitialize the HF data.
+ */
+function reinitializeData() {
+ hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+ );
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const copyButton = document.querySelector('.example #copy');
+ const pasteButton = document.querySelector('.example #paste');
+ const resetButton = document.querySelector('.example #reset');
+
+ copyButton.addEventListener('click', () => {
+ copy();
+ updateCopyInfo('Second row copied');
+ });
+ pasteButton.addEventListener('click', () => {
+ paste();
+ updateCopyInfo('Pasted into the first row');
+ });
+ resetButton.addEventListener('click', () => {
+ reinitializeData();
+ updateCopyInfo('');
+ renderTable();
+ });
+}
+
+/**
+ * Copy the second row.
+ */
+function copy() {
+ return hf.copy({
+ start: { sheet: 0, col: 0, row: 1 },
+ end: { sheet: 0, col: 2, row: 1 },
+ });
+}
+
+/**
+ * Paste the HF clipboard into the first row.
+ */
+function paste() {
+ hf.paste({ sheet: 0, col: 0, row: 0 });
+ renderTable();
+}
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress)) {
+ cellValue = hf.getCellValue(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Update the information about the copy/paste action.
+ *
+ * @param {string} message Message to display.
+ */
+function updateCopyInfo(message) {
+ const copyInfoDOM = document.querySelector('.example #copyInfo');
+
+ copyInfoDOM.innerText = message;
+}
+
+// Fill the HyperFormula sheet with data.
+reinitializeData();
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/clipboard-operations/example1.ts b/docs/examples/clipboard-operations/example1.ts
new file mode 100644
index 0000000000..c0c67e07f7
--- /dev/null
+++ b/docs/examples/clipboard-operations/example1.ts
@@ -0,0 +1,134 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg', 'Black', '=CONCATENATE(A1, " ",B1)'],
+ ['Anne', 'Carpenter', '=CONCATENATE(A2, " ", B2)'],
+ ['Chris', 'Aklips', '=CONCATENATE(A3, " ",B3)'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+/**
+ * Reinitialize the HF data.
+ */
+function reinitializeData() {
+ hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+ );
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const copyButton = document.querySelector('.example #copy');
+ const pasteButton = document.querySelector('.example #paste');
+ const resetButton = document.querySelector('.example #reset');
+
+ copyButton.addEventListener('click', () => {
+ copy();
+ updateCopyInfo('Second row copied');
+ });
+
+ pasteButton.addEventListener('click', () => {
+ paste();
+ updateCopyInfo('Pasted into the first row');
+ });
+
+ resetButton.addEventListener('click', () => {
+ reinitializeData();
+ updateCopyInfo('');
+ renderTable();
+ });
+}
+
+/**
+ * Copy the second row.
+ */
+function copy() {
+ return hf.copy({
+ start: { sheet: 0, col: 0, row: 1 },
+ end: { sheet: 0, col: 2, row: 1 },
+ });
+}
+
+/**
+ * Paste the HF clipboard into the first row.
+ */
+function paste() {
+ hf.paste({ sheet: 0, col: 0, row: 0 });
+ renderTable();
+}
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress)) {
+ cellValue = hf.getCellValue(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Update the information about the copy/paste action.
+ *
+ * @param {string} message Message to display.
+ */
+function updateCopyInfo(message) {
+ const copyInfoDOM = document.querySelector('.example #copyInfo');
+
+ copyInfoDOM.innerText = message;
+}
+
+// Fill the HyperFormula sheet with data.
+reinitializeData();
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/date-time/example1.css b/docs/examples/date-time/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/date-time/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/date-time/example1.html b/docs/examples/date-time/example1.html
new file mode 100644
index 0000000000..4193fe4caa
--- /dev/null
+++ b/docs/examples/date-time/example1.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+ Release 1.0.0 |
+ Release 4.3.1 |
+ Number of days between |
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/date-time/example1.js b/docs/examples/date-time/example1.js
new file mode 100644
index 0000000000..01d646fecf
--- /dev/null
+++ b/docs/examples/date-time/example1.js
@@ -0,0 +1,154 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+import moment from 'moment';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Function defining the way HF should handle the provided date string.
+ *
+ * @param {string} dateString The date string.
+ * @param {string} dateFormat The date format.
+ * @returns {{month: *, year: *, day: *}} Object with date-related information.
+ */
+const customParseDate = (dateString, dateFormat) => {
+ const momentDate = moment(dateString, dateFormat, true);
+
+ if (momentDate.isValid()) {
+ return {
+ year: momentDate.year(),
+ month: momentDate.month() + 1,
+ day: momentDate.date(),
+ };
+ }
+};
+
+/**
+ * Date formatting function.
+ *
+ * @param {{month: *, year: *, day: *}} dateObject Object with date-related information.
+ * @returns {string} Formatted date string.
+ */
+const getFormattedDate = (dateObject) => {
+ dateObject.month -= 1;
+
+ return moment(dateObject).format('MMM D YY');
+};
+
+/**
+ * Initial table data.
+ */
+const tableData = [['Jan 31 00', 'Jun 2 01', '=B1-A1']];
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ parseDateTime: customParseDate,
+ dateFormats: ['MMM D YY'],
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ const cellValue = displayValue(cellAddress, showFormula);
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Force the table to display either the formula, the value or a raw source data value.
+ *
+ * @param {SimpleCellAddress} cellAddress Cell address.
+ * @param {boolean} showFormula `true` if the formula should be visible.
+ */
+function displayValue(cellAddress, showFormula) {
+ // Declare which columns should display the raw source data, instead of the data from HyperFormula.
+ const sourceColumns = [0, 1];
+ let cellValue = '';
+
+ if (sourceColumns.includes(cellAddress.col)) {
+ cellValue = getFormattedDate(hf.numberToDate(hf.getCellValue(cellAddress)));
+ } else {
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+ }
+
+ return cellValue;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/date-time/example1.ts b/docs/examples/date-time/example1.ts
new file mode 100644
index 0000000000..b9fe3adde7
--- /dev/null
+++ b/docs/examples/date-time/example1.ts
@@ -0,0 +1,157 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+import moment from 'moment';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Function defining the way HF should handle the provided date string.
+ *
+ * @param {string} dateString The date string.
+ * @param {string} dateFormat The date format.
+ * @returns {{month: *, year: *, day: *}} Object with date-related information.
+ */
+const customParseDate = (dateString, dateFormat) => {
+ const momentDate = moment(dateString, dateFormat, true);
+
+ if (momentDate.isValid()) {
+ return {
+ year: momentDate.year(),
+ month: momentDate.month() + 1,
+ day: momentDate.date(),
+ };
+ }
+};
+
+/**
+ * Date formatting function.
+ *
+ * @param {{month: *, year: *, day: *}} dateObject Object with date-related information.
+ * @returns {string} Formatted date string.
+ */
+const getFormattedDate = (dateObject) => {
+ dateObject.month -= 1;
+
+ return moment(dateObject).format('MMM D YY');
+};
+
+/**
+ * Initial table data.
+ */
+const tableData = [['Jan 31 00', 'Jun 2 01', '=B1-A1']];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ parseDateTime: customParseDate,
+ dateFormats: ['MMM D YY'],
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ const cellValue = displayValue(cellAddress, showFormula);
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Force the table to display either the formula, the value or a raw source data value.
+ *
+ * @param {SimpleCellAddress} cellAddress Cell address.
+ * @param {boolean} showFormula `true` if the formula should be visible.
+ */
+function displayValue(cellAddress, showFormula) {
+ // Declare which columns should display the raw source data, instead of the data from HyperFormula.
+ const sourceColumns = [0, 1];
+ let cellValue = '';
+
+ if (sourceColumns.includes(cellAddress.col)) {
+ cellValue = getFormattedDate(hf.numberToDate(hf.getCellValue(cellAddress)));
+ } else {
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+ }
+
+ return cellValue;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/demo/example1.css b/docs/examples/demo/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/demo/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/demo/example1.html b/docs/examples/demo/example1.html
new file mode 100644
index 0000000000..976656b64e
--- /dev/null
+++ b/docs/examples/demo/example1.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Year_1 |
+ Year_2 |
+ Average |
+ Sum |
+
+
+
+
+
diff --git a/docs/examples/demo/example1.js b/docs/examples/demo/example1.js
new file mode 100644
index 0000000000..9921163782
--- /dev/null
+++ b/docs/examples/demo/example1.js
@@ -0,0 +1,132 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=AVERAGE(B1:C1)', '=SUM(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=AVERAGE(B2:C2)', '=SUM(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=AVERAGE(B3:C3)', '=SUM(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=AVERAGE(B4:C4)', '=SUM(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=AVERAGE(B5:C5)',
+ '=SUM(B5:C5)',
+ ],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SUM(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SUM(main!$C$1:main!$C$5)');
+
+// Bind the events to the buttons.
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SUM(Year_1)', '=SUM(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+TOTAL |
+
+ ${
+ calculated ? hf.calculateFormula(totals[0], sheetId).toFixed(2) : totals[0]
+ }
+ |
+
+ ${
+ calculated ? hf.calculateFormula(totals[1], sheetId).toFixed(2) : totals[1]
+ }
+ |
+ |
+
`;
+ newTbodyHTML += totalRowsHTML;
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+// Replace formulas with their results.
+function runCalculations() {
+ renderTable(true);
+}
+
+// Replace the values in the table with initial data.
+function resetTable() {
+ renderTable();
+}
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/demo/example1.ts b/docs/examples/demo/example1.ts
new file mode 100644
index 0000000000..15cf8ec0e1
--- /dev/null
+++ b/docs/examples/demo/example1.ts
@@ -0,0 +1,137 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=AVERAGE(B1:C1)', '=SUM(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=AVERAGE(B2:C2)', '=SUM(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=AVERAGE(B3:C3)', '=SUM(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=AVERAGE(B4:C4)', '=SUM(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=AVERAGE(B5:C5)',
+ '=SUM(B5:C5)',
+ ],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SUM(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SUM(main!$C$1:main!$C$5)');
+
+// Bind the events to the buttons.
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SUM(Year_1)', '=SUM(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+TOTAL |
+
+ ${
+ calculated ? hf.calculateFormula(totals[0], sheetId).toFixed(2) : totals[0]
+ }
+ |
+
+ ${
+ calculated ? hf.calculateFormula(totals[1], sheetId).toFixed(2) : totals[1]
+ }
+ |
+ |
+
`;
+
+ newTbodyHTML += totalRowsHTML;
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+// Replace formulas with their results.
+function runCalculations() {
+ renderTable(true);
+}
+
+// Replace the values in the table with initial data.
+function resetTable() {
+ renderTable();
+}
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/eslintrc.examples.js b/docs/examples/eslintrc.examples.js
new file mode 100644
index 0000000000..aa4f35cb39
--- /dev/null
+++ b/docs/examples/eslintrc.examples.js
@@ -0,0 +1,60 @@
+const jsdocOff = Object.keys(require('eslint-plugin-jsdoc').rules)
+ .reduce((acc, rule) => {
+ acc[`jsdoc/${rule}`] = 'off';
+
+ return acc;
+ }, {});
+
+module.exports = {
+ extends: ['../../.eslintrc.js', 'plugin:prettier/recommended'],
+ parserOptions: {
+ requireConfigFile: false
+ },
+ rules: {
+ ...jsdocOff,
+ "prettier/prettier": [
+ "error",
+ {
+ "singleQuote": true,
+ }
+ ],
+ 'no-restricted-syntax': 'off',
+ 'no-restricted-globals': 'off',
+ 'no-console': 'off',
+ 'no-await-in-loop': 'off',
+ 'no-unused-vars': 'off',
+ 'padding-line-between-statements': [
+ 'error',
+ { blankLine: 'always', prev: '*', next: 'multiline-block-like' },
+ { blankLine: 'always', prev: 'multiline-block-like', next: '*' },
+
+ // { blankLine: "always", prev: "*", next: "multiline-const" },
+ { blankLine: 'always', prev: 'multiline-const', next: '*' },
+
+ // { blankLine: "always", prev: "*", next: "multiline-let" },
+ { blankLine: 'always', prev: 'multiline-let', next: '*' },
+
+ // { blankLine: "always", prev: "*", next: "multiline-var" },
+ { blankLine: 'always', prev: 'multiline-var', next: '*' },
+
+ { blankLine: 'always', prev: ['singleline-const', 'singleline-let', 'singleline-var'], next: '*' },
+ {
+ blankLine: 'any',
+ prev: ['singleline-const', 'singleline-let', 'singleline-var'],
+ next: ['const', 'let', 'var']
+ },
+
+ // { blankLine: "always", prev: "*", next: "multiline-expression" },
+ { blankLine: 'always', prev: 'multiline-expression', next: '*' },
+
+ { blankLine: 'always', prev: 'expression', next: '*' },
+ { blankLine: 'any', prev: 'expression', next: 'expression' },
+
+ { blankLine: 'always', prev: 'import', next: '*' },
+ { blankLine: 'any', prev: 'import', next: 'import' },
+
+ { blankLine: 'always', prev: ['case', 'default'], next: '*' },
+ { blankLine: 'any', prev: ['case', 'default'], next: ['case', 'default'] }
+ ]
+ }
+};
diff --git a/docs/examples/i18n/example1.css b/docs/examples/i18n/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/i18n/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/i18n/example1.html b/docs/examples/i18n/example1.html
new file mode 100644
index 0000000000..f92527c957
--- /dev/null
+++ b/docs/examples/i18n/example1.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Lunch time |
+ Date of Birth |
+ Age |
+ Salary |
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/i18n/example1.js b/docs/examples/i18n/example1.js
new file mode 100644
index 0000000000..f4fa847a40
--- /dev/null
+++ b/docs/examples/i18n/example1.js
@@ -0,0 +1,212 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+import moment from 'moment';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/* start:skip-in-sandbox */
+const enUS = HyperFormula.languages.enUS;
+/* end:skip-in-sandbox */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ [
+ 'Greg Black',
+ '11:45 AM',
+ '05/23/1989',
+ '=YEAR(NOW())-YEAR(C1)',
+ '$80,000.00',
+ ],
+ [
+ 'Anne Carpenter',
+ '12:30 PM',
+ '01/01/1980',
+ '=YEAR(NOW())-YEAR(C2)',
+ '$95,000.00',
+ ],
+ [
+ 'Natalie Dem',
+ '1:30 PM',
+ '12/13/1973',
+ '=YEAR(NOW())-YEAR(C3)',
+ '$78,500.00',
+ ],
+ [
+ 'John Sieg',
+ '2:00 PM',
+ '10/31/1995',
+ '=YEAR(NOW())-YEAR(C4)',
+ '$114,000.00',
+ ],
+ [
+ 'Chris Aklips',
+ '11:30 AM',
+ '08/18/1987',
+ '=YEAR(NOW())-YEAR(C5)',
+ '$71,900.00',
+ ],
+ ['AVERAGE', null, null, '=AVERAGE(D1:D5)', '=AVERAGE(E1:E5)'],
+];
+
+const config = {
+ language: 'enUS',
+ dateFormats: ['MM/DD/YYYY', 'MM/DD/YY', 'YYYY/MM/DD'],
+ timeFormats: ['hh:mm', 'hh:mm:ss.sss'],
+ decimalSeparator: '.',
+ thousandSeparator: ',',
+ functionArgSeparator: ';',
+ currencySymbol: ['$', 'USD'],
+ localeLang: 'en-US',
+ licenseKey: 'gpl-v3',
+};
+
+HyperFormula.registerLanguage('enUS', enUS);
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty(config);
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+const columnTypes = ['string', 'time', 'date', 'number', 'currency'];
+
+/**
+ * Display value in human-readable format
+ *
+ * @param {SimpleCellAddress} cellAddress Cell address.
+ */
+function formatCellValue(cellAddress) {
+ if (hf.isCellEmpty(cellAddress)) {
+ return '';
+ }
+
+ if (columnTypes[cellAddress.col] === 'time') {
+ return formatTime(hf.numberToTime(hf.getCellValue(cellAddress)));
+ }
+
+ if (columnTypes[cellAddress.col] === 'date') {
+ return formatDate(hf.numberToDate(hf.getCellValue(cellAddress)));
+ }
+
+ if (columnTypes[cellAddress.col] === 'currency') {
+ return formatCurrency(hf.getCellValue(cellAddress));
+ }
+
+ return hf.getCellValue(cellAddress);
+}
+
+/**
+ * Date formatting function.
+ *
+ * @param {{month: *, year: *, day: *}} dateObject Object with date-related information.
+ */
+function formatDate(dateObject) {
+ dateObject.month -= 1;
+
+ return moment(dateObject).format('MM/DD/YYYY');
+}
+
+/**
+ * Time formatting function.
+ *
+ * @param dateTimeObject Object with date and time information.
+ */
+function formatTime(dateTimeObject) {
+ return moment(dateTimeObject).format('h:mm A');
+}
+
+/**
+ * Currency formatting function.
+ *
+ * @param value Number representing the currency value
+ */
+function formatCurrency(value) {
+ return value.toLocaleString('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ });
+}
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ newTbodyHTML += ``;
+
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = cellHasFormula && !calculated;
+ const displayValue = showFormula
+ ? hf.getCellFormula(cellAddress)
+ : formatCellValue(cellAddress);
+
+ newTbodyHTML += `${displayValue} | `;
+ }
+
+ newTbodyHTML += '
';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/i18n/example1.ts b/docs/examples/i18n/example1.ts
new file mode 100644
index 0000000000..6767563972
--- /dev/null
+++ b/docs/examples/i18n/example1.ts
@@ -0,0 +1,217 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+import enUS from 'hyperformula/es/i18n/languages/enUS';
+import moment from 'moment';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/* start:skip-in-sandbox */
+const enUS = HyperFormula.languages.enUS;
+/* end:skip-in-sandbox */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ [
+ 'Greg Black',
+ '11:45 AM',
+ '05/23/1989',
+ '=YEAR(NOW())-YEAR(C1)',
+ '$80,000.00',
+ ],
+ [
+ 'Anne Carpenter',
+ '12:30 PM',
+ '01/01/1980',
+ '=YEAR(NOW())-YEAR(C2)',
+ '$95,000.00',
+ ],
+ [
+ 'Natalie Dem',
+ '1:30 PM',
+ '12/13/1973',
+ '=YEAR(NOW())-YEAR(C3)',
+ '$78,500.00',
+ ],
+ [
+ 'John Sieg',
+ '2:00 PM',
+ '10/31/1995',
+ '=YEAR(NOW())-YEAR(C4)',
+ '$114,000.00',
+ ],
+ [
+ 'Chris Aklips',
+ '11:30 AM',
+ '08/18/1987',
+ '=YEAR(NOW())-YEAR(C5)',
+ '$71,900.00',
+ ],
+ ['AVERAGE', null, null, '=AVERAGE(D1:D5)', '=AVERAGE(E1:E5)'],
+];
+
+const config = {
+ language: 'enUS',
+ dateFormats: ['MM/DD/YYYY', 'MM/DD/YY', 'YYYY/MM/DD'],
+ timeFormats: ['hh:mm', 'hh:mm:ss.sss'],
+ decimalSeparator: '.',
+ thousandSeparator: ',',
+ functionArgSeparator: ';',
+ currencySymbol: ['$', 'USD'],
+ localeLang: 'en-US',
+ licenseKey: 'gpl-v3',
+};
+
+HyperFormula.registerLanguage('enUS', enUS);
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty(config);
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+const columnTypes = ['string', 'time', 'date', 'number', 'currency'];
+
+/**
+ * Display value in human-readable format
+ *
+ * @param {SimpleCellAddress} cellAddress Cell address.
+ */
+function formatCellValue(cellAddress) {
+ if (hf.isCellEmpty(cellAddress)) {
+ return '';
+ }
+
+ if (columnTypes[cellAddress.col] === 'time') {
+ return formatTime(hf.numberToTime(hf.getCellValue(cellAddress)));
+ }
+
+ if (columnTypes[cellAddress.col] === 'date') {
+ return formatDate(hf.numberToDate(hf.getCellValue(cellAddress)));
+ }
+
+ if (columnTypes[cellAddress.col] === 'currency') {
+ return formatCurrency(hf.getCellValue(cellAddress));
+ }
+
+ return hf.getCellValue(cellAddress);
+}
+
+/**
+ * Date formatting function.
+ *
+ * @param {{month: *, year: *, day: *}} dateObject Object with date-related information.
+ */
+function formatDate(dateObject) {
+ dateObject.month -= 1;
+
+ return moment(dateObject).format('MM/DD/YYYY');
+}
+
+/**
+ * Time formatting function.
+ *
+ * @param dateTimeObject Object with date and time information.
+ */
+function formatTime(dateTimeObject) {
+ return moment(dateTimeObject).format('h:mm A');
+}
+
+/**
+ * Currency formatting function.
+ *
+ * @param value Number representing the currency value
+ */
+function formatCurrency(value) {
+ return value.toLocaleString('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ });
+}
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ newTbodyHTML += ``;
+
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = cellHasFormula && !calculated;
+ const displayValue = showFormula
+ ? hf.getCellFormula(cellAddress)
+ : formatCellValue(cellAddress);
+
+ newTbodyHTML += `${displayValue} | `;
+ }
+
+ newTbodyHTML += '
';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/localizing-functions/example1.css b/docs/examples/localizing-functions/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/localizing-functions/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/localizing-functions/example1.html b/docs/examples/localizing-functions/example1.html
new file mode 100644
index 0000000000..c5c58267f2
--- /dev/null
+++ b/docs/examples/localizing-functions/example1.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Year_1 |
+ Year_2 |
+ Average |
+ Sum |
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/localizing-functions/example1.js b/docs/examples/localizing-functions/example1.js
new file mode 100644
index 0000000000..98d81e5b8c
--- /dev/null
+++ b/docs/examples/localizing-functions/example1.js
@@ -0,0 +1,148 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/* start:skip-in-sandbox */
+const frFR = HyperFormula.languages.frFR;
+/* end:skip-in-sandbox */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=MOYENNE(B1:C1)', '=SOMME(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=MOYENNE(B2:C2)', '=SOMME(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=MOYENNE(B3:C3)', '=SOMME(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=MOYENNE(B4:C4)', '=SOMME(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=MOYENNE(B5:C5)',
+ '=SOMME(B5:C5)',
+ ],
+];
+
+// register language
+HyperFormula.registerLanguage('frFR', frFR);
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ language: 'frFR',
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SOMME(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SOMME(main!$C$1:main!$C$5)');
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SOMME(Year_1)', '=SOMME(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+TOTAL |
+
+ ${
+ calculated ? hf.calculateFormula(totals[0], sheetId).toFixed(2) : totals[0]
+ }
+ |
+
+ ${
+ calculated ? hf.calculateFormula(totals[1], sheetId).toFixed(2) : totals[1]
+ }
+ |
+ |
+
`;
+ newTbodyHTML += totalRowsHTML;
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/localizing-functions/example1.ts b/docs/examples/localizing-functions/example1.ts
new file mode 100644
index 0000000000..b224f3cb1d
--- /dev/null
+++ b/docs/examples/localizing-functions/example1.ts
@@ -0,0 +1,155 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+import frFR from 'hyperformula/es/i18n/languages/frFR';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/* start:skip-in-sandbox */
+const frFR = HyperFormula.languages.frFR;
+/* end:skip-in-sandbox */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', 4.66, '=B1*1.3', '=MOYENNE(B1:C1)', '=SOMME(B1:C1)'],
+ ['Anne Carpenter', 5.25, '=$B$2*30%', '=MOYENNE(B2:C2)', '=SOMME(B2:C2)'],
+ ['Natalie Dem', 3.59, '=B3*2.7+2+1', '=MOYENNE(B3:C3)', '=SOMME(B3:C3)'],
+ ['John Sieg', 12.51, '=B4*(1.22+1)', '=MOYENNE(B4:C4)', '=SOMME(B4:C4)'],
+ [
+ 'Chris Aklips',
+ 7.63,
+ '=B5*1.1*SUM(10,20)+1',
+ '=MOYENNE(B5:C5)',
+ '=SOMME(B5:C5)',
+ ],
+];
+
+// register language
+HyperFormula.registerLanguage('frFR', frFR);
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ language: 'frFR',
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+// Add named expressions for the "TOTAL" row.
+hf.addNamedExpression('Year_1', '=SOMME(main!$B$1:main!$B$5)');
+hf.addNamedExpression('Year_2', '=SOMME(main!$C$1:main!$C$5)');
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const totals = ['=SOMME(Year_1)', '=SOMME(Year_2)'];
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+ let totalRowsHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+
+ if (!isNaN(cellValue)) {
+ cellValue = cellValue.toFixed(2);
+ }
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ totalRowsHTML = `
+TOTAL |
+
+ ${
+ calculated ? hf.calculateFormula(totals[0], sheetId).toFixed(2) : totals[0]
+ }
+ |
+
+ ${
+ calculated ? hf.calculateFormula(totals[1], sheetId).toFixed(2) : totals[1]
+ }
+ |
+ |
+
`;
+
+ newTbodyHTML += totalRowsHTML;
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/named-expressions/example1.css b/docs/examples/named-expressions/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/named-expressions/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/named-expressions/example1.html b/docs/examples/named-expressions/example1.html
new file mode 100644
index 0000000000..a276542d8b
--- /dev/null
+++ b/docs/examples/named-expressions/example1.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ A |
+ B |
+ C |
+ D |
+
+
+
+
+
diff --git a/docs/examples/named-expressions/example1.js b/docs/examples/named-expressions/example1.js
new file mode 100644
index 0000000000..7917f72527
--- /dev/null
+++ b/docs/examples/named-expressions/example1.js
@@ -0,0 +1,121 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ [10, 20, 20, 30],
+ [50, 60, 70, 80],
+ [90, 100, 110, 120],
+ ['=myOneCell', '=myTwoCells', '=myOneColumn', '=myTwoColumns'],
+ ['=myFormula+myNumber+34', '=myText', '=myOneRow', '=myTwoRows'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+// Add named expressions
+hf.addNamedExpression('myOneCell', '=main!$A$1');
+hf.addNamedExpression('myTwoCells', '=SUM(main!$A$1, main!$A$2)');
+hf.addNamedExpression('myOneColumn', '=SUM(main!$A$1:main!$A$3)');
+hf.addNamedExpression('myTwoColumns', '=SUM(main!$A$1:main!$B$3)');
+hf.addNamedExpression('myOneRow', '=SUM(main!$A$1:main!$D$1)');
+hf.addNamedExpression('myTwoRows', '=SUM(main!$A$1:main!$D$2)');
+hf.addNamedExpression('myFormula', '=SUM(0, 1, 1, 2, 3, 5, 8, 13)');
+hf.addNamedExpression('myNumber', '=21');
+hf.addNamedExpression('myText', 'Apollo 11');
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/named-expressions/example1.ts b/docs/examples/named-expressions/example1.ts
new file mode 100644
index 0000000000..594ecdaa07
--- /dev/null
+++ b/docs/examples/named-expressions/example1.ts
@@ -0,0 +1,125 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ [10, 20, 20, 30],
+ [50, 60, 70, 80],
+ [90, 100, 110, 120],
+ ['=myOneCell', '=myTwoCells', '=myOneColumn', '=myTwoColumns'],
+ ['=myFormula+myNumber+34', '=myText', '=myOneRow', '=myTwoRows'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+// Add named expressions
+hf.addNamedExpression('myOneCell', '=main!$A$1');
+hf.addNamedExpression('myTwoCells', '=SUM(main!$A$1, main!$A$2)');
+hf.addNamedExpression('myOneColumn', '=SUM(main!$A$1:main!$A$3)');
+hf.addNamedExpression('myTwoColumns', '=SUM(main!$A$1:main!$B$3)');
+hf.addNamedExpression('myOneRow', '=SUM(main!$A$1:main!$D$1)');
+hf.addNamedExpression('myTwoRows', '=SUM(main!$A$1:main!$D$2)');
+hf.addNamedExpression('myFormula', '=SUM(0, 1, 1, 2, 3, 5, 8, 13)');
+hf.addNamedExpression('myNumber', '=21');
+hf.addNamedExpression('myText', 'Apollo 11');
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Replace formulas with their results.
+ */
+function runCalculations() {
+ renderTable(true);
+}
+
+/**
+ * Replace the values in the table with initial data.
+ */
+function resetTable() {
+ renderTable();
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const runButton = document.querySelector('.example #run');
+ const resetButton = document.querySelector('.example #reset');
+
+ runButton.addEventListener('click', () => {
+ runCalculations();
+ });
+
+ resetButton.addEventListener('click', () => {
+ resetTable();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/sorting-data/example1.css b/docs/examples/sorting-data/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/sorting-data/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/sorting-data/example1.html b/docs/examples/sorting-data/example1.html
new file mode 100644
index 0000000000..27eec1da0c
--- /dev/null
+++ b/docs/examples/sorting-data/example1.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+ Name |
+
+ Score
+
+ |
+
+
+
+
+
diff --git a/docs/examples/sorting-data/example1.js b/docs/examples/sorting-data/example1.js
new file mode 100644
index 0000000000..97c2bfe97f
--- /dev/null
+++ b/docs/examples/sorting-data/example1.js
@@ -0,0 +1,148 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', '100'],
+ ['Anne Carpenter', '=SUM(100,100)'],
+ ['Natalie Dem', '500'],
+ ['John Sieg', '50'],
+ ['Chris Aklips', '20'],
+ ['Bart Hoopoe', '700'],
+ ['Chris Site', '80'],
+ ['Agnes Whitey', '90'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Sort the HF's dataset.
+ *
+ * @param {boolean} ascending `true` if sorting in ascending order, `false` otherwise.
+ * @param {Function} callback The callback function.
+ */
+function sort(ascending, callback) {
+ const rowCount = hf.getSheetDimensions(sheetId).height;
+ const colValues = [];
+ let newOrder = null;
+ const newOrderMapping = [];
+
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+ colValues.push({
+ rowIndex,
+ value: hf.getCellValue({
+ sheet: sheetId,
+ col: 1,
+ row: rowIndex,
+ }),
+ });
+ }
+
+ colValues.sort((objA, objB) => {
+ const delta = objA.value - objB.value;
+
+ return ascending ? delta : -delta;
+ });
+ newOrder = colValues.map((el) => el.rowIndex);
+ newOrder.forEach((orderIndex, arrIndex) => {
+ newOrderMapping[orderIndex] = arrIndex;
+ });
+ hf.setRowOrder(sheetId, newOrderMapping);
+ callback();
+}
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+const doSortASC = () => {
+ sort(true, () => {
+ renderTable(true);
+ });
+};
+
+const doSortDESC = () => {
+ sort(false, () => {
+ renderTable(true);
+ });
+};
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const ascSort = document.querySelector('.example #asc');
+ const descSort = document.querySelector('.example #desc');
+
+ ascSort.addEventListener('click', () => {
+ doSortASC();
+ });
+ descSort.addEventListener('click', () => {
+ doSortDESC();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/sorting-data/example1.ts b/docs/examples/sorting-data/example1.ts
new file mode 100644
index 0000000000..8454d6382f
--- /dev/null
+++ b/docs/examples/sorting-data/example1.ts
@@ -0,0 +1,154 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg Black', '100'],
+ ['Anne Carpenter', '=SUM(100,100)'],
+ ['Natalie Dem', '500'],
+ ['John Sieg', '50'],
+ ['Chris Aklips', '20'],
+ ['Bart Hoopoe', '700'],
+ ['Chris Site', '80'],
+ ['Agnes Whitey', '90'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+/**
+ * Sort the HF's dataset.
+ *
+ * @param {boolean} ascending `true` if sorting in ascending order, `false` otherwise.
+ * @param {Function} callback The callback function.
+ */
+function sort(ascending, callback) {
+ const rowCount = hf.getSheetDimensions(sheetId).height;
+ const colValues = [];
+ let newOrder = null;
+ const newOrderMapping = [];
+
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+ colValues.push({
+ rowIndex,
+ value: hf.getCellValue({
+ sheet: sheetId,
+ col: 1,
+ row: rowIndex,
+ }),
+ });
+ }
+
+ colValues.sort((objA, objB) => {
+ const delta = objA.value - objB.value;
+
+ return ascending ? delta : -delta;
+ });
+
+ newOrder = colValues.map((el) => el.rowIndex);
+
+ newOrder.forEach((orderIndex, arrIndex) => {
+ newOrderMapping[orderIndex] = arrIndex;
+ });
+
+ hf.setRowOrder(sheetId, newOrderMapping);
+
+ callback();
+}
+
+/**
+ * Fill the HTML table with data.
+ *
+ * @param {boolean} calculated `true` if it should render calculated values, `false` otherwise.
+ */
+function renderTable(calculated = false) {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellHasFormula = hf.doesCellHaveFormula(cellAddress);
+ const showFormula = calculated || !cellHasFormula;
+ let cellValue = '';
+
+ if (!hf.isCellEmpty(cellAddress) && showFormula) {
+ cellValue = hf.getCellValue(cellAddress);
+ } else {
+ cellValue = hf.getCellFormula(cellAddress);
+ }
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+const doSortASC = () => {
+ sort(true, () => {
+ renderTable(true);
+ });
+};
+
+const doSortDESC = () => {
+ sort(false, () => {
+ renderTable(true);
+ });
+};
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const ascSort = document.querySelector('.example #asc');
+ const descSort = document.querySelector('.example #desc');
+
+ ascSort.addEventListener('click', () => {
+ doSortASC();
+ });
+
+ descSort.addEventListener('click', () => {
+ doSortDESC();
+ });
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/examples/undo-redo/example1.css b/docs/examples/undo-redo/example1.css
new file mode 100644
index 0000000000..224282eb7a
--- /dev/null
+++ b/docs/examples/undo-redo/example1.css
@@ -0,0 +1,181 @@
+/* general */
+.example {
+ color: #606c76;
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ letter-spacing: .01em;
+ line-height: 1.6;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.example *,
+.example *::before,
+.example *::after {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+/* buttons */
+
+.example button {
+ border: 0.1em solid #1c49e4;
+ border-radius: .3em;
+ color: #fff;
+ cursor: pointer;
+ display: inline-block;
+ font-size: .85em;
+ font-family: inherit;
+ font-weight: 700;
+ height: 3em;
+ letter-spacing: .1em;
+ line-height: 3em;
+ padding: 0 3em;
+ text-align: center;
+ text-decoration: none;
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin-bottom: 20px;
+ background-color: #1c49e4;
+}
+
+.example button:hover {
+ background-color: #2350ea;
+}
+
+.example button.outline {
+ background-color: transparent;
+ color: #1c49e4;
+}
+
+/* labels */
+
+.example label {
+ display: inline-block;
+ margin-left: 5px;
+}
+
+/* inputs */
+
+.example input:not([type='checkbox']), .example select, .example textarea, .example fieldset {
+ margin-bottom: 1.5em;
+ border: 0.1em solid #d1d1d1;
+ border-radius: .4em;
+ height: 3.8em;
+ width: 12em;
+ padding: 0 .5em;
+}
+
+.example input:focus,
+.example select:focus {
+ outline: none;
+ border-color: #1c49e4;
+}
+
+/* message */
+
+.example .message-box {
+ border: 1px solid #1c49e433;
+ background-color: #1c49e405;
+ border-radius: 0.2em;
+ padding: 10px;
+}
+
+.example .message-box span {
+ animation-name: cell-appear;
+ animation-duration: 0.2s;
+ margin: 0;
+}
+
+/* table */
+
+.example table {
+ table-layout: fixed;
+ border-spacing: 0;
+ overflow-x: auto;
+ text-align: left;
+ width: 100%;
+ counter-reset: row-counter col-counter;
+}
+
+.example table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.example table tr td,
+.example table tr th {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ border-bottom: 0.1em solid #e1e1e1;
+ padding: 0 1em;
+ height: 3.5em;
+}
+
+/* table: header row */
+
+.example table thead tr th span::before {
+ display: inline-block;
+ width: 20px;
+}
+
+.example table.spreadsheet thead tr th span::before {
+ content: counter(col-counter, upper-alpha);
+}
+
+.example table.spreadsheet thead tr th {
+ counter-increment: col-counter;
+}
+
+/* table: first column */
+
+.example table tbody tr td:first-child {
+ text-align: center;
+ padding: 0;
+}
+
+.example table thead tr th:first-child {
+ padding-left: 40px;
+}
+
+.example table tbody tr td:first-child span {
+ width: 100%;
+ display: inline-block;
+ text-align: left;
+ padding-left: 15px;
+ margin-left: 0;
+}
+
+.example table tbody tr td:first-child span::before {
+ content: counter(row-counter);
+ display: inline-block;
+ width: 20px;
+ position: relative;
+ left: -10px;
+}
+
+.example table tbody tr {
+ counter-increment: row-counter;
+}
+
+/* table: summary row */
+
+.example table tbody tr.summary {
+ font-weight: 600;
+}
+
+/* updated-cell animation */
+
+.example table tr td.updated-cell span {
+ animation-name: cell-appear;
+ animation-duration: 0.6s;
+}
+
+@keyframes cell-appear {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/docs/examples/undo-redo/example1.html b/docs/examples/undo-redo/example1.html
new file mode 100644
index 0000000000..7b084324d0
--- /dev/null
+++ b/docs/examples/undo-redo/example1.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Value |
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/examples/undo-redo/example1.js b/docs/examples/undo-redo/example1.js
new file mode 100644
index 0000000000..9a3be5b486
--- /dev/null
+++ b/docs/examples/undo-redo/example1.js
@@ -0,0 +1,138 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+
+/* end:skip-in-compilation */
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg', '2'],
+ ['Chris', '4'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+// Clear the undo stack to prevent undoing the initialization steps.
+hf.clearUndoStack();
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellValue = hf.getCellValue(cellAddress);
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Clear the existing information.
+ */
+function clearInfo() {
+ const infoBoxDOM = document.querySelector('.example #info-box');
+
+ infoBoxDOM.innerHTML = ' ';
+}
+
+/**
+ * Display the provided message in the info box.
+ *
+ * @param {string} message Message to display.
+ */
+function displayInfo(message) {
+ const infoBoxDOM = document.querySelector('.example #info-box');
+
+ infoBoxDOM.innerText = message;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const removeRowButton = document.querySelector('.example #remove-row');
+ const undoButton = document.querySelector('.example #undo');
+
+ removeRowButton.addEventListener('click', () => {
+ removeSecondRow();
+ });
+ undoButton.addEventListener('click', () => {
+ undo();
+ });
+}
+
+/**
+ * Remove the second row from the table.
+ */
+function removeSecondRow() {
+ const filledRowCount = hf.getSheetDimensions(sheetId).height;
+
+ clearInfo();
+
+ if (filledRowCount < 2) {
+ displayInfo("There's not enough filled rows to perform this action.");
+
+ return;
+ }
+
+ hf.removeRows(sheetId, [1, 1]);
+ renderTable();
+}
+
+/**
+ * Run the HF undo action.
+ */
+function undo() {
+ clearInfo();
+
+ if (!hf.isThereSomethingToUndo()) {
+ displayInfo("There's nothing to undo.");
+
+ return;
+ }
+
+ hf.undo();
+ renderTable();
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+// Render the table.
+renderTable();
diff --git a/docs/examples/undo-redo/example1.ts b/docs/examples/undo-redo/example1.ts
new file mode 100644
index 0000000000..3b03b80341
--- /dev/null
+++ b/docs/examples/undo-redo/example1.ts
@@ -0,0 +1,141 @@
+/* start:skip-in-compilation */
+import HyperFormula from 'hyperformula';
+
+console.log(
+ `%c Using HyperFormula ${HyperFormula.version}`,
+ 'color: blue; font-weight: bold'
+);
+/* end:skip-in-compilation */
+
+/**
+ * Initial table data.
+ */
+const tableData = [
+ ['Greg', '2'],
+ ['Chris', '4'],
+];
+
+// Create an empty HyperFormula instance.
+const hf = HyperFormula.buildEmpty({
+ licenseKey: 'gpl-v3',
+});
+
+// Add a new sheet and get its id.
+const sheetName = hf.addSheet('main');
+const sheetId = hf.getSheetId(sheetName);
+
+// Fill the HyperFormula sheet with data.
+hf.setCellContents(
+ {
+ row: 0,
+ col: 0,
+ sheet: sheetId,
+ },
+ tableData
+);
+
+// Clear the undo stack to prevent undoing the initialization steps.
+hf.clearUndoStack();
+
+/**
+ * Fill the HTML table with data.
+ */
+function renderTable() {
+ const tbodyDOM = document.querySelector('.example tbody');
+ const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
+ const { height, width } = hf.getSheetDimensions(sheetId);
+ let newTbodyHTML = '';
+
+ for (let row = 0; row < height; row++) {
+ for (let col = 0; col < width; col++) {
+ const cellAddress = { sheet: sheetId, col, row };
+ const cellValue = hf.getCellValue(cellAddress);
+
+ newTbodyHTML += `
+ ${cellValue}
+ | `;
+ }
+
+ newTbodyHTML += '';
+ }
+
+ tbodyDOM.innerHTML = newTbodyHTML;
+}
+
+/**
+ * Clear the existing information.
+ */
+function clearInfo() {
+ const infoBoxDOM = document.querySelector('.example #info-box');
+
+ infoBoxDOM.innerHTML = ' ';
+}
+
+/**
+ * Display the provided message in the info box.
+ *
+ * @param {string} message Message to display.
+ */
+function displayInfo(message) {
+ const infoBoxDOM = document.querySelector('.example #info-box');
+
+ infoBoxDOM.innerText = message;
+}
+
+/**
+ * Bind the events to the buttons.
+ */
+function bindEvents() {
+ const removeRowButton = document.querySelector('.example #remove-row');
+ const undoButton = document.querySelector('.example #undo');
+
+ removeRowButton.addEventListener('click', () => {
+ removeSecondRow();
+ });
+
+ undoButton.addEventListener('click', () => {
+ undo();
+ });
+}
+
+/**
+ * Remove the second row from the table.
+ */
+function removeSecondRow() {
+ const filledRowCount = hf.getSheetDimensions(sheetId).height;
+
+ clearInfo();
+
+ if (filledRowCount < 2) {
+ displayInfo("There's not enough filled rows to perform this action.");
+
+ return;
+ }
+
+ hf.removeRows(sheetId, [1, 1]);
+ renderTable();
+}
+
+/**
+ * Run the HF undo action.
+ */
+function undo() {
+ clearInfo();
+
+ if (!hf.isThereSomethingToUndo()) {
+ displayInfo("There's nothing to undo.");
+
+ return;
+ }
+
+ hf.undo();
+ renderTable();
+}
+
+const ANIMATION_ENABLED = true;
+
+// Bind the button events.
+bindEvents();
+
+// Render the table.
+renderTable();
diff --git a/docs/guide/advanced-usage.md b/docs/guide/advanced-usage.md
index 4a8300f386..7ccb1f64c1 100644
--- a/docs/guide/advanced-usage.md
+++ b/docs/guide/advanced-usage.md
@@ -121,7 +121,14 @@ console.log(winningTeam)
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/advanced-usage/example1.html)
+
+@[code](@/docs/examples/advanced-usage/example1.css)
+
+@[code](@/docs/examples/advanced-usage/example1.js)
+
+@[code](@/docs/examples/advanced-usage/example1.ts)
+
+:::
diff --git a/docs/guide/basic-operations.md b/docs/guide/basic-operations.md
index 9b689c6685..591308df18 100644
--- a/docs/guide/basic-operations.md
+++ b/docs/guide/basic-operations.md
@@ -403,10 +403,16 @@ console.log(changes);
## Demo
-This demo presents several basic operations integrated with a
-sample UI.
+This demo presents several basic operations integrated with a sample UI.
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/basic-operations/example1.html)
+
+@[code](@/docs/examples/basic-operations/example1.css)
+
+@[code](@/docs/examples/basic-operations/example1.js)
+
+@[code](@/docs/examples/basic-operations/example1.ts)
+
+:::
diff --git a/docs/guide/basic-usage.md b/docs/guide/basic-usage.md
index 16372888ee..d1b392c9ec 100644
--- a/docs/guide/basic-usage.md
+++ b/docs/guide/basic-usage.md
@@ -64,7 +64,14 @@ works. It's time to move on to a more
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/basic-usage/example1.html)
+
+@[code](@/docs/examples/basic-usage/example1.css)
+
+@[code](@/docs/examples/basic-usage/example1.js)
+
+@[code](@/docs/examples/basic-usage/example1.ts)
+
+:::
diff --git a/docs/guide/batch-operations.md b/docs/guide/batch-operations.md
index 835e2087e4..54596320ae 100644
--- a/docs/guide/batch-operations.md
+++ b/docs/guide/batch-operations.md
@@ -123,7 +123,14 @@ The [paste](../api/classes/hyperformula.md#paste) method also can't be called wh
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/batch-operations/example1.html)
+
+@[code](@/docs/examples/batch-operations/example1.css)
+
+@[code](@/docs/examples/batch-operations/example1.js)
+
+@[code](@/docs/examples/batch-operations/example1.ts)
+
+:::
diff --git a/docs/guide/clipboard-operations.md b/docs/guide/clipboard-operations.md
index bf6ae78bd8..0360f0afd5 100644
--- a/docs/guide/clipboard-operations.md
+++ b/docs/guide/clipboard-operations.md
@@ -112,7 +112,14 @@ Depending on what was cut, the data is stored as:
## Demo
-
\ No newline at end of file
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/clipboard-operations/example1.html)
+
+@[code](@/docs/examples/clipboard-operations/example1.css)
+
+@[code](@/docs/examples/clipboard-operations/example1.js)
+
+@[code](@/docs/examples/clipboard-operations/example1.ts)
+
+:::
\ No newline at end of file
diff --git a/docs/guide/compatibility-with-microsoft-excel.md b/docs/guide/compatibility-with-microsoft-excel.md
index 9f96283d4c..65f252f4e1 100644
--- a/docs/guide/compatibility-with-microsoft-excel.md
+++ b/docs/guide/compatibility-with-microsoft-excel.md
@@ -1,6 +1,6 @@
# Compatibility with Microsoft Excel
-Achieve nearly full compatibility wih Microsoft Excel, using the right HyperFormula configuration.
+Achieve nearly full compatibility with Microsoft Excel, using the right HyperFormula configuration.
**Contents:**
[[toc]]
diff --git a/docs/guide/custom-functions.md b/docs/guide/custom-functions.md
index 9726b2a40a..14ace0edd4 100644
--- a/docs/guide/custom-functions.md
+++ b/docs/guide/custom-functions.md
@@ -363,8 +363,11 @@ This demo contains the implementation of both the
[`DOUBLE_RANGE`](#advanced-custom-function-example) custom functions.
## Function options
diff --git a/docs/guide/date-and-time-handling.md b/docs/guide/date-and-time-handling.md
index b84ef18890..98ad1fb0cf 100644
--- a/docs/guide/date-and-time-handling.md
+++ b/docs/guide/date-and-time-handling.md
@@ -98,7 +98,14 @@ And now, HyperFormula recognizes these values as valid dates and can operate on
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/date-time/example1.html)
+
+@[code](@/docs/examples/date-time/example1.css)
+
+@[code](@/docs/examples/date-time/example1.js)
+
+@[code](@/docs/examples/date-time/example1.ts)
+
+:::
diff --git a/docs/guide/demo.md b/docs/guide/demo.md
index 25c092022f..1d4e754a42 100644
--- a/docs/guide/demo.md
+++ b/docs/guide/demo.md
@@ -1,9 +1,16 @@
# Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/demo/example1.html)
+
+@[code](@/docs/examples/demo/example1.css)
+
+@[code](@/docs/examples/demo/example1.js)
+
+@[code](@/docs/examples/demo/example1.ts)
+
+:::
In this demo, you can see how HyperFormula handles basic operations by using API methods, such as:
diff --git a/docs/guide/i18n-features.md b/docs/guide/i18n-features.md
index 1fab319ac4..90eba856c0 100644
--- a/docs/guide/i18n-features.md
+++ b/docs/guide/i18n-features.md
@@ -105,7 +105,14 @@ localeLang: 'en-US',
This demo shows HyperFormula configured for the `en-US` locale.
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/i18n/example1.html)
+
+@[code](@/docs/examples/i18n/example1.css)
+
+@[code](@/docs/examples/i18n/example1.js)
+
+@[code](@/docs/examples/i18n/example1.ts)
+
+:::
diff --git a/docs/guide/integration-with-angular.md b/docs/guide/integration-with-angular.md
index 0eb483004b..49dfdd17a2 100644
--- a/docs/guide/integration-with-angular.md
+++ b/docs/guide/integration-with-angular.md
@@ -7,6 +7,9 @@ For more details, see the [client-side installation](client-side-installation.md
## Demo
diff --git a/docs/guide/integration-with-react.md b/docs/guide/integration-with-react.md
index d58de06e04..418dc190a4 100644
--- a/docs/guide/integration-with-react.md
+++ b/docs/guide/integration-with-react.md
@@ -7,6 +7,9 @@ For more details, see the [client-side installation](client-side-installation.md
## Demo
diff --git a/docs/guide/integration-with-svelte.md b/docs/guide/integration-with-svelte.md
index c2488e6bfb..df9b9f5b86 100644
--- a/docs/guide/integration-with-svelte.md
+++ b/docs/guide/integration-with-svelte.md
@@ -7,6 +7,9 @@ For more details, see the [client-side installation](client-side-installation.md
## Demo
diff --git a/docs/guide/integration-with-vue.md b/docs/guide/integration-with-vue.md
index 62d4f36d8a..baf9b8bda0 100644
--- a/docs/guide/integration-with-vue.md
+++ b/docs/guide/integration-with-vue.md
@@ -11,6 +11,9 @@ This demo uses the [Vue 3](https://v3.vuejs.org/) framework. If you are looking
:::
\ No newline at end of file
+ :src="`https://codesandbox.io/embed/github/handsontable/hyperformula-demos/tree/2.7.x/vue-3-demo?autoresize=1&fontsize=11&hidenavigation=1&theme=light&view=preview&v=${$page.buildDateURIEncoded}`"
+ style="width:100%; height:1070px; border:0; border-radius: 4px; overflow:hidden;"
+ title="handsontable/hyperformula-demos: react-demo"
+ allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
+ sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts">
+
diff --git a/docs/guide/localizing-functions.md b/docs/guide/localizing-functions.md
index fdf5946f02..4622b6c9c5 100644
--- a/docs/guide/localizing-functions.md
+++ b/docs/guide/localizing-functions.md
@@ -96,7 +96,14 @@ You can localize your custom functions as well. For details, see the [Custom fun
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/localizing-functions/example1.html)
+
+@[code](@/docs/examples/localizing-functions/example1.css)
+
+@[code](@/docs/examples/localizing-functions/example1.js)
+
+@[code](@/docs/examples/localizing-functions/example1.ts)
+
+:::
diff --git a/docs/guide/named-expressions.md b/docs/guide/named-expressions.md
index 8284ecff66..9c34c196cc 100644
--- a/docs/guide/named-expressions.md
+++ b/docs/guide/named-expressions.md
@@ -149,7 +149,14 @@ described in [that section](basic-operations.md#isitpossibleto-methods).
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/named-expressions/example1.html)
+
+@[code](@/docs/examples/named-expressions/example1.css)
+
+@[code](@/docs/examples/named-expressions/example1.js)
+
+@[code](@/docs/examples/named-expressions/example1.ts)
+
+:::
diff --git a/docs/guide/sorting-data.md b/docs/guide/sorting-data.md
index 1eaaa1dc0d..8faedd3ca9 100644
--- a/docs/guide/sorting-data.md
+++ b/docs/guide/sorting-data.md
@@ -195,7 +195,14 @@ if (!isColumnOrderOk) {
The demo below shows how to sort rows in ascending and descending order, based on the results (calculated values) of the cells in the second column.
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/sorting-data/example1.html)
+
+@[code](@/docs/examples/sorting-data/example1.css)
+
+@[code](@/docs/examples/sorting-data/example1.js)
+
+@[code](@/docs/examples/sorting-data/example1.ts)
+
+:::
diff --git a/docs/guide/undo-redo.md b/docs/guide/undo-redo.md
index c34b5f8849..dceb482025 100644
--- a/docs/guide/undo-redo.md
+++ b/docs/guide/undo-redo.md
@@ -25,7 +25,14 @@ that undo-redo will recognize them as a single cumulative operation.
## Demo
-
+::: example #example1 --html 1 --css 2 --js 3 --ts 4
+
+@[code](@/docs/examples/undo-redo/example1.html)
+
+@[code](@/docs/examples/undo-redo/example1.css)
+
+@[code](@/docs/examples/undo-redo/example1.js)
+
+@[code](@/docs/examples/undo-redo/example1.ts)
+
+:::
diff --git a/package-lock.json b/package-lock.json
index 0b9dea852c..2391e83b5f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,8 +15,17 @@
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.4",
+ "@babel/plugin-proposal-class-properties": "^7.13.0",
+ "@babel/plugin-proposal-decorators": "^7.13.5",
+ "@babel/plugin-proposal-private-methods": "^7.13.0",
+ "@babel/plugin-proposal-private-property-in-object": "^7.13.0",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-transform-modules-commonjs": "^7.13.8",
"@babel/plugin-transform-runtime": "^7.8.3",
+ "@babel/plugin-transform-typescript": "^7.24.7",
"@babel/preset-env": "^7.8.4",
+ "@babel/preset-react": "^7.13.13",
+ "@babel/preset-typescript": "^7.13.0",
"@babel/register": "^7.9.0",
"@babel/runtime": "^7.18.9",
"@microsoft/tsdoc": "^0.12.16",
@@ -35,8 +44,10 @@
"cross-env": "^7.0.0",
"env-cmd": "^10.1.0",
"eslint": "^7.0.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^43.1.1",
"eslint-plugin-license-header": "^0.6.0",
+ "eslint-plugin-prettier": "^4.2.1",
"full-icu": "^1.3.1",
"jasmine": "^4.0.0",
"jest": "^26.0.0",
@@ -58,6 +69,7 @@
"serve": "^14.2.0",
"string-replace-loader": "^2.3.0",
"tar": "^6.0.1",
+ "terser-webpack-plugin": "^4.2.3",
"ts-jest": "^26.0.0",
"ts-loader": "^7.0.2",
"ts-node": "^8.0.1",
@@ -167,12 +179,12 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.24.10",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz",
- "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==",
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
+ "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.24.9",
+ "@babel/types": "^7.25.6",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
@@ -223,19 +235,17 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.8.tgz",
- "integrity": "sha512-4f6Oqnmyp2PP3olgUMmOwC3akxSm5aBYraQ6YDdKy7NcAMkDECHWG0DEnV6M2UAkERgIBhYt8S27rURPg7SxWA==",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz",
+ "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==",
"dev": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-function-name": "^7.24.7",
"@babel/helper-member-expression-to-functions": "^7.24.8",
"@babel/helper-optimise-call-expression": "^7.24.7",
- "@babel/helper-replace-supers": "^7.24.7",
+ "@babel/helper-replace-supers": "^7.25.0",
"@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
- "@babel/helper-split-export-declaration": "^7.24.7",
+ "@babel/traverse": "^7.25.4",
"semver": "^6.3.1"
},
"engines": {
@@ -399,14 +409,14 @@
}
},
"node_modules/@babel/helper-replace-supers": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz",
- "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz",
+ "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==",
"dev": true,
"dependencies": {
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-member-expression-to-functions": "^7.24.7",
- "@babel/helper-optimise-call-expression": "^7.24.7"
+ "@babel/helper-member-expression-to-functions": "^7.24.8",
+ "@babel/helper-optimise-call-expression": "^7.24.7",
+ "@babel/traverse": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
@@ -524,10 +534,13 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz",
- "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==",
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz",
+ "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==",
"dev": true,
+ "dependencies": {
+ "@babel/types": "^7.25.6"
+ },
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -633,11 +646,35 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-proposal-private-methods": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz",
+ "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-proposal-private-property-in-object": {
- "version": "7.21.0-placeholder-for-preset-env.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
- "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "version": "7.21.11",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
+ "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
+ "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
"dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.18.6",
+ "@babel/helper-create-class-features-plugin": "^7.21.0",
+ "@babel/helper-plugin-utils": "^7.20.2",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
"engines": {
"node": ">=6.9.0"
},
@@ -906,6 +943,21 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz",
+ "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-syntax-unicode-sets-regex": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
@@ -1523,6 +1575,71 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-react-display-name": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz",
+ "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz",
+ "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-module-imports": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/plugin-syntax-jsx": "^7.24.7",
+ "@babel/types": "^7.25.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-development": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz",
+ "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-pure-annotations": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz",
+ "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-regenerator": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz",
@@ -1650,6 +1767,25 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.25.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz",
+ "integrity": "sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.24.7",
+ "@babel/helper-create-class-features-plugin": "^7.25.0",
+ "@babel/helper-plugin-utils": "^7.24.8",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
+ "@babel/plugin-syntax-typescript": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/plugin-transform-unicode-escapes": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
@@ -1808,6 +1944,18 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/preset-modules": {
"version": "0.1.6-no-external-plugins",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
@@ -1822,6 +1970,45 @@
"@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/@babel/preset-react": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz",
+ "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-validator-option": "^7.24.7",
+ "@babel/plugin-transform-react-display-name": "^7.24.7",
+ "@babel/plugin-transform-react-jsx": "^7.24.7",
+ "@babel/plugin-transform-react-jsx-development": "^7.24.7",
+ "@babel/plugin-transform-react-pure-annotations": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-typescript": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz",
+ "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-validator-option": "^7.24.7",
+ "@babel/plugin-syntax-jsx": "^7.24.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.24.7",
+ "@babel/plugin-transform-typescript": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
"node_modules/@babel/register": {
"version": "7.24.6",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.24.6.tgz",
@@ -1860,33 +2047,30 @@
}
},
"node_modules/@babel/template": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz",
- "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
+ "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.24.7",
- "@babel/parser": "^7.24.7",
- "@babel/types": "^7.24.7"
+ "@babel/parser": "^7.25.0",
+ "@babel/types": "^7.25.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.24.8",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz",
- "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==",
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz",
+ "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.24.7",
- "@babel/generator": "^7.24.8",
- "@babel/helper-environment-visitor": "^7.24.7",
- "@babel/helper-function-name": "^7.24.7",
- "@babel/helper-hoist-variables": "^7.24.7",
- "@babel/helper-split-export-declaration": "^7.24.7",
- "@babel/parser": "^7.24.8",
- "@babel/types": "^7.24.8",
+ "@babel/generator": "^7.25.6",
+ "@babel/parser": "^7.25.6",
+ "@babel/template": "^7.25.0",
+ "@babel/types": "^7.25.6",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -1895,9 +2079,9 @@
}
},
"node_modules/@babel/types": {
- "version": "7.24.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz",
- "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==",
+ "version": "7.25.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz",
+ "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.24.8",
@@ -2059,6 +2243,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@gar/promisify": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
+ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
+ "dev": true
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@@ -2893,6 +3083,16 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@@ -2970,6 +3170,54 @@
"node": ">= 8"
}
},
+ "node_modules/@npmcli/fs": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz",
+ "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==",
+ "dev": true,
+ "dependencies": {
+ "@gar/promisify": "^1.0.1",
+ "semver": "^7.3.5"
+ }
+ },
+ "node_modules/@npmcli/fs/node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@npmcli/move-file": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
+ "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
+ "deprecated": "This functionality has been moved to @npmcli/fs",
+ "dev": true,
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@npmcli/move-file/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@@ -3960,39 +4208,39 @@
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.4.32",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.32.tgz",
- "integrity": "sha512-8tCVWkkLe/QCWIsrIvExUGnhYCAOroUs5dzhSoKL5w4MJS8uIYiou+pOPSVIOALOQ80B0jBs+Ri+kd5+MBnCDw==",
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.33.tgz",
+ "integrity": "sha512-MoIREbkdPQlnGfSKDMgzTqzqx5nmEjIc0ydLVYlTACGBsfvOJ4tHSbZXKVF536n6fB+0eZaGEOqsGThPpdvF5A==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.24.7",
- "@vue/shared": "3.4.32",
+ "@vue/shared": "3.4.33",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.4.32",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.32.tgz",
- "integrity": "sha512-PbSgt9KuYo4fyb90dynuPc0XFTfFPs3sCTbPLOLlo+PrUESW1gn/NjSsUvhR+mI2AmmEzexwYMxbHDldxSOr2A==",
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.33.tgz",
+ "integrity": "sha512-GzB8fxEHKw0gGet5BKlpfXEqoBnzSVWwMnT+dc25wE7pFEfrU/QsvjZMP9rD4iVXHBBoemTct8mN0GJEI6ZX5A==",
"dev": true,
"dependencies": {
- "@vue/compiler-core": "3.4.32",
- "@vue/shared": "3.4.32"
+ "@vue/compiler-core": "3.4.33",
+ "@vue/shared": "3.4.33"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.4.32",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.32.tgz",
- "integrity": "sha512-STy9im/WHfaguJnfKjjVpMHukxHUrOKjm2vVCxiojQJyo3Sb6Os8SMXBr/MI+ekpstEGkDONfqAQoSbZhspLYw==",
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.33.tgz",
+ "integrity": "sha512-7rk7Vbkn21xMwIUpHQR4hCVejwE6nvhBOiDgoBcR03qvGqRKA7dCBSsHZhwhYUsmjlbJ7OtD5UFIyhP6BY+c8A==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.24.7",
- "@vue/compiler-core": "3.4.32",
- "@vue/compiler-dom": "3.4.32",
- "@vue/compiler-ssr": "3.4.32",
- "@vue/shared": "3.4.32",
+ "@vue/compiler-core": "3.4.33",
+ "@vue/compiler-dom": "3.4.33",
+ "@vue/compiler-ssr": "3.4.33",
+ "@vue/shared": "3.4.33",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.10",
"postcss": "^8.4.39",
@@ -4000,13 +4248,13 @@
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.4.32",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.32.tgz",
- "integrity": "sha512-nyu/txTecF6DrxLrpLcI34xutrvZPtHPBj9yRoPxstIquxeeyywXpYZrQMsIeDfBhlw1abJb9CbbyZvDw2kjdg==",
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.33.tgz",
+ "integrity": "sha512-0WveC9Ai+eT/1b6LCV5IfsufBZ0HP7pSSTdDjcuW302tTEgoBw8rHVHKPbGUtzGReUFCRXbv6zQDDgucnV2WzQ==",
"dev": true,
"dependencies": {
- "@vue/compiler-dom": "3.4.32",
- "@vue/shared": "3.4.32"
+ "@vue/compiler-dom": "3.4.33",
+ "@vue/shared": "3.4.33"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -4068,9 +4316,9 @@
"dev": true
},
"node_modules/@vue/shared": {
- "version": "3.4.32",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.32.tgz",
- "integrity": "sha512-ep4mF1IVnX/pYaNwxwOpJHyBtOMKWoKZMbnUyd+z0udqIxLUh7YCCd/JfDna8aUrmnG9SFORyIq2HzEATRrQsg==",
+ "version": "3.4.33",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.33.tgz",
+ "integrity": "sha512-aoRY0jQk3A/cuvdkodTrM4NMfxco8n55eG4H7ML/CRy7OryHfiqvug4xrCBBMbbN+dvXAetDDwZW9DXWWjBntA==",
"dev": true
},
"node_modules/@vuepress/core": {
@@ -4259,25 +4507,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/@vuepress/core/node_modules/fsevents": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
- "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
- "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "dependencies": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
"node_modules/@vuepress/core/node_modules/glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -5324,6 +5553,19 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -6747,47 +6989,76 @@
}
},
"node_modules/cacache": {
- "version": "12.0.4",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
- "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
+ "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==",
"dev": true,
"dependencies": {
- "bluebird": "^3.5.5",
- "chownr": "^1.1.1",
- "figgy-pudding": "^3.5.1",
+ "@npmcli/fs": "^1.0.0",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
"glob": "^7.1.4",
- "graceful-fs": "^4.1.15",
- "infer-owner": "^1.0.3",
- "lru-cache": "^5.1.1",
- "mississippi": "^3.0.0",
- "mkdirp": "^0.5.1",
- "move-concurrently": "^1.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
"promise-inflight": "^1.0.1",
- "rimraf": "^2.6.3",
- "ssri": "^6.0.1",
- "unique-filename": "^1.1.1",
- "y18n": "^4.0.0"
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/cacache/node_modules/chownr": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
- "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
- "dev": true
- },
- "node_modules/cacache/node_modules/rimraf": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
- "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "node_modules/cacache/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
- "glob": "^7.1.3"
+ "yallist": "^4.0.0"
},
- "bin": {
- "rimraf": "bin.js"
+ "engines": {
+ "node": ">=10"
}
},
+ "node_modules/cacache/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cacache/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cacache/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@@ -7018,9 +7289,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001642",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
- "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==",
+ "version": "1.0.30001643",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz",
+ "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==",
"dev": true,
"funding": [
{
@@ -7262,6 +7533,15 @@
"node": ">= 4.0"
}
},
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/cli-boxes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
@@ -7857,6 +8137,35 @@
"node": ">=0.10.0"
}
},
+ "node_modules/copy-webpack-plugin/node_modules/cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "dependencies": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
"node_modules/copy-webpack-plugin/node_modules/dir-glob": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
@@ -7961,6 +8270,19 @@
"node": ">=4"
}
},
+ "node_modules/copy-webpack-plugin/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
"node_modules/copy-webpack-plugin/node_modules/schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -7975,6 +8297,15 @@
"node": ">= 4"
}
},
+ "node_modules/copy-webpack-plugin/node_modules/serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
"node_modules/copy-webpack-plugin/node_modules/slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -7984,6 +8315,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/copy-webpack-plugin/node_modules/ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "dependencies": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
"node_modules/core-js": {
"version": "3.37.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz",
@@ -9067,6 +9407,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/del/node_modules/p-map": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+ "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/del/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -9465,9 +9814,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
- "version": "1.4.829",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.829.tgz",
- "integrity": "sha512-5qp1N2POAfW0u1qGAxXEtz6P7bO1m6gpZr5hdf5ve6lxpLM7MpiM4jIPz7xcrNlClQMafbyUDDWjlIQZ1Mw0Rw==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz",
+ "integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==",
"dev": true
},
"node_modules/elliptic": {
@@ -9874,201 +10223,6 @@
"esbuild-windows-arm64": "0.14.7"
}
},
- "node_modules/esbuild-android-arm64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz",
- "integrity": "sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/esbuild-darwin-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz",
- "integrity": "sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/esbuild-darwin-arm64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.7.tgz",
- "integrity": "sha512-68e7COhmwIiLXBEyxUxZSSU0akgv8t3e50e2QOtKdBUE0F6KIRISzFntLe2rYlNqSsjGWsIO6CCc9tQxijjSkw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/esbuild-freebsd-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz",
- "integrity": "sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/esbuild-freebsd-arm64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz",
- "integrity": "sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/esbuild-linux-32": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz",
- "integrity": "sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-linux-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz",
- "integrity": "sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-linux-arm": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz",
- "integrity": "sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-linux-arm64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz",
- "integrity": "sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-linux-mips64le": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz",
- "integrity": "sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-linux-ppc64le": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz",
- "integrity": "sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/esbuild-netbsd-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz",
- "integrity": "sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ]
- },
- "node_modules/esbuild-openbsd-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz",
- "integrity": "sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ]
- },
- "node_modules/esbuild-sunos-64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz",
- "integrity": "sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ]
- },
- "node_modules/esbuild-windows-32": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz",
- "integrity": "sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/esbuild-windows-64": {
"version": "0.14.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz",
@@ -10082,19 +10236,6 @@
"win32"
]
},
- "node_modules/esbuild-windows-arm64": {
- "version": "0.14.7",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz",
- "integrity": "sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ]
- },
"node_modules/escalade": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
@@ -10215,6 +10356,18 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+ "dev": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
"node_modules/eslint-plugin-jsdoc": {
"version": "43.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-43.2.0.tgz",
@@ -10270,6 +10423,27 @@
"requireindex": "^1.2.0"
}
},
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+ "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+ "dev": true,
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -10959,6 +11133,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true
+ },
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@@ -11591,20 +11771,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/full-icu": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/full-icu/-/full-icu-1.5.0.tgz",
@@ -12808,9 +12974,9 @@
}
},
"node_modules/import-local": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
- "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
+ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
"dev": true,
"dependencies": {
"pkg-dir": "^4.2.0",
@@ -12847,6 +13013,15 @@
"node": ">=0.8.19"
}
},
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/indexes-of": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
@@ -13686,9 +13861,9 @@
}
},
"node_modules/jasmine-core": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz",
- "integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz",
+ "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==",
"dev": true,
"peer": true
},
@@ -16831,6 +17006,96 @@
"node": ">=8"
}
},
+ "node_modules/minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-collect/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-collect/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-flush/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-flush/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-pipeline/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-pipeline/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
@@ -17223,9 +17488,9 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.17",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz",
- "integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==",
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
+ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true
},
"node_modules/nopt": {
@@ -17802,12 +18067,18 @@
}
},
"node_modules/p-map": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
- "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
"dev": true,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
"engines": {
- "node": ">=6"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-retry": {
@@ -19738,7 +20009,6 @@
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
- "optional": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -19749,6 +20019,18 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/pretty-error": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
@@ -21543,9 +21825,9 @@
}
},
"node_modules/serialize-javascript": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
- "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
+ "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
"dev": true,
"dependencies": {
"randombytes": "^2.1.0"
@@ -22478,14 +22760,35 @@
}
},
"node_modules/ssri": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
- "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
+ "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
+ "dev": true,
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/ssri/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dev": true,
"dependencies": {
- "figgy-pudding": "^3.5.1"
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
+ "node_modules/ssri/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -23232,66 +23535,137 @@
}
},
"node_modules/terser": {
- "version": "4.8.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
- "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
+ "version": "5.31.3",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz",
+ "integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==",
"dev": true,
"dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.8.2",
"commander": "^2.20.0",
- "source-map": "~0.6.1",
- "source-map-support": "~0.5.12"
+ "source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
- "node": ">=6.0.0"
+ "node": ">=10"
}
},
"node_modules/terser-webpack-plugin": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
- "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz",
+ "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==",
"dev": true,
"dependencies": {
- "cacache": "^12.0.2",
- "find-cache-dir": "^2.1.0",
- "is-wsl": "^1.1.0",
- "schema-utils": "^1.0.0",
- "serialize-javascript": "^4.0.0",
+ "cacache": "^15.0.5",
+ "find-cache-dir": "^3.3.1",
+ "jest-worker": "^26.5.0",
+ "p-limit": "^3.0.2",
+ "schema-utils": "^3.0.0",
+ "serialize-javascript": "^5.0.1",
"source-map": "^0.6.1",
- "terser": "^4.1.2",
- "webpack-sources": "^1.4.0",
- "worker-farm": "^1.7.0"
+ "terser": "^5.3.4",
+ "webpack-sources": "^1.4.3"
},
"engines": {
- "node": ">= 6.9.0"
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
},
"peerDependencies": {
- "webpack": "^4.0.0"
+ "webpack": "^4.0.0 || ^5.0.0"
}
},
- "node_modules/terser-webpack-plugin/node_modules/is-wsl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
- "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
"engines": {
- "node": ">=4"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
}
},
"node_modules/terser-webpack-plugin/node_modules/schema-utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
- "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
+ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"dependencies": {
- "ajv": "^6.1.0",
- "ajv-errors": "^1.0.0",
- "ajv-keywords": "^3.1.0"
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
},
"engines": {
- "node": ">= 4"
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/terser/node_modules/acorn": {
+ "version": "8.12.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
+ "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
}
},
"node_modules/terser/node_modules/commander": {
@@ -25371,25 +25745,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/watchpack-chokidar2/node_modules/fsevents": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
- "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
- "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "dependencies": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
"node_modules/watchpack-chokidar2/node_modules/glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -26206,25 +26561,6 @@
"node": ">=6"
}
},
- "node_modules/webpack-dev-server/node_modules/fsevents": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
- "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
- "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "dependencies": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
"node_modules/webpack-dev-server/node_modules/glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
@@ -26705,6 +27041,41 @@
"node": ">=0.10.0"
}
},
+ "node_modules/webpack/node_modules/cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "dependencies": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "node_modules/webpack/node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "node_modules/webpack/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
"node_modules/webpack/node_modules/define-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
@@ -26795,6 +27166,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/webpack/node_modules/is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/webpack/node_modules/json5": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
@@ -26868,6 +27248,19 @@
"node": ">=0.10.0"
}
},
+ "node_modules/webpack/node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
"node_modules/webpack/node_modules/schema-utils": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
@@ -26882,6 +27275,64 @@
"node": ">= 4"
}
},
+ "node_modules/webpack/node_modules/serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/webpack/node_modules/ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "dependencies": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "node_modules/webpack/node_modules/terser": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
+ "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/webpack/node_modules/terser-webpack-plugin": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+ "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+ "dev": true,
+ "dependencies": {
+ "cacache": "^12.0.2",
+ "find-cache-dir": "^2.1.0",
+ "is-wsl": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^4.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^4.1.2",
+ "webpack-sources": "^1.4.0",
+ "worker-farm": "^1.7.0"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0"
+ }
+ },
"node_modules/webpack/node_modules/to-regex-range": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
@@ -27357,6 +27808,18 @@
"node": ">=6"
}
},
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/zepto": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz",
diff --git a/package.json b/package.json
index aeb0c964f9..0a84f1de7d 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,9 @@
"scripts": {
"docs:dev": "npm run typedoc:build-api && cross-env NODE_OPTIONS=--openssl-legacy-provider vuepress dev docs --silent --no-clear-screen --no-cache",
"docs:build": "npm run bundle-all && npm run typedoc:build-api && cross-env NODE_OPTIONS=--openssl-legacy-provider vuepress build docs",
+ "docs:code-examples:generate-js": "bash docs/code-examples-generator.sh",
+ "docs:code-examples:generate-all-js": "bash docs/code-examples-generator.sh --generateAll",
+ "docs:code-examples:format-all-ts": "bash docs/code-examples-generator.sh --formatAllTsExamples",
"bundle-all": "cross-env HF_COMPILE=1 npm-run-all clean compile bundle:** verify-bundles",
"bundle:es": "(node script/if-ne-env.js HF_COMPILE=1 || npm run compile) && cross-env-shell BABEL_ENV=es env-cmd -f ht.config.js babel lib --out-dir es",
"bundle:cjs": "(node script/if-ne-env.js HF_COMPILE=1 || npm run compile) && cross-env-shell BABEL_ENV=commonjs env-cmd -f ht.config.js babel lib --out-dir commonjs",
@@ -103,6 +106,15 @@
"@babel/preset-env": "^7.8.4",
"@babel/register": "^7.9.0",
"@babel/runtime": "^7.18.9",
+ "@babel/preset-react": "^7.13.13",
+ "@babel/preset-typescript": "^7.13.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.13.8",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-proposal-decorators": "^7.13.5",
+ "@babel/plugin-proposal-class-properties": "^7.13.0",
+ "@babel/plugin-proposal-private-methods": "^7.13.0",
+ "@babel/plugin-proposal-private-property-in-object": "^7.13.0",
+ "@babel/plugin-transform-typescript": "^7.24.7",
"@microsoft/tsdoc": "^0.12.16",
"@types/jasmine": "^4.0.0",
"@types/jest": "^26.0.0",
@@ -119,8 +131,10 @@
"cross-env": "^7.0.0",
"env-cmd": "^10.1.0",
"eslint": "^7.0.0",
+ "eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^43.1.1",
"eslint-plugin-license-header": "^0.6.0",
+ "eslint-plugin-prettier": "^4.2.1",
"full-icu": "^1.3.1",
"jasmine": "^4.0.0",
"jest": "^26.0.0",
@@ -142,6 +156,7 @@
"serve": "^14.2.0",
"string-replace-loader": "^2.3.0",
"tar": "^6.0.1",
+ "terser-webpack-plugin": "^4.2.3",
"ts-jest": "^26.0.0",
"ts-loader": "^7.0.2",
"ts-node": "^8.0.1",
diff --git a/script/check-file.js b/script/check-file.js
index 4d2cde24df..151902739a 100644
--- a/script/check-file.js
+++ b/script/check-file.js
@@ -1,5 +1,6 @@
const { resolve } = require('path')
const assert = require('assert')
+const fs = require('fs')
const htConfig = require('../ht.config')
const [ /* node bin */ , /* path to this script */ , fileToCheck] = process.argv;
@@ -29,6 +30,13 @@ try {
assert(valueA1 === 42)
assert(valueB1 === 44)
+ // Check if the file contains no redundant license comments
+ if (fileToCheck.includes('.js')) {
+ const fileContent = fs.readFileSync(resolve(fileToCheck), 'utf8')
+ const licenseComments = fileContent.match(/@license/g)
+ assert.equal(licenseComments.length, 2)
+ }
+
console.log(`Bundle check: \u001b[1;37m${fileToCheck}\u001b[0m \u001b[0;32mOK\u001b[0m`)
} catch (ex) {
console.log(`Bundle check: \u001b[1;37m${fileToCheck}\u001b[0m \u001b[0;31mERROR\u001b[0m`)