Sizerboard = function(inputs){ const self = this, default_settings = { nulls : false, default_value : null, default_text : "", autostart : true, default_language : "english", timeout : 2000, print_format : "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}", gui_mode : "light", frames_per_second : 24, position : "body", default_settings_files : [ "/json/Sizerboard.settings.json", "/json/Sizerboard.settings.secrets.json" ], object_name : "sizerboard" }, settings = {}, sentences = {}, print_types = [ ["unkn", "unknown"], ["info", "information"], [" ok ", "ok", "yes", "y"], ["erro", "error", "wrong"], ["warn", "warning"], ["exce", "except", "exception"], ["note", "test", "tests", "notes", "data"] ], print_styles = { unkn : { dark : "color : #AAA;", light : "color : #666;" }, info : { dark : "color : #99F;", light : "color : #009;" }, ok : { dark : "color : #9F9;", light : "color : #090;" }, note : { dark : "color : #222;", light : "color : #EFEFEF;" } }, threads = [], hashes = []; let started = false, language, default_language, allow_settings_nulls, default_text, default_value, ajax_timeout, print_format = "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}", gui_mode = "light", threads_interval, frames_per_second = 24, preload_timeout = 2000, hashes_alphabet, hashes_length; const re_trace_block = new RegExp("^(" + [ /\s*at\s+(([^\s]+)\s+\()?(([^\(\)\:]+\:)?[^\(\)\:]+)(\:([0-9]+)\:[0-9]+)?\)?/.source, // Webkit /([^\@]+)\@([^:]+\:[^\:]+)\:([0-9]+)\:[0-9]+/.source, // Gecko ].join("|") + ")$"); let object_name = this.object_name; let item_self = this.item_self = document; let hash_self = this.hash_self; let base = this.base; let views = this.views; let projects = this.projects; let draw_box = this.draw_box; const construct = () => { basic_values(); self.print("info", "sizerboard_building"); object_name = self.object_name = self.settings("object_name"); base = self.base = new Sizerboard.Base(self, inputs); views = self.views = new Sizerboard.Views(self, inputs); projects = self.projects = new Sizerboard.Projects(self, inputs); draw_box = self.draw_box = new Sizerboard.DrawBox(self, inputs); self.print("ok", "sizerboard_built"); 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"]); print_format = self.settings("print_format"); gui_mode = self.settings("gui_mode"); }; this.start = callback => { const end = status => typeof callback == "function" && callback(status); self.print("info", "sizerboard_starting"); if(started){ self.print("warn", "sizerboard_already_started"); end(false); return false; }; started = true; basic_values(); self.print("info", "settings_loading"); self.execute_array_items(["default_settings_files", "settings_files"], (key, callback) => { self.settings_add(self.settings(key), true, callback); basic_values(); }, () => { self.print("ok", "settings_loaded"); frames_per_second = self.settings("frames_per_second"); preload_timeout = self.settings(["preload_timeout", "timeout"]); hashes_alphabet = self.settings(["hashes_alphabet", "alphabet"]); hashes_length = self.settings(["hashes_length", "length"]); threads_interval = setInterval(threads_method, 1000 / frames_per_second); self.print("info", "i18n_loading"); self.execute_array_items(["default_i18n_files", "i18n_files"], (key, callback) => { self.i18n_add(self.settings(key), true, callback); }, () => { self.print("ok", "i18n_loaded"); self.execute_array_items([views, base, projects, draw_box], (item, callback) => { item.start(callback); }, () => { self.print("ok", "sizerboard_started"); 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.set_keys = keys => (typeof keys == "object" && keys instanceof Array ? keys : [keys]).filter(key => typeof key == "string" && key.trim()).map(key => key.trim()); this.settings = (keys, own_inputs, _default, nulls) => { const m = (keys = self.set_keys(keys)).length; if(m){ const l = (own_inputs = ( own_inputs == "object" ? own_inputs instanceof Array ? own_inputs : [own_inputs] : [] ).concat( [inputs, settings, default_settings] )).length; nulls = self.nulls(nulls); for(let i = 0; i < l; i ++) if(own_inputs[i] && typeof own_inputs[i] == "object") for(let j = 0; j < m; j ++) if(own_inputs[i][keys[j]] !== undefined && (nulls || own_inputs[i][keys[j]] !== null)) return own_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.open("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_json = (data, yes, no) => { let json; try{ json = JSON.parse(data); }catch(exception){}; if(json) typeof yes == "function" && yes(json); else typeof no == "function" && no(); }; this.execute_array_dictionaries = (inputs, partial_callback, full_callback, i) => { if(!inputs || typeof inputs != "object" || !(inputs instanceof Array) || (i || (i = 0)) >= inputs.length){ typeof full_callback == "function" && full_callback(); return; }; const end = () => { inputs[i] && typeof inputs[i] == "object" && typeof partial_callback == "function" && partial_callback(inputs[i]); self.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); else end(); }else if(typeof inputs[i] == "string"){ if(/^(\{(.|[\r\n])*\}|\[(.|[\r\n])*\])$/.test(inputs[i].trim())) self.execute_json(inputs[i], json => self.execute_array_dictionaries(json instanceof Array ? json : [json], partial_callback, end, 0), end); else self.load_file(inputs[i], data => { self.execute_json(data, json => self.execute_array_dictionaries(json instanceof Array ? json : [json], partial_callback, end, 0), end); }); }else end(); }; this.execute_array_items = (items, action, callback, i) => { (typeof items != "object" || !(items instanceof Array)) && (items = [items]); if(!items || (i || (i = 0)) >= items.length){ typeof callback == "function" && callback(); return; }; const end = () => self.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) (overwrite || settings[key] === undefined) && !/^Sizerboard.*_(start|end)$/.test(key) && (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) && !/^Sizerboard.*_(start|end)$/.test(key) && (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 m = (keys = self.set_keys(keys)).length; if(m){ const languages = [language, default_language].concat(Object.keys(sentences)).filter((language, i, array) => array.indexOf(language) == i), l = languages.length; for(let i = 0; i < l; i ++) if(sentences[languages[i]]) for(let j = 0; j < m; j ++) if(sentences[languages[i]][keys[j]] !== undefined) return sentences[languages[i]][keys[j]]; return keys[0]; }; return self.default_text(_default); }; this.i18n = (keys, variables, _default) => { const text = i18n(keys, _default); return self.string_variables(typeof text == "object" ? text.join("") : text, variables, _default); }; this.get_print_type = type => { const l = print_types.length; type = type.toLowerCase(); for(let i = 0; i < l; i ++) if(print_types[i].includes(type)) return print_types[i][0].toUpperCase(); return print_types[0][0]; }; this.get_trace = i => (new Error()).stack.replace(/^Error\s*?[\r\n]+/, "").trim().split(/[\r\n]+/).slice(1 + (i || 0)).map(line => { const matches = line.match(re_trace_block); return matches ? { file : matches[4] || matches[9], method : matches[3] || matches[8] || "", line : Number(matches[7] || matches[10]) } : null; }).filter(line => line); this.print = (type, message, variables, i) => { const date = new Date(), own = { ...(( variables ? typeof variables == "object" ? [variables] : variables instanceof Array ? variables : [] : [] ).reduce((results, set) => typeof set == "object" ? {...results, ...set} : results, {})), ...self.get_trace(i || 1)[0], raw_type : type, type : self.get_print_type(type) }; ["year", "month", "day", "hours", "minutes", "seconds"].forEach(key => { const k = key != "minutes" ? key[0] : "i"; own[k + k] = ("00" + (own[k] = (own[key] = date["get" + ( key == "year" ? "FullYear" : key == "day" ? "Date" : key[0].toUpperCase() + key.substring(1) )]()) % 100)).slice(-2); }); own.yyyy = own.year; const final_message = self.string_variables(print_format, { ...own, message : self.i18n(message, own) }); switch(own.type){ case "INFO": console.log("%c" + final_message, print_styles.info[gui_mode]); break; case " OK ": console.log("%c" + final_message, print_styles.ok[gui_mode]); break; case "ERRO": case "EXCE": console.error(final_message); break; case "WARN": console.warn(final_message); break; case "NOTE": console.log("%c" + final_message, print_styles.note[gui_mode]); break; case "UNKN": default: console.log("%c" + final_message, print_styles.unkn[gui_mode]); break; }; }; const threads_method = () => threads.forEach(thread => { if(thread) try{ thread(); }catch(exception){}; }); this.thread_add = callback => { let error = ( callback === undefined ? 1 << 0 : callback === null ? 1 << 1 : typeof callback != "function" ? 1 << 2 : 0) << 1, i = null; if(!error){ const l = threads.length; for(i = 0; i < l; i ++) if(!threads[i]) break; threads[i] = callback; }; return [i, error]; }; this.thread_remove = i => { let error = ( i === undefined ? 1 << 0 : i === null ? 1 << 1 : isNaN(i) ? 1 << 2 : i != i >> 0 ? 1 << 3 : i < 0 ? 1 << 4 : i >= threads.length ? 1 << 5 : 0) << 1; !i && (threads[i] = null); return error; }; this.preload = (selector, callback) => { let callback_is_function = typeof callback == "function", error = ( (( selector === undefined ? 1 << 0 : selector === null ? 1 << 1 : 0) << 0) | (( callback === undefined ? 1 << 0 : callback === null ? 1 << 1 : !callback_is_function ? 1 << 2 : 0) << 10) | 0) << 1, item = null, asynchronous = false; const end = () => callback_is_function && callback(item, error, asynchronous); if(!error){ switch(typeof selector){ case "string": if(!(error |= ( !selector ? 1 << 5 : !(selector = selector.trim()) ? 1 << 6 : 0) << 5)){ try{ (item = item_self.querySelector(selector)) && end(); }catch(exception){ error |= 1 << 7; end(); }; if(!item && !error){ const date = Date.now(), [thread, suberror] = self.thread_add(() => { if(item = item_self.querySelector(selector)){ self.thread_remove(thread); end(); }else if(Date.now() - date > preload_timeout){ self.thread_remove(thread); error |= 1 << 8; end(); }; }); }; }; break; case "object": if(selector.tagName || selector.nodeName) item = selector; else error |= 1 << 4; end(); break; default: error |= 1 << 3; end(); break; }; }; return error; }; this.hash = () => { let hash; const l = hashes_alphabet.length; do{ hash = ""; while((hash += hashes_alphabet[Math.random() * l >> 0]).length < hashes_length); }while( hashes.includes(hash) || /^[0-9]/.test(hash) || document.querySelector("#" + hash + ",." + hash + ",[name=" + hash + "]") ); hashes.push(hash); return hash; }; this.build_preloader = callback => { const hash = self.hash(); self.preload("[data-preloader=" + hash + "]", (preloader, error, asynchronous) => { typeof callback == "function" && callback(error ? null : preloader.parentNode, error, asynchronous); preloader.remove(); }); return `
`; }; this.set_self = (item, hash) => { let error = (( ((item_self.tagName || item_self.nodeName) != "#document" ? 1 << 0 : 0) | (hash_self ? 1 << 1 : 0) | 0) || ( (( item === undefined ? 1 << 0 : item === null ? 1 << 1 : typeof item != "object" ? 1 << 2 : !item.tagName && !item.nodeName ? 1 << 3 : (item.tagName || item.nodeName) == "#document" ? 1 << 4 : 0) << 2) | (( hash === undefined ? 1 << 0 : hash === null ? 1 << 1 : typeof hash != "string" ? 1 << 2 : !hash ? 1 << 3 : !hash.trim() ? 1 << 4 : hash != hash.trim() ? 1 << 5 : hash.split("").some(character => !hashes_alphabet.includes(character)) ? 1 << 6 : hash.length < hashes_length ? 1 << 7 : 0) << 7) | 0)) << 1; if(!error){ item_self = self.item_self = item; hash_self = self.hash_self = hash; }; return error; }; construct(); };