diff --git a/erm.frontend/package.json b/erm.frontend/package.json index f2e5c261..4311fd34 100644 --- a/erm.frontend/package.json +++ b/erm.frontend/package.json @@ -94,7 +94,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "tailwindcss": "3.4.10", "vue": "3.5.4", diff --git a/gradle.properties b/gradle.properties index f0b1b210..f706798d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ application_version=1.0.0 platform_group=io.sc platform_version=8.1.49 platform_plugin_version=8.1.49 -platform_core_frontend_version=8.1.357 +platform_core_frontend_version=8.1.358 ########################################################### # dependencies version diff --git a/io.sc.engine.mv.frontend/package.json b/io.sc.engine.mv.frontend/package.json index 94e4a276..deeb09bf 100644 --- a/io.sc.engine.mv.frontend/package.json +++ b/io.sc.engine.mv.frontend/package.json @@ -94,7 +94,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "tailwindcss": "3.4.10", "vue": "3.5.4", diff --git a/io.sc.engine.rule.frontend/package.json b/io.sc.engine.rule.frontend/package.json index a18b884c..754eb0f8 100644 --- a/io.sc.engine.rule.frontend/package.json +++ b/io.sc.engine.rule.frontend/package.json @@ -94,7 +94,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "tailwindcss": "3.4.10", "vue": "3.5.4", diff --git a/io.sc.engine.st.frontend/package.json b/io.sc.engine.st.frontend/package.json index d8dbf7cf..86d1145f 100644 --- a/io.sc.engine.st.frontend/package.json +++ b/io.sc.engine.st.frontend/package.json @@ -94,7 +94,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "tailwindcss": "3.4.10", "vue": "3.5.4", diff --git a/io.sc.platform.core.frontend/package.json b/io.sc.platform.core.frontend/package.json index 65d32ec5..065b0219 100644 --- a/io.sc.platform.core.frontend/package.json +++ b/io.sc.platform.core.frontend/package.json @@ -1,6 +1,6 @@ { "name": "platform-core", - "version": "8.1.357", + "version": "8.1.358", "description": "前端核心包,用于快速构建前端的脚手架", "//main": "库的主文件", "main": "dist/platform-core.js", diff --git a/io.sc.platform.core.frontend/template-project/package.json b/io.sc.platform.core.frontend/template-project/package.json index ea711471..e506d971 100644 --- a/io.sc.platform.core.frontend/template-project/package.json +++ b/io.sc.platform.core.frontend/template-project/package.json @@ -1,6 +1,6 @@ { "name": "platform-core", - "version": "8.1.357", + "version": "8.1.358", "description": "前端核心包,用于快速构建前端的脚手架", "private": false, "keywords": [], @@ -106,7 +106,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "svg-path-commander": "2.0.10", "vue": "3.5.4", diff --git a/io.sc.platform.developer.frontend/package.json b/io.sc.platform.developer.frontend/package.json index 796b9702..7e3b298b 100644 --- a/io.sc.platform.developer.frontend/package.json +++ b/io.sc.platform.developer.frontend/package.json @@ -94,7 +94,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.2.2", - "platform-core": "8.1.357", + "platform-core": "8.1.358", "quasar": "2.16.11", "svg-path-commander": "2.0.10", "tailwindcss": "3.4.10", diff --git a/io.sc.platform.developer.frontend/public/svg-editor/Editor.js b/io.sc.platform.developer.frontend/public/svg-editor/Editor.js new file mode 100644 index 00000000..293b5b14 --- /dev/null +++ b/io.sc.platform.developer.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 = ` + +
+ icon + +
+
+
+
+
+ +
+
+ +`; +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 + * `