diff --git a/io.sc.platform.ai.frontend/.browserslistrc b/io.sc.platform.ai.frontend/.browserslistrc
new file mode 100644
index 00000000..1fff95c5
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.browserslistrc
@@ -0,0 +1,5 @@
+chrome >=89
+edge >=88
+firefox >=89
+safari >=15
+ios_saf >=15
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/.editorconfig b/io.sc.platform.ai.frontend/.editorconfig
new file mode 100644
index 00000000..4bd609e2
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.editorconfig
@@ -0,0 +1,15 @@
+#################################################################
+# 强制对使用该基本代码的所有人实施一致的编码样式
+#################################################################
+
+# 顶级配置(即不集成父配置)
+root = true
+
+# 针对所有文件
+[*]
+charset = utf-8 # 字符集: utf-8
+indent_size = 2 # 缩进大小: 2
+indent_style = space # 缩进风格: 空格
+end_of_line = lf # 行结束符: 换行符
+insert_final_newline = true # 是否在文件的最后插入一个空行
+trim_trailing_whitespace = true # 是否删除行尾的空格
diff --git a/io.sc.platform.ai.frontend/.eslintrc.cjs b/io.sc.platform.ai.frontend/.eslintrc.cjs
new file mode 100644
index 00000000..3c6261c6
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.eslintrc.cjs
@@ -0,0 +1,71 @@
+module.exports = {
+ // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
+ // This option interrupts the configuration hierarchy at this file
+ // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
+ root: true,
+
+ env: {
+ browser: true,
+ es2022: true,
+ node: true,
+ 'vue/setup-compiler-macros': true,
+ },
+
+ // Rules order is important, please avoid shuffling them
+ extends: [
+ // Base ESLint recommended rules
+ 'eslint:recommended',
+
+ // https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#usage
+ // ESLint typescript rules
+ 'plugin:@typescript-eslint/recommended',
+
+ // Vue ESLint recommended rules
+ // 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
+ // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
+ // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
+ 'plugin:vue/vue3-recommended',
+
+ // https://github.com/prettier/eslint-config-prettier#installation
+ // usage with Prettier, provided by 'eslint-config-prettier'.
+ 'plugin:prettier/recommended', // Recommended
+ ],
+
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ ecmaVersion: 2022,
+ parser: '@typescript-eslint/parser',
+ ecmaFeatures: {
+ jsx: false,
+ },
+ },
+
+ plugins: [
+ // required to apply rules which need type information
+ '@typescript-eslint',
+
+ // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
+ // required to lint *.vue files
+ 'vue',
+
+ // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
+ // Prettier has not been included as plugin to avoid performance impact
+ // add it as an extension for your IDE
+ ],
+
+ rules: {
+ semi: [1],
+ '@typescript-eslint/no-var-requires': 'off',
+ '@typescript-eslint/no-empty-object-type': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unused-expressions': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/no-this-alias': 'off',
+ 'vue/multi-word-component-names': 'off' /* 禁用 vue 组件名称检查规则 */,
+ 'no-prototype-builtins': 'off',
+ 'prefer-rest-params': 'off',
+ 'no-control-regex': 'off',
+ 'no-case-declarations': 'off',
+ 'vue/no-v-html': 'off'
+ },
+};
diff --git a/io.sc.platform.ai.frontend/.gitignore b/io.sc.platform.ai.frontend/.gitignore
new file mode 100644
index 00000000..f5dbd6f5
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.gitignore
@@ -0,0 +1,32 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.vuepress
+
+test-results/
+playwright-report/
diff --git a/io.sc.platform.ai.frontend/.npmignore b/io.sc.platform.ai.frontend/.npmignore
new file mode 100644
index 00000000..e69de29b
diff --git a/io.sc.platform.ai.frontend/.npmrc b/io.sc.platform.ai.frontend/.npmrc
new file mode 100644
index 00000000..ac3953c7
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.npmrc
@@ -0,0 +1,13 @@
+# npm 仓库地址, 在 npm install 时使用
+registry=http://nexus.sc.io:8000/repository/npm-public/
+
+# 用户邮箱
+email=
+# publish 时无需先进行 git 代码同步检查, 可避免 publish 时使用 --no-git-checks 选项
+git-checks=false
+
+# 注意: 以下 // 不是注释,不能去掉哦
+# 登录 npm 仓库的用户认证信息, 在 npm publish 时使用, publish 的 npm registry 在 package.json 文件中 publishConfig 部分配置
+# _authToken 可通过以下命令获取
+# curl -X PUT -H "Content-Type:application/json" -d '{"_id":"org.couchdb.user:admin","name":"admin","password":"admin"}' http://nexus.sc.io:8000/repository/npm-releases/-/user/org.couchdb.user:admin
+//nexus.sc.io:8000/repository/npm-releases/:_authToken=NpmToken.67c99588-56a6-3ce1-9bea-a9a6164f8090
diff --git a/io.sc.platform.ai.frontend/.prettierignore b/io.sc.platform.ai.frontend/.prettierignore
new file mode 100644
index 00000000..b5c08636
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.prettierignore
@@ -0,0 +1,3 @@
+build
+dist
+node_modules
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/.prettierrc.json b/io.sc.platform.ai.frontend/.prettierrc.json
new file mode 100644
index 00000000..f9e9ce41
--- /dev/null
+++ b/io.sc.platform.ai.frontend/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": true,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "printWidth": 160,
+ "trailingComma": "all"
+}
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/babel.config.cjs b/io.sc.platform.ai.frontend/babel.config.cjs
new file mode 100644
index 00000000..0ff56db5
--- /dev/null
+++ b/io.sc.platform.ai.frontend/babel.config.cjs
@@ -0,0 +1,16 @@
+module.exports = {
+ presets: [
+ "@babel/preset-env",
+ [
+ "@babel/preset-typescript",
+ {
+ allExtensions: true, //支持所有文件扩展名
+ },
+ ],
+ ],
+ plugins: [
+ "@babel/plugin-transform-class-properties",
+ "@babel/plugin-transform-object-rest-spread",
+ "@vue/babel-plugin-jsx",
+ ]
+}
diff --git a/io.sc.platform.ai.frontend/frontend-register.json b/io.sc.platform.ai.frontend/frontend-register.json
new file mode 100644
index 00000000..2fdc3765
--- /dev/null
+++ b/io.sc.platform.ai.frontend/frontend-register.json
@@ -0,0 +1,7 @@
+{
+ "enable": false,
+ "protocol": "http",
+ "host": "localhost",
+ "port": 8080,
+ "path": "/api/mvc/frontend/regist"
+}
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/nodemon.json b/io.sc.platform.ai.frontend/nodemon.json
new file mode 100644
index 00000000..99590b18
--- /dev/null
+++ b/io.sc.platform.ai.frontend/nodemon.json
@@ -0,0 +1,6 @@
+{
+ "watch": [
+ "./src/routes/routes.json"
+ ],
+ "exec": "pnpm serve"
+}
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/package.json b/io.sc.platform.ai.frontend/package.json
new file mode 100644
index 00000000..a913e00b
--- /dev/null
+++ b/io.sc.platform.ai.frontend/package.json
@@ -0,0 +1,126 @@
+{
+ "name": "io.sc.platform.ai.frontend",
+ "version": "8.2.10",
+ "description": "",
+ "private": false,
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "scripts": {
+ "dev": "nodemon",
+ "serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs",
+ "build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs",
+ "prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs",
+ "clean": "platform clean dist ./node_modules ./pnpm-lock.yaml ./package-lock.json",
+ "sync": "platform sync"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=7"
+ },
+ "publishConfig": {
+ "registry": "http://nexus.sc.io:8000/repository/npm-releases/",
+ "access": "public"
+ },
+ "devDependencies": {
+ "@babel/core": "7.26.0",
+ "@babel/plugin-transform-class-properties": "7.25.9",
+ "@babel/plugin-transform-object-rest-spread": "7.25.9",
+ "@babel/preset-env": "7.26.0",
+ "@babel/preset-typescript": "7.26.0",
+ "@quasar/app-webpack": "4.0.7",
+ "@quasar/cli": "2.4.1",
+ "@types/mockjs": "1.0.10",
+ "@types/node": "22.10.7",
+ "@typescript-eslint/eslint-plugin": "8.20.0",
+ "@typescript-eslint/parser": "8.20.0",
+ "@vue/babel-plugin-jsx": "1.2.5",
+ "@vue/compiler-sfc": "3.5.13",
+ "@vuepress/bundler-webpack": "2.0.0-rc.15",
+ "@vuepress/theme-default": "2.0.0-rc.49",
+ "@webpack-cli/serve": "3.0.1",
+ "autoprefixer": "10.4.20",
+ "babel-loader": "9.2.1",
+ "clean-webpack-plugin": "4.0.0",
+ "compression-webpack-plugin": "11.1.0",
+ "copy-webpack-plugin": "12.0.2",
+ "cross-env": "7.0.3",
+ "css-loader": "7.1.2",
+ "eslint": "9.18.0",
+ "eslint-config-prettier": "10.0.1",
+ "eslint-plugin-prettier": "5.2.2",
+ "eslint-plugin-vue": "9.32.0",
+ "eslint-webpack-plugin": "4.2.0",
+ "html-webpack-plugin": "5.6.3",
+ "json5": "2.2.3",
+ "mini-css-extract-plugin": "2.9.2",
+ "nodemon": "3.1.9",
+ "postcss": "8.5.1",
+ "postcss-import": "16.1.0",
+ "postcss-loader": "8.1.1",
+ "postcss-preset-env": "10.1.3",
+ "prettier": "3.4.2",
+ "sass": "1.83.4",
+ "sass-loader": "16.0.4",
+ "tailwindcss": "3.4.17",
+ "typescript": "5.5.4",
+ "vue-loader": "17.4.2",
+ "vuepress": "2.0.0-rc.15",
+ "webpack": "5.97.1",
+ "webpack-bundle-analyzer": "4.10.2",
+ "webpack-cli": "6.0.1",
+ "webpack-dev-server": "5.2.0",
+ "webpack-merge": "6.0.1"
+ },
+ "dependencies": {
+ "@codemirror/autocomplete": "6.18.4",
+ "@codemirror/commands": "6.8.0",
+ "@codemirror/lang-html": "6.4.9",
+ "@codemirror/lang-java": "6.0.1",
+ "@codemirror/lang-javascript": "6.2.2",
+ "@codemirror/lang-json": "6.0.1",
+ "@codemirror/lang-sql": "6.8.0",
+ "@codemirror/lang-xml": "6.1.0",
+ "@codemirror/language": "6.10.8",
+ "@codemirror/search": "6.5.8",
+ "@codemirror/state": "6.5.1",
+ "@codemirror/view": "6.36.2",
+ "@maxgraph/core": "0.14.0",
+ "@quasar/extras": "1.16.15",
+ "@quasar/quasar-ui-qmarkdown": "2.0.5",
+ "@univerjs/core": "0.5.4",
+ "@univerjs/design": "0.5.4",
+ "@univerjs/docs": "0.5.4",
+ "@univerjs/docs-ui": "0.5.4",
+ "@univerjs/engine-formula": "0.5.4",
+ "@univerjs/engine-render": "0.5.4",
+ "@univerjs/facade": "0.5.4",
+ "@univerjs/sheets": "0.5.4",
+ "@univerjs/sheets-formula": "0.5.4",
+ "@univerjs/sheets-ui": "0.5.4",
+ "@univerjs/thread-comment": "0.5.4",
+ "@univerjs/ui": "0.5.4",
+ "@vueuse/core": "12.4.0",
+ "axios": "1.8.2",
+ "codemirror": "6.0.1",
+ "dayjs": "1.11.13",
+ "echarts": "5.6.0",
+ "exceljs": "4.4.0",
+ "file-saver": "2.0.5",
+ "luckyexcel": "1.0.1",
+ "mockjs": "1.1.0",
+ "node-sql-parser": "5.3.6",
+ "pinia": "2.3.0",
+ "pinia-undo": "0.2.4",
+ "platform-core": "8.2.48",
+ "quasar": "2.17.6",
+ "sort-array": "5.0.0",
+ "svg-path-commander": "2.1.7",
+ "tailwindcss": "3.4.10",
+ "vue": "3.5.13",
+ "vue-dompurify-html": "5.2.0",
+ "vue-i18n": "11.0.1",
+ "vue-router": "4.5.0",
+ "xml-formatter": "3.6.3"
+ }
+}
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/postcss.config.cjs b/io.sc.platform.ai.frontend/postcss.config.cjs
new file mode 100644
index 00000000..f1c8dac8
--- /dev/null
+++ b/io.sc.platform.ai.frontend/postcss.config.cjs
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ }
+}
diff --git a/io.sc.platform.ai.frontend/public/configure.js b/io.sc.platform.ai.frontend/public/configure.js
new file mode 100644
index 00000000..76840a8b
--- /dev/null
+++ b/io.sc.platform.ai.frontend/public/configure.js
@@ -0,0 +1,43 @@
+// 在浏览器 window 对象中新建名为 APP 的容器变量, 用于存放平台的全局变量
+window.APP = {};
+// 全局配置
+window.APP.configure = {
+ // 项目名称
+ projectName: '',
+
+ // 应用上下文路径
+ webContextPath: '[(@{/})]'.startsWith('[') ? '/' : '[(@{/})]',
+
+ // 默认后端 API 请求的服务地址前缀
+ apiContextPaths: {
+ DEFAULT: '[(@{/})]'.startsWith('[') ? 'http://localhost:8080/' : '[(@{/})]',
+ },
+
+ // 前端运行环境
+ // alone: 前端独立部署, 即前端打包后独立部署于 web 服务上(如: nginx, caddy 等)
+ // develop: 前端运行在开发环境, 即前端在开发环境下运行(如: webpack)
+ // backend: 前端运行在后端环境, 即前后端一起打包后部署在应用服务器上(如: tomcat, undertow, jeety等)
+ fontendScenario: 'develop',
+
+ // router 历史模式
+ routerHistoryMode: 'hash',
+
+ // 是否启用本地 mock
+ enableLocalMock: false,
+
+ // mock 请求响应时间(单位:毫秒)
+ localMockTimeout: 10,
+
+ // 是否启用使用远程服务端配置
+ enableRemoteConfigure: true,
+
+ // 是否首先使用本地路由
+ useLocaleRouterFirst: true,
+
+ // axios 配置
+ axios: {
+ baseURL: '',
+ timeout: 1000 * 60,
+ crossdomain: true,
+ },
+};
diff --git a/io.sc.platform.ai.frontend/public/favicon.svg b/io.sc.platform.ai.frontend/public/favicon.svg
new file mode 100644
index 00000000..eab5885e
--- /dev/null
+++ b/io.sc.platform.ai.frontend/public/favicon.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/public/index.html b/io.sc.platform.ai.frontend/public/index.html
new file mode 100644
index 00000000..bd6ee3e0
--- /dev/null
+++ b/io.sc.platform.ai.frontend/public/index.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
正在加载, 请稍后......
+
+
+
+
+
+
+
+
+
diff --git a/io.sc.platform.ai.frontend/public/login-bg.jpg b/io.sc.platform.ai.frontend/public/login-bg.jpg
new file mode 100644
index 00000000..ecd0dc39
Binary files /dev/null and b/io.sc.platform.ai.frontend/public/login-bg.jpg differ
diff --git a/io.sc.platform.ai.frontend/public/logo.svg b/io.sc.platform.ai.frontend/public/logo.svg
new file mode 100644
index 00000000..2f63474f
--- /dev/null
+++ b/io.sc.platform.ai.frontend/public/logo.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/io.sc.platform.ai.frontend/public/svg-editor/Editor.js b/io.sc.platform.ai.frontend/public/svg-editor/Editor.js
new file mode 100644
index 00000000..293b5b14
--- /dev/null
+++ b/io.sc.platform.ai.frontend/public/svg-editor/Editor.js
@@ -0,0 +1,52014 @@
+const consoleLogger = {
+ type: 'logger',
+ log(args) {
+ this.output('log', args);
+ },
+ warn(args) {
+ this.output('warn', args);
+ },
+ error(args) {
+ this.output('error', args);
+ },
+ output(type, args) {
+ if (console && console[type]) console[type].apply(console, args);
+ }
+};
+class Logger {
+ constructor(concreteLogger) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ this.init(concreteLogger, options);
+ }
+ init(concreteLogger) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ this.prefix = options.prefix || 'i18next:';
+ this.logger = concreteLogger || consoleLogger;
+ this.options = options;
+ this.debug = options.debug;
+ }
+ log() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+ return this.forward(args, 'log', '', true);
+ }
+ warn() {
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ args[_key2] = arguments[_key2];
+ }
+ return this.forward(args, 'warn', '', true);
+ }
+ error() {
+ for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+ args[_key3] = arguments[_key3];
+ }
+ return this.forward(args, 'error', '');
+ }
+ deprecate() {
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
+ args[_key4] = arguments[_key4];
+ }
+ return this.forward(args, 'warn', 'WARNING DEPRECATED: ', true);
+ }
+ forward(args, lvl, prefix, debugOnly) {
+ if (debugOnly && !this.debug) return null;
+ if (typeof args[0] === 'string') args[0] = `${prefix}${this.prefix} ${args[0]}`;
+ return this.logger[lvl](args);
+ }
+ create(moduleName) {
+ return new Logger(this.logger, {
+ ...{
+ prefix: `${this.prefix}:${moduleName}:`
+ },
+ ...this.options
+ });
+ }
+ clone(options) {
+ options = options || this.options;
+ options.prefix = options.prefix || this.prefix;
+ return new Logger(this.logger, options);
+ }
+}
+var baseLogger = new Logger();
+class EventEmitter {
+ constructor() {
+ this.observers = {};
+ }
+ on(events, listener) {
+ events.split(' ').forEach(event => {
+ this.observers[event] = this.observers[event] || [];
+ this.observers[event].push(listener);
+ });
+ return this;
+ }
+ off(event, listener) {
+ if (!this.observers[event]) return;
+ if (!listener) {
+ delete this.observers[event];
+ return;
+ }
+ this.observers[event] = this.observers[event].filter(l => l !== listener);
+ }
+ emit(event) {
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+ if (this.observers[event]) {
+ const cloned = [].concat(this.observers[event]);
+ cloned.forEach(observer => {
+ observer(...args);
+ });
+ }
+ if (this.observers['*']) {
+ const cloned = [].concat(this.observers['*']);
+ cloned.forEach(observer => {
+ observer.apply(observer, [event, ...args]);
+ });
+ }
+ }
+}
+function defer$1() {
+ let res;
+ let rej;
+ const promise = new Promise((resolve, reject) => {
+ res = resolve;
+ rej = reject;
+ });
+ promise.resolve = res;
+ promise.reject = rej;
+ return promise;
+}
+function makeString(object) {
+ if (object == null) return '';
+ return '' + object;
+}
+function copy(a, s, t) {
+ a.forEach(m => {
+ if (s[m]) t[m] = s[m];
+ });
+}
+function getLastOfPath(object, path, Empty) {
+ function cleanKey(key) {
+ return key && key.indexOf('###') > -1 ? key.replace(/###/g, '.') : key;
+ }
+ function canNotTraverseDeeper() {
+ return !object || typeof object === 'string';
+ }
+ const stack = typeof path !== 'string' ? [].concat(path) : path.split('.');
+ while (stack.length > 1) {
+ if (canNotTraverseDeeper()) return {};
+ const key = cleanKey(stack.shift());
+ if (!object[key] && Empty) object[key] = new Empty();
+ if (Object.prototype.hasOwnProperty.call(object, key)) {
+ object = object[key];
+ } else {
+ object = {};
+ }
+ }
+ if (canNotTraverseDeeper()) return {};
+ return {
+ obj: object,
+ k: cleanKey(stack.shift())
+ };
+}
+function setPath(object, path, newValue) {
+ const {
+ obj,
+ k
+ } = getLastOfPath(object, path, Object);
+ obj[k] = newValue;
+}
+function pushPath(object, path, newValue, concat) {
+ const {
+ obj,
+ k
+ } = getLastOfPath(object, path, Object);
+ obj[k] = obj[k] || [];
+ if (concat) obj[k] = obj[k].concat(newValue);
+ if (!concat) obj[k].push(newValue);
+}
+function getPath(object, path) {
+ const {
+ obj,
+ k
+ } = getLastOfPath(object, path);
+ if (!obj) return undefined;
+ return obj[k];
+}
+function getPathWithDefaults(data, defaultData, key) {
+ const value = getPath(data, key);
+ if (value !== undefined) {
+ return value;
+ }
+ return getPath(defaultData, key);
+}
+function deepExtend(target, source, overwrite) {
+ for (const prop in source) {
+ if (prop !== '__proto__' && prop !== 'constructor') {
+ if (prop in target) {
+ if (typeof target[prop] === 'string' || target[prop] instanceof String || typeof source[prop] === 'string' || source[prop] instanceof String) {
+ if (overwrite) target[prop] = source[prop];
+ } else {
+ deepExtend(target[prop], source[prop], overwrite);
+ }
+ } else {
+ target[prop] = source[prop];
+ }
+ }
+ }
+ return target;
+}
+function regexEscape$1(str) {
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
+}
+var _entityMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '/': '/'
+};
+function escape$1(data) {
+ if (typeof data === 'string') {
+ return data.replace(/[&<>"'\/]/g, s => _entityMap[s]);
+ }
+ return data;
+}
+const chars = [' ', ',', '?', '!', ';'];
+function looksLikeObjectPath(key, nsSeparator, keySeparator) {
+ nsSeparator = nsSeparator || '';
+ keySeparator = keySeparator || '';
+ const possibleChars = chars.filter(c => nsSeparator.indexOf(c) < 0 && keySeparator.indexOf(c) < 0);
+ if (possibleChars.length === 0) return true;
+ const r = new RegExp(`(${possibleChars.map(c => c === '?' ? '\\?' : c).join('|')})`);
+ let matched = !r.test(key);
+ if (!matched) {
+ const ki = key.indexOf(keySeparator);
+ if (ki > 0 && !r.test(key.substring(0, ki))) {
+ matched = true;
+ }
+ }
+ return matched;
+}
+function deepFind(obj, path) {
+ let keySeparator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '.';
+ if (!obj) return undefined;
+ if (obj[path]) return obj[path];
+ const paths = path.split(keySeparator);
+ let current = obj;
+ for (let i = 0; i < paths.length; ++i) {
+ if (!current) return undefined;
+ if (typeof current[paths[i]] === 'string' && i + 1 < paths.length) {
+ return undefined;
+ }
+ if (current[paths[i]] === undefined) {
+ let j = 2;
+ let p = paths.slice(i, i + j).join(keySeparator);
+ let mix = current[p];
+ while (mix === undefined && paths.length > i + j) {
+ j++;
+ p = paths.slice(i, i + j).join(keySeparator);
+ mix = current[p];
+ }
+ if (mix === undefined) return undefined;
+ if (mix === null) return null;
+ if (path.endsWith(p)) {
+ if (typeof mix === 'string') return mix;
+ if (p && typeof mix[p] === 'string') return mix[p];
+ }
+ const joinedPath = paths.slice(i + j).join(keySeparator);
+ if (joinedPath) return deepFind(mix, joinedPath, keySeparator);
+ return undefined;
+ }
+ current = current[paths[i]];
+ }
+ return current;
+}
+function getCleanedCode(code) {
+ if (code && code.indexOf('_') > 0) return code.replace('_', '-');
+ return code;
+}
+class ResourceStore extends EventEmitter {
+ constructor(data) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+ ns: ['translation'],
+ defaultNS: 'translation'
+ };
+ super();
+ this.data = data || {};
+ this.options = options;
+ if (this.options.keySeparator === undefined) {
+ this.options.keySeparator = '.';
+ }
+ if (this.options.ignoreJSONStructure === undefined) {
+ this.options.ignoreJSONStructure = true;
+ }
+ }
+ addNamespaces(ns) {
+ if (this.options.ns.indexOf(ns) < 0) {
+ this.options.ns.push(ns);
+ }
+ }
+ removeNamespaces(ns) {
+ const index = this.options.ns.indexOf(ns);
+ if (index > -1) {
+ this.options.ns.splice(index, 1);
+ }
+ }
+ getResource(lng, ns, key) {
+ let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+ const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
+ const ignoreJSONStructure = options.ignoreJSONStructure !== undefined ? options.ignoreJSONStructure : this.options.ignoreJSONStructure;
+ let path = [lng, ns];
+ if (key && typeof key !== 'string') path = path.concat(key);
+ if (key && typeof key === 'string') path = path.concat(keySeparator ? key.split(keySeparator) : key);
+ if (lng.indexOf('.') > -1) {
+ path = lng.split('.');
+ }
+ const result = getPath(this.data, path);
+ if (result || !ignoreJSONStructure || typeof key !== 'string') return result;
+ return deepFind(this.data && this.data[lng] && this.data[lng][ns], key, keySeparator);
+ }
+ addResource(lng, ns, key, value) {
+ let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {
+ silent: false
+ };
+ const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
+ let path = [lng, ns];
+ if (key) path = path.concat(keySeparator ? key.split(keySeparator) : key);
+ if (lng.indexOf('.') > -1) {
+ path = lng.split('.');
+ value = ns;
+ ns = path[1];
+ }
+ this.addNamespaces(ns);
+ setPath(this.data, path, value);
+ if (!options.silent) this.emit('added', lng, ns, key, value);
+ }
+ addResources(lng, ns, resources) {
+ let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
+ silent: false
+ };
+ for (const m in resources) {
+ if (typeof resources[m] === 'string' || Object.prototype.toString.apply(resources[m]) === '[object Array]') this.addResource(lng, ns, m, resources[m], {
+ silent: true
+ });
+ }
+ if (!options.silent) this.emit('added', lng, ns, resources);
+ }
+ addResourceBundle(lng, ns, resources, deep, overwrite) {
+ let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {
+ silent: false
+ };
+ let path = [lng, ns];
+ if (lng.indexOf('.') > -1) {
+ path = lng.split('.');
+ deep = resources;
+ resources = ns;
+ ns = path[1];
+ }
+ this.addNamespaces(ns);
+ let pack = getPath(this.data, path) || {};
+ if (deep) {
+ deepExtend(pack, resources, overwrite);
+ } else {
+ pack = {
+ ...pack,
+ ...resources
+ };
+ }
+ setPath(this.data, path, pack);
+ if (!options.silent) this.emit('added', lng, ns, resources);
+ }
+ removeResourceBundle(lng, ns) {
+ if (this.hasResourceBundle(lng, ns)) {
+ delete this.data[lng][ns];
+ }
+ this.removeNamespaces(ns);
+ this.emit('removed', lng, ns);
+ }
+ hasResourceBundle(lng, ns) {
+ return this.getResource(lng, ns) !== undefined;
+ }
+ getResourceBundle(lng, ns) {
+ if (!ns) ns = this.options.defaultNS;
+ if (this.options.compatibilityAPI === 'v1') return {
+ ...{},
+ ...this.getResource(lng, ns)
+ };
+ return this.getResource(lng, ns);
+ }
+ getDataByLanguage(lng) {
+ return this.data[lng];
+ }
+ hasLanguageSomeTranslations(lng) {
+ const data = this.getDataByLanguage(lng);
+ const n = data && Object.keys(data) || [];
+ return !!n.find(v => data[v] && Object.keys(data[v]).length > 0);
+ }
+ toJSON() {
+ return this.data;
+ }
+}
+var postProcessor = {
+ processors: {},
+ addPostProcessor(module) {
+ this.processors[module.name] = module;
+ },
+ handle(processors, value, key, options, translator) {
+ processors.forEach(processor => {
+ if (this.processors[processor]) value = this.processors[processor].process(value, key, options, translator);
+ });
+ return value;
+ }
+};
+const checkedLoadedFor = {};
+class Translator extends EventEmitter {
+ constructor(services) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ super();
+ copy(['resourceStore', 'languageUtils', 'pluralResolver', 'interpolator', 'backendConnector', 'i18nFormat', 'utils'], services, this);
+ this.options = options;
+ if (this.options.keySeparator === undefined) {
+ this.options.keySeparator = '.';
+ }
+ this.logger = baseLogger.create('translator');
+ }
+ changeLanguage(lng) {
+ if (lng) this.language = lng;
+ }
+ exists(key) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+ interpolation: {}
+ };
+ if (key === undefined || key === null) {
+ return false;
+ }
+ const resolved = this.resolve(key, options);
+ return resolved && resolved.res !== undefined;
+ }
+ extractFromKey(key, options) {
+ let nsSeparator = options.nsSeparator !== undefined ? options.nsSeparator : this.options.nsSeparator;
+ if (nsSeparator === undefined) nsSeparator = ':';
+ const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
+ let namespaces = options.ns || this.options.defaultNS || [];
+ const wouldCheckForNsInKey = nsSeparator && key.indexOf(nsSeparator) > -1;
+ const seemsNaturalLanguage = !this.options.userDefinedKeySeparator && !options.keySeparator && !this.options.userDefinedNsSeparator && !options.nsSeparator && !looksLikeObjectPath(key, nsSeparator, keySeparator);
+ if (wouldCheckForNsInKey && !seemsNaturalLanguage) {
+ const m = key.match(this.interpolator.nestingRegexp);
+ if (m && m.length > 0) {
+ return {
+ key,
+ namespaces
+ };
+ }
+ const parts = key.split(nsSeparator);
+ if (nsSeparator !== keySeparator || nsSeparator === keySeparator && this.options.ns.indexOf(parts[0]) > -1) namespaces = parts.shift();
+ key = parts.join(keySeparator);
+ }
+ if (typeof namespaces === 'string') namespaces = [namespaces];
+ return {
+ key,
+ namespaces
+ };
+ }
+ translate(keys, options, lastKey) {
+ if (typeof options !== 'object' && this.options.overloadTranslationOptionHandler) {
+ options = this.options.overloadTranslationOptionHandler(arguments);
+ }
+ if (typeof options === 'object') options = {
+ ...options
+ };
+ if (!options) options = {};
+ if (keys === undefined || keys === null) return '';
+ if (!Array.isArray(keys)) keys = [String(keys)];
+ const returnDetails = options.returnDetails !== undefined ? options.returnDetails : this.options.returnDetails;
+ const keySeparator = options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
+ const {
+ key,
+ namespaces
+ } = this.extractFromKey(keys[keys.length - 1], options);
+ const namespace = namespaces[namespaces.length - 1];
+ const lng = options.lng || this.language;
+ const appendNamespaceToCIMode = options.appendNamespaceToCIMode || this.options.appendNamespaceToCIMode;
+ if (lng && lng.toLowerCase() === 'cimode') {
+ if (appendNamespaceToCIMode) {
+ const nsSeparator = options.nsSeparator || this.options.nsSeparator;
+ if (returnDetails) {
+ return {
+ res: `${namespace}${nsSeparator}${key}`,
+ usedKey: key,
+ exactUsedKey: key,
+ usedLng: lng,
+ usedNS: namespace,
+ usedParams: this.getUsedParamsDetails(options)
+ };
+ }
+ return `${namespace}${nsSeparator}${key}`;
+ }
+ if (returnDetails) {
+ return {
+ res: key,
+ usedKey: key,
+ exactUsedKey: key,
+ usedLng: lng,
+ usedNS: namespace,
+ usedParams: this.getUsedParamsDetails(options)
+ };
+ }
+ return key;
+ }
+ const resolved = this.resolve(keys, options);
+ let res = resolved && resolved.res;
+ const resUsedKey = resolved && resolved.usedKey || key;
+ const resExactUsedKey = resolved && resolved.exactUsedKey || key;
+ const resType = Object.prototype.toString.apply(res);
+ const noObject = ['[object Number]', '[object Function]', '[object RegExp]'];
+ const joinArrays = options.joinArrays !== undefined ? options.joinArrays : this.options.joinArrays;
+ const handleAsObjectInI18nFormat = !this.i18nFormat || this.i18nFormat.handleAsObject;
+ const handleAsObject = typeof res !== 'string' && typeof res !== 'boolean' && typeof res !== 'number';
+ if (handleAsObjectInI18nFormat && res && handleAsObject && noObject.indexOf(resType) < 0 && !(typeof joinArrays === 'string' && resType === '[object Array]')) {
+ if (!options.returnObjects && !this.options.returnObjects) {
+ if (!this.options.returnedObjectHandler) {
+ this.logger.warn('accessing an object - but returnObjects options is not enabled!');
+ }
+ const r = this.options.returnedObjectHandler ? this.options.returnedObjectHandler(resUsedKey, res, {
+ ...options,
+ ns: namespaces
+ }) : `key '${key} (${this.language})' returned an object instead of string.`;
+ if (returnDetails) {
+ resolved.res = r;
+ resolved.usedParams = this.getUsedParamsDetails(options);
+ return resolved;
+ }
+ return r;
+ }
+ if (keySeparator) {
+ const resTypeIsArray = resType === '[object Array]';
+ const copy = resTypeIsArray ? [] : {};
+ const newKeyToUse = resTypeIsArray ? resExactUsedKey : resUsedKey;
+ for (const m in res) {
+ if (Object.prototype.hasOwnProperty.call(res, m)) {
+ const deepKey = `${newKeyToUse}${keySeparator}${m}`;
+ copy[m] = this.translate(deepKey, {
+ ...options,
+ ...{
+ joinArrays: false,
+ ns: namespaces
+ }
+ });
+ if (copy[m] === deepKey) copy[m] = res[m];
+ }
+ }
+ res = copy;
+ }
+ } else if (handleAsObjectInI18nFormat && typeof joinArrays === 'string' && resType === '[object Array]') {
+ res = res.join(joinArrays);
+ if (res) res = this.extendTranslation(res, keys, options, lastKey);
+ } else {
+ let usedDefault = false;
+ let usedKey = false;
+ const needsPluralHandling = options.count !== undefined && typeof options.count !== 'string';
+ const hasDefaultValue = Translator.hasDefaultValue(options);
+ const defaultValueSuffix = needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, options) : '';
+ const defaultValueSuffixOrdinalFallback = options.ordinal && needsPluralHandling ? this.pluralResolver.getSuffix(lng, options.count, {
+ ordinal: false
+ }) : '';
+ const defaultValue = options[`defaultValue${defaultValueSuffix}`] || options[`defaultValue${defaultValueSuffixOrdinalFallback}`] || options.defaultValue;
+ if (!this.isValidLookup(res) && hasDefaultValue) {
+ usedDefault = true;
+ res = defaultValue;
+ }
+ if (!this.isValidLookup(res)) {
+ usedKey = true;
+ res = key;
+ }
+ const missingKeyNoValueFallbackToKey = options.missingKeyNoValueFallbackToKey || this.options.missingKeyNoValueFallbackToKey;
+ const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? undefined : res;
+ const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing;
+ if (usedKey || usedDefault || updateMissing) {
+ this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? defaultValue : res);
+ if (keySeparator) {
+ const fk = this.resolve(key, {
+ ...options,
+ keySeparator: false
+ });
+ if (fk && fk.res) this.logger.warn('Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.');
+ }
+ let lngs = [];
+ const fallbackLngs = this.languageUtils.getFallbackCodes(this.options.fallbackLng, options.lng || this.language);
+ if (this.options.saveMissingTo === 'fallback' && fallbackLngs && fallbackLngs[0]) {
+ for (let i = 0; i < fallbackLngs.length; i++) {
+ lngs.push(fallbackLngs[i]);
+ }
+ } else if (this.options.saveMissingTo === 'all') {
+ lngs = this.languageUtils.toResolveHierarchy(options.lng || this.language);
+ } else {
+ lngs.push(options.lng || this.language);
+ }
+ const send = (l, k, specificDefaultValue) => {
+ const defaultForMissing = hasDefaultValue && specificDefaultValue !== res ? specificDefaultValue : resForMissing;
+ if (this.options.missingKeyHandler) {
+ this.options.missingKeyHandler(l, namespace, k, defaultForMissing, updateMissing, options);
+ } else if (this.backendConnector && this.backendConnector.saveMissing) {
+ this.backendConnector.saveMissing(l, namespace, k, defaultForMissing, updateMissing, options);
+ }
+ this.emit('missingKey', l, namespace, k, res);
+ };
+ if (this.options.saveMissing) {
+ if (this.options.saveMissingPlurals && needsPluralHandling) {
+ lngs.forEach(language => {
+ this.pluralResolver.getSuffixes(language, options).forEach(suffix => {
+ send([language], key + suffix, options[`defaultValue${suffix}`] || defaultValue);
+ });
+ });
+ } else {
+ send(lngs, key, defaultValue);
+ }
+ }
+ }
+ res = this.extendTranslation(res, keys, options, resolved, lastKey);
+ if (usedKey && res === key && this.options.appendNamespaceToMissingKey) res = `${namespace}:${key}`;
+ if ((usedKey || usedDefault) && this.options.parseMissingKeyHandler) {
+ if (this.options.compatibilityAPI !== 'v1') {
+ res = this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey ? `${namespace}:${key}` : key, usedDefault ? res : undefined);
+ } else {
+ res = this.options.parseMissingKeyHandler(res);
+ }
+ }
+ }
+ if (returnDetails) {
+ resolved.res = res;
+ resolved.usedParams = this.getUsedParamsDetails(options);
+ return resolved;
+ }
+ return res;
+ }
+ extendTranslation(res, key, options, resolved, lastKey) {
+ var _this = this;
+ if (this.i18nFormat && this.i18nFormat.parse) {
+ res = this.i18nFormat.parse(res, {
+ ...this.options.interpolation.defaultVariables,
+ ...options
+ }, options.lng || this.language || resolved.usedLng, resolved.usedNS, resolved.usedKey, {
+ resolved
+ });
+ } else if (!options.skipInterpolation) {
+ if (options.interpolation) this.interpolator.init({
+ ...options,
+ ...{
+ interpolation: {
+ ...this.options.interpolation,
+ ...options.interpolation
+ }
+ }
+ });
+ const skipOnVariables = typeof res === 'string' && (options && options.interpolation && options.interpolation.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables);
+ let nestBef;
+ if (skipOnVariables) {
+ const nb = res.match(this.interpolator.nestingRegexp);
+ nestBef = nb && nb.length;
+ }
+ let data = options.replace && typeof options.replace !== 'string' ? options.replace : options;
+ if (this.options.interpolation.defaultVariables) data = {
+ ...this.options.interpolation.defaultVariables,
+ ...data
+ };
+ res = this.interpolator.interpolate(res, data, options.lng || this.language, options);
+ if (skipOnVariables) {
+ const na = res.match(this.interpolator.nestingRegexp);
+ const nestAft = na && na.length;
+ if (nestBef < nestAft) options.nest = false;
+ }
+ if (!options.lng && this.options.compatibilityAPI !== 'v1' && resolved && resolved.res) options.lng = resolved.usedLng;
+ if (options.nest !== false) res = this.interpolator.nest(res, function () {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+ if (lastKey && lastKey[0] === args[0] && !options.context) {
+ _this.logger.warn(`It seems you are nesting recursively key: ${args[0]} in key: ${key[0]}`);
+ return null;
+ }
+ return _this.translate(...args, key);
+ }, options);
+ if (options.interpolation) this.interpolator.reset();
+ }
+ const postProcess = options.postProcess || this.options.postProcess;
+ const postProcessorNames = typeof postProcess === 'string' ? [postProcess] : postProcess;
+ if (res !== undefined && res !== null && postProcessorNames && postProcessorNames.length && options.applyPostProcessor !== false) {
+ res = postProcessor.handle(postProcessorNames, res, key, this.options && this.options.postProcessPassResolved ? {
+ i18nResolved: {
+ ...resolved,
+ usedParams: this.getUsedParamsDetails(options)
+ },
+ ...options
+ } : options, this);
+ }
+ return res;
+ }
+ resolve(keys) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ let found;
+ let usedKey;
+ let exactUsedKey;
+ let usedLng;
+ let usedNS;
+ if (typeof keys === 'string') keys = [keys];
+ keys.forEach(k => {
+ if (this.isValidLookup(found)) return;
+ const extracted = this.extractFromKey(k, options);
+ const key = extracted.key;
+ usedKey = key;
+ let namespaces = extracted.namespaces;
+ if (this.options.fallbackNS) namespaces = namespaces.concat(this.options.fallbackNS);
+ const needsPluralHandling = options.count !== undefined && typeof options.count !== 'string';
+ const needsZeroSuffixLookup = needsPluralHandling && !options.ordinal && options.count === 0 && this.pluralResolver.shouldUseIntlApi();
+ const needsContextHandling = options.context !== undefined && (typeof options.context === 'string' || typeof options.context === 'number') && options.context !== '';
+ const codes = options.lngs ? options.lngs : this.languageUtils.toResolveHierarchy(options.lng || this.language, options.fallbackLng);
+ namespaces.forEach(ns => {
+ if (this.isValidLookup(found)) return;
+ usedNS = ns;
+ if (!checkedLoadedFor[`${codes[0]}-${ns}`] && this.utils && this.utils.hasLoadedNamespace && !this.utils.hasLoadedNamespace(usedNS)) {
+ checkedLoadedFor[`${codes[0]}-${ns}`] = true;
+ this.logger.warn(`key "${usedKey}" for languages "${codes.join(', ')}" won't get resolved as namespace "${usedNS}" was not yet loaded`, 'This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!');
+ }
+ codes.forEach(code => {
+ if (this.isValidLookup(found)) return;
+ usedLng = code;
+ const finalKeys = [key];
+ if (this.i18nFormat && this.i18nFormat.addLookupKeys) {
+ this.i18nFormat.addLookupKeys(finalKeys, key, code, ns, options);
+ } else {
+ let pluralSuffix;
+ if (needsPluralHandling) pluralSuffix = this.pluralResolver.getSuffix(code, options.count, options);
+ const zeroSuffix = `${this.options.pluralSeparator}zero`;
+ const ordinalPrefix = `${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;
+ if (needsPluralHandling) {
+ finalKeys.push(key + pluralSuffix);
+ if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) {
+ finalKeys.push(key + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator));
+ }
+ if (needsZeroSuffixLookup) {
+ finalKeys.push(key + zeroSuffix);
+ }
+ }
+ if (needsContextHandling) {
+ const contextKey = `${key}${this.options.contextSeparator}${options.context}`;
+ finalKeys.push(contextKey);
+ if (needsPluralHandling) {
+ finalKeys.push(contextKey + pluralSuffix);
+ if (options.ordinal && pluralSuffix.indexOf(ordinalPrefix) === 0) {
+ finalKeys.push(contextKey + pluralSuffix.replace(ordinalPrefix, this.options.pluralSeparator));
+ }
+ if (needsZeroSuffixLookup) {
+ finalKeys.push(contextKey + zeroSuffix);
+ }
+ }
+ }
+ }
+ let possibleKey;
+ while (possibleKey = finalKeys.pop()) {
+ if (!this.isValidLookup(found)) {
+ exactUsedKey = possibleKey;
+ found = this.getResource(code, ns, possibleKey, options);
+ }
+ }
+ });
+ });
+ });
+ return {
+ res: found,
+ usedKey,
+ exactUsedKey,
+ usedLng,
+ usedNS
+ };
+ }
+ isValidLookup(res) {
+ return res !== undefined && !(!this.options.returnNull && res === null) && !(!this.options.returnEmptyString && res === '');
+ }
+ getResource(code, ns, key) {
+ let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+ if (this.i18nFormat && this.i18nFormat.getResource) return this.i18nFormat.getResource(code, ns, key, options);
+ return this.resourceStore.getResource(code, ns, key, options);
+ }
+ getUsedParamsDetails() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ const optionsKeys = ['defaultValue', 'ordinal', 'context', 'replace', 'lng', 'lngs', 'fallbackLng', 'ns', 'keySeparator', 'nsSeparator', 'returnObjects', 'returnDetails', 'joinArrays', 'postProcess', 'interpolation'];
+ const useOptionsReplaceForData = options.replace && typeof options.replace !== 'string';
+ let data = useOptionsReplaceForData ? options.replace : options;
+ if (useOptionsReplaceForData && typeof options.count !== 'undefined') {
+ data.count = options.count;
+ }
+ if (this.options.interpolation.defaultVariables) {
+ data = {
+ ...this.options.interpolation.defaultVariables,
+ ...data
+ };
+ }
+ if (!useOptionsReplaceForData) {
+ data = {
+ ...data
+ };
+ for (const key of optionsKeys) {
+ delete data[key];
+ }
+ }
+ return data;
+ }
+ static hasDefaultValue(options) {
+ const prefix = 'defaultValue';
+ for (const option in options) {
+ if (Object.prototype.hasOwnProperty.call(options, option) && prefix === option.substring(0, prefix.length) && undefined !== options[option]) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+function capitalize(string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+}
+class LanguageUtil {
+ constructor(options) {
+ this.options = options;
+ this.supportedLngs = this.options.supportedLngs || false;
+ this.logger = baseLogger.create('languageUtils');
+ }
+ getScriptPartFromCode(code) {
+ code = getCleanedCode(code);
+ if (!code || code.indexOf('-') < 0) return null;
+ const p = code.split('-');
+ if (p.length === 2) return null;
+ p.pop();
+ if (p[p.length - 1].toLowerCase() === 'x') return null;
+ return this.formatLanguageCode(p.join('-'));
+ }
+ getLanguagePartFromCode(code) {
+ code = getCleanedCode(code);
+ if (!code || code.indexOf('-') < 0) return code;
+ const p = code.split('-');
+ return this.formatLanguageCode(p[0]);
+ }
+ formatLanguageCode(code) {
+ if (typeof code === 'string' && code.indexOf('-') > -1) {
+ const specialCases = ['hans', 'hant', 'latn', 'cyrl', 'cans', 'mong', 'arab'];
+ let p = code.split('-');
+ if (this.options.lowerCaseLng) {
+ p = p.map(part => part.toLowerCase());
+ } else if (p.length === 2) {
+ p[0] = p[0].toLowerCase();
+ p[1] = p[1].toUpperCase();
+ if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
+ } else if (p.length === 3) {
+ p[0] = p[0].toLowerCase();
+ if (p[1].length === 2) p[1] = p[1].toUpperCase();
+ if (p[0] !== 'sgn' && p[2].length === 2) p[2] = p[2].toUpperCase();
+ if (specialCases.indexOf(p[1].toLowerCase()) > -1) p[1] = capitalize(p[1].toLowerCase());
+ if (specialCases.indexOf(p[2].toLowerCase()) > -1) p[2] = capitalize(p[2].toLowerCase());
+ }
+ return p.join('-');
+ }
+ return this.options.cleanCode || this.options.lowerCaseLng ? code.toLowerCase() : code;
+ }
+ isSupportedCode(code) {
+ if (this.options.load === 'languageOnly' || this.options.nonExplicitSupportedLngs) {
+ code = this.getLanguagePartFromCode(code);
+ }
+ return !this.supportedLngs || !this.supportedLngs.length || this.supportedLngs.indexOf(code) > -1;
+ }
+ getBestMatchFromCodes(codes) {
+ if (!codes) return null;
+ let found;
+ codes.forEach(code => {
+ if (found) return;
+ const cleanedLng = this.formatLanguageCode(code);
+ if (!this.options.supportedLngs || this.isSupportedCode(cleanedLng)) found = cleanedLng;
+ });
+ if (!found && this.options.supportedLngs) {
+ codes.forEach(code => {
+ if (found) return;
+ const lngOnly = this.getLanguagePartFromCode(code);
+ if (this.isSupportedCode(lngOnly)) return found = lngOnly;
+ found = this.options.supportedLngs.find(supportedLng => {
+ if (supportedLng === lngOnly) return supportedLng;
+ if (supportedLng.indexOf('-') < 0 && lngOnly.indexOf('-') < 0) return;
+ if (supportedLng.indexOf(lngOnly) === 0) return supportedLng;
+ });
+ });
+ }
+ if (!found) found = this.getFallbackCodes(this.options.fallbackLng)[0];
+ return found;
+ }
+ getFallbackCodes(fallbacks, code) {
+ if (!fallbacks) return [];
+ if (typeof fallbacks === 'function') fallbacks = fallbacks(code);
+ if (typeof fallbacks === 'string') fallbacks = [fallbacks];
+ if (Object.prototype.toString.apply(fallbacks) === '[object Array]') return fallbacks;
+ if (!code) return fallbacks.default || [];
+ let found = fallbacks[code];
+ if (!found) found = fallbacks[this.getScriptPartFromCode(code)];
+ if (!found) found = fallbacks[this.formatLanguageCode(code)];
+ if (!found) found = fallbacks[this.getLanguagePartFromCode(code)];
+ if (!found) found = fallbacks.default;
+ return found || [];
+ }
+ toResolveHierarchy(code, fallbackCode) {
+ const fallbackCodes = this.getFallbackCodes(fallbackCode || this.options.fallbackLng || [], code);
+ const codes = [];
+ const addCode = c => {
+ if (!c) return;
+ if (this.isSupportedCode(c)) {
+ codes.push(c);
+ } else {
+ this.logger.warn(`rejecting language code not found in supportedLngs: ${c}`);
+ }
+ };
+ if (typeof code === 'string' && (code.indexOf('-') > -1 || code.indexOf('_') > -1)) {
+ if (this.options.load !== 'languageOnly') addCode(this.formatLanguageCode(code));
+ if (this.options.load !== 'languageOnly' && this.options.load !== 'currentOnly') addCode(this.getScriptPartFromCode(code));
+ if (this.options.load !== 'currentOnly') addCode(this.getLanguagePartFromCode(code));
+ } else if (typeof code === 'string') {
+ addCode(this.formatLanguageCode(code));
+ }
+ fallbackCodes.forEach(fc => {
+ if (codes.indexOf(fc) < 0) addCode(this.formatLanguageCode(fc));
+ });
+ return codes;
+ }
+}
+let sets = [{
+ lngs: ['ach', 'ak', 'am', 'arn', 'br', 'fil', 'gun', 'ln', 'mfe', 'mg', 'mi', 'oc', 'pt', 'pt-BR', 'tg', 'tl', 'ti', 'tr', 'uz', 'wa'],
+ nr: [1, 2],
+ fc: 1
+}, {
+ lngs: ['af', 'an', 'ast', 'az', 'bg', 'bn', 'ca', 'da', 'de', 'dev', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fi', 'fo', 'fur', 'fy', 'gl', 'gu', 'ha', 'hi', 'hu', 'hy', 'ia', 'it', 'kk', 'kn', 'ku', 'lb', 'mai', 'ml', 'mn', 'mr', 'nah', 'nap', 'nb', 'ne', 'nl', 'nn', 'no', 'nso', 'pa', 'pap', 'pms', 'ps', 'pt-PT', 'rm', 'sco', 'se', 'si', 'so', 'son', 'sq', 'sv', 'sw', 'ta', 'te', 'tk', 'ur', 'yo'],
+ nr: [1, 2],
+ fc: 2
+}, {
+ lngs: ['ay', 'bo', 'cgg', 'fa', 'ht', 'id', 'ja', 'jbo', 'ka', 'km', 'ko', 'ky', 'lo', 'ms', 'sah', 'su', 'th', 'tt', 'ug', 'vi', 'wo', 'zh'],
+ nr: [1],
+ fc: 3
+}, {
+ lngs: ['be', 'bs', 'cnr', 'dz', 'hr', 'ru', 'sr', 'uk'],
+ nr: [1, 2, 5],
+ fc: 4
+}, {
+ lngs: ['ar'],
+ nr: [0, 1, 2, 3, 11, 100],
+ fc: 5
+}, {
+ lngs: ['cs', 'sk'],
+ nr: [1, 2, 5],
+ fc: 6
+}, {
+ lngs: ['csb', 'pl'],
+ nr: [1, 2, 5],
+ fc: 7
+}, {
+ lngs: ['cy'],
+ nr: [1, 2, 3, 8],
+ fc: 8
+}, {
+ lngs: ['fr'],
+ nr: [1, 2],
+ fc: 9
+}, {
+ lngs: ['ga'],
+ nr: [1, 2, 3, 7, 11],
+ fc: 10
+}, {
+ lngs: ['gd'],
+ nr: [1, 2, 3, 20],
+ fc: 11
+}, {
+ lngs: ['is'],
+ nr: [1, 2],
+ fc: 12
+}, {
+ lngs: ['jv'],
+ nr: [0, 1],
+ fc: 13
+}, {
+ lngs: ['kw'],
+ nr: [1, 2, 3, 4],
+ fc: 14
+}, {
+ lngs: ['lt'],
+ nr: [1, 2, 10],
+ fc: 15
+}, {
+ lngs: ['lv'],
+ nr: [1, 2, 0],
+ fc: 16
+}, {
+ lngs: ['mk'],
+ nr: [1, 2],
+ fc: 17
+}, {
+ lngs: ['mnk'],
+ nr: [0, 1, 2],
+ fc: 18
+}, {
+ lngs: ['mt'],
+ nr: [1, 2, 11, 20],
+ fc: 19
+}, {
+ lngs: ['or'],
+ nr: [2, 1],
+ fc: 2
+}, {
+ lngs: ['ro'],
+ nr: [1, 2, 20],
+ fc: 20
+}, {
+ lngs: ['sl'],
+ nr: [5, 1, 2, 3],
+ fc: 21
+}, {
+ lngs: ['he', 'iw'],
+ nr: [1, 2, 20, 21],
+ fc: 22
+}];
+let _rulesPluralsTypes = {
+ 1: function (n) {
+ return Number(n > 1);
+ },
+ 2: function (n) {
+ return Number(n != 1);
+ },
+ 3: function (n) {
+ return 0;
+ },
+ 4: function (n) {
+ return Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
+ },
+ 5: function (n) {
+ return Number(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 ? 4 : 5);
+ },
+ 6: function (n) {
+ return Number(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2);
+ },
+ 7: function (n) {
+ return Number(n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
+ },
+ 8: function (n) {
+ return Number(n == 1 ? 0 : n == 2 ? 1 : n != 8 && n != 11 ? 2 : 3);
+ },
+ 9: function (n) {
+ return Number(n >= 2);
+ },
+ 10: function (n) {
+ return Number(n == 1 ? 0 : n == 2 ? 1 : n < 7 ? 2 : n < 11 ? 3 : 4);
+ },
+ 11: function (n) {
+ return Number(n == 1 || n == 11 ? 0 : n == 2 || n == 12 ? 1 : n > 2 && n < 20 ? 2 : 3);
+ },
+ 12: function (n) {
+ return Number(n % 10 != 1 || n % 100 == 11);
+ },
+ 13: function (n) {
+ return Number(n !== 0);
+ },
+ 14: function (n) {
+ return Number(n == 1 ? 0 : n == 2 ? 1 : n == 3 ? 2 : 3);
+ },
+ 15: function (n) {
+ return Number(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
+ },
+ 16: function (n) {
+ return Number(n % 10 == 1 && n % 100 != 11 ? 0 : n !== 0 ? 1 : 2);
+ },
+ 17: function (n) {
+ return Number(n == 1 || n % 10 == 1 && n % 100 != 11 ? 0 : 1);
+ },
+ 18: function (n) {
+ return Number(n == 0 ? 0 : n == 1 ? 1 : 2);
+ },
+ 19: function (n) {
+ return Number(n == 1 ? 0 : n == 0 || n % 100 > 1 && n % 100 < 11 ? 1 : n % 100 > 10 && n % 100 < 20 ? 2 : 3);
+ },
+ 20: function (n) {
+ return Number(n == 1 ? 0 : n == 0 || n % 100 > 0 && n % 100 < 20 ? 1 : 2);
+ },
+ 21: function (n) {
+ return Number(n % 100 == 1 ? 1 : n % 100 == 2 ? 2 : n % 100 == 3 || n % 100 == 4 ? 3 : 0);
+ },
+ 22: function (n) {
+ return Number(n == 1 ? 0 : n == 2 ? 1 : (n < 0 || n > 10) && n % 10 == 0 ? 2 : 3);
+ }
+};
+const nonIntlVersions = ['v1', 'v2', 'v3'];
+const intlVersions = ['v4'];
+const suffixesOrder = {
+ zero: 0,
+ one: 1,
+ two: 2,
+ few: 3,
+ many: 4,
+ other: 5
+};
+function createRules() {
+ const rules = {};
+ sets.forEach(set => {
+ set.lngs.forEach(l => {
+ rules[l] = {
+ numbers: set.nr,
+ plurals: _rulesPluralsTypes[set.fc]
+ };
+ });
+ });
+ return rules;
+}
+class PluralResolver {
+ constructor(languageUtils) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ this.languageUtils = languageUtils;
+ this.options = options;
+ this.logger = baseLogger.create('pluralResolver');
+ if ((!this.options.compatibilityJSON || intlVersions.includes(this.options.compatibilityJSON)) && (typeof Intl === 'undefined' || !Intl.PluralRules)) {
+ this.options.compatibilityJSON = 'v3';
+ this.logger.error('Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.');
+ }
+ this.rules = createRules();
+ }
+ addRule(lng, obj) {
+ this.rules[lng] = obj;
+ }
+ getRule(code) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ if (this.shouldUseIntlApi()) {
+ try {
+ return new Intl.PluralRules(getCleanedCode(code), {
+ type: options.ordinal ? 'ordinal' : 'cardinal'
+ });
+ } catch (err) {
+ return;
+ }
+ }
+ return this.rules[code] || this.rules[this.languageUtils.getLanguagePartFromCode(code)];
+ }
+ needsPlural(code) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ const rule = this.getRule(code, options);
+ if (this.shouldUseIntlApi()) {
+ return rule && rule.resolvedOptions().pluralCategories.length > 1;
+ }
+ return rule && rule.numbers.length > 1;
+ }
+ getPluralFormsOfKey(code, key) {
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ return this.getSuffixes(code, options).map(suffix => `${key}${suffix}`);
+ }
+ getSuffixes(code) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ const rule = this.getRule(code, options);
+ if (!rule) {
+ return [];
+ }
+ if (this.shouldUseIntlApi()) {
+ return rule.resolvedOptions().pluralCategories.sort((pluralCategory1, pluralCategory2) => suffixesOrder[pluralCategory1] - suffixesOrder[pluralCategory2]).map(pluralCategory => `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${pluralCategory}`);
+ }
+ return rule.numbers.map(number => this.getSuffix(code, number, options));
+ }
+ getSuffix(code, count) {
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ const rule = this.getRule(code, options);
+ if (rule) {
+ if (this.shouldUseIntlApi()) {
+ return `${this.options.prepend}${options.ordinal ? `ordinal${this.options.prepend}` : ''}${rule.select(count)}`;
+ }
+ return this.getSuffixRetroCompatible(rule, count);
+ }
+ this.logger.warn(`no plural rule found for: ${code}`);
+ return '';
+ }
+ getSuffixRetroCompatible(rule, count) {
+ const idx = rule.noAbs ? rule.plurals(count) : rule.plurals(Math.abs(count));
+ let suffix = rule.numbers[idx];
+ if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
+ if (suffix === 2) {
+ suffix = 'plural';
+ } else if (suffix === 1) {
+ suffix = '';
+ }
+ }
+ const returnSuffix = () => this.options.prepend && suffix.toString() ? this.options.prepend + suffix.toString() : suffix.toString();
+ if (this.options.compatibilityJSON === 'v1') {
+ if (suffix === 1) return '';
+ if (typeof suffix === 'number') return `_plural_${suffix.toString()}`;
+ return returnSuffix();
+ } else if (this.options.compatibilityJSON === 'v2') {
+ return returnSuffix();
+ } else if (this.options.simplifyPluralSuffix && rule.numbers.length === 2 && rule.numbers[0] === 1) {
+ return returnSuffix();
+ }
+ return this.options.prepend && idx.toString() ? this.options.prepend + idx.toString() : idx.toString();
+ }
+ shouldUseIntlApi() {
+ return !nonIntlVersions.includes(this.options.compatibilityJSON);
+ }
+}
+function deepFindWithDefaults(data, defaultData, key) {
+ let keySeparator = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '.';
+ let ignoreJSONStructure = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
+ let path = getPathWithDefaults(data, defaultData, key);
+ if (!path && ignoreJSONStructure && typeof key === 'string') {
+ path = deepFind(data, key, keySeparator);
+ if (path === undefined) path = deepFind(defaultData, key, keySeparator);
+ }
+ return path;
+}
+class Interpolator {
+ constructor() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ this.logger = baseLogger.create('interpolator');
+ this.options = options;
+ this.format = options.interpolation && options.interpolation.format || (value => value);
+ this.init(options);
+ }
+ init() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ if (!options.interpolation) options.interpolation = {
+ escapeValue: true
+ };
+ const iOpts = options.interpolation;
+ this.escape = iOpts.escape !== undefined ? iOpts.escape : escape$1;
+ this.escapeValue = iOpts.escapeValue !== undefined ? iOpts.escapeValue : true;
+ this.useRawValueToEscape = iOpts.useRawValueToEscape !== undefined ? iOpts.useRawValueToEscape : false;
+ this.prefix = iOpts.prefix ? regexEscape$1(iOpts.prefix) : iOpts.prefixEscaped || '{{';
+ this.suffix = iOpts.suffix ? regexEscape$1(iOpts.suffix) : iOpts.suffixEscaped || '}}';
+ this.formatSeparator = iOpts.formatSeparator ? iOpts.formatSeparator : iOpts.formatSeparator || ',';
+ this.unescapePrefix = iOpts.unescapeSuffix ? '' : iOpts.unescapePrefix || '-';
+ this.unescapeSuffix = this.unescapePrefix ? '' : iOpts.unescapeSuffix || '';
+ this.nestingPrefix = iOpts.nestingPrefix ? regexEscape$1(iOpts.nestingPrefix) : iOpts.nestingPrefixEscaped || regexEscape$1('$t(');
+ this.nestingSuffix = iOpts.nestingSuffix ? regexEscape$1(iOpts.nestingSuffix) : iOpts.nestingSuffixEscaped || regexEscape$1(')');
+ this.nestingOptionsSeparator = iOpts.nestingOptionsSeparator ? iOpts.nestingOptionsSeparator : iOpts.nestingOptionsSeparator || ',';
+ this.maxReplaces = iOpts.maxReplaces ? iOpts.maxReplaces : 1000;
+ this.alwaysFormat = iOpts.alwaysFormat !== undefined ? iOpts.alwaysFormat : false;
+ this.resetRegExp();
+ }
+ reset() {
+ if (this.options) this.init(this.options);
+ }
+ resetRegExp() {
+ const regexpStr = `${this.prefix}(.+?)${this.suffix}`;
+ this.regexp = new RegExp(regexpStr, 'g');
+ const regexpUnescapeStr = `${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`;
+ this.regexpUnescape = new RegExp(regexpUnescapeStr, 'g');
+ const nestingRegexpStr = `${this.nestingPrefix}(.+?)${this.nestingSuffix}`;
+ this.nestingRegexp = new RegExp(nestingRegexpStr, 'g');
+ }
+ interpolate(str, data, lng, options) {
+ let match;
+ let value;
+ let replaces;
+ const defaultData = this.options && this.options.interpolation && this.options.interpolation.defaultVariables || {};
+ function regexSafe(val) {
+ return val.replace(/\$/g, '$$$$');
+ }
+ const handleFormat = key => {
+ if (key.indexOf(this.formatSeparator) < 0) {
+ const path = deepFindWithDefaults(data, defaultData, key, this.options.keySeparator, this.options.ignoreJSONStructure);
+ return this.alwaysFormat ? this.format(path, undefined, lng, {
+ ...options,
+ ...data,
+ interpolationkey: key
+ }) : path;
+ }
+ const p = key.split(this.formatSeparator);
+ const k = p.shift().trim();
+ const f = p.join(this.formatSeparator).trim();
+ return this.format(deepFindWithDefaults(data, defaultData, k, this.options.keySeparator, this.options.ignoreJSONStructure), f, lng, {
+ ...options,
+ ...data,
+ interpolationkey: k
+ });
+ };
+ this.resetRegExp();
+ const missingInterpolationHandler = options && options.missingInterpolationHandler || this.options.missingInterpolationHandler;
+ const skipOnVariables = options && options.interpolation && options.interpolation.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables;
+ const todos = [{
+ regex: this.regexpUnescape,
+ safeValue: val => regexSafe(val)
+ }, {
+ regex: this.regexp,
+ safeValue: val => this.escapeValue ? regexSafe(this.escape(val)) : regexSafe(val)
+ }];
+ todos.forEach(todo => {
+ replaces = 0;
+ while (match = todo.regex.exec(str)) {
+ const matchedVar = match[1].trim();
+ value = handleFormat(matchedVar);
+ if (value === undefined) {
+ if (typeof missingInterpolationHandler === 'function') {
+ const temp = missingInterpolationHandler(str, match, options);
+ value = typeof temp === 'string' ? temp : '';
+ } else if (options && Object.prototype.hasOwnProperty.call(options, matchedVar)) {
+ value = '';
+ } else if (skipOnVariables) {
+ value = match[0];
+ continue;
+ } else {
+ this.logger.warn(`missed to pass in variable ${matchedVar} for interpolating ${str}`);
+ value = '';
+ }
+ } else if (typeof value !== 'string' && !this.useRawValueToEscape) {
+ value = makeString(value);
+ }
+ const safeValue = todo.safeValue(value);
+ str = str.replace(match[0], safeValue);
+ if (skipOnVariables) {
+ todo.regex.lastIndex += value.length;
+ todo.regex.lastIndex -= match[0].length;
+ } else {
+ todo.regex.lastIndex = 0;
+ }
+ replaces++;
+ if (replaces >= this.maxReplaces) {
+ break;
+ }
+ }
+ });
+ return str;
+ }
+ nest(str, fc) {
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ let match;
+ let value;
+ let clonedOptions;
+ function handleHasOptions(key, inheritedOptions) {
+ const sep = this.nestingOptionsSeparator;
+ if (key.indexOf(sep) < 0) return key;
+ const c = key.split(new RegExp(`${sep}[ ]*{`));
+ let optionsString = `{${c[1]}`;
+ key = c[0];
+ optionsString = this.interpolate(optionsString, clonedOptions);
+ const matchedSingleQuotes = optionsString.match(/'/g);
+ const matchedDoubleQuotes = optionsString.match(/"/g);
+ if (matchedSingleQuotes && matchedSingleQuotes.length % 2 === 0 && !matchedDoubleQuotes || matchedDoubleQuotes.length % 2 !== 0) {
+ optionsString = optionsString.replace(/'/g, '"');
+ }
+ try {
+ clonedOptions = JSON.parse(optionsString);
+ if (inheritedOptions) clonedOptions = {
+ ...inheritedOptions,
+ ...clonedOptions
+ };
+ } catch (e) {
+ this.logger.warn(`failed parsing options string in nesting for key ${key}`, e);
+ return `${key}${sep}${optionsString}`;
+ }
+ delete clonedOptions.defaultValue;
+ return key;
+ }
+ while (match = this.nestingRegexp.exec(str)) {
+ let formatters = [];
+ clonedOptions = {
+ ...options
+ };
+ clonedOptions = clonedOptions.replace && typeof clonedOptions.replace !== 'string' ? clonedOptions.replace : clonedOptions;
+ clonedOptions.applyPostProcessor = false;
+ delete clonedOptions.defaultValue;
+ let doReduce = false;
+ if (match[0].indexOf(this.formatSeparator) !== -1 && !/{.*}/.test(match[1])) {
+ const r = match[1].split(this.formatSeparator).map(elem => elem.trim());
+ match[1] = r.shift();
+ formatters = r;
+ doReduce = true;
+ }
+ value = fc(handleHasOptions.call(this, match[1].trim(), clonedOptions), clonedOptions);
+ if (value && match[0] === str && typeof value !== 'string') return value;
+ if (typeof value !== 'string') value = makeString(value);
+ if (!value) {
+ this.logger.warn(`missed to resolve ${match[1]} for nesting ${str}`);
+ value = '';
+ }
+ if (doReduce) {
+ value = formatters.reduce((v, f) => this.format(v, f, options.lng, {
+ ...options,
+ interpolationkey: match[1].trim()
+ }), value.trim());
+ }
+ str = str.replace(match[0], value);
+ this.regexp.lastIndex = 0;
+ }
+ return str;
+ }
+}
+function parseFormatStr(formatStr) {
+ let formatName = formatStr.toLowerCase().trim();
+ const formatOptions = {};
+ if (formatStr.indexOf('(') > -1) {
+ const p = formatStr.split('(');
+ formatName = p[0].toLowerCase().trim();
+ const optStr = p[1].substring(0, p[1].length - 1);
+ if (formatName === 'currency' && optStr.indexOf(':') < 0) {
+ if (!formatOptions.currency) formatOptions.currency = optStr.trim();
+ } else if (formatName === 'relativetime' && optStr.indexOf(':') < 0) {
+ if (!formatOptions.range) formatOptions.range = optStr.trim();
+ } else {
+ const opts = optStr.split(';');
+ opts.forEach(opt => {
+ if (!opt) return;
+ const [key, ...rest] = opt.split(':');
+ const val = rest.join(':').trim().replace(/^'+|'+$/g, '');
+ if (!formatOptions[key.trim()]) formatOptions[key.trim()] = val;
+ if (val === 'false') formatOptions[key.trim()] = false;
+ if (val === 'true') formatOptions[key.trim()] = true;
+ if (!isNaN(val)) formatOptions[key.trim()] = parseInt(val, 10);
+ });
+ }
+ }
+ return {
+ formatName,
+ formatOptions
+ };
+}
+function createCachedFormatter(fn) {
+ const cache = {};
+ return function invokeFormatter(val, lng, options) {
+ const key = lng + JSON.stringify(options);
+ let formatter = cache[key];
+ if (!formatter) {
+ formatter = fn(getCleanedCode(lng), options);
+ cache[key] = formatter;
+ }
+ return formatter(val);
+ };
+}
+class Formatter {
+ constructor() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ this.logger = baseLogger.create('formatter');
+ this.options = options;
+ this.formats = {
+ number: createCachedFormatter((lng, opt) => {
+ const formatter = new Intl.NumberFormat(lng, {
+ ...opt
+ });
+ return val => formatter.format(val);
+ }),
+ currency: createCachedFormatter((lng, opt) => {
+ const formatter = new Intl.NumberFormat(lng, {
+ ...opt,
+ style: 'currency'
+ });
+ return val => formatter.format(val);
+ }),
+ datetime: createCachedFormatter((lng, opt) => {
+ const formatter = new Intl.DateTimeFormat(lng, {
+ ...opt
+ });
+ return val => formatter.format(val);
+ }),
+ relativetime: createCachedFormatter((lng, opt) => {
+ const formatter = new Intl.RelativeTimeFormat(lng, {
+ ...opt
+ });
+ return val => formatter.format(val, opt.range || 'day');
+ }),
+ list: createCachedFormatter((lng, opt) => {
+ const formatter = new Intl.ListFormat(lng, {
+ ...opt
+ });
+ return val => formatter.format(val);
+ })
+ };
+ this.init(options);
+ }
+ init(services) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
+ interpolation: {}
+ };
+ const iOpts = options.interpolation;
+ this.formatSeparator = iOpts.formatSeparator ? iOpts.formatSeparator : iOpts.formatSeparator || ',';
+ }
+ add(name, fc) {
+ this.formats[name.toLowerCase().trim()] = fc;
+ }
+ addCached(name, fc) {
+ this.formats[name.toLowerCase().trim()] = createCachedFormatter(fc);
+ }
+ format(value, format, lng) {
+ let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+ const formats = format.split(this.formatSeparator);
+ const result = formats.reduce((mem, f) => {
+ const {
+ formatName,
+ formatOptions
+ } = parseFormatStr(f);
+ if (this.formats[formatName]) {
+ let formatted = mem;
+ try {
+ const valOptions = options && options.formatParams && options.formatParams[options.interpolationkey] || {};
+ const l = valOptions.locale || valOptions.lng || options.locale || options.lng || lng;
+ formatted = this.formats[formatName](mem, l, {
+ ...formatOptions,
+ ...options,
+ ...valOptions
+ });
+ } catch (error) {
+ this.logger.warn(error);
+ }
+ return formatted;
+ } else {
+ this.logger.warn(`there was no format function for ${formatName}`);
+ }
+ return mem;
+ }, value);
+ return result;
+ }
+}
+function removePending(q, name) {
+ if (q.pending[name] !== undefined) {
+ delete q.pending[name];
+ q.pendingCount--;
+ }
+}
+class Connector extends EventEmitter {
+ constructor(backend, store, services) {
+ let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+ super();
+ this.backend = backend;
+ this.store = store;
+ this.services = services;
+ this.languageUtils = services.languageUtils;
+ this.options = options;
+ this.logger = baseLogger.create('backendConnector');
+ this.waitingReads = [];
+ this.maxParallelReads = options.maxParallelReads || 10;
+ this.readingCalls = 0;
+ this.maxRetries = options.maxRetries >= 0 ? options.maxRetries : 5;
+ this.retryTimeout = options.retryTimeout >= 1 ? options.retryTimeout : 350;
+ this.state = {};
+ this.queue = [];
+ if (this.backend && this.backend.init) {
+ this.backend.init(services, options.backend, options);
+ }
+ }
+ queueLoad(languages, namespaces, options, callback) {
+ const toLoad = {};
+ const pending = {};
+ const toLoadLanguages = {};
+ const toLoadNamespaces = {};
+ languages.forEach(lng => {
+ let hasAllNamespaces = true;
+ namespaces.forEach(ns => {
+ const name = `${lng}|${ns}`;
+ if (!options.reload && this.store.hasResourceBundle(lng, ns)) {
+ this.state[name] = 2;
+ } else if (this.state[name] < 0) ;else if (this.state[name] === 1) {
+ if (pending[name] === undefined) pending[name] = true;
+ } else {
+ this.state[name] = 1;
+ hasAllNamespaces = false;
+ if (pending[name] === undefined) pending[name] = true;
+ if (toLoad[name] === undefined) toLoad[name] = true;
+ if (toLoadNamespaces[ns] === undefined) toLoadNamespaces[ns] = true;
+ }
+ });
+ if (!hasAllNamespaces) toLoadLanguages[lng] = true;
+ });
+ if (Object.keys(toLoad).length || Object.keys(pending).length) {
+ this.queue.push({
+ pending,
+ pendingCount: Object.keys(pending).length,
+ loaded: {},
+ errors: [],
+ callback
+ });
+ }
+ return {
+ toLoad: Object.keys(toLoad),
+ pending: Object.keys(pending),
+ toLoadLanguages: Object.keys(toLoadLanguages),
+ toLoadNamespaces: Object.keys(toLoadNamespaces)
+ };
+ }
+ loaded(name, err, data) {
+ const s = name.split('|');
+ const lng = s[0];
+ const ns = s[1];
+ if (err) this.emit('failedLoading', lng, ns, err);
+ if (data) {
+ this.store.addResourceBundle(lng, ns, data);
+ }
+ this.state[name] = err ? -1 : 2;
+ const loaded = {};
+ this.queue.forEach(q => {
+ pushPath(q.loaded, [lng], ns);
+ removePending(q, name);
+ if (err) q.errors.push(err);
+ if (q.pendingCount === 0 && !q.done) {
+ Object.keys(q.loaded).forEach(l => {
+ if (!loaded[l]) loaded[l] = {};
+ const loadedKeys = q.loaded[l];
+ if (loadedKeys.length) {
+ loadedKeys.forEach(n => {
+ if (loaded[l][n] === undefined) loaded[l][n] = true;
+ });
+ }
+ });
+ q.done = true;
+ if (q.errors.length) {
+ q.callback(q.errors);
+ } else {
+ q.callback();
+ }
+ }
+ });
+ this.emit('loaded', loaded);
+ this.queue = this.queue.filter(q => !q.done);
+ }
+ read(lng, ns, fcName) {
+ let tried = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
+ let wait = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : this.retryTimeout;
+ let callback = arguments.length > 5 ? arguments[5] : undefined;
+ if (!lng.length) return callback(null, {});
+ if (this.readingCalls >= this.maxParallelReads) {
+ this.waitingReads.push({
+ lng,
+ ns,
+ fcName,
+ tried,
+ wait,
+ callback
+ });
+ return;
+ }
+ this.readingCalls++;
+ const resolver = (err, data) => {
+ this.readingCalls--;
+ if (this.waitingReads.length > 0) {
+ const next = this.waitingReads.shift();
+ this.read(next.lng, next.ns, next.fcName, next.tried, next.wait, next.callback);
+ }
+ if (err && data && tried < this.maxRetries) {
+ setTimeout(() => {
+ this.read.call(this, lng, ns, fcName, tried + 1, wait * 2, callback);
+ }, wait);
+ return;
+ }
+ callback(err, data);
+ };
+ const fc = this.backend[fcName].bind(this.backend);
+ if (fc.length === 2) {
+ try {
+ const r = fc(lng, ns);
+ if (r && typeof r.then === 'function') {
+ r.then(data => resolver(null, data)).catch(resolver);
+ } else {
+ resolver(null, r);
+ }
+ } catch (err) {
+ resolver(err);
+ }
+ return;
+ }
+ return fc(lng, ns, resolver);
+ }
+ prepareLoading(languages, namespaces) {
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+ let callback = arguments.length > 3 ? arguments[3] : undefined;
+ if (!this.backend) {
+ this.logger.warn('No backend was added via i18next.use. Will not load resources.');
+ return callback && callback();
+ }
+ if (typeof languages === 'string') languages = this.languageUtils.toResolveHierarchy(languages);
+ if (typeof namespaces === 'string') namespaces = [namespaces];
+ const toLoad = this.queueLoad(languages, namespaces, options, callback);
+ if (!toLoad.toLoad.length) {
+ if (!toLoad.pending.length) callback();
+ return null;
+ }
+ toLoad.toLoad.forEach(name => {
+ this.loadOne(name);
+ });
+ }
+ load(languages, namespaces, callback) {
+ this.prepareLoading(languages, namespaces, {}, callback);
+ }
+ reload(languages, namespaces, callback) {
+ this.prepareLoading(languages, namespaces, {
+ reload: true
+ }, callback);
+ }
+ loadOne(name) {
+ let prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
+ const s = name.split('|');
+ const lng = s[0];
+ const ns = s[1];
+ this.read(lng, ns, 'read', undefined, undefined, (err, data) => {
+ if (err) this.logger.warn(`${prefix}loading namespace ${ns} for language ${lng} failed`, err);
+ if (!err && data) this.logger.log(`${prefix}loaded namespace ${ns} for language ${lng}`, data);
+ this.loaded(name, err, data);
+ });
+ }
+ saveMissing(languages, namespace, key, fallbackValue, isUpdate) {
+ let options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
+ let clb = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : () => {};
+ if (this.services.utils && this.services.utils.hasLoadedNamespace && !this.services.utils.hasLoadedNamespace(namespace)) {
+ this.logger.warn(`did not save key "${key}" as the namespace "${namespace}" was not yet loaded`, 'This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!');
+ return;
+ }
+ if (key === undefined || key === null || key === '') return;
+ if (this.backend && this.backend.create) {
+ const opts = {
+ ...options,
+ isUpdate
+ };
+ const fc = this.backend.create.bind(this.backend);
+ if (fc.length < 6) {
+ try {
+ let r;
+ if (fc.length === 5) {
+ r = fc(languages, namespace, key, fallbackValue, opts);
+ } else {
+ r = fc(languages, namespace, key, fallbackValue);
+ }
+ if (r && typeof r.then === 'function') {
+ r.then(data => clb(null, data)).catch(clb);
+ } else {
+ clb(null, r);
+ }
+ } catch (err) {
+ clb(err);
+ }
+ } else {
+ fc(languages, namespace, key, fallbackValue, clb, opts);
+ }
+ }
+ if (!languages || !languages[0]) return;
+ this.store.addResource(languages[0], namespace, key, fallbackValue);
+ }
+}
+function get$1() {
+ return {
+ debug: false,
+ initImmediate: true,
+ ns: ['translation'],
+ defaultNS: ['translation'],
+ fallbackLng: ['dev'],
+ fallbackNS: false,
+ supportedLngs: false,
+ nonExplicitSupportedLngs: false,
+ load: 'all',
+ preload: false,
+ simplifyPluralSuffix: true,
+ keySeparator: '.',
+ nsSeparator: ':',
+ pluralSeparator: '_',
+ contextSeparator: '_',
+ partialBundledLanguages: false,
+ saveMissing: false,
+ updateMissing: false,
+ saveMissingTo: 'fallback',
+ saveMissingPlurals: true,
+ missingKeyHandler: false,
+ missingInterpolationHandler: false,
+ postProcess: false,
+ postProcessPassResolved: false,
+ returnNull: false,
+ returnEmptyString: true,
+ returnObjects: false,
+ joinArrays: false,
+ returnedObjectHandler: false,
+ parseMissingKeyHandler: false,
+ appendNamespaceToMissingKey: false,
+ appendNamespaceToCIMode: false,
+ overloadTranslationOptionHandler: function handle(args) {
+ let ret = {};
+ if (typeof args[1] === 'object') ret = args[1];
+ if (typeof args[1] === 'string') ret.defaultValue = args[1];
+ if (typeof args[2] === 'string') ret.tDescription = args[2];
+ if (typeof args[2] === 'object' || typeof args[3] === 'object') {
+ const options = args[3] || args[2];
+ Object.keys(options).forEach(key => {
+ ret[key] = options[key];
+ });
+ }
+ return ret;
+ },
+ interpolation: {
+ escapeValue: true,
+ format: (value, format, lng, options) => value,
+ prefix: '{{',
+ suffix: '}}',
+ formatSeparator: ',',
+ unescapePrefix: '-',
+ nestingPrefix: '$t(',
+ nestingSuffix: ')',
+ nestingOptionsSeparator: ',',
+ maxReplaces: 1000,
+ skipOnVariables: true
+ }
+ };
+}
+function transformOptions(options) {
+ if (typeof options.ns === 'string') options.ns = [options.ns];
+ if (typeof options.fallbackLng === 'string') options.fallbackLng = [options.fallbackLng];
+ if (typeof options.fallbackNS === 'string') options.fallbackNS = [options.fallbackNS];
+ if (options.supportedLngs && options.supportedLngs.indexOf('cimode') < 0) {
+ options.supportedLngs = options.supportedLngs.concat(['cimode']);
+ }
+ return options;
+}
+function noop$2() {}
+function bindMemberFunctions(inst) {
+ const mems = Object.getOwnPropertyNames(Object.getPrototypeOf(inst));
+ mems.forEach(mem => {
+ if (typeof inst[mem] === 'function') {
+ inst[mem] = inst[mem].bind(inst);
+ }
+ });
+}
+class I18n extends EventEmitter {
+ constructor() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ let callback = arguments.length > 1 ? arguments[1] : undefined;
+ super();
+ this.options = transformOptions(options);
+ this.services = {};
+ this.logger = baseLogger;
+ this.modules = {
+ external: []
+ };
+ bindMemberFunctions(this);
+ if (callback && !this.isInitialized && !options.isClone) {
+ if (!this.options.initImmediate) {
+ this.init(options, callback);
+ return this;
+ }
+ setTimeout(() => {
+ this.init(options, callback);
+ }, 0);
+ }
+ }
+ init() {
+ var _this = this;
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ let callback = arguments.length > 1 ? arguments[1] : undefined;
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ if (!options.defaultNS && options.defaultNS !== false && options.ns) {
+ if (typeof options.ns === 'string') {
+ options.defaultNS = options.ns;
+ } else if (options.ns.indexOf('translation') < 0) {
+ options.defaultNS = options.ns[0];
+ }
+ }
+ const defOpts = get$1();
+ this.options = {
+ ...defOpts,
+ ...this.options,
+ ...transformOptions(options)
+ };
+ if (this.options.compatibilityAPI !== 'v1') {
+ this.options.interpolation = {
+ ...defOpts.interpolation,
+ ...this.options.interpolation
+ };
+ }
+ if (options.keySeparator !== undefined) {
+ this.options.userDefinedKeySeparator = options.keySeparator;
+ }
+ if (options.nsSeparator !== undefined) {
+ this.options.userDefinedNsSeparator = options.nsSeparator;
+ }
+ function createClassOnDemand(ClassOrObject) {
+ if (!ClassOrObject) return null;
+ if (typeof ClassOrObject === 'function') return new ClassOrObject();
+ return ClassOrObject;
+ }
+ if (!this.options.isClone) {
+ if (this.modules.logger) {
+ baseLogger.init(createClassOnDemand(this.modules.logger), this.options);
+ } else {
+ baseLogger.init(null, this.options);
+ }
+ let formatter;
+ if (this.modules.formatter) {
+ formatter = this.modules.formatter;
+ } else if (typeof Intl !== 'undefined') {
+ formatter = Formatter;
+ }
+ const lu = new LanguageUtil(this.options);
+ this.store = new ResourceStore(this.options.resources, this.options);
+ const s = this.services;
+ s.logger = baseLogger;
+ s.resourceStore = this.store;
+ s.languageUtils = lu;
+ s.pluralResolver = new PluralResolver(lu, {
+ prepend: this.options.pluralSeparator,
+ compatibilityJSON: this.options.compatibilityJSON,
+ simplifyPluralSuffix: this.options.simplifyPluralSuffix
+ });
+ if (formatter && (!this.options.interpolation.format || this.options.interpolation.format === defOpts.interpolation.format)) {
+ s.formatter = createClassOnDemand(formatter);
+ s.formatter.init(s, this.options);
+ this.options.interpolation.format = s.formatter.format.bind(s.formatter);
+ }
+ s.interpolator = new Interpolator(this.options);
+ s.utils = {
+ hasLoadedNamespace: this.hasLoadedNamespace.bind(this)
+ };
+ s.backendConnector = new Connector(createClassOnDemand(this.modules.backend), s.resourceStore, s, this.options);
+ s.backendConnector.on('*', function (event) {
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+ _this.emit(event, ...args);
+ });
+ if (this.modules.languageDetector) {
+ s.languageDetector = createClassOnDemand(this.modules.languageDetector);
+ if (s.languageDetector.init) s.languageDetector.init(s, this.options.detection, this.options);
+ }
+ if (this.modules.i18nFormat) {
+ s.i18nFormat = createClassOnDemand(this.modules.i18nFormat);
+ if (s.i18nFormat.init) s.i18nFormat.init(this);
+ }
+ this.translator = new Translator(this.services, this.options);
+ this.translator.on('*', function (event) {
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
+ args[_key2 - 1] = arguments[_key2];
+ }
+ _this.emit(event, ...args);
+ });
+ this.modules.external.forEach(m => {
+ if (m.init) m.init(this);
+ });
+ }
+ this.format = this.options.interpolation.format;
+ if (!callback) callback = noop$2;
+ if (this.options.fallbackLng && !this.services.languageDetector && !this.options.lng) {
+ const codes = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);
+ if (codes.length > 0 && codes[0] !== 'dev') this.options.lng = codes[0];
+ }
+ if (!this.services.languageDetector && !this.options.lng) {
+ this.logger.warn('init: no languageDetector is used and no lng is defined');
+ }
+ const storeApi = ['getResource', 'hasResourceBundle', 'getResourceBundle', 'getDataByLanguage'];
+ storeApi.forEach(fcName => {
+ this[fcName] = function () {
+ return _this.store[fcName](...arguments);
+ };
+ });
+ const storeApiChained = ['addResource', 'addResources', 'addResourceBundle', 'removeResourceBundle'];
+ storeApiChained.forEach(fcName => {
+ this[fcName] = function () {
+ _this.store[fcName](...arguments);
+ return _this;
+ };
+ });
+ const deferred = defer$1();
+ const load = () => {
+ const finish = (err, t) => {
+ if (this.isInitialized && !this.initializedStoreOnce) this.logger.warn('init: i18next is already initialized. You should call init just once!');
+ this.isInitialized = true;
+ if (!this.options.isClone) this.logger.log('initialized', this.options);
+ this.emit('initialized', this.options);
+ deferred.resolve(t);
+ callback(err, t);
+ };
+ if (this.languages && this.options.compatibilityAPI !== 'v1' && !this.isInitialized) return finish(null, this.t.bind(this));
+ this.changeLanguage(this.options.lng, finish);
+ };
+ if (this.options.resources || !this.options.initImmediate) {
+ load();
+ } else {
+ setTimeout(load, 0);
+ }
+ return deferred;
+ }
+ loadResources(language) {
+ let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop$2;
+ let usedCallback = callback;
+ const usedLng = typeof language === 'string' ? language : this.language;
+ if (typeof language === 'function') usedCallback = language;
+ if (!this.options.resources || this.options.partialBundledLanguages) {
+ if (usedLng && usedLng.toLowerCase() === 'cimode' && (!this.options.preload || this.options.preload.length === 0)) return usedCallback();
+ const toLoad = [];
+ const append = lng => {
+ if (!lng) return;
+ if (lng === 'cimode') return;
+ const lngs = this.services.languageUtils.toResolveHierarchy(lng);
+ lngs.forEach(l => {
+ if (l === 'cimode') return;
+ if (toLoad.indexOf(l) < 0) toLoad.push(l);
+ });
+ };
+ if (!usedLng) {
+ const fallbacks = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);
+ fallbacks.forEach(l => append(l));
+ } else {
+ append(usedLng);
+ }
+ if (this.options.preload) {
+ this.options.preload.forEach(l => append(l));
+ }
+ this.services.backendConnector.load(toLoad, this.options.ns, e => {
+ if (!e && !this.resolvedLanguage && this.language) this.setResolvedLanguage(this.language);
+ usedCallback(e);
+ });
+ } else {
+ usedCallback(null);
+ }
+ }
+ reloadResources(lngs, ns, callback) {
+ const deferred = defer$1();
+ if (!lngs) lngs = this.languages;
+ if (!ns) ns = this.options.ns;
+ if (!callback) callback = noop$2;
+ this.services.backendConnector.reload(lngs, ns, err => {
+ deferred.resolve();
+ callback(err);
+ });
+ return deferred;
+ }
+ use(module) {
+ if (!module) throw new Error('You are passing an undefined module! Please check the object you are passing to i18next.use()');
+ if (!module.type) throw new Error('You are passing a wrong module! Please check the object you are passing to i18next.use()');
+ if (module.type === 'backend') {
+ this.modules.backend = module;
+ }
+ if (module.type === 'logger' || module.log && module.warn && module.error) {
+ this.modules.logger = module;
+ }
+ if (module.type === 'languageDetector') {
+ this.modules.languageDetector = module;
+ }
+ if (module.type === 'i18nFormat') {
+ this.modules.i18nFormat = module;
+ }
+ if (module.type === 'postProcessor') {
+ postProcessor.addPostProcessor(module);
+ }
+ if (module.type === 'formatter') {
+ this.modules.formatter = module;
+ }
+ if (module.type === '3rdParty') {
+ this.modules.external.push(module);
+ }
+ return this;
+ }
+ setResolvedLanguage(l) {
+ if (!l || !this.languages) return;
+ if (['cimode', 'dev'].indexOf(l) > -1) return;
+ for (let li = 0; li < this.languages.length; li++) {
+ const lngInLngs = this.languages[li];
+ if (['cimode', 'dev'].indexOf(lngInLngs) > -1) continue;
+ if (this.store.hasLanguageSomeTranslations(lngInLngs)) {
+ this.resolvedLanguage = lngInLngs;
+ break;
+ }
+ }
+ }
+ changeLanguage(lng, callback) {
+ var _this2 = this;
+ this.isLanguageChangingTo = lng;
+ const deferred = defer$1();
+ this.emit('languageChanging', lng);
+ const setLngProps = l => {
+ this.language = l;
+ this.languages = this.services.languageUtils.toResolveHierarchy(l);
+ this.resolvedLanguage = undefined;
+ this.setResolvedLanguage(l);
+ };
+ const done = (err, l) => {
+ if (l) {
+ setLngProps(l);
+ this.translator.changeLanguage(l);
+ this.isLanguageChangingTo = undefined;
+ this.emit('languageChanged', l);
+ this.logger.log('languageChanged', l);
+ } else {
+ this.isLanguageChangingTo = undefined;
+ }
+ deferred.resolve(function () {
+ return _this2.t(...arguments);
+ });
+ if (callback) callback(err, function () {
+ return _this2.t(...arguments);
+ });
+ };
+ const setLng = lngs => {
+ if (!lng && !lngs && this.services.languageDetector) lngs = [];
+ const l = typeof lngs === 'string' ? lngs : this.services.languageUtils.getBestMatchFromCodes(lngs);
+ if (l) {
+ if (!this.language) {
+ setLngProps(l);
+ }
+ if (!this.translator.language) this.translator.changeLanguage(l);
+ if (this.services.languageDetector && this.services.languageDetector.cacheUserLanguage) this.services.languageDetector.cacheUserLanguage(l);
+ }
+ this.loadResources(l, err => {
+ done(err, l);
+ });
+ };
+ if (!lng && this.services.languageDetector && !this.services.languageDetector.async) {
+ setLng(this.services.languageDetector.detect());
+ } else if (!lng && this.services.languageDetector && this.services.languageDetector.async) {
+ if (this.services.languageDetector.detect.length === 0) {
+ this.services.languageDetector.detect().then(setLng);
+ } else {
+ this.services.languageDetector.detect(setLng);
+ }
+ } else {
+ setLng(lng);
+ }
+ return deferred;
+ }
+ getFixedT(lng, ns, keyPrefix) {
+ var _this3 = this;
+ const fixedT = function (key, opts) {
+ let options;
+ if (typeof opts !== 'object') {
+ for (var _len3 = arguments.length, rest = new Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) {
+ rest[_key3 - 2] = arguments[_key3];
+ }
+ options = _this3.options.overloadTranslationOptionHandler([key, opts].concat(rest));
+ } else {
+ options = {
+ ...opts
+ };
+ }
+ options.lng = options.lng || fixedT.lng;
+ options.lngs = options.lngs || fixedT.lngs;
+ options.ns = options.ns || fixedT.ns;
+ options.keyPrefix = options.keyPrefix || keyPrefix || fixedT.keyPrefix;
+ const keySeparator = _this3.options.keySeparator || '.';
+ let resultKey;
+ if (options.keyPrefix && Array.isArray(key)) {
+ resultKey = key.map(k => `${options.keyPrefix}${keySeparator}${k}`);
+ } else {
+ resultKey = options.keyPrefix ? `${options.keyPrefix}${keySeparator}${key}` : key;
+ }
+ return _this3.t(resultKey, options);
+ };
+ if (typeof lng === 'string') {
+ fixedT.lng = lng;
+ } else {
+ fixedT.lngs = lng;
+ }
+ fixedT.ns = ns;
+ fixedT.keyPrefix = keyPrefix;
+ return fixedT;
+ }
+ t() {
+ return this.translator && this.translator.translate(...arguments);
+ }
+ exists() {
+ return this.translator && this.translator.exists(...arguments);
+ }
+ setDefaultNamespace(ns) {
+ this.options.defaultNS = ns;
+ }
+ hasLoadedNamespace(ns) {
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ if (!this.isInitialized) {
+ this.logger.warn('hasLoadedNamespace: i18next was not initialized', this.languages);
+ return false;
+ }
+ if (!this.languages || !this.languages.length) {
+ this.logger.warn('hasLoadedNamespace: i18n.languages were undefined or empty', this.languages);
+ return false;
+ }
+ const lng = options.lng || this.resolvedLanguage || this.languages[0];
+ const fallbackLng = this.options ? this.options.fallbackLng : false;
+ const lastLng = this.languages[this.languages.length - 1];
+ if (lng.toLowerCase() === 'cimode') return true;
+ const loadNotPending = (l, n) => {
+ const loadState = this.services.backendConnector.state[`${l}|${n}`];
+ return loadState === -1 || loadState === 2;
+ };
+ if (options.precheck) {
+ const preResult = options.precheck(this, loadNotPending);
+ if (preResult !== undefined) return preResult;
+ }
+ if (this.hasResourceBundle(lng, ns)) return true;
+ if (!this.services.backendConnector.backend || this.options.resources && !this.options.partialBundledLanguages) return true;
+ if (loadNotPending(lng, ns) && (!fallbackLng || loadNotPending(lastLng, ns))) return true;
+ return false;
+ }
+ loadNamespaces(ns, callback) {
+ const deferred = defer$1();
+ if (!this.options.ns) {
+ if (callback) callback();
+ return Promise.resolve();
+ }
+ if (typeof ns === 'string') ns = [ns];
+ ns.forEach(n => {
+ if (this.options.ns.indexOf(n) < 0) this.options.ns.push(n);
+ });
+ this.loadResources(err => {
+ deferred.resolve();
+ if (callback) callback(err);
+ });
+ return deferred;
+ }
+ loadLanguages(lngs, callback) {
+ const deferred = defer$1();
+ if (typeof lngs === 'string') lngs = [lngs];
+ const preloaded = this.options.preload || [];
+ const newLngs = lngs.filter(lng => preloaded.indexOf(lng) < 0);
+ if (!newLngs.length) {
+ if (callback) callback();
+ return Promise.resolve();
+ }
+ this.options.preload = preloaded.concat(newLngs);
+ this.loadResources(err => {
+ deferred.resolve();
+ if (callback) callback(err);
+ });
+ return deferred;
+ }
+ dir(lng) {
+ if (!lng) lng = this.resolvedLanguage || (this.languages && this.languages.length > 0 ? this.languages[0] : this.language);
+ if (!lng) return 'rtl';
+ const rtlLngs = ['ar', 'shu', 'sqr', 'ssh', 'xaa', 'yhd', 'yud', 'aao', 'abh', 'abv', 'acm', 'acq', 'acw', 'acx', 'acy', 'adf', 'ads', 'aeb', 'aec', 'afb', 'ajp', 'apc', 'apd', 'arb', 'arq', 'ars', 'ary', 'arz', 'auz', 'avl', 'ayh', 'ayl', 'ayn', 'ayp', 'bbz', 'pga', 'he', 'iw', 'ps', 'pbt', 'pbu', 'pst', 'prp', 'prd', 'ug', 'ur', 'ydd', 'yds', 'yih', 'ji', 'yi', 'hbo', 'men', 'xmn', 'fa', 'jpr', 'peo', 'pes', 'prs', 'dv', 'sam', 'ckb'];
+ const languageUtils = this.services && this.services.languageUtils || new LanguageUtil(get$1());
+ return rtlLngs.indexOf(languageUtils.getLanguagePartFromCode(lng)) > -1 || lng.toLowerCase().indexOf('-arab') > 1 ? 'rtl' : 'ltr';
+ }
+ static createInstance() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ let callback = arguments.length > 1 ? arguments[1] : undefined;
+ return new I18n(options, callback);
+ }
+ cloneInstance() {
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ let callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop$2;
+ const forkResourceStore = options.forkResourceStore;
+ if (forkResourceStore) delete options.forkResourceStore;
+ const mergedOptions = {
+ ...this.options,
+ ...options,
+ ...{
+ isClone: true
+ }
+ };
+ const clone = new I18n(mergedOptions);
+ if (options.debug !== undefined || options.prefix !== undefined) {
+ clone.logger = clone.logger.clone(options);
+ }
+ const membersToCopy = ['store', 'services', 'language'];
+ membersToCopy.forEach(m => {
+ clone[m] = this[m];
+ });
+ clone.services = {
+ ...this.services
+ };
+ clone.services.utils = {
+ hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone)
+ };
+ if (forkResourceStore) {
+ clone.store = new ResourceStore(this.store.data, mergedOptions);
+ clone.services.resourceStore = clone.store;
+ }
+ clone.translator = new Translator(clone.services, mergedOptions);
+ clone.translator.on('*', function (event) {
+ for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
+ args[_key4 - 1] = arguments[_key4];
+ }
+ clone.emit(event, ...args);
+ });
+ clone.init(mergedOptions, callback);
+ clone.translator.options = mergedOptions;
+ clone.translator.backendConnector.services.utils = {
+ hasLoadedNamespace: clone.hasLoadedNamespace.bind(clone)
+ };
+ return clone;
+ }
+ toJSON() {
+ return {
+ options: this.options,
+ store: this.store,
+ language: this.language,
+ languages: this.languages,
+ resolvedLanguage: this.resolvedLanguage
+ };
+ }
+}
+const instance = I18n.createInstance();
+instance.createInstance = I18n.createInstance;
+instance.createInstance;
+instance.dir;
+instance.init;
+instance.loadResources;
+instance.reloadResources;
+instance.use;
+instance.changeLanguage;
+instance.getFixedT;
+instance.t;
+instance.exists;
+instance.setDefaultNamespace;
+instance.hasLoadedNamespace;
+instance.loadNamespaces;
+instance.loadLanguages;
+
+function __variableDynamicImportRuntime0__(path) {
+ switch (path) {
+ case './locale/lang.af.js':
+ return Promise.resolve().then(function () { return lang_af$1; });
+ case './locale/lang.ar.js':
+ return Promise.resolve().then(function () { return lang_ar$1; });
+ case './locale/lang.az.js':
+ return Promise.resolve().then(function () { return lang_az$1; });
+ case './locale/lang.be.js':
+ return Promise.resolve().then(function () { return lang_be$1; });
+ case './locale/lang.bg.js':
+ return Promise.resolve().then(function () { return lang_bg$1; });
+ case './locale/lang.ca.js':
+ return Promise.resolve().then(function () { return lang_ca$1; });
+ case './locale/lang.cs.js':
+ return Promise.resolve().then(function () { return lang_cs$1; });
+ case './locale/lang.cy.js':
+ return Promise.resolve().then(function () { return lang_cy$1; });
+ case './locale/lang.da.js':
+ return Promise.resolve().then(function () { return lang_da$1; });
+ case './locale/lang.de.js':
+ return Promise.resolve().then(function () { return lang_de$1; });
+ case './locale/lang.el.js':
+ return Promise.resolve().then(function () { return lang_el$1; });
+ case './locale/lang.en.js':
+ return Promise.resolve().then(function () { return lang_en$1; });
+ case './locale/lang.es.js':
+ return Promise.resolve().then(function () { return lang_es$1; });
+ case './locale/lang.et.js':
+ return Promise.resolve().then(function () { return lang_et$1; });
+ case './locale/lang.fa.js':
+ return Promise.resolve().then(function () { return lang_fa$1; });
+ case './locale/lang.fi.js':
+ return Promise.resolve().then(function () { return lang_fi$1; });
+ case './locale/lang.fr.js':
+ return Promise.resolve().then(function () { return lang_fr$1; });
+ case './locale/lang.fy.js':
+ return Promise.resolve().then(function () { return lang_fy$1; });
+ case './locale/lang.ga.js':
+ return Promise.resolve().then(function () { return lang_ga$1; });
+ case './locale/lang.gl.js':
+ return Promise.resolve().then(function () { return lang_gl$1; });
+ case './locale/lang.he.js':
+ return Promise.resolve().then(function () { return lang_he$1; });
+ case './locale/lang.hi.js':
+ return Promise.resolve().then(function () { return lang_hi$1; });
+ case './locale/lang.hr.js':
+ return Promise.resolve().then(function () { return lang_hr$1; });
+ case './locale/lang.hu.js':
+ return Promise.resolve().then(function () { return lang_hu$1; });
+ case './locale/lang.hy.js':
+ return Promise.resolve().then(function () { return lang_hy$1; });
+ case './locale/lang.id.js':
+ return Promise.resolve().then(function () { return lang_id$1; });
+ case './locale/lang.is.js':
+ return Promise.resolve().then(function () { return lang_is$1; });
+ case './locale/lang.it.js':
+ return Promise.resolve().then(function () { return lang_it$1; });
+ case './locale/lang.ja.js':
+ return Promise.resolve().then(function () { return lang_ja$1; });
+ case './locale/lang.ko.js':
+ return Promise.resolve().then(function () { return lang_ko$1; });
+ case './locale/lang.lt.js':
+ return Promise.resolve().then(function () { return lang_lt$1; });
+ case './locale/lang.lv.js':
+ return Promise.resolve().then(function () { return lang_lv$1; });
+ case './locale/lang.mk.js':
+ return Promise.resolve().then(function () { return lang_mk$1; });
+ case './locale/lang.ms.js':
+ return Promise.resolve().then(function () { return lang_ms$1; });
+ case './locale/lang.mt.js':
+ return Promise.resolve().then(function () { return lang_mt$1; });
+ case './locale/lang.nl.js':
+ return Promise.resolve().then(function () { return lang_nl$1; });
+ case './locale/lang.no.js':
+ return Promise.resolve().then(function () { return lang_no$1; });
+ case './locale/lang.pl.js':
+ return Promise.resolve().then(function () { return lang_pl$1; });
+ case './locale/lang.pt-BR.js':
+ return Promise.resolve().then(function () { return lang_ptBR$1; });
+ case './locale/lang.pt-PT.js':
+ return Promise.resolve().then(function () { return lang_ptPT$1; });
+ case './locale/lang.ro.js':
+ return Promise.resolve().then(function () { return lang_ro$1; });
+ case './locale/lang.ru.js':
+ return Promise.resolve().then(function () { return lang_ru$1; });
+ case './locale/lang.sk.js':
+ return Promise.resolve().then(function () { return lang_sk$1; });
+ case './locale/lang.sl.js':
+ return Promise.resolve().then(function () { return lang_sl$1; });
+ case './locale/lang.sq.js':
+ return Promise.resolve().then(function () { return lang_sq$1; });
+ case './locale/lang.sr.js':
+ return Promise.resolve().then(function () { return lang_sr$1; });
+ case './locale/lang.sv.js':
+ return Promise.resolve().then(function () { return lang_sv$1; });
+ case './locale/lang.sw.js':
+ return Promise.resolve().then(function () { return lang_sw$1; });
+ case './locale/lang.test.js':
+ return Promise.resolve().then(function () { return lang_test$1; });
+ case './locale/lang.th.js':
+ return Promise.resolve().then(function () { return lang_th$1; });
+ case './locale/lang.tl.js':
+ return Promise.resolve().then(function () { return lang_tl$1; });
+ case './locale/lang.tr.js':
+ return Promise.resolve().then(function () { return lang_tr$1; });
+ case './locale/lang.uk.js':
+ return Promise.resolve().then(function () { return lang_uk$1; });
+ case './locale/lang.vi.js':
+ return Promise.resolve().then(function () { return lang_vi$1; });
+ case './locale/lang.yi.js':
+ return Promise.resolve().then(function () { return lang_yi$1; });
+ case './locale/lang.zh-CN.js':
+ return Promise.resolve().then(function () { return lang_zhCN$1; });
+ case './locale/lang.zh-HK.js':
+ return Promise.resolve().then(function () { return lang_zhHK$1; });
+ case './locale/lang.zh-TW.js':
+ return Promise.resolve().then(function () { return lang_zhTW$1; });
+ default:
+ return new Promise(function (resolve, reject) {
+ (typeof queueMicrotask === 'function' ? queueMicrotask : setTimeout)(reject.bind(null, new Error("Unknown variable dynamic import: " + path)));
+ });
+ }
+}
+
+/**
+ * The string keys of the object are two-letter language codes.
+ * @tutorial LocaleDocs
+ * @typedef {PlainObject} module:locale.LocaleStrings
+ */
+// keyed to an array of objects with "id" and "title" or "textContent" properties
+/**
+ * @typedef {PlainObject} module:locale.LocaleSelectorValue
+ */
+
+let langParam;
+
+/**
+* The "data" property is generally set to an an array of objects with
+* "id" and "title" or "textContent" properties.
+* @typedef {PlainObject} module:locale.AddLangExtensionLocaleData
+* @property {module:locale.LocaleStrings[]} data See {@tutorial LocaleDocs}
+*/
+
+/**
+* @interface module:locale.LocaleEditorInit
+*/
+/**
+ * @function module:locale.LocaleEditorInit#addLangData
+ * @param {string} langParam
+ * @returns {module:locale.AddLangExtensionLocaleData}
+*/
+/**
+* @typedef {PlainObject} module:locale.LangAndData
+* @property {string} langParam
+* @property {module:locale.LocaleStrings} langData
+*/
+
+/**
+ *
+ * @function module:locale.putLocale
+ * @param {string} givenParam
+ * @param {string[]} goodLangs
+ * @fires module:svgcanvas.SvgCanvas#event:ext_addLangData
+ * @fires module:svgcanvas.SvgCanvas#event:ext_langReady
+ * @fires module:svgcanvas.SvgCanvas#event:ext_langChanged
+ * @returns {Promise} Resolves to result of {@link module:locale.readLang}
+*/
+
+const putLocale = async function (givenParam, goodLangs) {
+ if (givenParam) {
+ langParam = givenParam;
+ } else if (navigator.userLanguage) {
+ // Explorer
+ langParam = navigator.userLanguage;
+ } else if (navigator.language) {
+ // FF, Opera, ...
+ langParam = navigator.language;
+ }
+
+ // Set to English if language is not in list of good langs
+ if (!goodLangs.includes(langParam) && langParam !== 'test') {
+ langParam = 'en';
+ }
+ const module = await __variableDynamicImportRuntime0__(`./locale/lang.${encodeURIComponent(langParam)}.js`);
+ instance.init({
+ lng: langParam,
+ debug: false,
+ resources: {
+ [langParam]: {
+ translation: module.default
+ }
+ }
+ });
+ return {
+ langParam,
+ i18next: instance
+ };
+};
+const t$2 = function (key) {
+ return instance.t(key);
+};
+
+/* globals svgEditor */
+const template$j = document.createElement('template');
+template$j.innerHTML = `
+
+
+
![icon]()
+
+`;
+/**
+ * @class ToolButton
+ */
+class ToolButton extends HTMLElement {
+ /**
+ * @function constructor
+ */
+ constructor() {
+ super();
+ // create the shadowDom and insert the template
+ this._shadowRoot = this.attachShadow({
+ mode: 'open'
+ });
+ this._shadowRoot.append(template$j.content.cloneNode(true));
+ // locate the component
+ this.$div = this._shadowRoot.querySelector('div');
+ this.$img = this._shadowRoot.querySelector('img');
+ this.imgPath = svgEditor.configObj.curConfig.imgPath;
+ }
+
+ /**
+ * @function observedAttributes
+ * @returns {any} observed
+ */
+ static get observedAttributes() {
+ return ['title', 'src', 'pressed', 'disabled', 'size', 'style'];
+ }
+
+ /**
+ * @function attributeChangedCallback
+ * @param {string} name
+ * @param {string} oldValue
+ * @param {string} newValue
+ * @returns {void}
+ */
+ attributeChangedCallback(name, oldValue, newValue) {
+ if (oldValue === newValue) return;
+ switch (name) {
+ case 'title':
+ {
+ const shortcut = this.getAttribute('shortcut');
+ this.$div.setAttribute('title', `${t$2(newValue)} ${shortcut ? `[${t$2(shortcut)}]` : ''}`);
+ }
+ break;
+ case 'style':
+ this.$div.style = newValue;
+ break;
+ case 'src':
+ if (newValue.indexOf('data:') !== -1) {
+ this.$img.setAttribute('src', newValue);
+ } else {
+ this.$img.setAttribute('src', this.imgPath + '/' + newValue);
+ }
+ break;
+ case 'pressed':
+ if (newValue === null) {
+ this.$div.classList.remove('pressed');
+ } else {
+ this.$div.classList.add('pressed');
+ }
+ break;
+ case 'size':
+ if (newValue === 'small') {
+ this.$div.classList.add('small');
+ } else {
+ this.$div.classList.remove('small');
+ }
+ break;
+ case 'disabled':
+ if (newValue) {
+ this.$div.classList.add('disabled');
+ } else {
+ this.$div.classList.remove('disabled');
+ }
+ break;
+ default:
+ console.error(`unknown attribute: ${name}`);
+ break;
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get title() {
+ return this.getAttribute('title');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set title(value) {
+ this.setAttribute('title', value);
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get pressed() {
+ return this.hasAttribute('pressed');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set pressed(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('pressed', 'true');
+ } else {
+ this.removeAttribute('pressed');
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get disabled() {
+ return this.hasAttribute('disabled');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set disabled(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('disabled', 'true');
+ } else {
+ this.removeAttribute('disabled');
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get src() {
+ return this.getAttribute('src');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set src(value) {
+ this.setAttribute('src', value);
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get size() {
+ return this.getAttribute('size');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set size(value) {
+ this.setAttribute('size', value);
+ }
+
+ /**
+ * @function connectedCallback
+ * @returns {void}
+ */
+ connectedCallback() {
+ // capture shortcuts
+ const shortcut = this.getAttribute('shortcut');
+ if (shortcut) {
+ // register the keydown event
+ document.addEventListener('keydown', e => {
+ // only track keyboard shortcuts for the body containing the SVG-Editor
+ if (e.target.nodeName !== 'BODY') return;
+ // normalize key
+ const key = `${e.metaKey ? 'meta+' : ''}${e.ctrlKey ? 'ctrl+' : ''}${e.key.toUpperCase()}`;
+ if (shortcut !== key) return;
+ // launch the click event
+ this.click();
+ e.preventDefault();
+ });
+ }
+ }
+}
+
+// Register
+customElements.define('se-button', ToolButton);
+
+/* globals svgEditor */
+
+/**
+ * @class FlyingButton
+ */
+class FlyingButton extends HTMLElement {
+ /**
+ * @function constructor
+ */
+ constructor() {
+ super();
+ // create the shadowDom and insert the template
+ this.imgPath = svgEditor.configObj.curConfig.imgPath;
+ this.template = this.createTemplate(this.imgPath);
+ this._shadowRoot = this.attachShadow({
+ mode: 'open'
+ });
+ this._shadowRoot.append(this.template.content.cloneNode(true));
+ // locate the component
+ this.$button = this._shadowRoot.querySelector('.menu-button');
+ this.$handle = this._shadowRoot.querySelector('.handle');
+ this.$overall = this._shadowRoot.querySelector('.overall');
+ this.$img = this._shadowRoot.querySelector('img');
+ this.$menu = this._shadowRoot.querySelector('.menu');
+ // the last element of the div is the slot
+ // we retrieve all elements added in the slot (i.e. se-buttons)
+ this.$elements = this.$menu.lastElementChild.assignedElements();
+ }
+
+ /**
+ * @function createTemplate
+ * @param {string} imgPath
+ * @returns {any} template
+ */
+
+ createTemplate(imgPath) {
+ const template = document.createElement('template');
+ template.innerHTML = `
+
+
+
+
+
+
`;
+ return template;
+ }
+
+ /**
+ * @function observedAttributes
+ * @returns {any} observed
+ */
+ static get observedAttributes() {
+ return ['title', 'pressed', 'disabled', 'opened'];
+ }
+
+ /**
+ * @function attributeChangedCallback
+ * @param {string} name
+ * @param {string} oldValue
+ * @param {string} newValue
+ * @returns {void}
+ */
+ attributeChangedCallback(name, oldValue, newValue) {
+ if (oldValue === newValue) return;
+ switch (name) {
+ case 'title':
+ {
+ const shortcut = this.getAttribute('shortcut');
+ this.$button.setAttribute('title', `${t$2(newValue)} ${shortcut ? `[${t$2(shortcut)}]` : ''}`);
+ }
+ break;
+ case 'pressed':
+ if (newValue) {
+ this.$overall.classList.add('pressed');
+ } else {
+ this.$overall.classList.remove('pressed');
+ }
+ break;
+ case 'opened':
+ if (newValue) {
+ this.$menu.classList.add('open');
+ } else {
+ this.$menu.classList.remove('open');
+ }
+ break;
+ case 'disabled':
+ if (newValue) {
+ this.$overall.classList.add('disabled');
+ } else {
+ this.$overall.classList.remove('disabled');
+ }
+ break;
+ default:
+ console.error(`unknown attribute: ${name}`);
+ break;
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get title() {
+ return this.getAttribute('title');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set title(value) {
+ this.setAttribute('title', value);
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get pressed() {
+ return this.hasAttribute('pressed');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set pressed(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('pressed', 'true');
+ } else {
+ this.removeAttribute('pressed', '');
+ // close also the menu if open
+ this.removeAttribute('opened');
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get opened() {
+ return this.hasAttribute('opened');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set opened(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('opened', 'opened');
+ } else {
+ this.removeAttribute('opened');
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get disabled() {
+ return this.hasAttribute('disabled');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set disabled(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('disabled', 'true');
+ } else {
+ this.removeAttribute('disabled', '');
+ }
+ }
+
+ /**
+ * @function connectedCallback
+ * @returns {void}
+ */
+ connectedCallback() {
+ this.activeSlot = this.shadowRoot.querySelector('slot').assignedElements()[0];
+ this.$img.setAttribute('src', this.imgPath + '/' + this.activeSlot.getAttribute('src'));
+ // capture click event on the button to manage the logic
+ const onClickHandler = ev => {
+ ev.stopPropagation();
+ switch (ev.target.nodeName) {
+ case 'SE-FLYINGBUTTON':
+ if (this.pressed) {
+ this.setAttribute('opened', 'opened');
+ } else {
+ // launch current action
+ this.activeSlot.click();
+ this.setAttribute('pressed', 'pressed');
+ }
+ break;
+ case 'SE-BUTTON':
+ // change to the current action
+ this.$img.setAttribute('src', this.imgPath + '/' + ev.target.getAttribute('src'));
+ this.activeSlot = ev.target;
+ this.setAttribute('pressed', 'pressed');
+ // and close the menu
+ this.$menu.classList.remove('open');
+ break;
+ case 'DIV':
+ // this is a click on the handle so let's open/close the menu.
+ if (this.opened) {
+ this.removeAttribute('opened');
+ } else {
+ this.setAttribute('opened', 'opened');
+ // In case menu scroll on top or bottom position based popup position set
+ const rect = this.getBoundingClientRect();
+ this.$menu.style.top = rect.top + 'px';
+ }
+ break;
+ default:
+ console.error('unkonw nodeName for:', ev.target, ev.target.className);
+ }
+ };
+ // capture event from slots
+ svgEditor.$click(this, onClickHandler);
+ svgEditor.$click(this.$handle, onClickHandler);
+ }
+}
+
+// Register
+customElements.define('se-flyingbutton', FlyingButton);
+
+/* globals svgEditor */
+
+/**
+ * @class ExplorerButton
+ */
+class ExplorerButton extends HTMLElement {
+ /**
+ * @function constructor
+ */
+ constructor() {
+ super();
+ // create the shadowDom and insert the template
+ // create the shadowDom and insert the template
+ this.imgPath = svgEditor.configObj.curConfig.imgPath;
+ this.template = this.createTemplate(this.imgPath);
+ this._shadowRoot = this.attachShadow({
+ mode: 'open'
+ });
+ this._shadowRoot.append(this.template.content.cloneNode(true));
+ // locate the component
+ this.$button = this._shadowRoot.querySelector('.menu-button');
+ this.$overall = this._shadowRoot.querySelector('.overall');
+ this.$img = this._shadowRoot.querySelector('.menu-button img');
+ this.$menu = this._shadowRoot.querySelector('.menu');
+ this.$handle = this._shadowRoot.querySelector('.handle');
+ this.$lib = this._shadowRoot.querySelector('.image-lib');
+ this.files = [];
+ this.request = new XMLHttpRequest();
+ this.imgPath = svgEditor.configObj.curConfig.imgPath;
+ }
+
+ /**
+ * @function createTemplate
+ * @param {string} imgPath
+ * @returns {any} template
+ */
+
+ createTemplate(imgPath) {
+ const template = document.createElement('template');
+ template.innerHTML = `
+
+
+ `;
+ return template;
+ }
+
+ /**
+ * @function observedAttributes
+ * @returns {any} observed
+ */
+ static get observedAttributes() {
+ return ['title', 'pressed', 'disabled', 'lib', 'src'];
+ }
+
+ /**
+ * @function attributeChangedCallback
+ * @param {string} name
+ * @param {string} oldValue
+ * @param {string} newValue
+ * @returns {void}
+ */
+ async attributeChangedCallback(name, oldValue, newValue) {
+ if (oldValue === newValue) return;
+ switch (name) {
+ case 'title':
+ {
+ const shortcut = this.getAttribute('shortcut');
+ this.$button.setAttribute('title', `${newValue} [${shortcut}]`);
+ }
+ break;
+ case 'pressed':
+ if (newValue) {
+ this.$overall.classList.add('pressed');
+ } else {
+ this.$overall.classList.remove('pressed');
+ }
+ break;
+ case 'disabled':
+ if (newValue) {
+ this.$overall.classList.add('disabled');
+ } else {
+ this.$overall.classList.remove('disabled');
+ }
+ break;
+ case 'lib':
+ try {
+ const response = await fetch(`${newValue}index.json`);
+ const json = await response.json();
+ const {
+ lib
+ } = json;
+ this.$menu.innerHTML = lib.map((menu, i) => ``).join('');
+ await this.updateLib(lib[0]);
+ } catch (error) {
+ console.error(error);
+ }
+ break;
+ case 'src':
+ this.$img.setAttribute('src', this.imgPath + '/' + newValue);
+ break;
+ default:
+ console.error(`unknown attribute: ${name}`);
+ break;
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get title() {
+ return this.getAttribute('title');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set title(value) {
+ this.setAttribute('title', value);
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get pressed() {
+ return this.hasAttribute('pressed');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set pressed(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('pressed', 'true');
+ } else {
+ this.removeAttribute('pressed', '');
+ }
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get disabled() {
+ return this.hasAttribute('disabled');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set disabled(value) {
+ // boolean value => existence = true
+ if (value) {
+ this.setAttribute('disabled', 'true');
+ } else {
+ this.removeAttribute('disabled', '');
+ }
+ }
+
+ /**
+ * @function connectedCallback
+ * @returns {void}
+ */
+ connectedCallback() {
+ // capture click event on the button to manage the logic
+ const onClickHandler = ev => {
+ ev.stopPropagation();
+ switch (ev.target.nodeName) {
+ case 'SE-EXPLORERBUTTON':
+ this.$menu.classList.add('open');
+ this.$lib.classList.add('open-lib');
+ break;
+ case 'SE-BUTTON':
+ // change to the current action
+ this.currentAction = ev.target;
+ this.$img.setAttribute('src', this.currentAction.getAttribute('src'));
+ this.dataset.draw = this.data[this.currentAction.dataset.shape];
+ this._shadowRoot.querySelectorAll('.image-lib [pressed]').forEach(b => {
+ b.pressed = false;
+ });
+ this.currentAction.setAttribute('pressed', 'pressed');
+ // and close the menu
+ this.$menu.classList.remove('open');
+ this.$lib.classList.remove('open-lib');
+ break;
+ case 'DIV':
+ if (ev.target.classList[0] === 'handle') {
+ // this is a click on the handle so let's open/close the menu.
+ this.$menu.classList.toggle('open');
+ this.$lib.classList.toggle('open-lib');
+ } else {
+ this._shadowRoot.querySelectorAll('.menu > .pressed').forEach(b => {
+ b.classList.remove('pressed');
+ });
+ ev.target.classList.add('pressed');
+ this.updateLib(ev.target.dataset.menu);
+ }
+ break;
+ default:
+ console.error('unknown nodeName for:', ev.target, ev.target.className);
+ }
+ };
+ // capture event from slots
+ svgEditor.$click(this, onClickHandler);
+ svgEditor.$click(this.$menu, onClickHandler);
+ svgEditor.$click(this.$lib, onClickHandler);
+ svgEditor.$click(this.$handle, onClickHandler);
+ }
+
+ /**
+ * @function updateLib
+ * @param {string} lib
+ * @returns {void}
+ */
+ async updateLib(lib) {
+ const libDir = this.getAttribute('lib');
+ try {
+ // initialize buttons for all shapes defined for this library
+ const response = await fetch(`${libDir}${lib}.json`);
+ const json = await response.json();
+ this.data = json.data;
+ const size = json.size ?? 300;
+ const fill = json.fill ? '#333' : 'none';
+ const off = size * 0.05;
+ const vb = [-off, -off, size + off * 2, size + off * 2].join(' ');
+ const stroke = json.fill ? 0 : size / 30;
+ this.$lib.innerHTML = Object.entries(this.data).map(_ref => {
+ let [key, path] = _ref;
+ const encoded = btoa(`
+ `);
+ return ``;
+ }).join('');
+ } catch (error) {
+ console.error(`could not read file:${libDir}${lib}.json`, error);
+ }
+ }
+}
+
+// Register
+customElements.define('se-explorerbutton', ExplorerButton);
+
+/* globals svgEditor */
+const template$i = document.createElement('template');
+template$i.innerHTML = `
+
+
+
+
+
+`;
+class SeZoom extends HTMLElement {
+ constructor() {
+ super();
+ this.handleMouseDown = this.handleMouseDown.bind(this);
+ this.handleMouseUp = this.handleMouseUp.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.initPopup = this.initPopup.bind(this);
+ this.handleInput = this.handleInput.bind(this);
+
+ // create the shadowDom and insert the template
+ this._shadowRoot = this.attachShadow({
+ mode: 'open'
+ });
+ // locate the component
+ this._shadowRoot.append(template$i.content.cloneNode(true));
+
+ // prepare the slot element
+ this.slotElement = this._shadowRoot.querySelector('slot');
+ this.slotElement.addEventListener('slotchange', this.handleOptionsChange.bind(this));
+
+ // hookup events for the input box
+ this.inputElement = this._shadowRoot.querySelector('input');
+ this.inputElement.addEventListener('click', this.handleClick.bind(this));
+ this.inputElement.addEventListener('change', this.handleInput);
+ this.inputElement.addEventListener('keydown', this.handleKeyDown);
+ this.clickArea = this._shadowRoot.querySelector('#down');
+ this.clickArea.addEventListener('click', this.handleClick.bind(this));
+ this.imgPath = svgEditor.configObj.curConfig.imgPath;
+ this.downImageElement = this.clickArea.querySelector('img');
+ this.downImageElement.setAttribute('src', this.imgPath + '/' + this.downImageElement.getAttribute('src'));
+
+ // set src for imageElement
+ this.imageElement = this._shadowRoot.querySelector('img');
+ this.imageElement.setAttribute('src', this.imgPath + '/' + this.getAttribute('src'));
+
+ // hookup events for arrow buttons
+ this.arrowUp = this._shadowRoot.querySelector('#arrow-up');
+ this.arrowUp.addEventListener('click', this.increment.bind(this));
+ this.arrowUp.addEventListener('mousedown', e => this.handleMouseDown('up', true));
+ this.arrowUp.addEventListener('mouseleave', e => this.handleMouseUp('up'));
+ this.arrowUp.addEventListener('mouseup', e => this.handleMouseUp('up'));
+ this.arrowDown = this._shadowRoot.querySelector('#arrow-down');
+ this.arrowDown.addEventListener('click', this.decrement.bind(this));
+ this.arrowDown.addEventListener('mousedown', e => this.handleMouseDown('down', true));
+ this.arrowDown.addEventListener('mouseleave', e => this.handleMouseUp('down'));
+ this.arrowDown.addEventListener('mouseup', e => this.handleMouseUp('down'));
+ this.optionsContainer = this._shadowRoot.querySelector('#options-container');
+
+ // add an event listener to close the popup
+ document.addEventListener('click', e => this.handleClose(e));
+ this.changedTimeout = null;
+ }
+ static get observedAttributes() {
+ return ['value'];
+ }
+
+ /**
+ * @function get
+ * @returns {any}
+ */
+ get value() {
+ return this.getAttribute('value');
+ }
+
+ /**
+ * @function set
+ * @returns {void}
+ */
+ set value(value) {
+ this.setAttribute('value', value);
+ }
+
+ /**
+ * @function attributeChangedCallback
+ * @param {string} name
+ * @param {string} oldValue
+ * @param {string} newValue
+ * @returns {void}
+ */
+ attributeChangedCallback(name, oldValue, newValue) {
+ if (oldValue === newValue) {
+ switch (name) {
+ case 'value':
+ if (parseInt(this.inputElement.value) !== newValue) {
+ this.inputElement.value = newValue;
+ }
+ break;
+ }
+ return;
+ }
+ switch (name) {
+ case 'value':
+ this.inputElement.value = newValue;
+ this.dispatchEvent(new CustomEvent('change', {
+ detail: {
+ value: newValue
+ }
+ }));
+ break;
+ }
+ }
+
+ /**
+ * @function handleOptionsChange
+ * @returns {void}
+ */
+ handleOptionsChange() {
+ if (this.slotElement.assignedElements().length > 0) {
+ this.options = this.slotElement.assignedElements();
+ this.selectedValue = this.options[0].textContent;
+ this.initPopup();
+ this.options.forEach(option => {
+ option.addEventListener('click', e => this.handleSelect(e));
+ });
+ }
+ }
+
+ /**
+ * @function handleClick
+ * @returns {void}
+ */
+ handleClick() {
+ this.optionsContainer.style.display = 'flex';
+ this.inputElement.select();
+ this.initPopup();
+ }
+
+ /**
+ * @function handleSelect
+ * @param {Event} e
+ * @returns {void}
+ */
+ handleSelect(e) {
+ this.value = e.target.getAttribute('value');
+ this.title = e.target.getAttribute('text');
+ }
+
+ /**
+ * @function handleShow
+ * @returns {void}
+ * initialises the popup menu position
+ */
+ initPopup() {
+ const zoomPos = this.getBoundingClientRect();
+ const popupPos = this.optionsContainer.getBoundingClientRect();
+ const top = zoomPos.top - popupPos.height;
+ const left = zoomPos.left;
+ this.optionsContainer.style.position = 'fixed';
+ this.optionsContainer.style.top = `${top}px`;
+ this.optionsContainer.style.left = `${left}px`;
+ }
+
+ /**
+ * @function handleClose
+ * @param {Event} e
+ * @returns {void}
+ * Close the popup menu
+ */
+ handleClose(e) {
+ if (e.target !== this) {
+ this.optionsContainer.style.display = 'none';
+ this.inputElement.blur();
+ }
+ }
+
+ /**
+ * @function handleInput
+ * @returns {void}
+ */
+ handleInput() {
+ if (this.changedTimeout) {
+ clearTimeout(this.changedTimeout);
+ }
+ this.changedTimeout = setTimeout(this.triggerInputChanged.bind(this), 500);
+ }
+
+ /**
+ * @function triggerInputChanged
+ * @returns {void}
+ */
+ triggerInputChanged() {
+ const newValue = this.inputElement.value;
+ this.value = newValue;
+ }
+
+ /**
+ * @function increment
+ * @returns {void}
+ */
+ increment() {
+ this.value = parseInt(this.value) + 10;
+ }
+
+ /**
+ * @function decrement
+ * @returns {void}
+ */
+ decrement() {
+ if (this.value - 10 <= 0) {
+ this.value = 10;
+ } else {
+ this.value = parseInt(this.value) - 10;
+ }
+ }
+
+ /**
+ * @function handleMouseDown
+ * @param {string} dir
+ * @param {boolean} isFirst
+ * @returns {void}
+ * Increment/Decrement on mouse held down, if its the first call add a delay before starting
+ */
+ handleMouseDown(dir, isFirst) {
+ if (dir === 'up') {
+ this.incrementHold = true;
+ !isFirst && this.increment();
+ setTimeout(() => {
+ if (this.incrementHold) {
+ this.handleMouseDown(dir, false);
+ }
+ }, isFirst ? 500 : 50);
+ } else if (dir === 'down') {
+ this.decrementHold = true;
+ !isFirst && this.decrement();
+ setTimeout(() => {
+ if (this.decrementHold) {
+ this.handleMouseDown(dir, false);
+ }
+ }, isFirst ? 500 : 50);
+ }
+ }
+
+ /**
+ * @function handleMouseUp
+ * @param {string} dir
+ * @returns {void}
+ */
+ handleMouseUp(dir) {
+ if (dir === 'up') {
+ this.incrementHold = false;
+ } else {
+ this.decrementHold = false;
+ }
+ }
+
+ /**
+ * @function handleKeyDown
+ * @param {Event} e
+ * @returns {void}
+ */
+ handleKeyDown(e) {
+ if (e.key === 'ArrowUp') {
+ this.increment();
+ } else if (e.key === 'ArrowDown') {
+ this.decrement();
+ }
+ }
+}
+
+// Register
+customElements.define('se-zoom', SeZoom);
+
+/**
+ * JavaScript template literals for constructing DOM nodes from HTML
+ *
+ * @module html
+ */
+
+/**
+ * A JavaScript template string literal that returns an HTML document fragment.
+ *
+ * Example:
+ *
+ * const fragment = fragmentFrom.html`Hello, world.`
+ *
+ * returns a `DocumentFragment` whose `innerHTML` is `Hello, world.`
+ *
+ * This function is called `html` so that it can be easily used with HTML
+ * syntax-highlighting extensions for various popular code editors.
+ *
+ * See also [templateFrom.html](template#html), which returns a similar result but
+ * as an HTMLTemplateElement.
+ *
+ * @param {TemplateStringsArray} strings - the strings passed to the JavaScript template
+ * literal
+ * @param {string[]} substitutions - the variable values passed to the
+ * JavaScript template literal
+ * @returns {DocumentFragment}
+ */
+const fragmentFrom = {
+ html(strings) {
+ for (var _len = arguments.length, substitutions = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ substitutions[_key - 1] = arguments[_key];
+ }
+ return templateFrom.html(strings, ...substitutions).content;
+ }
+};
+
+/**
+ * A JavaScript template string literal that returns an HTML template.
+ *
+ * Example:
+ *
+ * const myTemplate = templateFrom.html`Hello, world.`
+ *
+ * returns an `HTMLTemplateElement` whose `innerHTML` is `Hello, world.`
+ *
+ * This function is called `html` so that it can be easily used with HTML
+ * syntax-highlighting extensions for various popular code editors.
+ *
+ * See also [html](html), a helper which returns a similar result but as an
+ * DocumentFragment.
+ *
+ * @param {TemplateStringsArray} strings - the strings passed to the JavaScript template
+ * literal
+ * @param {string[]} substitutions - the variable values passed to the
+ * JavaScript template literal
+ * @returns {HTMLTemplateElement}
+ */
+const templateFrom = {
+ html(strings) {
+ const template = document.createElement("template");
+ for (var _len2 = arguments.length, substitutions = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
+ substitutions[_key2 - 1] = arguments[_key2];
+ }
+ template.innerHTML = String.raw(strings, ...substitutions);
+ return template;
+ }
+};
+
+/**
+ * Collection of shared Symbol objects for internal component communication.
+ *
+ * The shared `Symbol` objects in this module let mixins and a component
+ * internally communicate without exposing these internal properties and methods
+ * in the component's public API. They also help avoid unintentional name
+ * collisions, as a component developer must specifically import the `internal`
+ * module and reference one of its symbols.
+ *
+ * To use these `Symbol` objects in your own component, include this module and
+ * then create a property or method whose key is the desired Symbol. E.g.,
+ * [ShadowTemplateMixin](ShadowTemplateMixin) expects a component to define
+ * a property called [template](#template):
+ *
+ * import { template } from 'elix/src/core/internal.js';
+ * import { templateFrom } from 'elix/src/core/htmlLiterals.js'
+ * import ShadowTemplateMixin from 'elix/src/core/ShadowTemplateMixin.js';
+ *
+ * class MyElement extends ShadowTemplateMixin(HTMLElement) {
+ * [template]() {
+ * return templateFrom.html`Hello, world.`;
+ * }
+ * }
+ *
+ * The above use of the internal `template` member lets the mixin find the
+ * component's template in a way that will not pollute the component's public
+ * API or interfere with other component logic. For example, if for some reason
+ * the component wants to define a separate property with the plain string name,
+ * "template", it can do so without affecting the above property setter.
+ *
+ * @module internal
+ */
+
+/**
+ * Symbol for the default state for this element.
+ */
+const defaultState$1 = Symbol("defaultState");
+
+/**
+ * Symbol for the `delegatesFocus` property.
+ *
+ * [DelegatesFocusMixin](DelegatesFocusMixin) defines this property, returning
+ * true to indicate that the focus is being delegated, even in browsers that
+ * don't support that natively. Mixins like [KeyboardMixin](KeyboardMixin) use
+ * this to accommodate focus delegation.
+ */
+const delegatesFocus$1 = Symbol("delegatesFocus");
+
+/**
+ * Symbol for the `firstRender` property.
+ *
+ * [ReactiveMixin](ReactiveMixin) sets the property to `true` during the
+ * element's first `render` and `rendered` callback, then `false` in subsequent
+ * callbacks.
+ *
+ * You can inspect this property in your own `rendered` callback handler to do
+ * work like wiring up events that should only happen once.
+ */
+const firstRender$1 = Symbol("firstRender");
+
+/**
+ * Symbol for the `focusTarget` property.
+ *
+ * [DelegatesFocusMixin](DelegatesFocusMixin) defines this property as either:
+ * 1) the element itself, in browsers that support native focus delegation or,
+ * 2) the shadow root's first focusable element.
+ */
+const focusTarget$1 = Symbol("focusTarget");
+
+/**
+ * Symbol for the `hasDynamicTemplate` property.
+ *
+ * If your component class does not always use the same template, define a
+ * static class property getter with this symbol and have it return `true`.
+ * This will disable template caching for your component.
+ */
+const hasDynamicTemplate$1 = Symbol("hasDynamicTemplate");
+
+/**
+ * Symbol for the `ids` property.
+ *
+ * [ShadowTemplateMixin](ShadowTemplateMixin) defines a shorthand function
+ * `internal.ids` that can be used to obtain a reference to a shadow element with
+ * a given ID.
+ *
+ * Example: if component's template contains a shadow element
+ * `