16 KiB
Tengo la siguiente base estructural hecha sobre la que trabajar. Para empezar, Tenemos el archivo principal de la aplicación, el cual es /Public/ecma/Application/HollowBond.ecma.js.
"use strict";
const HollowBond = (function(){
const HollowBond = function(){
const self = this,
screen = {x : 0, y : 0};
let cells = 40,
thread_interval = null,
fps = 12;
this.item_self = document;
this.settings = new SettingsManager(self);
this.i18n = new I18NManager(self);
this.views = {};
const constructor = () => {
const position = self.settings.get("position", null, "body");
let target, preload = setInterval(() => {
if(target = document.querySelector(position)){
clearInterval(preload);
self.item_self = target.appendChild(document.createElement("div"));
self.item_self.className = self.item_self.id = self.settings.get("class_name", null, "hollow-bond");
self.item_self.setAttribute("data-cells", cells = self.settings.get("cells", null, cells));
thread_interval = setInterval(thread, 1000 / fps);
build();
};
}, fps = self.settings.get(["frames_per_second", "fps"], null, fps));
Common.SCSS(SCSS);
};
const thread = () => {
if(screen.x != self.item_self.offsetWidth || screen.y != self.item_self.offsetHeight){
screen.x = self.item_self.offsetWidth;
screen.y = self.item_self.offsetHeight;
self.item_self.style.fontSize = ((screen.x < screen.y ? screen.x : screen.y) / cells) + "px";
};
};
const build = () => {};
/**
* @param {...(HTMLElement|string|Array.<any|null>)} inputs
* @returns {Array.<HTMLElement>}
* @access public
* @static
*/
this.html = (...inputs) => {
const fragment = new DocumentFragment(),
master = (
inputs[0] && (inputs[0].tagName || inputs[0].nodeName) ? inputs[0] :
typeof inputs[0] == "string" ? document.querySelector(inputs[0]) :
null),
items = [];
(master ? inputs.slice(1) : inputs).forEach(subinputs => {
if(subinputs && (subinputs.tagName || subinputs.nodeName))
fragment.appendChild(subinputs);
else if(typeof subinputs == "string")
fragment.appendChild(document.createTextNode(subinputs));
else if(subinputs instanceof Array){
const [tag, attributes, children] = subinputs.concat([null, null]).slice(0, 3),
item = document.createElement(tag);
attributes && attributes.constructor == Object && Object.entries(attributes).forEach(([key, value]) => {
if(/^on[_\-]?/i.test(key) && typeof value == "function")
item.addEventListener(key.replace(/^on[_\-]?/i, "").replace(/[^a-z0-9]+/gi, "").toLowerCase(), event => {
value(item, event);
});
else if(value !== null)
item.setAttribute(key.replace(/[^a-z0-9]+/gi, "-"), value);
});
if(children instanceof Array)
self.html(...children).forEach(child => item.appendChild(child));
else if(typeof children == "string")
item.innerHTML += children;
fragment.appendChild(item);
};
});
items.push(...fragment.childNodes);
master && master.appendChild(fragment);
return items;
};
constructor();
};
return HollowBond;
})();
Dentro de este hay dos gestores, siendo el primero de ellos el de configuración /Public/ecma/Managers/SettingsManager.ecma.js:
"use strict";
const SettingsManager = (function(){
const SettingsManager = function(hollow_bond){
const self = this,
settings = {};
const constructor = () => {
self.add(settings, true);
};
this.add = (block, overwrite = true) => {
for(const key in block)
if(overwrite || settings[key] === undefined)
settings[key] = block[key];
};
this.get = (keys, inputs = null, _default = null) => Common.get_value(keys, [inputs, settings], _default);
constructor();
};
return SettingsManager;
})();
Y el segundo el de gestión de textos e idiomas /Public/ecma/Managers/I18NManager.ecma.js:
"use strict";
const I18NManager = (function(){
const I18NManager = function(hollow_bond){
const self = this,
sentences = {};
let language = "espanol";
const constructor = () => {
[i18n, typeof custom_i18n != "undefined" ? custom_i18n : {}].forEach(block => {
self.add(block, true);
});
self.set_language(hollow_bond.settings.get("language"), null, Object.keys(sentences)[0] || language);
};
this.add = (new_sentences, overwrite = true) => {
for(const language in new_sentences){
if(!sentences[language])
sentences[language] = {};
for(const key in new_sentences[language])
if(overwrite || sentences[language][key] === undefined)
sentences[language][key] = new_sentences[language][key];
};
};
const get_sentence = (strings, _default) => {
const done = [];
if(typeof strings == "string")
strings = [strings];
for(const current of [language].concat(Object.keys(sentences)))
if(!done.includes(current)){
done.push(current);
for(const key of strings)
if(sentences[current][key] !== undefined)
return sentences[current][key];
};
if(_default !== null)
return _default;
for(const text of strings)
return text;
return "";
};
this.get = (strings, inputs = null, _default = null) => Common.string_variables(get_sentence(strings, _default), inputs);
this.update = () => {
document.querySelectorAll("[data-i18n]").forEach(element => {
const text = self.get(element.getAttribute("data-i18n"));
if(element.getAttribute("data-i18n-without") != "true")
element.textContent = text;
["title", "alt", "placeholder"].forEach(key => {
element.hasAttribute(key) && element.setAttribute(key, text);
});
})
};
this.set_language = new_language => {
if(language != new_language && sentences[new_language]){
language = new_language;
self.update();
};
};
this.get_languages = () => Object.keys(sentences);
this.get_current_language = () => language;
constructor();
};
return I18NManager;
})();
Por otro lado, tenemos una librería con métodos comunes la cual es /Public/ecma/Utils/Common.ecma.js:
"use strict";
const Common = (function(){
const Common = function(){};
Common.get_keys = (...items) => items.reduce((keys, item) => {
if(item){
if(typeof item == "string" && /^[a-z_][a-z0-9_]*$/i.test(item) && !keys.includes(item))
keys.push(item);
else if(item instanceof Array)
for(const key of Common.get_keys(...item))
keys.includes(item) || keys.push(key);
};
return keys;
}, []);
Common.get_dictionaries = (...items) => items.reduce((dictionaries, item) => {
if(item){
if(item.constructor == Object)
dictionaries.push(item);
else if(item instanceof Array)
dictionaries.push(...Common.get_dictionaries(...item));
};
return dictionaries;
}, []);
Common.get_value = (keys, inputs, _default = null) => {
if((keys = Common.get_keys(keys)).length)
for(const subinputs of Common.get_dictionaries(inputs))
for(const key of keys)
if(subinputs[key] !== undefined)
return subinputs[key];
return _default;
};
Common.get_dictionary = (items, overwrite = false) => {
if(items){
if(items.constructor == Object)
return items;
else if(items instanceof Array)
return items.reduce((dictionary, item) => {
Object.entries(Common.get_dictionary(item)).forEach(([key, value]) => {
if(overwrite || dictionary[key] === undefined)
dictionary[key] = value;
});
return dictionary;
}, {});
};
return {};
};
Common.string_variables = (string, variables, _default = null) => {
variables = Common.get_dictionary(variables);
return ("" + string).replace(/\{([^\{\}]+)\}/g, (all, key) => (
variables[key] !== undefined ? variables[key] :
_default !== null ? _default :
all));
};
Common.execute_array = (array, each_callback, end_callback, i = 0) => {
if(i == array.length)
end_callback();
else
each_callback(array[i], () => {
Common.execute_array(array, each_callback, end_callback, i + 1);
});
};
Common.SCSS = (data, selector = "") => {
/** @type {Arrya.<string>} */
const childs = [];
/** @type {string} */
let css = ``;
Object.entries(data).forEach(([key, value]) => {
if(value && value.constructor == Object)
key.split(",").forEach(key => {
/** @type {string} */
const subselector = `${(selector ? selector + (
key[0] == "&" ? "" : " "
) : "") + (
key[0] == "&" ? key.substring(1) : key
)}`,
/** @type {Array<string>} */
subcss = Common.SCSS(value, subselector);
childs.push(`\n\n${subselector}{${subcss[0]}\n}`, ...subcss.slice(1));
});
else
css += `\n ${key.replace(/[^a-z0-9]+/gi, "-")} : ${value === null ? "none" : value};`;
});
if(!selector){
/** @type {HTMLStyleElement} */
let style = document.querySelector("head>style");
if(!style){
style = document.querySelector("head").appendChild(document.createElement("style"));
[
["data-type", "text/css;charset=utf-8"],
["data-language", "CSS3"],
["charset", "utf-8"]
].forEach(([key, value]) => style.setAttribute(key, value === null ? "none" : value));
};
style.appendChild(document.createTextNode(css + childs.join("")));
};
return [css, ...childs];
};
return Common;
})();
Otra librería la cual es un diccionario para almacenar la configuración la cual es /Public/ecma/Utils/Settings.ecma.js:
"use strict";
/** @type {Dictionary.<string, any|null>} */
const settings = {
cells : 40,
position : "body",
class_name : "hollow-bond",
frames_per_second : 12,
language : "espanol",
...(typeof custom_settings == "undefined" ? {} : custom_settings)
};
Otra librería la cual es un diccionario de diccionarios con los textos donde el primer nivel, la clave indica el idioma, y en el diccionario anidado, la clave identifica al texto internacionalizado /Public/ecma/Utils/I18N.ecma.js:
"use strict";
/** @type {Dictionary.<string, Dictionary.<string, string>>} */
const i18n = {
espanol : {}
};
Por otro lado, tenemos la librería de estilos, que es un JS que simula una especie de SASS la cual es /Public/ecma/Utils/Styles.ecma.js:
"use strict";
/** @type {Dictionary.<string, any|null>} */
const SCSS = {
...{
"body,html" : {
height : "100%",
margin : "0em"
}
},
...{
".hollow-bond" : {
position : "relative",
top : "0em",
left : "0em",
width : "100%",
height : "100%"
}
},
...(typeof custom_styles == "undefined" ? {} : custom_styles)
};
Por otro lado, tenemos un directorio que está excluído del proyecto mediante /.gitignore que es /Public/data donde metemos cosas personalizadas o que no queramos incluir en el mismo, como pueden ser bases de datos JS, Assets agenos, etc.
Finalmente tenemos el Index del proyecto el cual es /Public/index.html:
<!DOCTYPE html>
<html lang="es">
<head>
<title data-i18n="hollow_bond">Hollow Bond</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="./images/logo.svg">
<meta name="xdoc:link" content="https://hollowbond.k3y.pw/" />
<meta name="xdoc:git" content="https://git.k3y.pw/KyMAN/HollowBond" />
<meta name="xdoc:authors" content="KyMAN" />
<meta name="xdoc:ai-authors" content="Gemini" />
<meta name="xdoc:since" content="20260922" />
<meta name="xdoc:version" content="20260922" />
<meta name="xdoc:license" content=" Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International" />
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./data/database.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./data/i18n.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./data/settings.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./data/styles.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Utils/Styles.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Utils/Common.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Utils/I18N.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Utils/Settings.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Managers/SettingsManager.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Managers/I18NManager.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" src="./ecma/Application/HollowBond.ecma.js" charset="utf-8" data-crossorigin="anonymous"></script>
<script data-type="text/javascript;charset=utf-8" data-language="ECMAScript 2015" charset="utf-8">
"use strict";
const hollow_bond = new HollowBond();
</script>
</head>
<body></body>
</html>