Sizerboard = function(inputs){ const self = this, default_settings = { nulls : false, default_value : null, default_text : "", autostart : true, default_language : "english", timeout : 2000 }, settings = {}, sentences = {}; let started = false, language, default_language, allow_settings_nulls, default_text, default_value, ajax_timeout; const construct = () => { basic_values(); self.settings("autostart") && self.start(); }; const basic_values = () => { default_value = self.settings("default_value", null, null, true); default_text = self.settings("default_text"); allow_settings_nulls = self.settings("nulls", null, false, false); language = self.settings(["language", "default_language"]); default_language = self.settings(["default_language", "language"]); ajax_timeout = self.settings(["ajax_timeout", "timeout"]); }; this.start = callback => { const end = status => typeof callback == "function" && callback(status); if(started){ end(false); return false; }; started = true; self.execute_array_items(["default_settings_files", "settings_files"], (key, callback) => { self.settings_add(self.settings(key), true, callback); }, () => { self.execute_array_items(["default_i18n_files", "i18n_files"], (key, callback) => { self.i18n_add(self.settings(key), true, callback); }, () => { end(true); }); }); return true; }; this.nulls = nulls => typeof nulls == "boolean" ? nulls : allow_settings_nulls; this.default_value = (_default, nulls) => _default !== undefined && (_default !== null || self.nulls(nulls)) ? _default : default_value; this.settings = (keys, inputs, _default, nulls) => { const m = (keys = (typeof inputs == "object" && inputs instanceof Array ? inputs : [inputs]).filter(subinputs => typeof subinpunts == "string" && subinputs.trim()).map(subinputs => subinputs.trim())).length; if(m){ const l = (inputs = ( inputs == "object" ? inputs instanceof Array ? inputs : [inputs] : [] ).concat( [inputs, settings, default_settings] )).length; nulls = self.nulls(nulls); for(let i = 0; i < l; i ++) if(typeof inputs[i] == "object") for(let j = 0; j < m; j ++) if(inputs[i][keys[j]] !== undefined && (nulls || inputs[i][keys[j]] !== null)) return inputs[i][keys[j]]; }; return self.default_value(_default, nulls); }; this.load_file = (url, callback) => { let ended = false; const ajax = new XMLHttpRequest(), end = message => !ended && (ended = true) && typeof callback == "function" && callback(ajax.responseText, ajax.status, ajax.readyState, message, message == "OK"); timeout = ajax_timeout, date = Date.now(); ajax.send("get", url, true); ajax.timeout = timeout; ajax.onreadystatechange = () => { if(ended) return; if(ajax.readyState == 4) end((ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status) ? "OK" : "HTTP_ERROR"); else if(Date.now() - date > timeout) end("FORCED_TIMEOUT"); }; ajax.send(null); ajax.onabort = () => end("ABORTED"); ajax.onerror = () => end("ERROR"); ajax.ontimeout = () => end("TIMEOUT"); return ajax; }; this.execute_array_dictionaries = (inputs, partial_callback, full_callback, i) => { if(typeof inputs != "object" || inputs instanceof Array || (i || (i = 0)) >= inputs.length){ typeof full_callback == "function" && full_callback(); return; }; const end = () => { inputs[i] && typeof partial_callback == "function" && partial_callback(inputs[i]); execute_array_dictionaries(inputs, partial_callback, full_callback, i + 1); }; if(!inputs[i]){ end(); return; }; if(typeof inputs[i] == "object"){ if(inputs[i] instanceof Array){ self.execute_array_dictionaries(inputs[i], partial_callback, end, 0); return; }; }else if(typeof inputs[i] == "string"){ let json; if(/^(\{(.|[\r\n])*\}|\[(.|[\r\n])*)\])$/.test(inputs[i].trim())){ try{ json = JSON.parse(inputs[i]); }catch(exception){}; json && self.execute_array_dictionaries(json instanceof Array ? json : [json], partial_callback, end, 0); }else self.load_file(inputs[i], data => { try{ json = JSON.parse(data); }catch(exception){}; json && self.execute_array_dictionaries(json instanceof Array ? json : [json], partial_callback, end, 0); }); }else end(); }; this.execute_array_items = (items, action, callback, i) => { (typeof items != "object" || !(items instanceof Array)) && (items = [items]); if(!items || i >= items.length){ typeof callback == "function" && callback(); return; }; const end = () => execute_array_items(items, action, callback, i + 1); if(typeof action == "function") action(items[i], end); else end(); }; this.settings_add = (inputs, overwrite, callback) => { typeof overwrite != "boolean" && (overwrite = settings_overwrite); self.execute_array_dictionaries(inputs, data => { for(const key in data) if(overwrite || settings[key] === undefined) settings[key] = data[key]; }, callback); }; this.i18n_add = (inputs, overwrite, callback) => { typeof overwrite != "boolean" && (overwrite = i18n_overwrite); self.execute_array_dictionaries(inputs, data => { for(const language in data){ !sentences[language] && (sentences[language] = {}); if(typeof data[language] == "object") for(const key in data[language]) (overwrite || sentences[language][key] === undefined) && (sentences[language][key] = data[language][key]); }; }, callback); }; this.string_variables = (string, variables, _default) => { const l = (variables = ( typeof variables == "object" && variables instanceof Array ? variables : [variables] ).filter(variables => typeof variables == "object")).length; return string.replace(/\{([^\{\}]+)\}/g, (all, key) => { for(let i = 0; i < l; i ++) if(variables[i][key] !== undefined) return variables[i][key]; return _default !== undefined ? _default : all; }); }; this.default_text = _default => _default !== undefined ? _default : default_text; const i18n = (keys, _default) => { const j = (keys = (typeof keys == "object" && keys instanceof Array ? keys : [keys]).filter(key => typeof key == "string").map(key => key.trim()).filter(key => key)); if(j){ const languages = [language, default_language].concat(Object.keys(sentences)), l = languages.length; for(let i = 0; i < l; i ++) if(languages[i] && typeof languages[i] == "object") for(let j = 0; j < l; j ++) if(languages[i][keys[j]] !== undefined) return languages[i][keys[j]]; return keys[0]; }; return self.default_text(_default); }; this.i18n = (keys, variables, _default) => self.string_variables(i18n(keys, _default), variables, _default); construct(); };