470 lines
16 KiB
Markdown
470 lines
16 KiB
Markdown
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`.
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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`:
|
|
|
|
```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>
|
|
``` |