AnP/Public/ecma/Components/BaseComponent.ecma.js

452 lines
15 KiB
JavaScript

"use strict";
import {Attributes} from "../Application/Attributes.ecma.js";
import {Utils} from "../Utils/Utils.ecma.js";
import {Check} from "../Utils/Check.ecma.js";
/**
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
* @typedef {import("../Models/ThreadModel.ecma.js").ThreadModel} ThreadModel
*/
/**
* @class
* @constructor
* @param {!AnP} anp
* @returns {void}
* @access public
*/
export const BaseComponent = (function(){
/**
* @constructs BaseComponent
* @param {!AnP} anp
* @returns {void}
* @access private
*/
const BaseComponent = function(anp){
/** @type {BaseComponent} */
const self = this,
/** @type {Array.<Array.<Object.<string, any|null>>>} */
identifiers = [];
/** @type {boolean} */
let started = false,
/** @type {ThreadModel|null} */
thread = null;
/**
* @returns {void}
* @access private
*/
const constructor = () => {};
/**
* @param {!ThreadModel} thread
* @returns {void}
* @access private
*/
const analyzer = thread => {
/** @type {Array.<number>} */
const for_delete = [];
identifiers.forEach((cache, i) => {
if(!cache.item && !(cache.item = document.querySelector("#" + cache.id))){
for_delete.push(i);
return;
};
/** @type {number} */
const zoom = Number(cache.item.getAttribute("data-zoom")) / 100;
if(
cache.x != cache.item.offsetWidth ||
cache.y != cache.item.offsetHeight ||
cache.zoom != zoom
){
cache.x = cache.item.offsetWidth;
cache.y = cache.item.offsetHeight;
cache.zoom = zoom;
/** @type {number} */
const cells = Number(cache.item.getAttribute("data-cells")),
/** @type {number} */
minimum_size = Number(cache.item.getAttribute("data-minimum-size")),
/** @type {number} */
size = zoom * (cache.x < cache.y ? cache.x : cache.y) / cells;
cache.item.style.fontSize = (size > minimum_size ? size : minimum_size) + "px";
cache.item.querySelectorAll("[data-maximum-zoom]").forEach(item => {
/** @type {number} */
const maximum_zoom = Number(item.getAttribute("data-maximum-zoom")) / 100,
/** @type {number} */
maximum_size = maximum_zoom * (cache.x < cache.y ? cache.x : cache.y) / cells;
item.style.fontSize = size > maximum_size ? maximum_size + "px" : "inherit";
});
};
});
if(for_delete.length){
for_delete.reverse();
for_delete.forEach(i => {
identifiers.splice(i, 1);
});
};
};
/**
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {Object.<string, any|null>}
* @access private
*/
const get_attributes = (inputs = null) => {
/** @type {string} */
const id = anp.random_chain(),
/** @type {Object.<string, any|null>} */
attributes = Utils.get_dictionary(inputs),
/** @type {anp_attributes_load_callback|null} */
on_load_execute = attributes.on_load_execute;
Attributes.remove(attributes, [
"class", "classes", "id", "data_hash", "hash", "cells", "minimum_size",
"zoom", "data_cells", "data_minimum_size", "data_zoom", "on_load_execute",
"data_dark_mode", "data_mobile", "dark_mode", "mobile"
]);
return {
...attributes,
on_load_execute : item => {
identifiers.push({id : id});
Utils.execute(on_load_execute, item);
},
class : Attributes.join_classes(Utils.get_value(["class", "classes"], inputs, []), ["anp", id]),
id : id,
data_hash : id,
data_cells : anp.settings.get(["base_cells", "data_cells", "cells"], inputs, 40),
data_minimum_size : anp.settings.get(["base_minimum_size", "data_minimum_size", "minimum_size"], inputs, 12),
data_zoom : anp.settings.get(["base_zoom", "data_zoom", "zoom"], inputs, 100),
data_gui_mode : anp.settings.get(["base_gui_mode", "data_gui_mode", "gui_mode"], inputs, "default"),
data_dark_mode : anp.is_dark_mode(),
data_mobile : anp.is_mobile()
};
};
/**
* @param {?anp_start_callback} callback
* @returns {boolean}
* @access public
*/
this.start = (callback = null) => {
/** @type {!anp_start_callback} */
const end = ok => {
Utils.execute(callback, ok);
return ok;
};
if(started)
return end(false);
started = true;
/** @type {string|HTMLElement|null} */
const position = anp.settings.get("position");
thread = anp.threads.add(analyzer, {
timer : 100,
bucle : true
});
if(position)
anp.preload(position, (position, asyncrhonous, ok) => {
if(ok){
/** @type {Object.<string, any|null>} */
const attributes = {
on_load_execute : item => {
anp.item_self = item;
anp.hash_self = item.getAttribute("id");
end(true);
}
};
["name", "git", "link", "since", "version"].forEach(key => {
const value = anp.settings.get("application_" + key);
Check.is_null_or_undefined(value) ||
(attributes["data_" + key] = value);
});
if(position.classList.contains("anp")){
/** @type {HTMLElement} */
const footer = position.querySelector("footer"),
/** @type {HTMLFieldSetElement} */
gui_controls = footer.insertBefore(document.createElement("fieldset"), footer.childNodes[0]);
anp.components.set_to(gui_controls, ...self.build_view_menu());
gui_controls.setAttribute("class", "gui-controls");
anp.attributes.set(position, get_attributes(attributes));
}else{
try{
position.innerHTML = self.build(attributes);
}catch(exception){
console.error(exception);
};
};
}else
end(false);
});
else
end(true);
return true;
};
/**
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @returns {string}
* @access public
*/
this.build = inputs => {
/** @type {Array.<any|null>} */
const licenses = anp.settings.get(["application_licenses", "application_license", "licenses", "license"], inputs, []),
/** @type {Object.<string, any|null>} */
attributes = get_attributes(inputs);
return anp.components.set(["div", attributes, [
["header", null, [
["h1", {class : "logo", title : "AnP"}, [
["a", {href : "https://anp.k3y.pw/", target : "_blank"}, [
["image", {sources : "/images/AnP.png", title : "AnP"}],
["span", {class : "text"}, "AnP"]
]]
]]
]],
["main"],
["footer", {
"data_maximum_zoom" : anp.settings.get(["base_footer_maximum_zoom", "footer_maximum_zoom"], inputs, 100)
}, [].concat(
[["fieldset", {class : "gui-controls"}, self.build_view_menu()]],
(licenses ? [["licenses", licenses]] : [])
)]
]]);
};
/**
* @returns {Array.<any|null>}
* @access public
*/
this.build_view_menu = (inputs = null) => [
["legend", {data_i18n : "gui_controls"}, anp.i18n.get("gui_controls")],
["button", ["less_zoom", set_less_zoom]],
["number", {
name : "zoom",
min : anp.settings.get("zoom_minimum", inputs, 1),
max : anp.settings.get("zoom_maximum", inputs, 200),
value : anp.settings.get(["default_zoom", "base_zoom", "zoom"], inputs, 100),
step : anp.settings.get("zoom_step", inputs, 1),
onchange : change_zoom
}],
["button", ["reset_zoom", reset_zoom]],
["button", ["zoom_mode", {
onclick : change_zoom_mode,
data_modes : anp.settings.get("zoom_modes", inputs, [50, 75, 100, 125, 150])
}]],
["button", ["more_zoom", set_more_zoom]],
["button", ["gui_mode", change_gui_mode]],
["button", ["more_options", {
onclick : show_more_options,
disabled : true
}]]
];
/**
* @param {!HTMLElement} item
* @param {!number} value
* @returns {void}
* @access public
*/
this.change_zoom = (item, value) => {
/** @type {HTMLInputElement} */
const number_field = (item = BaseComponent.get(item)).querySelector("footer .gui-controls [type=number]");
item.setAttribute("data-zoom", value);
Number(number_field.value) != value && (number_field.value = value);
};
/**
* @param {!HTMLElement} button
* @param {!number} value
* @returns {void}
* @access private
*/
const set_zoom = (button, value) => {
if(Check.is_number(value)){
/** @type {HTMLInputElement} */
const number_field = button.parentNode.querySelector("[type=number]");
if(value){
value += Number(number_field.value) || 0;
if(value < 0){
/** @type {number} */
const minimum = Number(number_field.getAttribute("min"));
!isNaN(minimum) && minimum > value && (value = minimum);
}else{
/** @type {number} */
const maximum = Number(number_field.getAttribute("max"));
!isNaN(maximum) && maximum < value && (value = maximum);
};
number_field.value = value;
}else
number_field.value = value = 100;
self.change_zoom(button, value);
};
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const set_less_zoom = (item, event = null) => {
set_zoom(item, -BaseComponent.get_input_gui_step(item));
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const change_zoom_mode = (item, event = null) => {
/** @type {number} */
const current_zoom = Number(item.parentNode.querySelector("[type=number]").value),
/** @type {Array.<number>} */
modes = Utils.variables_decode(item.getAttribute("data-modes"));
modes.some((value, i) => {
if(current_zoom < value){
self.change_zoom(item, value);
return true;
};
}) || self.change_zoom(item, modes[0]);
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const reset_zoom = (item, event = null) => {
set_zoom(item, 0);
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const set_more_zoom = (item, event = null) => {
set_zoom(item, BaseComponent.get_input_gui_step(item));
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const change_gui_mode = (item, event = null) => {
(item = BaseComponent.get(item)).setAttribute("data-gui-mode", {
"default" : "dark",
"dark" : "light",
"light" : "default"
}[item.getAttribute("data-gui-mode")] || "default");
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const show_more_options = (item, event = null) => {
console.log("PASA");
};
/**
* @param {!HTMLElement} item
* @param {?Event} [event = null]
* @returns {void}
* @access private
*/
const change_zoom = (item, event = null) => {
self.change_zoom(item, Number(item.value));
};
constructor();
};
/**
* @param {!HTMLElement} item
* @returns {HTMLElement}
* @access public
* @static
*/
BaseComponent.get = item => {
if(item)
while(true){
if(!item.classList)
return null;
if(item.classList.contains("anp") || !(item = item.parentNode))
break;
};
return item || null;
};
/**
* @param {!HTMLElement} item
* @returns {number}
* @access public
* @static
*/
BaseComponent.get_input_gui_step = item => Number(item.parentNode.querySelector("[type=number]").getAttribute("step"));
return BaseComponent;
})();