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

279 lines
9.2 KiB
JavaScript

"use strict";
import {RE} from "../Utils/Patterns.ecma.js";
import {Common} from "../Utils/Common.ecma.js";
import {Check} from "../Utils/Checks.ecma.js";
import {
Div, Span, A, Img, Header, Footer, Main, H1, Nav, UL, LI
} from "../Utils/HTMLDSL.ecma.js";
/**
* @typedef {import("../Application/AnP.ecma.js").AnP} AnP
*/
/**
* @class BaseComponent
* @constructor
* @param {!AnP} anp
* @return {void}
* @access public
* @static
*/
export const BaseComponent = (function(){
/**
* @constructs BaseComponent
* @param {!AnP} anp
* @return {void}
* @access private
* @static
*/
const BaseComponent = function(anp){
/** @type {BaseComponent} */
const self = this,
/** @type {Object.<number, Object.<string, number>>} */
caches = {};
/** @type {integer|null} */
let thread = null,
/** @type {Array.<number>} */
zooms = [.25, .5, .75, 1.0, 1.5, 2.0];
/**
* @returns {void}
* @access private
*/
const constructor = () => {
thread = anp.threads.add(thread_method, {
autostart : true,
bucle : true
});
};
/**
* @returns {void}
* @access private
*/
const thread_method = () => {
Object.entries(caches).forEach(([i, cache]) => {
/** @type {HTMLDivElement} */
const base = document.querySelector(".anp[data-i='" + i + "']"),
date = Date.now();
if(!base){
delete caches[i];
return;
};
/** @type {number} */
const cells = Number(base.getAttribute("data-cells")),
/** @type {number} */
zoom = Number(base.getAttribute("data-zoom"));
if(
cache.x != base.offsetWidth ||
cache.y != base.offsetHeight ||
cache.cells != cells ||
cache.zoom != zoom ||
false){
cache.x = base.offsetWidth;
cache.y = base.offsetHeight;
cache.cells = cells;
cache.zoom = zoom;
base.style.fontSize = ((cache.x < cache.y ? cache.x : cache.y) / (cells / zoom)) + "px";
};
if(date - cache.last_time > 2000){
/** @type {string} */
const gui_mode = Check.is_dark_mode() ? "dark" : "light",
/** @type {boolean} */
is_mobile = Check.is_mobile();
cache.last_time = date;
if(cache.gui_mode != gui_mode || cache.mobile != is_mobile){
cache.gui_mode = gui_mode;
cache.mobile = is_mobile;
base.setAttribute("data-gui-mode", gui_mode);
base.setAttribute("data-is-mobile", is_mobile);
};
};
});
};
/**
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @return {Array.<any|null>}
* @access public
*/
this.build = (inputs = null) => {
/** @type {string} */
const name = anp.settings.get(["application_name", "name"], inputs, "AnP"),
/** @type {string} */
link = anp.settings.get(["application_link", "link"], inputs, "https://anp.k3y.pw/"),
/** @type {string} */
git = anp.settings.get(["application_git", "git"], inputs, "https://git.k3y.pw/KyMAN/AnP/"),
/** @type {string} */
logo = anp.settings.get(["application_logo", "logo"], inputs, "images/logo.webp"),
/** @type {string} */
id = anp.unique_keys.create(),
/** @type {string} */
gui_mode = Check.is_dark_mode() ? "dark" : "light",
/** @type {boolean} */
is_mobile = Check.is_mobile();
/** @type {number} */
let i;
while(caches[i = Math.random() * (1 << 28) | 0]);
caches[i] = {
x : 0,
y : 0,
cells : anp.settings.get(["application_cells", "cells"], inputs, 40),
zoom : anp.settings.get(["application_zoom", "zoom"], inputs, 1.0),
gui_mode : gui_mode,
mobile : is_mobile,
last_time : Date.now()
};
return Div({
id : id,
class : Common.unique(["anp", id].concat(anp.settings.get([
"application_class", "class"
], inputs, "").split(RE.WHITE_SPACES))).join(" ").trim(),
data_hash : id,
data_i : i,
data_cells : caches[i].cells,
data_zoom : caches[i].zoom,
data_forced_gui_mode : anp.settings.get(["application_gui_mode", "gui_mode"], inputs, "default"), // default, light, dark
data_gui_mode : gui_mode, // default, light, dark
data_is_mobile : is_mobile,
data_name : name,
data_link : link,
data_git : git,
data_logo : logo
}, [
Header(null, [
H1({title : name}, [
A({href : link, target : "_blank"}, [
anp.components.image({sources : [logo], alt : name}),
Span({class : "text"}, name)
])
]),
Nav({class : "top-menu"}, [
UL(null, anp.settings.get("main_menu", inputs, [
["home", "#"],
["git", git, "_blank"]
]).map(([name, link, target]) => LI({
data_i18n : name,
data_i18n_without : true,
title : anp.i18n.get(name, inputs)
}, [
A({
href : link,
target : target || "_self"
}, [
anp.components.icon(name),
anp.components.i18n(name)
])
])))
]),
anp.components.session_mini.build()
]),
Main(null, [
anp.components.aichat.build(inputs)
]),
Footer(null, [
anp.components.buttons([
["zoom", change_zoom],
["reset_zoom", reset_zoom],
["gui_mode", change_gui_mode]
], {class : "gui-controls"}),
anp.components.licenses.build(anp.settings.get(["application_licenses", "licenses"], inputs, {
copyright : [[2019, 2027], "KyMAN"],
cc_by_nc_sa_4 : []
})),
anp.components.i18n_selector.build()
]),
Div({class : "preloader"})
]);
};
/**
* @param {!HTMLElement} item
* @returns {HTMLDivElement|null}
* @access public
*/
this.get_from = item => {
if(item)
while(
!item.classList.contains("anp") &&
(item = item.parentElement)
);
return item;
};
/**
* @param {!HTMLElement} item
* @param {!Event} event
* @return {void}
* @access private
*/
const change_zoom = (item, event) => {
/** @type {HTMLDivElement} */
const base = self.get_from(item),
/** @type {number} */
i = zooms.indexOf(Number(base.getAttribute("data-zoom")));
if(isNaN(i))
base.setAttribute("data-zoom", 1.0);
else
base.setAttribute("data-zoom", zooms[(i + 1) % zooms.length]);
};
/**
* @param {!HTMLElement} item
* @param {!Event} event
* @return {void}
* @access private
*/
const reset_zoom = (item, event) => {
self.get_from(item).setAttribute("data-zoom", 1.0);
};
/**
* @param {!HTMLElement} item
* @param {!Event} event
* @return {void}
* @access private
*/
const change_gui_mode = (item, event) => {
/** @type {HTMLDivElement} */
const base = self.get_from(item);
base.setAttribute("data-forced-gui-mode", {
default : "light",
light : "dark",
dark : "default"
}[base.getAttribute("data-forced-gui-mode")]);
};
constructor();
};
return BaseComponent;
})();