Sizerboard/Public/ecma/Sizerboard.ecma.js

603 lines
16 KiB
JavaScript
Raw Permalink Normal View History

2024-03-20 18:39:48 +00:00
Sizerboard = function(inputs){
const self = this,
2024-03-31 19:20:12 +00:00
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"
2024-03-31 19:20:12 +00:00
},
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 = [];
2024-03-31 19:20:12 +00:00
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;
2024-03-20 18:39:48 +00:00
2024-03-31 19:20:12 +00:00
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");
2024-03-31 19:20:12 +00:00
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");
2024-03-31 19:20:12 +00:00
};
2024-03-20 18:39:48 +00:00
this.start = callback => {
const end = status => typeof callback == "function" && callback(status);
self.print("info", "sizerboard_starting");
2024-03-20 18:39:48 +00:00
if(started){
self.print("warn", "sizerboard_already_started");
2024-03-20 18:39:48 +00:00
end(false);
return false;
};
started = true;
2024-03-31 19:20:12 +00:00
basic_values();
self.print("info", "settings_loading");
2024-03-31 19:20:12 +00:00
self.execute_array_items(["default_settings_files", "settings_files"], (key, callback) => {
self.settings_add(self.settings(key), true, callback);
basic_values();
2024-03-31 19:20:12 +00:00
}, () => {
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");
2024-03-31 19:20:12 +00:00
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);
});
2024-03-31 19:20:12 +00:00
});
});
2024-03-20 18:39:48 +00:00
return true;
};
2024-03-31 19:20:12 +00:00
this.nulls = nulls => typeof nulls == "boolean" ? nulls : allow_settings_nulls;
2024-03-20 18:39:48 +00:00
2024-03-31 19:20:12 +00:00
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());
2024-03-20 18:39:48 +00:00
this.settings = (keys, own_inputs, _default, nulls) => {
2024-03-20 18:39:48 +00:00
const m = (keys = self.set_keys(keys)).length;
2024-03-20 18:39:48 +00:00
if(m){
const l = (own_inputs = (
own_inputs == "object" ? own_inputs instanceof Array ? own_inputs : [own_inputs] : []
2024-03-20 18:39:48 +00:00
).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")
2024-03-20 18:39:48 +00:00
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]];
2024-03-20 18:39:48 +00:00
};
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");
2024-03-31 19:20:12 +00:00
timeout = ajax_timeout,
2024-03-20 18:39:48 +00:00
date = Date.now();
ajax.open("get", url, true);
2024-03-20 18:39:48 +00:00
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();
};
2024-03-20 18:39:48 +00:00
this.execute_array_dictionaries = (inputs, partial_callback, full_callback, i) => {
if(!inputs || typeof inputs != "object" || !(inputs instanceof Array) || (i || (i = 0)) >= inputs.length){
2024-03-20 18:39:48 +00:00
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);
2024-03-20 18:39:48 +00:00
};
if(!inputs[i]){
end();
return;
};
if(typeof inputs[i] == "object"){
if(inputs[i] instanceof Array)
2024-03-20 18:39:48 +00:00
self.execute_array_dictionaries(inputs[i], partial_callback, end, 0);
else
end();
2024-03-20 18:39:48 +00:00
}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
2024-03-20 18:39:48 +00:00
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);
2024-03-20 18:39:48 +00:00
});
}else
end();
};
2024-03-31 19:20:12 +00:00
this.execute_array_items = (items, action, callback, i) => {
(typeof items != "object" || !(items instanceof Array)) && (items = [items]);
if(!items || (i || (i = 0)) >= items.length){
2024-03-31 19:20:12 +00:00
typeof callback == "function" && callback();
return;
};
const end = () => self.execute_array_items(items, action, callback, i + 1);
2024-03-31 19:20:12 +00:00
if(typeof action == "function")
action(items[i], end);
else
end();
};
2024-03-20 18:39:48 +00:00
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]);
2024-03-20 18:39:48 +00:00
}, callback);
};
2024-03-31 19:20:12 +00:00
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) &&
2024-03-31 19:20:12 +00:00
(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) => {
2024-03-31 19:20:12 +00:00
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;
2024-03-31 19:20:12 +00:00
if(m){
2024-03-31 19:20:12 +00:00
const languages = [language, default_language].concat(Object.keys(sentences)).filter((language, i, array) => array.indexOf(language) == i),
2024-03-31 19:20:12 +00:00
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]];
2024-03-31 19:20:12 +00:00
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 `<div data-preloader="` + hash + `"></div>`;
};
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;
};
2024-03-20 18:39:48 +00:00
construct();
};