diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..66ad78a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/Data
+/Public/data
+*.[Ss]ecrets.*
+*.[Ss]ecret.*
+*.deleted.*
+/Python/websockets
+__pycache__
+.sass-cache
\ No newline at end of file
diff --git a/Artbook/AnP_telegram.png b/Artbook/AnP_telegram.png
new file mode 100755
index 0000000..911f643
Binary files /dev/null and b/Artbook/AnP_telegram.png differ
diff --git a/Artbook/AnPv1.svg b/Artbook/AnPv1.svg
new file mode 100755
index 0000000..7d13385
--- /dev/null
+++ b/Artbook/AnPv1.svg
@@ -0,0 +1,353 @@
+
+
diff --git a/Artbook/AnPv1.xcf b/Artbook/AnPv1.xcf
new file mode 100755
index 0000000..9cea9f5
Binary files /dev/null and b/Artbook/AnPv1.xcf differ
diff --git a/Artbook/images.svg b/Artbook/images.svg
new file mode 100755
index 0000000..fa8d2ca
--- /dev/null
+++ b/Artbook/images.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/Artbook/images.xcf b/Artbook/images.xcf
new file mode 100755
index 0000000..a457992
Binary files /dev/null and b/Artbook/images.xcf differ
diff --git a/JSON/AnP.routes.json b/JSON/AnP.routes.json
new file mode 100644
index 0000000..d579b34
--- /dev/null
+++ b/JSON/AnP.routes.json
@@ -0,0 +1,4 @@
+[
+ "get:/ /Public",
+ "post:/ai/new_message ai@new_message"
+]
\ No newline at end of file
diff --git a/JSON/AnP.settings.json b/JSON/AnP.settings.json
new file mode 100644
index 0000000..ebe075d
--- /dev/null
+++ b/JSON/AnP.settings.json
@@ -0,0 +1,87 @@
+{
+
+ "AnP_start" : null,
+ "end_print_types" : ["UNKN", "EXCE", "ERRO"],
+ "root_projects_paths" : [],
+ "print_format" : "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}",
+ "exception_format" : " '[{line}]{file}({method})'{lines}\n\n{exception_message}",
+ "AnP_end" : null,
+
+ "AnP_SettingsManager_start" : null,
+ "default_settings_files" : "/JSON/AnP.settings.json",
+ "default_secrets_files" : "/JSON/AnP.secrets.json",
+ "AnP_SettingsManager_end" : null,
+
+ "AnP_I18NManager_start" : null,
+ "default_i18n_files" : [
+ "/JSON/I18N/AnP.i18n.english.json",
+ "/JSON/I18N/AnP.i18n.espanol.json",
+ "/JSON/I18N/AnP.i18n.galego.json",
+ "/JSON/I18N/AnP.i18n.nihongo.json",
+ "/JSON/I18N/AnP.i18n.russkiy.json"
+ ],
+ "default_language" : "english",
+ "language" : "english",
+ "AnP_I18NManager_end" : null,
+
+ "AnP_PrintTypesManager_start" : null,
+ "default_print_types" : [
+ ["unkn", "unknown"],
+ ["info", "information"],
+ ["warn", "warning"],
+ ["erro", "error", "no", "wrong", "bad", "fail", "incorrect"],
+ [" ok ", "ok", "success", "good", "correct", "right", "yes"],
+ ["exce", "exception", "traceback", "trace"],
+ ["test", "debug", "comment", "log", "print"]
+ ],
+ "AnP_PrintTypesManager_end" : null,
+
+ "AnP_TerminalManager_start" : null,
+ "AnP_TerminalManager_end" : null,
+
+ "AnP_ModelsManager_start" : null,
+ "AnP_ModelsManager_end" : null,
+
+ "AnP_ControllersManager_start" : null,
+ "default_controllers" : {
+ "ai" : "AIController"
+ },
+ "AnP_ControllersManager_end" : null,
+
+ "AnP_IndexesManager_start" : null,
+ "default_indexes" : [
+ ["index.html", "index.htm", "index.w.md", "index.md"]
+ ],
+ "AnP_IndexesManager_end" : null,
+
+ "AnP_RoutesManager_start" : null,
+ "default_routes_files" : "/JSON/AnP.routes.json",
+ "AnP_RoutesManager_end" : null,
+
+ "AnP_HTTPServer_start" : null,
+ "http_server_host" : "",
+ "http_server_port" : 18000,
+ "AnP_HTTPServer_end" : null,
+
+ "AnP_WebSocketServer_start" : null,
+ "web_socket_server_host" : "",
+ "web_socket_server_port" : 18765,
+ "AnP_WebSocketServer_end" : null,
+
+ "AnP_TitlesManager_start" : null,
+ "default_titles_files" : [
+ "/JSON/AnP.titles.json",
+ "/JSON/AnP.titles.secrets.json"
+ ],
+ "AnP_TitlesManager_end" : null,
+
+ "AnP_AIModel_start" : null,
+ "titles_model" : "gemma3:1b",
+ "titles_temperature" : 0.0,
+ "titles_prompt_file" : "/MD/AnP.titles-prompt.md",
+ "responses_model" : "gemma3:1b",
+ "responses_temperature" : 7.0,
+ "response_with_titles_prompt_file" : "/MD/AnP.response-with-titles.md",
+ "AnP_AIModel_end" : null
+
+}
\ No newline at end of file
diff --git a/JSON/I18N/AnP.i18n.english.json b/JSON/I18N/AnP.i18n.english.json
new file mode 100644
index 0000000..c713dda
--- /dev/null
+++ b/JSON/I18N/AnP.i18n.english.json
@@ -0,0 +1,3 @@
+{
+ "english" : {}
+}
\ No newline at end of file
diff --git a/JSON/I18N/AnP.i18n.espanol.json b/JSON/I18N/AnP.i18n.espanol.json
new file mode 100644
index 0000000..c5eb83e
--- /dev/null
+++ b/JSON/I18N/AnP.i18n.espanol.json
@@ -0,0 +1,47 @@
+{
+ "espanol" : {
+
+ "AnP_start" : null,
+ "AnP_end" : null,
+
+ "AnP_SettingsManager_start" : null,
+ "AnP_SettingsManager_end" : null,
+
+ "AnP_I18NManager_start" : null,
+ "AnP_I18NManager_end" : null,
+
+ "AnP_PrintTypesManager_start" : null,
+ "AnP_PrintTypesManager_end" : null,
+
+ "AnP_TerminalManager_start" : null,
+ "terminal_manager_unknown_command" : "El comando '{command}' es desconocido.",
+ "terminal_manager_exception" : "Hubo una excepción al intentar procesar el comando '{command}'.",
+ "AnP_TerminalManager_end" : null,
+
+ "AnP_ModelsManager_start" : null,
+ "AnP_ModelsManager_end" : null,
+
+ "AnP_ControllersManager_start" : null,
+ "AnP_ControllersManager_end" : null,
+
+ "AnP_IndexesManager_start" : null,
+ "AnP_IndexesManager_end" : null,
+
+ "AnP_RoutesManager_start" : null,
+ "AnP_RoutesManager_end" : null,
+
+ "AnP_WebSocketServerDriver_start" : null,
+ "web_socket_server_client_close_exception" : "El Web Socket Servidor '{host}:{port}' ha cerrado la conexión con el cliente '{key}'.",
+ "web_socket_server_client_connected" : "El cliente '{key}' se ha conectado al Web Socket Servidor '{host}:{port}' desde '{client_host}:{client_port}'.",
+ "web_socket_server_client_exception" : "Excepción al intentar procesar un mensaje de recepción del cliente '{key}' en el Web Socket Servidor '{host}:{port}'.",
+ "web_socket_server_client_disconnected" : "El cliente '{key}' se ha desconectado del Web Socket Servidor '{host}:{port}'.",
+ "AnP_WebSocketServerDriver_end" : null,
+
+ "AnP_TitlesManager_start" : null,
+ "AnP_TitlesManager_end" : null,
+
+ "AnP_AIModel_start" : null,
+ "AnP_AIModel_end" : null
+
+ }
+}
\ No newline at end of file
diff --git a/JSON/I18N/AnP.i18n.galego.json b/JSON/I18N/AnP.i18n.galego.json
new file mode 100644
index 0000000..39f43be
--- /dev/null
+++ b/JSON/I18N/AnP.i18n.galego.json
@@ -0,0 +1,3 @@
+{
+ "galego" : {}
+}
\ No newline at end of file
diff --git a/JSON/I18N/AnP.i18n.nihongo.json b/JSON/I18N/AnP.i18n.nihongo.json
new file mode 100644
index 0000000..eff18c6
--- /dev/null
+++ b/JSON/I18N/AnP.i18n.nihongo.json
@@ -0,0 +1,3 @@
+{
+ "nihongo" : {}
+}
\ No newline at end of file
diff --git a/JSON/I18N/AnP.i18n.russkiy.json b/JSON/I18N/AnP.i18n.russkiy.json
new file mode 100644
index 0000000..b765480
--- /dev/null
+++ b/JSON/I18N/AnP.i18n.russkiy.json
@@ -0,0 +1,3 @@
+{
+ "russkiy" : {}
+}
\ No newline at end of file
diff --git a/Public/ecma/Application/AnP.ecma.js b/Public/ecma/Application/AnP.ecma.js
new file mode 100644
index 0000000..e44c964
--- /dev/null
+++ b/Public/ecma/Application/AnP.ecma.js
@@ -0,0 +1,191 @@
+"use strict";
+
+import {FilesDriver} from "../Drivers/FilesDriver.ecma.js";
+import {PrintTypesManager} from "../Managers/PrintTypesManager.ecma.js";
+import {SettingsManager} from "../Managers/SettingsManager.ecma.js";
+import {I18NManager} from "../Managers/I18NManager.ecma.js";
+import {ThreadsManager} from "../Managers/ThreadsManager.ecma.js";
+import {UniqueKeysManager} from "../Managers/UniqueKeysManager.ecma.js";
+import {SessionsManager} from "../Managers/SessionsManager.ecma.js";
+import {ModelsManager} from "../Managers/ModelsManager.ecma.js";
+import {ViewsManager} from "../Managers/ViewsManager.ecma.js";
+import {RoutesManager} from "../Managers/RoutesManager.ecma.js";
+import {Components} from "./Components.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @class AnP
+ * @constructor
+ * @param {?(Object.|Array)} [inputs = null]
+ * @param {?simple_callback} [callback = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const AnP = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs AnP
+ * @param {?(Object.|Array)} [inputs = null]
+ * @param {?simple_callback} [callback = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const AnP = function(inputs = null, callback = null){
+
+ /** @type {AnP} */
+ const self = this;
+ /** @type {boolean} */
+ let started = false;
+
+ /** @type {FilesDriver} */
+ this.files = new FilesDriver(self);
+ /** @type {PrintTypesManager} */
+ this.print_types = new PrintTypesManager(self);
+ /** @type {SettingsManager} */
+ this.settings = new SettingsManager(self, inputs);
+ /** @type {I18NManager} */
+ this.i18n = new I18NManager(self);
+ /** @type {ThreadsManager} */
+ this.threads = new ThreadsManager(self);
+ /** @type {UniqueKeysManager} */
+ this.unique_keys = new UniqueKeysManager(self);
+ /** @type {SessionsManager} */
+ this.sessions = new SessionsManager(self);
+ /** @type {ModelsManager} */
+ this.models = new ModelsManager(self);
+ /** @type {Components} */
+ this.components = new Components(self);
+ /** @type {ViewsManager} */
+ this.views = new ViewsManager(self);
+ /** @type {RoutesManager} */
+ this.routes = new RoutesManager(self);
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ self.settings.get("autostart", null, true) && self.start(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array([
+ "files", "settings", "print_types", "i18n", "threads", "models", "views", "routes"
+ ], (key, next) => {
+ self[key].update(next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+ Common.execute_array([
+ "files", "settings", "print_types", "i18n", "threads", "models", "views", "routes"
+ ], (key, next) => {
+ self[key].reset(next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(ok => {
+ if(ok !== false && self.settings.get("build_base_gui")){
+ Common.preload(self.settings.get("position"), (position, asynchronous, ok) => {
+ if(position){
+ Common.HTML(position, self.components.base.build());
+ end(true);
+ }else
+ end(false);
+ });
+ }else
+ end(ok);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!string} type
+ * @param {!(string|Array.)} message
+ * @param {?(Object.|Array)} [inputs = null]
+ * @param {!number} [i = 0]
+ * @return {void}
+ * @access public
+ */
+ this.print = (type, message, inputs = null, i = 0) => {};
+
+ /**
+ * @param {!Error} exception
+ * @param {!(string|Array.)} message
+ * @param {?(Object.|Array)} [inputs = null]
+ * @param {!number} [i = 0]
+ * @return {void}
+ * @access public
+ */
+ this.exception = (exception, message, inputs = null, i = 0) => {};
+
+ constructor();
+
+ };
+
+ return AnP;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Application/Components.ecma.js b/Public/ecma/Application/Components.ecma.js
new file mode 100644
index 0000000..e9180c9
--- /dev/null
+++ b/Public/ecma/Application/Components.ecma.js
@@ -0,0 +1,357 @@
+"use strict";
+
+import {BaseComponent} from "../Components/BaseComponent.ecma.js";
+import {FormsComponent} from "../Components/FormsComponent.ecma.js";
+import {DatesComponent} from "../Components/DatesComponent.ecma.js";
+import {SelectsComponent} from "../Components/SelectsComponent.ecma.js";
+import {TablesComponent} from "../Components/TablesComponent.ecma.js";
+import {SessionMiniComponent} from "../Components/SessionMiniComponent.ecma.js";
+import {LicensesComponent} from "../Components/LicensesComponent.ecma.js";
+import {I18NComponent} from "../Components/I18NComponent.ecma.js";
+import { AIChatComponent } from "../Components/AIChatComponent.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+import {
+ Span, Img, Button, Label, Input, Textarea, Div
+} from "../Utils/HTMLDSL.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class Components
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const Components = (function(){
+
+ /**
+ * @callback event_callback
+ * @param {!HTMLElement} element
+ * @param {!Event} event
+ * @return {any|null|void}
+ */
+
+ /**
+ * @constructs Components
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const Components = function(anp){
+
+ /** @type {Components} */
+ const self = this;
+
+ /** @type {BaseComponent} */
+ this.base = new BaseComponent(anp);
+ /** @type {FormsComponent} */
+ this.forms = new FormsComponent(anp);
+ /** @type {DatesComponent} */
+ this.dates = new DatesComponent(anp);
+ /** @type {SelectsComponent} */
+ this.selects = new SelectsComponent(anp);
+ /** @type {TablesComponent} */
+ this.tables = new TablesComponent(anp);
+ /** @type {SessionMiniComponent} */
+ this.session_mini = new SessionMiniComponent(anp);
+ /** @type {LicensesComponent} */
+ this.licenses = new LicensesComponent(anp);
+ /** @type {I18NComponent} */
+ this.i18n_selector = new I18NComponent(anp);
+ /** @type {AIChatComponent} */
+ this.aichat = new AIChatComponent(anp);
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {!(Object.|Array.)} inputs
+ * @returns {Object.}
+ * @access public
+ */
+ this.set_attributes = (keys, inputs) => Common.get_array(keys).reduce((attributes, key) => {
+
+ /** @type {any|null} */
+ const value = Common.get_value(key, inputs, null);
+
+ value !== null && (attributes[Common.get_array(key)[0]] = value);
+
+ return attributes;
+ }, {});
+
+ /**
+ *
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {Array.}
+ */
+ this.image = (inputs = null) => {
+
+ /** @type {string|null} */
+ const i18n = Common.get_value("i18n", inputs, null),
+ /** @type {string|null} */
+ alternative = Common.get_value(["alt", "alternative"], inputs, null),
+ /** @type {string|null} */
+ text = i18n ? anp.i18n.get([alternative, i18n], inputs) : alternative;
+ /** @type {Array.|string} */
+ let sources = Common.get_value(["src", "source", "sources"], inputs, null);
+
+ return Span({
+ class : "image",
+ data_i : 0,
+ data_data : Common.base64_encode(Check.is_array(sources) ? sources : sources = [sources])
+ }, [
+ Img({
+ src : sources[0],
+ on_load : (item, event) => {
+ item.parentNode.querySelector("span").style.backgroundImage = `url(${item.getAttribute("src")})`;
+ },
+ on_error : (item, event) => {
+
+ const i = Number(item.parentNode.getAttribute("data_i")) + 1;
+
+ item.setAttribute("src", Common.json_decode(Common.base64_decode(item.parentNode.getAttribute("data-data")))[i]);
+ item.parentNode.setAttribute("data_i", i);
+
+ },
+ ...(i18n ? {data_i18n : i18n, data_i18n_without : true, alt : text} : text ? {alt : text} : {})
+ }),
+ Span()
+ ]);
+ };
+
+ this.i18n = (i18n, tag = "span", variables = null) => [tag, {
+ data_i18n : i18n,
+ ...(variables ? {data_i18n_variables : Common.data_encode(variables)} : {})
+ }, anp.i18n.get(i18n, variables)];
+
+ this.icon = (icon, tag = "span") => [tag, {data_icon : icon}];
+
+ this.button = (name, action, inputs = null) => {
+
+ const text = anp.i18n.get(name, (
+ Check.is_bool(inputs) ? {toogled : inputs} :
+ Check.is_string(inputs) ? {type : inputs} :
+ inputs)),
+ has_action = Check.is_function(action),
+ type = Check.is_string(action) ? action : Common.get_value("type", inputs, "button"),
+ toggled = Common.get_value("toggled", inputs, null);
+
+ has_action || (action = null);
+
+ return Button({
+ type : type,
+ data_i18n : name,
+ data_i18n_without : true,
+ title : text,
+ ...(
+ toggled !== null ? {
+ data_toggled : toggled,
+ on_click : (button, event) => {
+ button.setAttribute("data_toggled", button.getAttribute("data_toggled") != "true");
+ Common.execute(action, (button, event));
+ }
+ } :
+ has_action ? {on_click : action} :
+ {}),
+ }, [
+ self.icon(name),
+ self.i18n(name)
+ ]);
+ };
+
+ this.checkbox = (name, inputs = null) => Label({
+ class : "checkbox",
+ data_i18n : name,
+ data_i18n_without : true,
+ title : anp.i18n.get(name),
+ }, [
+ Input({
+ type : "checkbox",
+ name : name,
+ checked : Common.get_value("checked", inputs, false),
+ ...self.set_attributes([
+ ["on_change", "onchange"],
+ ["id", "identifier"]
+ ], inputs)
+ }),
+ self.icon("checkbox"),
+ self.i18n(name)
+ ]);
+
+ this.radio = (name, inputs = null) => {
+
+ const set = Common.get_value("set", inputs);
+
+ return Label({
+ class : "radio radio-button",
+ data_i18n : name,
+ data_i18n_without : true,
+ title : anp.i18n.get(name),
+ }, [
+ Input({
+ type : "radio",
+ name : set ? set + "[]" : name,
+ value : name,
+ checked : Common.get_value("checked", inputs, false),
+ ...self.set_attributes([
+ ["on_change", "onchange"],
+ ["id", "identifier"]
+ ], inputs)
+ }),
+ self.icon("radio"),
+ self.i18n(name)
+ ]);
+ };
+
+ this.text = (name, inputs = null) => {
+
+ const text = anp.i18n.get(name),
+ minimum_length = Common.get_value("minimum_length", inputs, null),
+ maximum_length = Common.get_value("maximum_length", inputs, null);
+
+ return Span({
+ class : "input input-text",
+ data_i18n : name,
+ data_i18n_without : true,
+ title : text
+ }, [
+ Common.get_value("multiline", inputs, false) ? Textarea({
+ name : name,
+ data_i18n : name,
+ data_i18n_without : true,
+ placeholder : text + "...",
+ ...self.set_attributes([
+ ["id", "identifier"],
+ ["maxlength", "maximum_length"],
+ "pattern",
+ "value"
+ ], inputs)
+ }) : Input({
+ type : "text",
+ name : name,
+ data_i18n : name,
+ data_i18n_without : true,
+ placeholder : text + "...",
+ ...self.set_attributes([
+ ["id", "identifier"],
+ ["maxlength", "maximum_length"],
+ "pattern",
+ "value"
+ ], inputs)
+ }),
+ Span({class : "minimum", data_minimum : minimum_length}, "" + minimum_length),
+ Span({class : "length"}, "" + Common.get_value("value", inputs, "").length),
+ Span({class : "maximum", data_maximum : maximum_length}, "" + maximum_length)
+ ]);
+ };
+
+ this.password = (name, inputs = null) => {
+
+ const text = anp.i18n.get(name),
+ minimum_length = Common.get_value("minimum_length", inputs, 0),
+ maximum_length = Common.get_value("maximum_length", inputs, 0);
+
+ return Span({
+ data_i18n : name,
+ data_i18n_without : true,
+ title : text
+ }, [
+ Input({
+ type : "password",
+ name : name,
+ data_i18n : name,
+ data_i18n_without : true,
+ placeholder : text + "...",
+ ...self.set_attributes([
+ ["id", "identifier"],
+ ["maxlength", "maximum_length"],
+ "pattern",
+ "value"
+ ], inputs)
+ }),
+ Span({class : "minimum", data_minimum : minimum_length}, "" + minimum_length),
+ Span({class : "length"}, "" + Common.get_value("value", inputs, "").length),
+ Span({class : "maximum", data_maximum : maximum_length}, "" + maximum_length)
+ ]);
+ };
+
+ this.email = (name, inputs = null) => {
+
+ const text = anp.i18n.get(name);
+
+ return Span({
+ data_i18n : name,
+ data_i18n_without : true,
+ title : text
+ }, [
+ Input({
+ type : "email",
+ name : name,
+ data_i18n : name,
+ data_i18n_without : true,
+ placeholder : text + "...",
+ pattern : "[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$"
+ })
+ ]);
+ };
+
+ this.number = (name, inputs = null) => {
+
+ const text = anp.i18n.get(name),
+ minimum = Common.get_value("minimum", inputs, 0),
+ maximum = Common.get_value("maximum", inputs, 0),
+ value = Common.get_value("value", inputs, 0);
+
+ return Span({
+ data_i18n : name,
+ data_i18n_without : true,
+ title : text
+ }, [
+ Input({
+ type : "number",
+ name : name,
+ data_i18n : name,
+ data_i18n_without : true,
+ placeholder : text + "...",
+ ...self.set_attributes([
+ ["id", "identifier"],
+ ["min", "minimum"],
+ ["max", "maximum"],
+ "step",
+ "value"
+ ], inputs)
+ }),
+ Span({class : "minimum", data_minimum : minimum}, "" + minimum),
+ Span({class : "value"}, "" + value),
+ Span({class : "maximum", data_maximum : maximum}, "" + maximum)
+ ]);
+ };
+
+ /**
+ * @param {!Array.|Array.]>>} buttons
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @returns {Array.}
+ */
+ this.buttons = (buttons, inputs = null) => Div({
+ class : ["buttons"].concat(Common.get_array(Common.get_value("class", inputs, []))).join(" ").trim(),
+ ...Common.get_dictionary(inputs, false, ["class"])
+ }, buttons.map(([name, action, inputs]) => (
+ self.button(name, action, inputs)
+ )));
+
+ constructor();
+
+ };
+
+ return Components;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Application/Event.ecma.js b/Public/ecma/Application/Event.ecma.js
new file mode 100644
index 0000000..db849d5
--- /dev/null
+++ b/Public/ecma/Application/Event.ecma.js
@@ -0,0 +1,97 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @class Event
+ * @constructor
+ * @param {boolean} [once = false]
+ * @param {boolean} [autoexecute = false]
+ * @returns {void}
+ * @access public
+ * @static
+ */
+export const Event = (function(){
+
+ /**
+ * @callback event_callback
+ * @param {...(any|null)} parameters
+ * @return {void}
+ */
+
+ /**
+ * @constructs Event
+ * @param {boolean} [once = false]
+ * @param {boolean} [autoexecute = false]
+ * @returns {void}
+ * @access public
+ * @static
+ */
+ const Event = function(once = false, autoexecute = false){
+
+ /** @type {Event} */
+ const self = this,
+ /** @type {Object.} */
+ events = {},
+ /** @type {number} */
+ id_i = 0;
+ /** @type {boolean} */
+ this.once = once;
+ /** @type {boolean} */
+ this.autoexecute = autoexecute;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {...(any|null)} parameters
+ * @returns {Array.}
+ * @access public
+ */
+ this.execute = (...parameters) => [...Object.entries(events)].map(([id, callback]) => {
+
+ /** @type {any|null} */
+ const response = Common.execute(callback, ...parameters);
+
+ if(self.once)
+ delete events[id];
+
+ return response;
+ });
+
+ /**
+ * @param {!event_callback} callback
+ * @returns {number|null}
+ * @access public
+ */
+ this.add = callback => {
+ if(Check.is_function(callback)){
+
+ events[++ id_i] = callback;
+
+ return id_i;
+ };
+ return null;
+ };
+
+ /**
+ * @param {!number} id
+ * @returns {boolean}
+ * @access public
+ */
+ this.remove = id => {
+ if(events[id]){
+ delete events[id];
+ return true;
+ };
+ return false;
+ };
+
+ };
+
+ return Event;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/AIChatComponent.ecma.js b/Public/ecma/Components/AIChatComponent.ecma.js
new file mode 100644
index 0000000..c4cb7b0
--- /dev/null
+++ b/Public/ecma/Components/AIChatComponent.ecma.js
@@ -0,0 +1,88 @@
+"use strict";
+
+import {Fieldset, Section, Form, Div} from "../Utils/HTMLDSL.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class AIChatComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const AIChatComponent = (function(){
+
+ /**
+ * @constructs AIChatComponent
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const AIChatComponent = function(anp){
+
+ /** @type {AIChatComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ this.build = (inputs = null) => {
+
+ const name = Common.get_value("name", inputs, "aichat");
+
+ return Fieldset({class : "aichat"}, [
+ anp.components.i18n(name, "legend"),
+ Section({class : "messages"}),
+ Form({
+ method : "post",
+ action : "#",
+ on_submit : send,
+ on_key_down : check_keys
+ }, [
+ anp.components.text("message", {
+ multiline : true
+ }),
+ anp.components.button("send", "submit")
+ ])
+ ]);
+ };
+
+ const send = (item, event) => {
+
+ const text_box = item.querySelector("[name=message]"),
+ text = text_box.value.trim();
+
+ event.preventDefault();
+
+ if(text){
+ Common.HTML(".aichat .messages", Fieldset({
+ class : "message",
+ data_type : "user"
+ }, [
+ anp.components.i18n("user", "legend"),
+ Div({class : "content"}, text)
+ ]));
+ text_box.value = "";
+ };
+
+ return false;
+ };
+
+ const check_keys = (item, event) => {
+ event.key == "Enter" && !event.shiftKey && send(item, event);
+ };
+
+ constructor();
+ };
+
+ return AIChatComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/BaseComponent.ecma.js b/Public/ecma/Components/BaseComponent.ecma.js
new file mode 100644
index 0000000..f0cb5b2
--- /dev/null
+++ b/Public/ecma/Components/BaseComponent.ecma.js
@@ -0,0 +1,279 @@
+"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.>} */
+ caches = {};
+ /** @type {integer|null} */
+ let thread = null,
+ /** @type {Array.} */
+ 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.|Array.)} [inputs = null]
+ * @return {Array.}
+ * @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;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/DatesComponent.ecma.js b/Public/ecma/Components/DatesComponent.ecma.js
new file mode 100644
index 0000000..17d699f
--- /dev/null
+++ b/Public/ecma/Components/DatesComponent.ecma.js
@@ -0,0 +1,49 @@
+"use strict";
+
+/**
+ * @class DatesComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const DatesComponent = (function(){
+
+ /**
+ * @constructs DatesComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const DatesComponent = function(anp){
+
+ /** @type {DatesComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ setTimeout(() => {
+ anp.components.dates = self;
+ anp.components.date = self.build;
+ }, 0);
+
+ };
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @return {Array.}
+ * @access public
+ */
+ this.build = inputs => {};
+
+ constructor();
+ };
+
+ return DatesComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/FormsComponent.ecma.js b/Public/ecma/Components/FormsComponent.ecma.js
new file mode 100644
index 0000000..9872e28
--- /dev/null
+++ b/Public/ecma/Components/FormsComponent.ecma.js
@@ -0,0 +1,152 @@
+"use strict";
+
+import {Fieldset, Div, Form, UL} from "../Utils/HTMLDSL.ecma.js";
+
+/**
+ * @class FormsComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const FormsComponent = (function(){
+
+ /**
+ * @callback form_submit_callback
+ * @param {!HTMLElement} form
+ * @param {!Event} event
+ * @return {any|null|void}
+ */
+
+ /**
+ * @constructs FormsComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const FormsComponent = function(anp){
+
+ /** @type {FormsComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ setTimeout(() => {
+ anp.components.forms = self;
+ anp.components.form = self.build;
+ }, 0);
+
+ };
+
+ /**
+ * @param {string} name
+ * @param {!(Object.|Array.)} inputs
+ * @return {Array.}
+ * @access public
+ */
+ this.build = (name, inputs) => {
+
+ /** @type {form_submit_callback|null} */
+ const submit = Common.get_value("submit", inputs, null);
+
+ return Form({
+ data_name : name,
+ method : Common.get_value("method", inputs, "post"),
+ action : Common.get_value("action", inputs, "#"),
+ ...(submit ? {on_submit : submit} : {})
+ }, [
+ Fieldset({class : "form"}, [
+ anp.components.i18n(name, "legend"),
+ anp.components.i18n(name + "_description", "p"),
+ Div({class : "form-fields"}, Common.get_value("structure", inputs, []).map(([type, name, ...inputs], i) => {
+
+ /** @type {string} */
+ let id = anp.unique_keys.create(true),
+ /** @type {number} */
+ l = inputs.length - 1;
+
+ if(!Check.is_dictionary(inputs[l])){
+ inputs.push({});
+ l ++;
+ };
+ inputs[l].id = id;
+
+ return Div({
+ data_field : name,
+ data_i : i
+ }, [
+ Label({
+ for : id,
+ data_i18n : name,
+ data_i18n_without : true,
+ title : anp.components.i18n(name, "label")
+ }, [
+ anp.components.i18n(name),
+ anp.components.i18n(name + "_description"),
+ ]),
+ Span({class : "value"}, [
+ anp.components[type](name, ...inputs)
+ ]),
+ UL({class : "errors"})
+ ]);
+ })),
+ UL({class : "global-errors"}),
+ Div({class : "buttons"}, Common.get_value(["actions", "buttons"], inputs, []).concat(
+ anp.components.button(Common.get_value("submit_reset", inputs, "reset"), "reset"),
+ submit ? anp.components.button(Common.get_value("submit_name", inputs, "submit"), "submit") : null
+ ))
+ ])
+ ]);
+ };
+
+ this.get = item => {
+
+ if(item)
+ while(item.tagName.toLowerCase() != "form" && (item = item.parentElement));
+
+ return item;
+ };
+
+ this.get_values = form => [...self.get(form).querySelectorAll("[name]")].reduce((values, field) => {
+
+ let name = field.getAttribute("name");
+ const is_array = self.is_array(field),
+ tag = field.tagName.toLowerCase(),
+ type = field.getAttribute("type").toLowerCase().trim();
+
+ if(is_array){
+ (values[name = name.slice(0, -2)] = values[name] || []).push(self.get_value(field));
+ }else
+ values[name] = self.get_value(field);
+
+ return values;
+ }, {});
+
+ this.is_array = input => input.getAttribute("name").endsWith("[]");
+
+ this.get_value = input => {
+ switch(input.tagName.toLowerCase()){
+ case "select":
+ return input.multiple ? [...input.options].filter(option => option.selected).map(option => option.value) : input.value;
+ };
+ switch(input.getAttribute("type")){
+ case "checkbox":
+ case "radio":
+ return self.is_array(input) ? input.checked ? input.value : null : input.checked;
+ case "number":
+ return parseFloat(input.value) || 0;
+ };
+ return input.value;
+ };
+
+ constructor();
+ };
+
+ return FormsComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/I18NComponent.ecma.js b/Public/ecma/Components/I18NComponent.ecma.js
new file mode 100644
index 0000000..ee76bfd
--- /dev/null
+++ b/Public/ecma/Components/I18NComponent.ecma.js
@@ -0,0 +1,83 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Nav, Span, Img, UL, LI} from "../Utils/HTMLDSL.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @constructs I18NComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const I18NComponent = (function(){
+
+ /**
+ * @constructs I18NComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const I18NComponent = function(anp){
+
+ /** @type {I18NComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.build = () => Nav({class : "i18n-selector"}, [
+ UL(null, anp.settings.get("i18n_selector").map(([name, text, flag]) => LI({
+ data_language : name,
+ data_selected : anp.i18n.is_language_selected(name),
+ on_click : change,
+ data_role : "link",
+ title : text
+ }, [
+ Img({src : flag, alt : text}),
+ Span(null, text)
+ ])))
+ ]);
+
+ /**
+ * @param {!HTMLElement} item
+ * @param {!Event} event
+ * @return {void}
+ * @access private
+ */
+ const change = (item, event) => {
+ if(anp.i18n.change(item.getAttribute("data-language"))){
+ item.parentNode.childNodes.forEach(option => {
+ option.setAttribute("data-selected", option === item ? "true" : "false");
+ });
+ anp.components.base.get_from(item).querySelectorAll("[data-i18n]").forEach(element => {
+
+ /** @type {string} */
+ const text = anp.i18n.get(element.getAttribute("data-i18n"), Common.data_decode(element.getAttribute("data-i18n-variables")));
+
+ element.getAttribute("data-i18n-without") != "true" && (element.innerText = text);
+ element.hasAttribute("title") && (element.setAttribute("title", text));
+ element.hasAttribute("placeholder") && (element.setAttribute("placeholder", text + "..."));
+ element.hasAttribute("alt") && (element.setAttribute("alt", text));
+
+ });
+ };
+ };
+
+ constructor();
+ };
+
+ return I18NComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/LicensesComponent.ecma.js b/Public/ecma/Components/LicensesComponent.ecma.js
new file mode 100644
index 0000000..83127f2
--- /dev/null
+++ b/Public/ecma/Components/LicensesComponent.ecma.js
@@ -0,0 +1,81 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Div, A} from "../Utils/HTMLDSL.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @constructs LicensesComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const LicensesComponent = (function(){
+
+ /**
+ * @constructs LicensesComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const LicensesComponent = function(anp){
+
+ /** @type {LicensesComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {!Object.} licenses
+ * @returns {Array.}
+ * @access public
+ */
+ this.build = licenses => Div({
+ class : "licenses",
+ }, Object.entries(licenses).map(([name, inputs]) => (
+ self[name] ? self[name](...Common.get_array(inputs)) :
+ null)));
+
+ /**
+ * @param {!(number|[number, number])} years
+ * @param {!(string|Array.)} authors
+ * @returns {Array.}
+ * @access public
+ */
+ this.copyright = (years, authors) => anp.components.i18n("copyright", "span", {
+ authors : Common.get_array(authors).join(", "),
+ years : Common.get_array(years).join("-")
+ });
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.cc_by_nc_sa_4 = () => A({
+ href : anp.settings.get("cc_by_nc_sa_4_link", null, "https://creativecommons.org/licenses/by-nc-sa/4.0/"),
+ target : "_blank",
+ data_i18n : "cc_by_nc_sa_4",
+ data_i18n_without : true,
+ title : anp.i18n.get("cc_by_nc_sa_4")
+ }, [
+ anp.components.i18n("cc_by_nc_sa_4"),
+ anp.components.image({
+ i18n : "cc_by_nc_sa_4",
+ sources : [anp.settings.get("cc_by_nc_sa_4_icon", null, "https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png")],
+ })
+ ])
+
+ constructor();
+ };
+
+ return LicensesComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/SelectsComponent.ecma.js b/Public/ecma/Components/SelectsComponent.ecma.js
new file mode 100644
index 0000000..97b00f1
--- /dev/null
+++ b/Public/ecma/Components/SelectsComponent.ecma.js
@@ -0,0 +1,49 @@
+"use strict";
+
+/**
+ * @class SelectsComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const SelectsComponent = (function(){
+
+ /**
+ * @constructs SelectsComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const SelectsComponent = function(anp){
+
+ /** @type {SelectsComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ setTimeout(() => {
+ anp.components.dates = self;
+ anp.components.date = self.build;
+ }, 0);
+
+ };
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @return {Array.}
+ * @access public
+ */
+ this.build = inputs => {};
+
+ constructor();
+ };
+
+ return SelectsComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/SessionMiniComponent.ecma.js b/Public/ecma/Components/SessionMiniComponent.ecma.js
new file mode 100644
index 0000000..fca7234
--- /dev/null
+++ b/Public/ecma/Components/SessionMiniComponent.ecma.js
@@ -0,0 +1,79 @@
+"use strict";
+
+import {Div, UL, LI, Span, Nav, A} from "../Utils/HTMLDSL.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class SessionMiniComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const SessionMiniComponent = (function(){
+
+ /**
+ * @constructs SessionMiniComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const SessionMiniComponent = function(anp){
+
+ /** @type {SessionMiniComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @returns {Array.}
+ * @access public
+ */
+ this.build = () => Div({
+ class : "sessions-mini",
+ data_status : "unlogged"
+ }, [
+ anp.components.image({}),
+ UL({class : "info"}, Object.entries({
+ user : "Guest",
+ ip : "::1"
+ }).map(([field, _default]) => LI({
+ class : field,
+ data_i18n : field,
+ data_i18n_without : true,
+ title : anp.i18n.get(field),
+ }, [
+ anp.components.icon(field),
+ anp.components.i18n(field),
+ Span({class : "value"}, _default)
+ ]))),
+ Nav({class : "actions"}, [
+ UL(null, ["login", "register", "logout"].map(action => LI({class : action}, [
+ A({
+ href : "#/" + action,
+ data_i18n : action,
+ data_i18n_without : true,
+ title : anp.i18n.get(action)
+ }, [
+ anp.components.icon(action),
+ anp.components.i18n(action)
+ ])
+ ])))
+ ])
+ ]);
+
+ constructor();
+
+ };
+
+ return SessionMiniComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Components/TablesComponent.ecma.js b/Public/ecma/Components/TablesComponent.ecma.js
new file mode 100644
index 0000000..25b64cf
--- /dev/null
+++ b/Public/ecma/Components/TablesComponent.ecma.js
@@ -0,0 +1,49 @@
+"use strict";
+
+/**
+ * @class TablesComponent
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const TablesComponent = (function(){
+
+ /**
+ * @constructs TablesComponent
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const TablesComponent = function(anp){
+
+ /** @type {TablesComponent} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ setTimeout(() => {
+ anp.components.tables = self;
+ anp.components.table = self.build;
+ }, 0);
+
+ };
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @return {Array.}
+ * @access public
+ */
+ this.build = inputs => {};
+
+ constructor();
+ };
+
+ return TablesComponent;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Controllers/AIChatController.ecma.js b/Public/ecma/Controllers/AIChatController.ecma.js
new file mode 100644
index 0000000..b2edc89
--- /dev/null
+++ b/Public/ecma/Controllers/AIChatController.ecma.js
@@ -0,0 +1,39 @@
+"use strict";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class AIChat
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const AIChat = (function(){
+
+ /**
+ * @constructs AIChat
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const AIChat = function(anp){
+
+ /** @type {AIChat} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ constructor();
+ };
+
+ return AIChat;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Controllers/SessionsController.ecma.js b/Public/ecma/Controllers/SessionsController.ecma.js
new file mode 100644
index 0000000..acb618f
--- /dev/null
+++ b/Public/ecma/Controllers/SessionsController.ecma.js
@@ -0,0 +1,45 @@
+"use strict";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class SessionsController
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const SessionsController = (function(){
+
+ /**
+ * @constructs SessionsController
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const SessionsController = function(anp){
+
+ /** @type {SessionsController} */
+ const self = this;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ this.login = () => {};
+
+ this.logout = () => {};
+
+ this.register = () => {};
+
+ constructor();
+ };
+
+ return SessionsController;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Drivers/FilesDriver.ecma.js b/Public/ecma/Drivers/FilesDriver.ecma.js
new file mode 100644
index 0000000..d83472d
--- /dev/null
+++ b/Public/ecma/Drivers/FilesDriver.ecma.js
@@ -0,0 +1,310 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class FilesDriver
+ * @constructor
+ * @param {!AnP} anp
+ * @param {?(Object.|Array)} [inputs = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const FilesDriver = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @callback ok_callback
+ * @param {!boolean} ok
+ * @return {void}
+ */
+
+ /**
+ * @callback load_callback
+ * @param {?string} content
+ * @param {!boolean} ok
+ * @return {void}
+ */
+
+ /**
+ * @callback load_json_callback
+ * @param {!Array.|Array.>} results
+ * @return {void}
+ */
+
+ /**
+ * @constructs FilesDriver
+ * @param {!AnP} anp
+ * @param {?(Object.|Array)} [inputs = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const FilesDriver = function(anp, inputs = null){
+
+ /** @type {FilesDriver} */
+ const self = this;
+ /** @type {Array.} */
+ let default_root_urls = [""],
+ /** @type {number} */
+ default_timeout = 2000,
+ /** @type {boolean} */
+ default_asynchronous = true,
+ /** @type {string} */
+ default_method = "get",
+ /** @type {boolean} */
+ started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+
+ default_timeout = anp.settings.get(["files_driver_timeout", "timeout"], inputs, default_timeout);
+ default_asynchronous = anp.settings.get(["files_driver_asynchronous", "asynchronous"], inputs, default_asynchronous);
+ default_method = anp.settings.get(["files_driver_method", "method"], inputs, default_method);
+
+ Common.execute(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ default_root_urls = [""];
+ default_timeout = 2000;
+ default_asynchronous = true;
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!string} method
+ * @param {!string} url
+ * @param {!boolean} asynchronous
+ * @param {!number} timeout
+ * @param {?load_callback} [callback = null]
+ * @param {!Array.} root_urls
+ * @param {!number} [i = 0]
+ * @param {!Array.} [root_urls_done = []]
+ * @return {void}
+ * @access public
+ */
+ const load_url_route = (method, url, asynchronous, timeout, callback, root_urls, i = 0, root_urls_done = []) => {
+
+ if(i == root_urls.length){
+ Common.execute(callback, null, false);
+ return;
+ };
+
+ /** @type {simple_callback} */
+ const next = () => {
+ load_url_route(method, url, asynchronous, timeout, callback, root_urls, i + 1, root_urls_done.concat(root_urls[i]));
+ };
+
+ if(root_urls_done.includes(root_urls[i])){
+ next();
+ return;
+ };
+
+ /** @type {boolean} */
+ let ended = false;
+ /** @type {XMLHttpRequest} */
+ const ajax = new XMLHttpRequest(),
+ /** @type {ok_callback} */
+ end = (ok = false) => {
+ if(ended)
+ return;
+ ended = true;
+ if(ok)
+ Common.execute(callback, ajax.responseText, true);
+ else
+ next();
+ },
+ /** @type {number} */
+ date = Date.now();
+
+ ajax.open(method, root_urls[i] + url, asynchronous);
+ 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));
+ else if(Date.now() - date >= timeout)
+ end(false);
+ };
+ ajax.send(null);
+
+ ajax.onload = end;
+ ajax.onerror = end;
+ ajax.ontimeout = end;
+
+ };
+
+ /**
+ * @param {!string} url
+ * @param {?load_callback} [callback = null]
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @return {void}
+ * @access public
+ */
+ this.load = (url, callback, inputs = null) => {
+ load_url_route(
+ Common.get_value(["files_driver_method", "method"], inputs, "get").toLowerCase(),
+ url,
+ Common.get_value(["files_driver_asynchronous", "asynchronous"], inputs, default_asynchronous),
+ Common.get_value(["files_driver_timeout", "timeout"], inputs, default_timeout),
+ callback,
+ Common.get_value(["files_driver_root_urls", "root_urls"], inputs, []).concat(default_root_urls)
+ );
+ };
+
+ /**
+ * @param {?any} data
+ * @param {!load_json_callback} callback
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @return {void}
+ * @access public
+ */
+ this.load_json = (data, callback, inputs = null) => {
+
+ if(Check.is_bool(inputs))
+ inputs = {only_dictionaries : inputs};
+
+ /** @type {boolean} */
+ const only_dictionaries = Common.get_value([
+ "files_driver_load_json_only_dictionaries",
+ "load_json_only_dictionaries",
+ "only_dictionaries"
+ ], Check.is_bool(inputs) ? inputs = {
+ only_dictionaries : inputs
+ } : inputs, false),
+ /** @type {Array.|Array.>} */
+ results = [],
+ /** @type {simple_callback} */
+ end = () => {
+ Common.execute(callback, results);
+ };
+
+ if(Check.is_dictionary(data)){
+ results.push(data);
+ end();
+ }else if(Check.is_array(data)){
+ if(only_dictionaries){
+
+ /** @type {number} */
+ const l = data.length;
+ /** @type {number} */
+ let loaded = 0;
+
+ for(let item of data)
+ self.load_json(item, subresults => {
+ results.push(...subresults);
+ ++ loaded == l && end();
+ }, inputs);
+
+ }else{
+ results.push(data);
+ end();
+ };
+ }else if(Check.is_string(data)){
+
+ /** @type {Object.|Array.|null} */
+ let json;
+
+ if(Check.is_json(data, false) && (json = JSON.parse(data)) !== null)
+ self.load_json(json, subresults => {
+ results.push(...subresults);
+ end();
+ }, inputs);
+ else
+ self.load(data, (content, ok) => {
+ self.load_json(content, subresults => {
+ results.push(...subresults);
+ end();
+ }, inputs);
+ }, inputs);
+
+ }else
+ end();
+
+ };
+
+ constructor();
+
+ };
+
+ return FilesDriver;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Drivers/WebSocketsDriver.ecma.js b/Public/ecma/Drivers/WebSocketsDriver.ecma.js
new file mode 100644
index 0000000..1c1478f
--- /dev/null
+++ b/Public/ecma/Drivers/WebSocketsDriver.ecma.js
@@ -0,0 +1,18 @@
+"use strict";
+
+export const WebSocketsDriver = (function(){
+
+ const WebSocketsDriver = function(anp){
+
+ const self = this,
+ clients = {};
+ let client_i = 0;
+
+ const constructor = () => {};
+
+ constructor();
+
+ };
+
+ return WebSocketsDriver;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/ControllersManager.ecma.js b/Public/ecma/Managers/ControllersManager.ecma.js
new file mode 100644
index 0000000..75c9894
--- /dev/null
+++ b/Public/ecma/Managers/ControllersManager.ecma.js
@@ -0,0 +1,157 @@
+"use strict";
+
+import {Check} from "../Utils/Checks.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class ControllersManager
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const ControllersManager = (function(){
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @callback action_callback
+ * @param {...(any|null)} parameters
+ * @return {void}
+ */
+
+ /**
+ * @constructs ControllersManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const ControllersManager = function(anp){
+
+ /** @type {ControllersManager} */
+ const self = this,
+ /** @type {Object.>} */
+ controllers = {};
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+
+ Common.execute(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ Common.clear_dictionary(controllers);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(end);
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ *
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?default_callback} [callback = null]
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ Object.entries(data).forEach(([key, Controller]) => {
+ if(!Controller || (!overwrite && controllers[key]))
+ return;
+ if(Check.is_function(Controller))
+ controllers[key] = new Controller(anp);
+ else if(Check.is_object(Controller))
+ controllers[key] = Controller;
+ else if(Check.is_string(Controller)){
+
+ /** @type {Object.|Function|null} */
+ const Model = anp.models.get(Controller);
+
+ if(Check.is_object(Model))
+ controllers[key] = Model;
+ else if(Check.is_function(Model))
+ controllers[key] = new Model(anp);
+
+ };
+ });
+ Common.execute(callback);
+ }, true);
+ };
+
+ this.execute = (name, action, ...parameters) => {};
+
+ constructor();
+
+ };
+
+ return ControllersManager;
+
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/I18NManager.ecma.js b/Public/ecma/Managers/I18NManager.ecma.js
new file mode 100644
index 0000000..548016b
--- /dev/null
+++ b/Public/ecma/Managers/I18NManager.ecma.js
@@ -0,0 +1,224 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class I18NManager
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const I18NManager = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs I18NManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const I18NManager = function(anp){
+
+ /** @type {!AnP} */
+ const self = this,
+ /** @type {Object.>>} */
+ sentences = {};
+ /** @type {boolean} */
+ let started = false,
+ /** @type {string} */
+ language = "english",
+ /** @type {string} */
+ default_language = "english";
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array(["default_i18n_files", "i18n_files", "default_i18n", "i18n"], (key, next) => {
+ self.add(anp.settings.get(key), true, next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ Common.clear_dictionary(sentences);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|Array.)} texts
+ * @param {?(string|Array.)} languages
+ * @returns {string|Array.|null}
+ * @access private
+ */
+ const get_sentence = (texts, languages) => {
+
+ /** @type {Array.} */
+ const keys = Common.get_keys(texts);
+
+ if(keys.length){
+
+ /** @type {Array.} */
+ const languages_done = [];
+
+ for(let language_key of Common.get_keys(languages).concat(
+ [language, default_language],
+ Object.keys(sentences)
+ )){
+ if(languages_done.includes(language_key))
+ continue;
+ languages_done.push(language_key);
+ if(!sentences[language_key])
+ continue;
+ for(let key of keys)
+ if(sentences[language_key][key] !== undefined)
+ return sentences[language_key][key];
+ };
+
+ };
+ return Common.get_texts(texts)
+
+ };
+
+ /**
+ * @param {!(string|Array.)} texts
+ * @param {?(Object.|Array)} [subinputs = null]
+ * @param {?(string|Array.)} [languages = null]
+ * @returns {any|null}
+ * @access public
+ */
+ this.get = (texts, inputs = null, languages = null) => {
+
+ /** @type {string|Array.|null} */
+ const sentence = get_sentence(texts, languages);
+
+ return Common.string_variables((
+ Check.is_array(sentence) ? sentence.join("") :
+ sentence), inputs);
+ };
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?simple_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ for(let subinputs of data)
+ Object.entries(subinputs).forEach(([new_language, new_sentences]) => {
+ sentences[new_language] || (sentences[new_language] = {});
+ Check.is_dictionary(new_sentences) &&
+ Object.entries(new_sentences).filter(([key, _]) => !Check.is_mark_key(key)).forEach(([key, sentence]) => {
+ sentences[new_language][key] = sentence;
+ });
+ });
+ Common.execute(callback);
+ }, true);
+ };
+
+ /**
+ * @param {!string} new_language
+ * @return {boolean}
+ * @access public
+ */
+ this.change = new_language => {
+ if(sentences[new_language] && new_language != language){
+ language = new_language;
+ return true;
+ };
+ return false;
+ };
+
+ /**
+ * @param {!string} check_language
+ * @returns {boolean}
+ * @access public
+ */
+ this.is_language_selected = check_language => language == check_language;
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ I18NManager.DEFAULT_SETTINGS = {};
+
+ return I18NManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/ModelsManager.ecma.js b/Public/ecma/Managers/ModelsManager.ecma.js
new file mode 100644
index 0000000..dec9a7a
--- /dev/null
+++ b/Public/ecma/Managers/ModelsManager.ecma.js
@@ -0,0 +1,148 @@
+"use strict";
+
+import {Check} from "../Utils/Checks.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class ModelsManager
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const ModelsManager = (function(){
+
+ /**
+ * @callback default_callback
+ * @return {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs ModelsManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const ModelsManager = function(anp){
+
+ /** @type {ModelsManager} */
+ const self = this,
+ /** @type {Object.|Function>} */
+ models = {};
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+
+ Common.execute(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ Common.clear_dictionary(models);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(end);
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?default_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ Object.entries(data).forEach(([key, Model]) => {
+ Model &&
+ (overwrite || !models[key]) &&
+ (Check.is_function(Model) || Check.is_object(Model)) &&
+ (models[key] = Model);
+ });
+ Common.execute(callback);
+ }, true);
+ };
+
+ /**
+ * @param {!string} name
+ * @returns {Object.|Function|null}
+ */
+ this.get = name => models[name] || null;
+
+ constructor();
+
+ };
+
+ return ModelsManager;
+
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/PrintTypesManager.ecma.js b/Public/ecma/Managers/PrintTypesManager.ecma.js
new file mode 100644
index 0000000..16137f4
--- /dev/null
+++ b/Public/ecma/Managers/PrintTypesManager.ecma.js
@@ -0,0 +1,185 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class PrintTypesManager
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const PrintTypesManager = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs PrintTypesManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const PrintTypesManager = function(anp){
+
+ /** @type {!AnP} */
+ const self = this,
+ /** @type {Array.>} */
+ types = [
+ ["unkn", "unknown"]
+ ];
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array(["default_print_types_files", "print_types_files", "default_print_types", "print_types"], (key, next) => {
+ self.add(anp.settings.get(key), true, next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ types.splice(0, types.length);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|Array.)} texts
+ * @param {?(Object.|Array)} [subinputs = null]
+ * @param {?(string|Array.)} [languages = null]
+ * @returns {any|null}
+ * @access public
+ */
+ this.get = type => {
+
+ type = type.toLowerCase();
+
+ for(let group of types)
+ if(group.includes(type))
+ return group[0];
+ return types[0][0];
+ };
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?simple_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ if(Check.is_array(data)){
+ if(data.length && data.every(Check.is_key)){
+
+ /** @type {number} */
+ let i = -1;
+ /** @type {number} */
+ const l = types.length;
+
+ data = data.map(type => type.toLowerCase())
+
+ for(; i < l; i ++)
+ if(data[0] == types[i][0]){
+ types.push(...data.filter(type => !types.includes(type)));
+ break;
+ };
+
+ i == l && types.push(data);
+
+ Common.execute(callback);
+
+ }else{
+ Common.execute_array(data.filter(subitem => !Check.is_dictionary(subitem)), (block, next) => {
+ self.add(block, overwrite, next);
+ }, callback);
+ };
+ }else
+ Common.execute(callback);
+ }, false);
+ };
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ PrintTypesManager.DEFAULT_SETTINGS = {};
+
+ return PrintTypesManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/RoutesManager.ecma.js b/Public/ecma/Managers/RoutesManager.ecma.js
new file mode 100644
index 0000000..07456d3
--- /dev/null
+++ b/Public/ecma/Managers/RoutesManager.ecma.js
@@ -0,0 +1,151 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {RouteModel} from "../Models/RouteModel.ecma.js";
+
+/**
+ * @typedef {import("../AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class RoutesManager
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const RoutesManager = (function(){
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs RoutesManager
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const RoutesManager = function(anp){
+
+ /** @type {RoutesManager} */
+ const self = this,
+ /** @type {Array.} */
+ routes = [];
+ /** @type {boolean} */
+ let started = false,
+ /** @type {string} */
+ last_url = "",
+ /** @type {number|null} */
+ thread = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ thread = anp.threads.add(thread_method, {
+ bucle : true,
+ autoplay : true
+ });
+
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const thread_method = () => {
+
+ /** @type {string} */
+ const current_url = window.location.hash.substring(1);
+
+ if(last_url != current_url){
+ last_url = current_url;
+ self.go(last_url);
+ };
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array(["default_routes_files", "routes_files", "default_routes", "routes"], (key, next) => {
+ self.add(anp.settings.get(key), true, next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ routes.splice(0, routes.length);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ this.add = (inputs, overwrite = false, callback = null) => {
+ Common.execute(callback, true);
+ };
+
+ this.go = path => {};
+
+ constructor();
+ };
+
+ return RoutesManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/SessionsManager.ecma.js b/Public/ecma/Managers/SessionsManager.ecma.js
new file mode 100644
index 0000000..39c5e71
--- /dev/null
+++ b/Public/ecma/Managers/SessionsManager.ecma.js
@@ -0,0 +1,81 @@
+"use strict";
+
+import {SessionModel} from "../Models/SessionModel.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class SessionsManager
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const SessionsManager = (function(){
+
+ /**
+ * @constructs SessionsManager
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const SessionsManager = function(anp){
+
+ /** @type {SessionsManager} */
+ const self = this,
+ /** @type {Object.} */
+ sessions = {};
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {!(Object.|Array.)} inputs
+ * @return {number}
+ * @access public
+ */
+ this.set = inputs => {
+
+ /** @type {number} */
+ let i;
+
+ while(sessions[i = Math.random() * (1 << 28) | 0]);
+
+ return (sessions[i] = new SessionModel(i, inputs)).i;
+ };
+
+ /**
+ * @param {!(number|Array.)} ids
+ * @param {!(string|Array.)} permissions
+ * @returns {boolean}
+ * @access public
+ */
+ this.check_permissions = (ids, permissions) => Common.get_array(ids).some(id => (
+ sessions[id] && sessions[id].check_permissions(permissions)
+ ));
+
+ /**
+ * @param {!number} id
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = id => {
+ if(sessions[id]){
+ delete sessions[id];
+ return true;
+ };
+ return false;
+ };
+
+ constructor();
+ };
+
+ return SessionsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/SettingsManager.ecma.js b/Public/ecma/Managers/SettingsManager.ecma.js
new file mode 100644
index 0000000..4090041
--- /dev/null
+++ b/Public/ecma/Managers/SettingsManager.ecma.js
@@ -0,0 +1,189 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class SettingsManager
+ * @constructor
+ * @param {!AnP} anp
+ * @param {?(Object.|Array)} [inputs = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const SettingsManager = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs SettingsManager
+ * @param {!AnP} anp
+ * @param {?(Object.|Array)} [inputs = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const SettingsManager = function(anp, inputs = null){
+
+ /** @type {!AnP} */
+ const self = this,
+ /** @type {Object.} */
+ settings = {},
+ /** @type {Object.} */
+ secrets = {};
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array(["default_settings_files", "settings_files", "default_settings", "settings"], (key, next) => {
+ self.add(self.get(key), true, next);
+ }, () => {
+ Common.execute_array(["default_secrets_files", "secrets_files", "default_secrets", "secrets"], (key, next) => {
+ self.add_secrets(self.get(key), true, next);
+ }, callback, true);
+ }, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ [settings, secrets].forEach(Common.clear_dictionary);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {?(Object.|Array)} [subinputs = null]
+ * @param {?any} [_default = null]
+ * @returns {any|null}
+ * @access public
+ */
+ this.get = (keys, subinputs = null, _default = null) => Common.get_value(keys, [
+ subinputs, inputs, secrets, settings, SettingsManager.DEFAULT_SETTINGS
+ ], _default);
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?simple_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ for(let subinputs of data)
+ Object.entries(subinputs).filter(([key, _]) => (
+ !Check.is_mark_key(key) &&
+ (overwrite || settings[key] === undefined)
+ )).forEach(([key, value]) => {
+ settings[key] = value;
+ });
+ Common.execute(callback);
+ }, true);
+ };
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?simple_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add_secrets = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ for(let subinputs of data)
+ Object.entries(subinputs).filter(([key, _]) => (
+ !Check.is_mark_key(key) &&
+ (overwrite || secrets[key] === undefined))
+ ).forEach(([key, value]) => {
+ secrets[key] = value;
+ });
+ Common.execute(callback);
+ }, true);
+ };
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ SettingsManager.DEFAULT_SETTINGS = {
+ autostart : true,
+ default_settings_files : "/json/AnP.settings.json"
+ };
+
+ return SettingsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/ThreadsManager.ecma.js b/Public/ecma/Managers/ThreadsManager.ecma.js
new file mode 100644
index 0000000..e8f90b2
--- /dev/null
+++ b/Public/ecma/Managers/ThreadsManager.ecma.js
@@ -0,0 +1,258 @@
+"use strict";
+
+import {ThreadModel} from "../Models/ThreadModel.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class ThreadsManager
+ * @constructor
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const ThreadsManager = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs ThreadsManager
+ * @param {!AnP} anp
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const ThreadsManager = function(anp){
+
+ /** @type {!AnP} */
+ const self = this,
+ /** @type {Object.>} */
+ threads = {};
+ /** @type {boolean} */
+ let started = false,
+ /** @type {number} */
+ thread_i = 0,
+ /** @type {number} */
+ interval = null,
+ /** @type {string} */
+ mode = "interval",
+ /** @type {number} */
+ frames_per_second = 60,
+ /** @type {number} */
+ timer = 1000 / frames_per_second;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+
+ self.start();
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+
+ mode = anp.settings.get("threads_mode", null, mode);
+
+ Common.execute(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ Common.clear_dictionary(threads);
+ mode = "interval";
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => {
+ executor();
+ end(true);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const execute = () => {
+
+ /** @type {number} */
+ const time = Date.now();
+
+ [...Object.entries(threads)].forEach(([i, thread]) => {
+ if(!thread.stopped && (
+ !thread.frames_per_second || time - thread.last >= thread.timer
+ )){
+ thread.callback(thread);
+ if(thread.bucle)
+ thread.last = time;
+ else
+ delete threads[i];
+ };
+ });
+
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const executor = () => {
+
+ if(!started)
+ return;
+
+ switch(mode){
+ case "interval":
+ interval = setInterval(execute, timer);
+ break;
+ case "timeout":
+ setTimeout(() => {
+ execute();
+ executor();
+ }, timer);
+ break;
+ case "animation":
+
+ /** @type {number} */
+ const time = Date.now();
+
+ requestAnimationFrame(() => {
+ execute();
+ setTimeout(executor, timer - (Date.now() - time));
+ });
+
+ break;
+ case "only_animation":
+ requestAnimationFrame(() => {
+ execute();
+ executor();
+ });
+ break;
+ };
+
+ };
+
+ /**
+ * @param {?any} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?simple_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (callback, inputs = null) => {
+ if(!Check.is_function(callback))
+ return null;
+
+ threads[++ thread_i] = new ThreadModel(callback, thread_i, inputs);
+
+ return thread_i;
+ };
+
+ /**
+ * @param {!number} i
+ * @return {void}
+ * @access public
+ */
+ this.play = i => {
+ threads[i] && threads[i].stopped && (threads[i].stopped = false);
+ };
+
+ /**
+ * @param {!number} i
+ * @return {void}
+ * @access public
+ */
+ this.stop = i => {
+ threads[i] && !threads[i].stopped && (threads[i].stopped = true);
+ };
+
+ /**
+ * @param {!number} i
+ * @return {boolean}
+ * @access public
+ */
+ this.close = i => {
+ if(threads[i]){
+ delete threads[i];
+ return true;
+ };
+ return false;
+ };
+
+ constructor();
+
+ };
+
+ /** @type {Object.} */
+ ThreadsManager.DEFAULT_SETTINGS = {};
+
+ return ThreadsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/UniqueKeysManager.ecma.js b/Public/ecma/Managers/UniqueKeysManager.ecma.js
new file mode 100644
index 0000000..16bd864
--- /dev/null
+++ b/Public/ecma/Managers/UniqueKeysManager.ecma.js
@@ -0,0 +1,221 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+import {Check} from "../Utils/Checks.ecma.js";
+import {RE} from "../Utils/Patterns.ecma.js";
+import {UniqueKeyModel} from "../Models/UniqueKeyModel.ecma.js";
+
+/**
+ * @typedef {import("../Application/AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class UniqueKeysManager
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const UniqueKeysManager = (function(){
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs UniqueKeysManager
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const UniqueKeysManager = function(anp){
+
+ /** @type {UniqueKeysManager} */
+ const self = this,
+ /** @type {Object.} */
+ keys = {},
+ /** @type {Array.} */
+ alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".split("");
+ /** @type {boolean} */
+ let started = false,
+ /** @type {number} */
+ length = 13,
+ /** @type {number} */
+ keys_i = 0,
+ /** @type {number|null} */
+ thread = null;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+
+ /** @type {string|Array.} */
+ const new_alphabet = anp.settings.get("unique_keys_alphabet", null, alphabet);
+
+ alphabet.splice(0, keys.length, ...Common.unique(
+ Check.is_string(new_alphabet) ? new_alphabet.split("") :
+ Check.is_array(new_alphabet) ? new_alphabet :
+ alphabet));
+ length = anp.settings.get("unique_keys_length", null, length);
+
+ Common.execute(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ keys.splice(0, keys.length);
+ alphabet.splice(0, keys.length, ..."123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".split(""));
+ length = 13;
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => {
+ thread = anp.threads.add(thread_method);
+ end(true);
+ });
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const thread_method = () => {
+ [...Object.entries(keys)].forEach(([i, model]) => {
+ if(model.is_html_item){
+ if(model.loaded){
+ if(!document.querySelector("#" + model.key + ",." + model.key + ",[name=" + model.key + "]"))
+ delete keys[i];
+ }else if(document.querySelector("#" + model.key + ",." + model.key + ",[name=" + model.key + "]"))
+ model.loaded = true;
+ };
+ });
+ };
+
+ /**
+ * @param {!string} key
+ * @returns {boolean}
+ * @access public
+ */
+ this.exists = key => Object.values(keys).some(k => k.key === key);
+
+ /**
+ * @param {!boolean} [is_html_item = false]
+ * @return {string}
+ * @access public
+ */
+ this.create = (is_html_item = false) => {
+
+ /** @type {string} */
+ let key;
+ /** @type {number} */
+ const l = alphabet.length,
+ /** @type {number} */
+ shift = Math.ceil(Math.log2(alphabet.length));
+
+ do{
+ key = "";
+ do{
+
+ /** @type {number} */
+ let random = (Math.random() * (1 << 28)) | 0;
+
+ while(random && (key += alphabet[random % l]).length < length)
+ random >>= shift;
+
+ }while(key.length < length);
+ }while(
+ !RE.KEY.test(key) ||
+ document.querySelector("#" + key + ",." + key + ",[name=" + key + "]") ||
+ self.exists(key)
+ );
+ keys[++ keys_i] = new UniqueKeyModel(key, is_html_item, keys_i);
+
+ return key;
+ };
+
+ /**
+ * @param {!string} key
+ * @returns {boolean}
+ * @access public
+ */
+ this.remove = key => {
+ return [...Object.entries(keys)].some(([i, model]) => {
+ if(model.key == key){
+ delete keys[i];
+ return true;
+ };
+ return false;
+ });
+ };
+
+ constructor();
+
+ };
+
+ return UniqueKeysManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Managers/ViewsManager.ecma.js b/Public/ecma/Managers/ViewsManager.ecma.js
new file mode 100644
index 0000000..6f89f50
--- /dev/null
+++ b/Public/ecma/Managers/ViewsManager.ecma.js
@@ -0,0 +1,138 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @typedef {import("../AnP.ecma.js").AnP} AnP
+ */
+
+/**
+ * @class ViewsManager
+ * @constructor
+ * @param {!AnP} anp
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const ViewsManager = (function(){
+
+ /**
+ * @callback continue_callback
+ * @param {!boolean} ok
+ * @return {boolean}
+ */
+
+ /**
+ * @constructs ViewsManager
+ * @param {!AnP} anp
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const ViewsManager = function(anp){
+
+ /** @type {ViewsManager} */
+ const self = this,
+ /** @type {Object.>} */
+ views = {};
+ /** @type {boolean} */
+ let started = false;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {};
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.update = (callback = null) => {
+ Common.execute_array(["default_views_files", "views_files", "default_views", "views"], (key, next) => {
+ self.add(self.get(key), true, next);
+ }, callback, true);
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.reset = (callback = null) => {
+
+ Common.clear_dictionary(views);
+
+ self.update(callback);
+
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.start = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(started){
+ end(false);
+ return false;
+ };
+ started = true;
+
+ self.update(() => end(true));
+
+ return true;
+ };
+
+ /**
+ * @param {?continue_callback} callback
+ * @returns {boolean}
+ * @access public
+ */
+ this.close = (callback = null) => {
+
+ /** @type {continue_callback} */
+ const end = ok => Common.execute(callback, ok);
+
+ if(!started){
+ end(false);
+ return false;
+ };
+ started = false;
+
+ end(true);
+
+ return true;
+ };
+
+ /**
+ * @param {?(Object.|Array.)} inputs
+ * @param {!boolean} [overwrite = false]
+ * @param {?continue_callback} [callback = null]
+ * @return {void}
+ * @access public
+ */
+ this.add = (inputs, overwrite = false, callback = null) => {
+ anp.files.load_json(inputs, data => {
+ data.forEach(set => {
+ Object.entries(set).forEach(([key, value]) => {
+ if(overwrite || !views[key])
+ views[key] = value;
+ });
+ });
+ Common.execute(callback, true);
+ }, true);
+ };
+
+ this.get = (key, inputs = null) => {};
+
+ constructor();
+ };
+
+ return ViewsManager;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Models/RouteModel.ecma.js b/Public/ecma/Models/RouteModel.ecma.js
new file mode 100644
index 0000000..886b72f
--- /dev/null
+++ b/Public/ecma/Models/RouteModel.ecma.js
@@ -0,0 +1,59 @@
+"use strict";
+
+import {Check} from "../Utils/Checks.ecma.js";
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @class RouteModel
+ * @constructor
+ * @param {!(string|RegExp)} route
+ * @param {!string} view
+ * @returns {void}
+ * @access public
+ * @static
+ */
+export const RouteModel = (function(){
+
+ /**
+ * @constructs RouteModel
+ * @param {!(string|RegExp)} route
+ * @param {!string} view
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const RouteModel = function(route, view){
+
+ /** @type {RouteModel} */
+ const self = this;
+
+ /** @type {RegExp|null} */
+ this.route = null;
+ /** @type {Array.} */
+ this.variables = [];
+ /** @type {string} */
+ this.view = view;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+ if(Check.is_string(route))
+ route = new RegExp("^" + Common.to_regular_expression(route.replace(RE.STRING_VARIABLES, (_, key) => {
+ self.variables.push(key);
+ return "#######";
+ })).replace(/#{7}/g, "([^\\/]+)") + "\\/?$", "i");
+ else if(Check.is_regular_expression(route))
+ (this.route = route).source.replace(/\([^\(\)]+\)/g, all => {
+ self.variables.push("" + self.variables.length);
+ return all;
+ });
+ };
+
+ constructor();
+
+ };
+
+ return RouteModel;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Models/SessionModel.ecma.js b/Public/ecma/Models/SessionModel.ecma.js
new file mode 100644
index 0000000..dca5e25
--- /dev/null
+++ b/Public/ecma/Models/SessionModel.ecma.js
@@ -0,0 +1,70 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @class SessionModel
+ * @constructor
+ * @param {!(Object.|Array.)} inputs
+ * @return {void}
+ * @access private
+ * @static
+ */
+export const SessionModel = (function(){
+
+ /**
+ * @constructs SessionModel
+ * @param {!number} i
+ * @param {!(Object.|Array.)} inputs
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const SessionModel = function(i, inputs){
+
+ /** @type {SessionModel} */
+ const self = this;
+ /** @type {number|null} */
+ let id = null,
+ /** @type {Array.} */
+ permissions = [],
+ /** @type {number} */
+ last_check = 0;
+
+ /** @type {string|null} */
+ this.nick = null;
+ /** @type {number} */
+ this.type = SessionModel.LOCAL;
+ /** @type {number} */
+ this.i = i;
+
+ /**
+ * @returns {void}
+ * @access private
+ */
+ const constructor = () => {
+ id = Common.get_value("id", inputs, null);
+ permissions = Common.get_value("permissions", inputs, []);
+ self.nick = Common.get_value("nick", inputs, null);
+ self.type = Common.get_value("type", inputs, self.type);
+ last_check = Date.now();
+ };
+
+ /**
+ * @param {!(string|Array.)} permissions
+ * @returns {boolean}
+ * @access public
+ */
+ this.check_permissions = permissions => permissions.some(self.permissions.includes);
+
+ constructor();
+
+ };
+
+ /** @type {number} */
+ SessionModel.LOCAL = 1 << 0;
+ /** @type {number} */
+ SessionModel.REMOTE = 1 << 1;
+
+ return SessionModel;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Models/ThreadModel.ecma.js b/Public/ecma/Models/ThreadModel.ecma.js
new file mode 100644
index 0000000..49dd910
--- /dev/null
+++ b/Public/ecma/Models/ThreadModel.ecma.js
@@ -0,0 +1,53 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @class ThreadModel
+ * @constructor
+ * @param {!thread_callback} callback
+ * @param {!(Object.|Array.)} inputs
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const ThreadModel = (function(){
+
+ /**
+ * @callback thread_callback
+ * @param {!ThreadModel} thread
+ * @return {void}
+ */
+
+ /**
+ * @constructs ThreadModel
+ * @param {!thread_callback} callback
+ * @param {!number} i
+ * @param {?(Object.|Array.)} [inputs = null]
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const ThreadModel = function(callback, i, inputs = null){
+ /** @type {number} */
+ this.i = i;
+ /** @type {thread_callback} */
+ this.callback = callback;
+ /** @type {boolean} */
+ this.autoplay = Common.get_value("autoplay", inputs, true);
+ /** @type {boolean} */
+ this.play_immediately = Common.get_value("play_immediately", inputs, false);
+ /** @type {number} */
+ this.last = this.play_immediately ? 0 : Date.now();
+ /** @type {boolean} */
+ this.stopped = Common.get_value("stopped", inputs, false);
+ /** @type {number} */
+ this.frames_per_second = Common.get_value(["frames_per_second", "fps"], inputs, 0);
+ /** @type {number} */
+ this.timer = this.frames_per_second > 0 ? 1000 / this.frames_per_second : 0;
+ /** @type {boolean} */
+ this.bucle = Common.get_value("bucle", inputs, false);
+ };
+
+ return ThreadModel;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Models/UniqueKeyModel.ecma.js b/Public/ecma/Models/UniqueKeyModel.ecma.js
new file mode 100644
index 0000000..a7bb84e
--- /dev/null
+++ b/Public/ecma/Models/UniqueKeyModel.ecma.js
@@ -0,0 +1,45 @@
+"use strict";
+
+import {Common} from "../Utils/Common.ecma.js";
+
+/**
+ * @class ThreadModel
+ * @constructor
+ * @param {!thread_callback} callback
+ * @param {!(Object.|Array.)} inputs
+ * @return {void}
+ * @access public
+ * @static
+ */
+export const UniqueKeyModel = (function(){
+
+ /**
+ * @callback thread_callback
+ * @param {!UniqueKeyModel} thread
+ * @return {void}
+ */
+
+ /**
+ * @constructs UniqueKeyModel
+ * @param {!string} key
+ * @param {!boolean} is_html_item
+ * @param {!number} i
+ * @return {void}
+ * @access private
+ * @static
+ */
+ const UniqueKeyModel = function(key, is_html_item, i){
+ /** @type {number} */
+ this.i = i;
+ /** @type {string} */
+ this.key = key;
+ /** @type {boolean} */
+ this.is_html_item = is_html_item;
+ /** @type {boolean} */
+ this.loaded = false;
+ /** @type {number} */
+ this.date = Date.now();
+ };
+
+ return UniqueKeyModel;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/Checks.ecma.js b/Public/ecma/Utils/Checks.ecma.js
new file mode 100644
index 0000000..3d34666
--- /dev/null
+++ b/Public/ecma/Utils/Checks.ecma.js
@@ -0,0 +1,147 @@
+"use strict";
+
+import {RE} from "./Patterns.ecma.js";
+import {Common} from "./Common.ecma.js";
+
+/**
+ * @class Check
+ * @constructor
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const Check = (function(){
+
+ /**
+ * @constructs Check
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const Check = function(){};
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_string = item => typeof item == "string";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_key = item => typeof item == "string" && RE.KEY.test(item);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_object = item => item && typeof item == "object";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_dictionary = item => item && item.constructor == Object;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_array = item => item instanceof Array;
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_function = item => typeof item == "function";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_bool = item => typeof item == "boolean";
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_json_item = item => Check.is_dictionary(item) || Check.is_array(item);
+
+ /**
+ * @param {?any} item
+ * @param {!boolean} [full_check = null]
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_json = (item, full_check = true) => (
+ item && Check.is_string(item) &&
+ ["[]", "{}"].includes((item = item.trim())[0] + item[item.length - 1]) &&
+ (!full_check || JSON.parse(item, () => null) !== null)
+ );
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_html_item = item => item && (item.tagName || item.nodeName);
+
+ /**
+ * @param {?any} item
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_regular_expression = item => item instanceof RegExp;
+
+ /**
+ * @param {!string} key
+ * @param {!(string|Array.)} [marks = "AnP"]
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_mark_key = (key, marks = "AnP") => (
+ Check.is_key(key) &&
+ Common.get_keys(marks).some(mark => key.startsWith(mark + "_")) &&
+ ["end", "start"].some(close => key.endsWith("_" + close))
+ );
+
+ /**
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_mobile = () => ((a) => (
+ /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) ||
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))
+ ))(navigator.userAgent || navigator.vendor || window.opera);
+
+ /**
+ * @returns {boolean}
+ * @access public
+ * @static
+ */
+ Check.is_dark_mode = () => window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
+
+ return Check;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/Common.ecma.js b/Public/ecma/Utils/Common.ecma.js
new file mode 100644
index 0000000..9ef38a0
--- /dev/null
+++ b/Public/ecma/Utils/Common.ecma.js
@@ -0,0 +1,471 @@
+"use strict";
+
+import {Check} from "./Checks.ecma.js";
+import {RE} from "./Patterns.ecma.js";
+
+/**
+ * @class Common
+ * @constructor
+ * @returns {void}
+ * @access private
+ * @static
+ */
+export const Common = (function(){
+
+ /**
+ * @callback execute_callback
+ * @param {...(any|null)} parameters
+ * @returns {any|null}
+ */
+
+ /**
+ * @callback simple_callback
+ * @returns {void}
+ */
+
+ /**
+ * @callback each_item_callback
+ * @param {?any} item
+ * @param {!simple_callback} next
+ * @return {void}
+ */
+
+ /**
+ * @callback preload_callback
+ * @param {?HTMLElement} item
+ * @param {!boolean} asynchronous
+ * @param {!boolean} ok
+ * @return {void}
+ */
+
+ /**
+ * @constructs Common
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const Common = function(){};
+
+ /** @type {Object.>} */
+ Common.REGULAR_EXPRESSION = {
+ TO : {
+ "\n" : "n",
+ "\r" : "r",
+ "\t" : "t"
+ },
+ FROM : {
+ n : "\n",
+ r : "\r",
+ t : "\t"
+ }
+ };
+
+ /**
+ * @param {...(any|null)} items
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Common.get_keys = (...items) => items.reduce((keys, item) => {
+
+ if(Check.is_key(item))
+ keys.includes(item) || keys.push(item);
+ else if(Check.is_array(item))
+ keys.push(...Common.get_keys(...item).filter(key => !keys.includes(key)));
+
+ return keys;
+ }, []);
+
+ /**
+ * @param {...(any|null)} items
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Common.get_texts = (...items) => items.reduce((texts, item) => {
+
+ if(Check.is_string(item))
+ texts.includes(item) || texts.push(item);
+ else if(Check.is_array(item))
+ texts.push(...Common.get_texts(...item));
+
+ return texts;
+ }, []);
+
+ /**
+ * @param {...(any|null)} items
+ * @returns {Array.>}
+ * @access public
+ * @static
+ */
+ Common.get_dictionaries = (...items) => items.reduce((dictionaries, item) => {
+
+ if(Check.is_dictionary(item))
+ dictionaries.push(item);
+ else if(Check.is_array(item))
+ dictionaries.push(...Common.get_dictionaries(...item));
+
+ return dictionaries;
+ }, []);
+
+ /**
+ * @param {any|null} items
+ * @param {!boolean} [overwrite = false]
+ * @param {!Array.} [except = []]
+ * @returns {Object.}
+ * @access public
+ * @static
+ */
+ Common.get_dictionary = (items, overwrite = false, except = []) => {
+ if(Check.is_dictionary(items))
+ return Object.entries(items).filter(([key, _]) => !except.includes(key)).reduce((dictionary, [key, value]) => {
+ dictionary[key] = value;
+ return dictionary;
+ }, {});
+ if(Check.is_array(items))
+ return items.reduce((dictionary, item) => {
+
+ if(Check.is_dictionary(item))
+ Object.keys(item).filter(key => (
+ overwrite || dictionary[key] === undefined
+ ) && !except.includes(key)).forEach(key => {
+ dictionary[key] = item[key];
+ });
+ else if(Check.is_array(item))
+ Object.entries(Common.get_dictionary(item, overwrite, except)).filter(([key, _]) => (
+ overwrite || dictionary[key] === undefined
+ ) && !except.includes(key)).forEach(([key, value]) => {
+ dictionary[key] = value;
+ });
+
+ return dictionary;
+ }, {});
+ return {};
+ };
+
+ /**
+ * @param {?any} items
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Common.get_array = items => Check.is_array(items) ? items : [items];
+
+ /**
+ * @param {!(string|Array.)} keys
+ * @param {!(Object.|Array)} inputs
+ * @param {?any} [_default = null]
+ * @returns {any|null}
+ * @access public
+ * @static
+ */
+ Common.get_value = (keys, inputs, _default = null) => {
+ if((keys = Common.get_keys(keys)).length)
+ for(let subinputs of Common.get_dictionaries(inputs))
+ for(let key of keys)
+ if(subinputs[key] !== undefined)
+ return subinputs[key];
+ return _default;
+ };
+
+ /**
+ * @param {!string} string
+ * @param {!(Object.|Array)} inputs
+ * @param {?any} [_default = null]
+ * @returns {string}
+ * @access public
+ * @static
+ */
+ Common.string_variables = (string, inputs, _default = null) => {
+
+ inputs = Common.get_dictionary(inputs);
+
+ return ("" + string).replace(RE.STRING_VARIABLES, (all, key) => (
+ inputs[key] !== undefined ? inputs[key] :
+ _default !== null ? _default :
+ all));
+ };
+
+ /**
+ * @param {!execute_callback} callback
+ * @param {...(any|null)} parameters
+ * @returns {any|null}
+ * @access public
+ * @static
+ */
+ Common.execute = (callback, ...parameters) => Check.is_function(callback) ? callback(...parameters) : null;
+
+ /**
+ * @param {!Array.} array
+ * @param {!each_item_callback} each_callback
+ * @param {?simple_callback} end_callback
+ * @param {!number} [i = null]
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const execute_array_ordered = (array, each_callback, end_callback, i = 0) => {
+ if(i == array.length)
+ Common.execute(end_callback);
+ else
+ Common.execute(each_callback, array[i], () => {
+ execute_array_ordered(array, each_callback, end_callback, i + 1);
+ });
+ };
+
+ /**
+ * @param {!Array.} array
+ * @param {!each_item_callback} each_callback
+ * @param {?simple_callback} end_callback
+ * @returns {void}
+ * @access private
+ * @static
+ */
+ const execute_array_unordered = (array, each_callback, end_callback) => {
+
+ /** @type {number} */
+ const l = array.length;
+ /** @type {number} */
+ let i = 0;
+
+ if(array.length){
+ for(let item of array)
+ Common.execute(each_callback, item, () => {
+ ++ i == l && Common.execute(end_callback);
+ });
+ }else
+ Common.execute(end_callback);
+
+ };
+
+ /**
+ * @param {!Array.} array
+ * @param {!each_item_callback} each_callback
+ * @param {?simple_callback} end_callback
+ * @param {!boolean} [ordered = false]
+ * @returns {void}
+ * @access public
+ * @static
+ */
+ Common.execute_array = (array, each_callback, end_callback = null, ordered = false) => {
+ if(ordered)
+ execute_array_ordered(array, each_callback, end_callback);
+ else
+ execute_array_unordered(array, each_callback, end_callback);
+ };
+
+ /**
+ * @param {!Object.} dictionary
+ * @returns {void}
+ * @access public
+ * @static
+ */
+ Common.clear_dictionary = dictionary => {
+ [...Object.keys(dictionary)].forEach(key => {
+ delete dictionary[key];
+ });
+ };
+
+ /**
+ * @param {!string} string
+ * @returns {string}
+ * @access public
+ * @static
+ */
+ Common.to_kebab = string => string.replace(RE.TO_KEBAB, (_, capital, rest, special) => (
+ capital ? "-" + (capital + rest).toLowerCase() :
+ special ? "-" :
+ "")).toLowerCase();
+
+ /**
+ * @param {!(string|HTMLElement)} selector
+ * @param {!preload_callback} callback
+ * @param {!number} [timeout = 2000]
+ * @param {!number} [fps = 10]
+ * @returns {void}
+ * @access public
+ * @static
+ */
+ Common.preload = (selector, callback, timeout = 2000, fps = 10) => {
+ if(!Check.is_function(callback))
+ return;
+ if(Check.is_html_item(selector))
+ return callback(selector, false, true);
+ if(!Check.is_string(selector))
+ return callback(null, false, false);
+
+ /** @type {HTMLElement|null} */
+ let item;
+
+ try{
+ if(item = document.querySelector(selector))
+ try{
+ return callback(item, false, true);
+ }catch(exception){
+ console.error(exception);
+ };
+ }catch(exception){
+ return callback(null, false, false);
+ };
+
+ /** @type {number} */
+ const date = Date.now(),
+ /** @type {number} */
+ interval = setInterval(() => {
+ if(item = document.querySelector(selector)){
+ clearInterval(interval);
+ return callback(item, true, true);
+ };
+ if(Date.now() - date > timeout){
+ clearInterval(interval);
+ return callback(null, true, false);
+ };
+ }, 1000 / fps);
+
+ };
+
+ /**
+ * @param {...(HTMLElement|string|Array.)} inputs
+ * @returns {Array.}
+ * @access public
+ * @static
+ */
+ Common.HTML = (...inputs) => {
+
+ /** @type {DocumentFragment} */
+ const fragment = new DocumentFragment(),
+ /** @type {HTMLElement|null} */
+ master = (
+ Check.is_html_item(inputs[0]) ? inputs[0] :
+ Check.is_string(inputs[0]) ? document.querySelector(inputs[0]) :
+ null);
+
+ (master ? inputs.slice(1) : inputs).forEach(subinputs => {
+ if(Check.is_html_item(subinputs))
+ fragment.appendChild(subinputs);
+ else if(Check.is_string(subinputs))
+ fragment.appendChild(document.createTextNode(subinputs));
+ else if(Check.is_array(subinputs)){
+
+ /** @type {[string, Object.|null, any|null]} */
+ const [tag, attributes, children] = subinputs.concat([null, null]).slice(0, 3),
+ /** @type {HTMLElement} */
+ item = document.createElement(tag);
+
+ Check.is_dictionary(attributes) && Object.entries(attributes).forEach(([key, value]) => {
+ if(RE.ON_ATTRIBUTE.test(key) && Check.is_function(value))
+ item.addEventListener(key.replace(RE.ON_ATTRIBUTE, "").replace(RE.SPECIAL_HTML_EVENT_CHARACTERS, "").toLowerCase(), event => {
+ value(item, event);
+ });
+ else if(value !== null)
+ item.setAttribute(Common.to_kebab(key), value);
+ });
+
+ if(Check.is_array(children))
+ Common.HTML(...children).forEach(child => item.appendChild(child));
+ else if(Check.is_string(children))
+ item.innerHTML += children;
+
+ fragment.appendChild(item);
+
+ };
+ });
+
+ master && master.appendChild(fragment);
+
+ return [...fragment.childNodes];
+ };
+
+ /**
+ * @param {?any} value
+ * @returns {any|null}
+ * @access public
+ * @static
+ */
+ Common.unique = value => (
+ Check.is_string(value) ? value.split("").filter((char, i, array) => array.indexOf(char) == i).join("") :
+ Check.is_array(value) ? value.filter((item, i, array) => array.indexOf(item) == i) :
+ value);
+
+ /**
+ * @param {?any} value
+ * @return {string|null}
+ * @access public
+ * @static
+ */
+ Common.json_encode = value => {
+ try{
+ return JSON.stringify(value);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {?any} value
+ * @returns {Object.|Array.|null}
+ * @access public
+ * @static
+ */
+ Common.json_decode = value => {
+ try{
+ return JSON.parse(value);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {?any} value
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Common.base64_encode = value => {
+ if(Check.is_json_item(value))
+ return btoa(JSON.stringify(value));
+ try{
+ return btoa(value);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {?any} value
+ * @returns {string|null}
+ * @access public
+ * @static
+ */
+ Common.base64_decode = value => {
+ try{
+ return atob(value);
+ }catch(exception){};
+ return null;
+ };
+
+ /**
+ * @param {!(Object.|Array.)} data
+ * @returns {string}
+ * @access public
+ * @static
+ */
+ Common.data_encode = data => Common.base64_encode(Common.json_encode(data));
+
+ /**
+ * @param {!string} data
+ * @returns {(Object.|Array.)}
+ * @access public
+ * @static
+ */
+ Common.data_decode = data => Common.json_decode(Common.base64_decode(data));
+
+ /**
+ * @param {!string} string
+ * @returns {string}
+ * @access public
+ * @static
+ */
+ Common.to_regular_expression = string => string.replace(RE.TO_REGULAR_EXPRESSION, character => (
+ "\\" + (Common.REGULAR_EXPRESSION.FROM[character] || character)
+ ));
+
+ return Common;
+})();
\ No newline at end of file
diff --git a/Public/ecma/Utils/HTMLDSL.ecma.js b/Public/ecma/Utils/HTMLDSL.ecma.js
new file mode 100644
index 0000000..945754a
--- /dev/null
+++ b/Public/ecma/Utils/HTMLDSL.ecma.js
@@ -0,0 +1,349 @@
+"use strict";
+
+/**
+ * @callback element_callback
+ * @param {!Object.} [attributes = {}]
+ * @param {!Array.} [children = []]
+ * @returns {!Array.}
+ */
+
+/** @type {element_callback} */
+export const Div = (attributes = {}, children = []) => ["div", attributes, children];
+/** @type {element_callback} */
+export const Span = (attributes = {}, children = []) => ["span", attributes, children];
+/** @type {element_callback} */
+export const Footer = (attributes = {}, children = []) => ["footer", attributes, children];
+
+/** @type {element_callback} */
+export const Header = (attributes = {}, children = []) => ["header", attributes, children];
+/** @type {element_callback} */
+export const Main = (attributes = {}, children = []) => ["main", attributes, children];
+
+/** @type {element_callback} */
+export const H1 = (attributes = {}, children = []) => ["h1", attributes, children];
+/** @type {element_callback} */
+export const H2 = (attributes = {}, children = []) => ["h2", attributes, children];
+/** @type {element_callback} */
+export const H3 = (attributes = {}, children = []) => ["h3", attributes, children];
+/** @type {element_callback} */
+export const H4 = (attributes = {}, children = []) => ["h4", attributes, children];
+/** @type {element_callback} */
+export const H5 = (attributes = {}, children = []) => ["h5", attributes, children];
+/** @type {element_callback} */
+export const H6 = (attributes = {}, children = []) => ["h6", attributes, children];
+
+/** @type {element_callback} */
+export const Heading1 = H1;
+/** @type {element_callback} */
+export const Heading2 = H2;
+/** @type {element_callback} */
+export const Heading3 = H3;
+/** @type {element_callback} */
+export const Heading4 = H4;
+/** @type {element_callback} */
+export const Heading5 = H5;
+/** @type {element_callback} */
+export const Heading6 = H6;
+
+/** @type {element_callback} */
+export const Heading = (level, attributes = {}, children = []) => {
+ const tag = "h" + Math.min(Math.max(1, level), 6);
+ return [tag, attributes, children];
+};
+/** @type {element_callback} */
+export const H = Heading;
+
+/** @type {element_callback} */
+export const A = (attributes = {}, children = []) => ["a", attributes, children];
+/** @type {element_callback} */
+export const Anchor = A;
+
+/** @type {element_callback} */
+export const Img = (attributes = {}, children = []) => ["img", attributes, children];
+/** @type {element_callback} */
+export const Image = Img;
+
+/** @type {element_callback} */
+export const P = (attributes = {}, children = []) => ["p", attributes, children];
+/** @type {element_callback} */
+export const Paragraph = P;
+
+/** @type {element_callback} */
+export const Section = (attributes = {}, children = []) => ["section", attributes, children];
+/** @type {element_callback} */
+export const Article = (attributes = {}, children = []) => ["article", attributes, children];
+
+/** @type {element_callback} */
+export const Aside = (attributes = {}, children = []) => ["aside", attributes, children];
+
+/** @type {element_callback} */
+export const Label = (attributes = {}, children = []) => ["label", attributes, children];
+
+/** @type {element_callback} */
+export const Form = (attributes = {}, children = []) => ["form", attributes, children];
+
+/** @type {element_callback} */
+export const Fieldset = (attributes = {}, children = []) => ["fieldset", attributes, children];
+/** @type {element_callback} */
+export const Legend = (attributes = {}, children = []) => ["legend", attributes, children];
+
+/** @type {element_callback} */
+export const Input = (attributes = {}, children = []) => ["input", attributes, children];
+/** @type {element_callback} */
+export const Checkbox = (attributes = {}, children = []) => ["input", Object.assign({type : "checkbox"}, attributes), children];
+/** @type {element_callback} */
+export const Radio = (attributes = {}, children = []) => ["input", Object.assign({type : "radio"}, attributes), children];
+/** @type {element_callback} */
+export const Submit = (attributes = {}, children = []) => ["input", Object.assign({type : "submit"}, attributes), children];
+/** @type {element_callback} */
+export const Reset = (attributes = {}, children = []) => ["input", Object.assign({type : "reset"}, attributes), children];
+/** @type {element_callback} */
+export const Hidden = (attributes = {}, children = []) => ["input", Object.assign({type : "hidden"}, attributes), children];
+/** @type {element_callback} */
+export const Text = (attributes = {}, children = []) => ["input", Object.assign({type : "text"}, attributes), children];
+/** @type {element_callback} */
+export const Date = (attributes = {}, children = []) => ["input", Object.assign({type : "date"}, attributes), children];
+/** @type {element_callback} */
+export const File = (attributes = {}, children = []) => ["input", Object.assign({type : "file"}, attributes), children];
+/** @type {element_callback} */
+export const Password = (attributes = {}, children = []) => ["input", Object.assign({type : "password"}, attributes), children];
+/** @type {element_callback} */
+export const Email = (attributes = {}, children = []) => ["input", Object.assign({type : "email"}, attributes), children];
+/** @type {element_callback} */
+export const Number = (attributes = {}, children = []) => ["input", Object.assign({type : "number"}, attributes), children];
+/** @type {element_callback} */
+export const Integer = (attributes = {}, children = []) => ["input", Object.assign({type : "number", step : 1}, attributes), children];
+/** @type {element_callback} */
+export const Float = Number;
+/** @type {element_callback} */
+export const Range = (attributes = {}, children = []) => ["input", Object.assign({type : "range"}, attributes), children];
+/** @type {element_callback} */
+export const Color = (attributes = {}, children = []) => ["input", Object.assign({type : "color"}, attributes), children];
+/** @type {element_callback} */
+export const RadioButton = Radio;
+
+/** @type {element_callback} */
+export const Button = (attributes = {}, children = []) => ["button", attributes, children];
+/** @type {element_callback} */
+export const Textarea = (attributes = {}, children = []) => ["textarea", attributes, children];
+/** @type {element_callback} */
+export const Select = (attributes = {}, children = []) => ["select", attributes, children];
+/** @type {element_callback} */
+export const Option = (attributes = {}, children = []) => ["option", attributes, children];
+
+/** @type {element_callback} */
+export const Optgroup = (attributes = {}, children = []) => ["optgroup", attributes, children];
+/** @type {element_callback} */
+export const OptionGroup = Optgroup;
+
+/** @type {element_callback} */
+export const Table = (attributes = {}, children = []) => ["table", attributes, children];
+
+/** @type {element_callback} */
+export const TBody = (attributes = {}, children = []) => ["tbody", attributes, children];
+/** @type {element_callback} */
+export const THead = (attributes = {}, children = []) => ["thead", attributes, children];
+/** @type {element_callback} */
+export const TFoot = (attributes = {}, children = []) => ["tfoot", attributes, children];
+/** @type {element_callback} */
+export const TR = (attributes = {}, children = []) => ["tr", attributes, children];
+/** @type {element_callback} */
+export const TD = (attributes = {}, children = []) => ["td", attributes, children];
+/** @type {element_callback} */
+export const TH = (attributes = {}, children = []) => ["th", attributes, children];
+
+/** @type {element_callback} */
+export const TableBody = TBody;
+/** @type {element_callback} */
+export const TableHead = THead;
+/** @type {element_callback} */
+export const TableFoot = TFoot;
+/** @type {element_callback} */
+export const TableRow = TR;
+/** @type {element_callback} */
+export const TableCell = TD;
+/** @type {element_callback} */
+export const TableHeaderCell = TH;
+
+/** @type {element_callback} */
+export const UL = (attributes = {}, children = []) => ["ul", attributes, children];
+/** @type {element_callback} */
+export const OL = (attributes = {}, children = []) => ["ol", attributes, children];
+/** @type {element_callback} */
+export const LI = (attributes = {}, children = []) => ["li", attributes, children];
+/** @type {element_callback} */
+export const DL = (attributes = {}, children = []) => ["dl", attributes, children];
+/** @type {element_callback} */
+export const DT = (attributes = {}, children = []) => ["dt", attributes, children];
+/** @type {element_callback} */
+export const DD = (attributes = {}, children = []) => ["dd", attributes, children];
+/** @type {element_callback} */
+export const Nav = (attributes = {}, children = []) => ["nav", attributes, children];
+
+/** @type {element_callback} */
+export const UnorderedList = UL;
+/** @type {element_callback} */
+export const OrderedList = OL;
+/** @type {element_callback} */
+export const ListItem = LI;
+/** @type {element_callback} */
+export const DescriptionList = DL;
+/** @type {element_callback} */
+export const DescriptionTerm = DT;
+/** @type {element_callback} */
+export const DescriptionDetails = DD;
+/** @type {element_callback} */
+export const Navigation = Nav;
+
+/** @type {element_callback} */
+export const I = (attributes = {}, children = []) => ["i", attributes, children];
+/** @type {element_callback} */
+export const Italic = I;
+
+/** @type {element_callback} */
+export const B = (attributes = {}, children = []) => ["b", attributes, children];
+/** @type {element_callback} */
+export const Strong = B;
+/** @type {element_callback} */
+export const Bold = B;
+
+/** @type {element_callback} */
+export const U = (attributes = {}, children = []) => ["u", attributes, children];
+/** @type {element_callback} */
+export const Underline = U;
+
+/** @type {element_callback} */
+export const S = (attributes = {}, children = []) => ["s", attributes, children];
+/** @type {element_callback} */
+export const Strike = S;
+/** @type {element_callback} */
+export const StrikeThrough = S;
+
+/** @type {element_callback} */
+export const Mark = (attributes = {}, children = []) => ["mark", attributes, children];
+
+/** @type {element_callback} */
+export const Small = (attributes = {}, children = []) => ["small", attributes, children];
+
+/** @type {element_callback} */
+export const Sub = (attributes = {}, children = []) => ["sub", attributes, children];
+/** @type {element_callback} */
+export const Subscript = Sub;
+
+/** @type {element_callback} */
+export const Sup = (attributes = {}, children = []) => ["sup", attributes, children];
+/** @type {element_callback} */
+export const Superscript = Sup;
+
+/** @type {element_callback} */
+export const BR = (attributes = {}, children = []) => ["br", attributes, children];
+/** @type {element_callback} */
+export const Break = BR;
+
+/** @type {element_callback} */
+export const HR = (attributes = {}, children = []) => ["hr", attributes, children];
+/** @type {element_callback} */
+export const HorizontalRule = HR;
+
+/** @type {element_callback} */
+export const Datalist = (attributes = {}, children = []) => ["datalist", attributes, children];
+
+/** @type {element_callback} */
+export const Output = (attributes = {}, children = []) => ["output", attributes, children];
+
+/** @type {element_callback} */
+export const Progress = (attributes = {}, children = []) => ["progress", attributes, children];
+
+/** @type {element_callback} */
+export const Meter = (attributes = {}, children = []) => ["meter", attributes, children];
+
+/** @type {element_callback} */
+export const Details = (attributes = {}, children = []) => ["details", attributes, children];
+
+/** @type {element_callback} */
+export const Summary = (attributes = {}, children = []) => ["summary", attributes, children];
+
+/** @type {element_callback} */
+export const Dialog = (attributes = {}, children = []) => ["dialog", attributes, children];
+
+/** @type {element_callback} */
+export const Menu = (attributes = {}, children = []) => ["menu", attributes, children];
+
+/** @type {element_callback} */
+export const Menuitem = (attributes = {}, children = []) => ["menuitem", attributes, children];
+
+/** @type {element_callback} */
+export const Menuitemcheckbox = (attributes = {}, children = []) => ["menuitemcheckbox", attributes, children];
+
+/** @type {element_callback} */
+export const Menuitemradio = (attributes = {}, children = []) => ["menuitemradio", attributes, children];
+
+/** @type {element_callback} */
+export const Slot = (attributes = {}, children = []) => ["slot", attributes, children];
+
+/** @type {element_callback} */
+export const Template = (attributes = {}, children = []) => ["template", attributes, children];
+
+/** @type {element_callback} */
+export const Canvas = (attributes = {}, children = []) => ["canvas", attributes, children];
+
+/** @type {element_callback} */
+export const SVG = (attributes = {}, children = []) => ["svg", attributes, children];
+
+/** @type {element_callback} */
+export const MathML = (attributes = {}, children = []) => ["math", attributes, children];
+
+/** @type {element_callback} */
+export const Audio = (attributes = {}, children = []) => ["audio", attributes, children];
+
+/** @type {element_callback} */
+export const Video = (attributes = {}, children = []) => ["video", attributes, children];
+
+/** @type {element_callback} */
+export const Source = (attributes = {}, children = []) => ["source", attributes, children];
+
+/** @type {element_callback} */
+export const Track = (attributes = {}, children = []) => ["track", attributes, children];
+
+/** @type {element_callback} */
+export const Iframe = (attributes = {}, children = []) => ["iframe", attributes, children];
+
+/** @type {element_callback} */
+export const Embed = (attributes = {}, children = []) => ["embed", attributes, children];
+
+/** @type {element_callback} */
+export const Object = (attributes = {}, children = []) => ["object", attributes, children];
+
+/** @type {element_callback} */
+export const Param = (attributes = {}, children = []) => ["param", attributes, children];
+
+/** @type {element_callback} */
+export const Picture = (attributes = {}, children = []) => ["picture", attributes, children];
+
+/** @type {element_callback} */
+export const SourceElement = (attributes = {}, children = []) => ["source", attributes, children];
+
+/** @type {element_callback} */
+export const Map = (attributes = {}, children = []) => ["map", attributes, children];
+
+/** @type {element_callback} */
+export const Area = (attributes = {}, children = []) => ["area", attributes, children];
+
+/** @type {element_callback} */
+export const Meta = (attributes = {}, children = []) => ["meta", attributes, children];
+
+/** @type {element_callback} */
+export const Link = (attributes = {}, children = []) => ["link", attributes, children];
+
+/** @type {element_callback} */
+export const Base = (attributes = {}, children = []) => ["base", attributes, children];
+
+/** @type {element_callback} */
+export const Head = (attributes = {}, children = []) => ["head", attributes, children];
+
+/** @type {element_callback} */
+export const Body = (attributes = {}, children = []) => ["body", attributes, children];
+
+/** @type {element_callback} */
+export const HTML = (attributes = {}, children = []) => ["html", attributes, children];
+
diff --git a/Public/ecma/Utils/Patterns.ecma.js b/Public/ecma/Utils/Patterns.ecma.js
new file mode 100644
index 0000000..56b3996
--- /dev/null
+++ b/Public/ecma/Utils/Patterns.ecma.js
@@ -0,0 +1,12 @@
+"use strict";
+
+/** @type {Object.} */
+export const RE = {
+ KEY : /^[a-z_][a-z0-9_]*$/i,
+ STRING_VARIABLES : /\{([a-z_][a-z0-9_]*)\}/gi,
+ TO_KEBAB : /[\-_]?([A-Z])([A-Z0-9]*|[a-z0-9]*)|([^a-z0-9]+)/g,
+ ON_ATTRIBUTE : /^on[\-_]?/i,
+ SPECIAL_HTML_EVENT_CHARACTERS : /[^a-z0-9]+/gi,
+ WHITE_SPACES : /(?:\s+|[\r\n]+)+/g,
+ TO_REGULAR_EXPRESSION : /[\.\-\^\$\\\/\[\]\(\)\{\}\!\?\*\+\n\r\t]/g
+};
\ No newline at end of file
diff --git a/Public/favicon.ico b/Public/favicon.ico
new file mode 100644
index 0000000..cad6265
Binary files /dev/null and b/Public/favicon.ico differ
diff --git a/Public/images/AnP-180.png b/Public/images/AnP-180.png
new file mode 100755
index 0000000..5f0b5f4
Binary files /dev/null and b/Public/images/AnP-180.png differ
diff --git a/Public/images/AnP-192.png b/Public/images/AnP-192.png
new file mode 100755
index 0000000..4a3e8b7
Binary files /dev/null and b/Public/images/AnP-192.png differ
diff --git a/Public/images/AnP-270.png b/Public/images/AnP-270.png
new file mode 100755
index 0000000..9259dec
Binary files /dev/null and b/Public/images/AnP-270.png differ
diff --git a/Public/images/AnP-32.png b/Public/images/AnP-32.png
new file mode 100755
index 0000000..3d44a5e
Binary files /dev/null and b/Public/images/AnP-32.png differ
diff --git a/Public/images/AnP-512.png b/Public/images/AnP-512.png
new file mode 100755
index 0000000..9b0990f
Binary files /dev/null and b/Public/images/AnP-512.png differ
diff --git a/Public/images/AnP.png b/Public/images/AnP.png
new file mode 100755
index 0000000..9b0990f
Binary files /dev/null and b/Public/images/AnP.png differ
diff --git a/Public/images/default_avatar.png b/Public/images/default_avatar.png
new file mode 100755
index 0000000..0fe9617
Binary files /dev/null and b/Public/images/default_avatar.png differ
diff --git a/Public/images/flags/english.svg b/Public/images/flags/english.svg
new file mode 100644
index 0000000..5eb5a47
--- /dev/null
+++ b/Public/images/flags/english.svg
@@ -0,0 +1,124 @@
+
+
+
+
diff --git a/Public/images/flags/espanol.svg b/Public/images/flags/espanol.svg
new file mode 100644
index 0000000..c80e1a7
--- /dev/null
+++ b/Public/images/flags/espanol.svg
@@ -0,0 +1,117 @@
+
+
+
+
diff --git a/Public/images/flags/galego.svg b/Public/images/flags/galego.svg
new file mode 100644
index 0000000..53ce3b0
--- /dev/null
+++ b/Public/images/flags/galego.svg
@@ -0,0 +1,112 @@
+
+
+
+
diff --git a/Public/images/flags/nihongo.svg b/Public/images/flags/nihongo.svg
new file mode 100644
index 0000000..42739eb
--- /dev/null
+++ b/Public/images/flags/nihongo.svg
@@ -0,0 +1,114 @@
+
+
+
+
diff --git a/Public/images/flags/russkiy.svg b/Public/images/flags/russkiy.svg
new file mode 100644
index 0000000..5ed18ae
--- /dev/null
+++ b/Public/images/flags/russkiy.svg
@@ -0,0 +1,116 @@
+
+
+
+
diff --git a/Public/index.html b/Public/index.html
new file mode 100644
index 0000000..782911f
--- /dev/null
+++ b/Public/index.html
@@ -0,0 +1,52 @@
+
+
+
+ AnP
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Public/json/AnP.settings.json b/Public/json/AnP.settings.json
new file mode 100644
index 0000000..0ec5acd
--- /dev/null
+++ b/Public/json/AnP.settings.json
@@ -0,0 +1,28 @@
+{
+ "autostart" : true,
+ "application_name" : "AnP",
+ "application_link" : "https://anp.k3y.pw/",
+ "application_git" : "https://git.k3y.pw/KyMAN/AnP",
+ "application_logo" : "/images/AnP.png",
+ "default_settings_files" : "/json/AnP.settings.json",
+ "default_secrets_files" : "/json/AnP.secrets.json",
+ "default_i18n_files" : [
+ "/json/i18n/AnP.i18n.english.json",
+ "/json/i18n/AnP.i18n.espanol.json",
+ "/json/i18n/AnP.i18n.galego.json",
+ "/json/i18n/AnP.i18n.nihongo.json",
+ "/json/i18n/AnP.i18n.russkiy.json"
+ ],
+ "cells" : 40,
+ "position" : "body",
+ "build_base_gui" : true,
+ "cc_by_nc_sa_4_link" : "https://creativecommons.org/licenses/by-nc-sa/4.0/",
+ "cc_by_nc_sa_4_icon" : "https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png",
+ "i18n_selector" : [
+ ["english", "English", "/images/flags/english.svg"],
+ ["espanol", "Español", "/images/flags/espanol.svg"],
+ ["galego", "Galego", "/images/flags/galego.svg"],
+ ["nihongo", "日本語", "/images/flags/nihongo.svg"],
+ ["russkiy", "Русский", "/images/flags/russkiy.svg"]
+ ]
+}
\ No newline at end of file
diff --git a/Public/json/i18n/AnP.i18n.english.json b/Public/json/i18n/AnP.i18n.english.json
new file mode 100644
index 0000000..5c0ac7d
--- /dev/null
+++ b/Public/json/i18n/AnP.i18n.english.json
@@ -0,0 +1,19 @@
+{
+ "english" : {
+ "home" : "Home",
+ "git" : "Git",
+ "user" : "User",
+ "ip" : "IP",
+ "login" : "Login",
+ "logout" : "Logout",
+ "register" : "Register",
+ "copyright" : "© Copyright {years} {authors}",
+ "cc_by_nc_sa_4" : "CC-BY-NC-SA-4.0: Creative Commons Attribution, Non-Commercial and Share Alike, version 4.0.",
+ "zoom" : "Zoom",
+ "reset_zoom" : "Reset Zoom",
+ "gui_mode" : "GUI Mode",
+ "aichat" : "AIChat",
+ "message" : "Message",
+ "send" : "Send"
+ }
+}
\ No newline at end of file
diff --git a/Public/json/i18n/AnP.i18n.espanol.json b/Public/json/i18n/AnP.i18n.espanol.json
new file mode 100644
index 0000000..ea672c2
--- /dev/null
+++ b/Public/json/i18n/AnP.i18n.espanol.json
@@ -0,0 +1,19 @@
+{
+ "espanol" : {
+ "home" : "Principal",
+ "git" : "Git",
+ "user" : "Usuario",
+ "ip" : "IP",
+ "login" : "Acceder",
+ "logout" : "Salir",
+ "register" : "Registrarse",
+ "copyright" : "© Copyright {years} {authors}",
+ "cc_by_nc_sa_4" : "CC-BY-NC-SA-4.0: Creative Commons de Atribución, No Comercial y Permitido el Compartir, versión 4.0.",
+ "zoom" : "Zoom",
+ "reset_zoom" : "Reiniciar Zoom",
+ "gui_mode" : "Modo del GUI",
+ "aichat" : "AIChat",
+ "message" : "Mensaje",
+ "send" : "Enviar"
+ }
+}
\ No newline at end of file
diff --git a/Public/json/i18n/AnP.i18n.galego.json b/Public/json/i18n/AnP.i18n.galego.json
new file mode 100644
index 0000000..39f43be
--- /dev/null
+++ b/Public/json/i18n/AnP.i18n.galego.json
@@ -0,0 +1,3 @@
+{
+ "galego" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/AnP.i18n.nihongo.json b/Public/json/i18n/AnP.i18n.nihongo.json
new file mode 100644
index 0000000..eff18c6
--- /dev/null
+++ b/Public/json/i18n/AnP.i18n.nihongo.json
@@ -0,0 +1,3 @@
+{
+ "nihongo" : {}
+}
\ No newline at end of file
diff --git a/Public/json/i18n/AnP.i18n.russkiy.json b/Public/json/i18n/AnP.i18n.russkiy.json
new file mode 100644
index 0000000..b765480
--- /dev/null
+++ b/Public/json/i18n/AnP.i18n.russkiy.json
@@ -0,0 +1,3 @@
+{
+ "russkiy" : {}
+}
\ No newline at end of file
diff --git a/Public/json/views/AnP.views.sessions.json b/Public/json/views/AnP.views.sessions.json
new file mode 100644
index 0000000..75069c4
--- /dev/null
+++ b/Public/json/views/AnP.views.sessions.json
@@ -0,0 +1,25 @@
+{
+ "login" : {
+ "type" : "form",
+ "submit" : "login@sessions",
+ "structure" : [
+ ["username", "text"],
+ ["password", "password"]
+ ]
+ },
+ "unloggin" : {
+ "type" : "form",
+ "submit" : "logout@sessions",
+ "structure" : []
+ },
+ "register" : {
+ "type" : "form",
+ "submit" : "register@sessions",
+ "structure" : [
+ ["username", "text"],
+ ["password", "password"],
+ ["confirm_password", "password"],
+ ["email", "email"]
+ ]
+ }
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.base.scss b/Public/scss/AnP.base.scss
new file mode 100644
index 0000000..814be71
--- /dev/null
+++ b/Public/scss/AnP.base.scss
@@ -0,0 +1,264 @@
+@mixin main_color_web($mode){
+ background-color : map-deep-get($color, $mode, "back");
+ color : map-deep-get($color, $mode, "fore");
+ a[href],[data-role=link],button,[type=submit],[type=button],[type=reset],[role=button]{
+ color : map-deep-get($color, $mode, "primary");
+ &:hover{color : map-deep-get($color, $mode, "secondary");}
+ }
+ button,[type=submit],[type=button],[type=reset],[role=button]{
+ border-color : map-deep-get($color, $mode, "primary");
+ box-shadow : 0em 0em .2em inset map-deep-get($color, $mode, "primary");
+ &:hover{
+ border-color : map-deep-get($color, $mode, "secondary");
+ box-shadow : 0em 0em .2em inset map-deep-get($color, $mode, "secondary");
+ }
+ }
+}
+
+.anp{
+
+ position : relative;
+ top : 0em;
+ left : 0em;
+ width : 100%;
+ height : 100%;
+ overflow : hidden;
+ &,input,textarea,select,button{font-family : $font-normal;}
+
+ button,input,textarea,select{font-size : 1em;}
+
+ a[href]{text-decoration : none;}
+ a[href],[data-role=link],button,[type=submit],[type=button],[type=reset],[role=button]{
+ cursor : pointer;
+ transition-duration : $transition-out;
+ transition-property : color;
+ &:hover{transition-duration : $transition-in;}
+ }
+ button,[type=submit],[type=button],[type=reset],[role=button]{
+ cursor : pointer;
+ border-width : .1em;
+ border-style : solid;
+ border-radius : $border-radius;
+ transition-property : color, border-color, box-shadow;
+ }
+ .buttons{
+ text-align : center;
+ button{border-radius : 0em;}
+ &>:first-child{
+ border-top-left-radius : $border-radius;
+ border-bottom-left-radius : $border-radius;
+ }
+ &>:last-child{
+ border-top-right-radius : $border-radius;
+ border-bottom-right-radius : $border-radius;
+ }
+ }
+
+ header,main,footer{
+ position : absolute;
+ left : 0em;
+ width : 100%;
+ }
+ header{
+ display : flex;
+ align-items : center;
+ top : 0em;
+ height : $header-height;
+ z-index : 20;
+ }
+ main{
+ top : $header-height;
+ bottom : $footer-height;
+ z-index : 10;
+ }
+ footer{
+ display : flex;
+ align-items : center;
+ bottom : 0em;
+ height : $footer-height;
+ z-index : 30;
+ }
+
+ &[data-forced-gui-mode=default][data-gui-mode=default]{
+ @include main_color_web(light);
+ }
+ @each $key in (dark, light){
+ &[data-forced-gui-mode=#{$key}],&[data-forced-gui-mode=default][data-gui-mode=#{$key}]{
+ @include main_color_web($key);
+ }
+ }
+
+ .input{
+ display : inline-block;
+ position : relative;
+ width : 100%;
+ min-width : 0%;
+ &>textarea{
+ width : 100%;
+ height : 5em;
+ min-width : 0%;
+ min-height : 0%;
+ resize : none;
+ }
+ }
+
+ h1{
+ flex-grow : 0;
+ font-size : 1em;
+ margin : 0em;
+ white-space : nowrap;
+ a>*,.image>*{vertical-align : middle;}
+ img{
+ width : auto;
+ height : .8 * $header-height;
+ margin-top : .1 * $header-height;
+ margin-left : 1em + .1 * $header-height;
+ margin-right : .1 * $header-height;
+ &+span{display : none;}
+ }
+ .text{
+ font-size : .8 * $header-height;
+ font-weight : 900;
+ }
+ }
+
+ .top-menu{
+ flex-grow : 1;
+ &>ul{
+ margin : 0em;
+ padding : 0em;
+ text-align : center;
+ list-style-type : none;
+ li{
+ display : inline-block;
+ margin : 0em 1em;
+ }
+ }
+ }
+
+ .sessions-mini{
+ flex-grow : 0;
+ display : relative;
+ width : 3 * $header-height;
+ height : 100%;
+ .image{
+ position : absolute;
+ top : 0em;
+ right : 0em;
+ width : $header-height;
+ height : $header-height;
+ cursor : pointer;
+ background-color : $color-grey;
+ overflow : hidden;
+ border-radius : 0% 0% 0% 50%;
+ transition-duration : $transition-out;
+ transition-property : border-radius;
+ &:hover{
+ border-radius : 0% 0% 0% 10%;
+ transition-duration : $transition-in;
+ }
+ }
+ img{
+ width : auto;
+ height : 100%;
+ &+span{display : none;}
+ }
+ .info{
+ position : absolute;
+ top : .2em;
+ right : $header-height + .5em;
+ margin : 0em;
+ padding : 0em;
+ text-align : right;
+ list-style-type : none;
+ li{font-size : .85em;}
+ [data-icon]+[data-i18n]{display : none;}
+ }
+ nav{
+ position : absolute;
+ bottom : .5em;
+ right : $header-height + .5em;
+ text-align : right;
+ ul{
+ margin : 0em;
+ padding : 0em;
+ text-align : center;
+ list-style-type : none;
+ li{
+ display : inline-block;
+ margin : 0em .2em;
+ }
+ }
+ [data-icon]{
+ &::before{margin : 0em;}
+ &+[data-i18n]{display : none;}
+ }
+ }
+ }
+
+ .gui-controls{
+ flex-grow : 0;
+ white-space : nowrap;
+ [data-icon]{
+ &::before{margin : 0em;}
+ &+[data-i18n]{display : none;}
+ }
+ }
+
+ .licenses{
+ display : flex;
+ align-items : center;
+ flex-grow : 1;
+ &>*{
+ margin : 0em .3em;
+ text-align : center;
+ font-size : .7em;
+ font-weight : 900;
+ }
+ &>[data-i18n=cc_by_nc_sa_4]{
+ display : flex;
+ align-items : center;
+ span{flex-grow : 1;}
+ img{
+ flex-grow : 0;
+ width : auto;
+ height : $footer-height;
+ margin : 0em .3em;
+ }
+ }
+ }
+
+ .i18n-selector{
+ flex-grow : 0;
+ width : 10em;
+ white-space : nowrap;
+ ul{
+ position : absolute;
+ bottom : 0em;
+ right : 0em;
+ margin : 0em;
+ padding : 0em .3em;
+ text-align : right;
+ list-style-type : none;
+ &:hover li{
+ margin-top : 0em;
+ opacity : 1;
+ transition-duration : $transition-in;
+ }
+ }
+ li{
+ position : relative;
+ margin-top : -1em - .28em;
+ transition-duration : $transition-out;
+ transition-property : margin-top, opacity;
+ &>*{vertical-align : middle;}
+ }
+ [data-selected=false]{opacity : 0;}
+ img{
+ width : auto;
+ height : 1em;
+ margin-right : .3em;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.common.scss b/Public/scss/AnP.common.scss
new file mode 100644
index 0000000..2af7d1b
--- /dev/null
+++ b/Public/scss/AnP.common.scss
@@ -0,0 +1,19 @@
+// @use "sass:map";
+// @use "sass:list";
+// @use "sass:meta";
+
+@function unicode($code){
+ @return unquote("\"") + unquote(str-insert($code, "\\", 1)) + unquote("\"");
+}
+
+@function map-deep-get($scope, $keys...){
+
+ $i : 1;
+
+ @while (type-of($scope) == map) and ($i <= length($keys)){
+ $scope : map-get($scope, nth($keys, $i));
+ $i : $i + 1;
+ }
+
+ @return $scope;
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.css b/Public/scss/AnP.css
new file mode 100644
index 0000000..f2259c9
--- /dev/null
+++ b/Public/scss/AnP.css
@@ -0,0 +1,1057 @@
+@font-face {
+ font-family: "FA6FB";
+ font-style: normal;
+ font-weight: 400;
+ font-display: block;
+ src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.ttf") format("truetype"); }
+@font-face {
+ font-family: "FA6FR";
+ font-style: normal;
+ font-weight: 400;
+ font-display: block;
+ src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.ttf") format("truetype"); }
+@font-face {
+ font-family: "FA6FS";
+ font-style: normal;
+ font-weight: 900;
+ font-display: block;
+ src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.woff2") format("woff2"), url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.ttf") format("truetype"); }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzwdL_nz.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzMdL_nz.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz8dL_nz.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xIIzI.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc2CsTKlA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc5CsTKlA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc1CsTKlA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc2CsTKlA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc5CsTKlA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc1CsTKlA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: italic;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxEIzIFKw.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxLIzIFKw.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxHIzIFKw.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxIIzI.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBBc4.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu5mxKOzY.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7mxKOzY.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4WxKOzY.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7WxKOzY.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7GxKOzY.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBBc4.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBBc4.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCBc4EsA.woff2") format("woff2");
+ unicode-range: U+1F00-1FFF; }
+/* greek */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfBxc4EsA.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCxc4EsA.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto";
+ font-style: normal;
+ font-weight: 900;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfBBc4.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: italic;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+/* cyrillic-ext */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2") format("woff2");
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }
+/* cyrillic */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2") format("woff2");
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }
+/* greek */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2") format("woff2");
+ unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF; }
+/* vietnamese */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2") format("woff2");
+ unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB; }
+/* latin-ext */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2") format("woff2");
+ unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
+/* latin */
+@font-face {
+ font-family: "Roboto Mono";
+ font-style: normal;
+ font-weight: 100 700;
+ font-display: swap;
+ src: url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2") format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
+.anp {
+ position: relative;
+ top: 0em;
+ left: 0em;
+ width: 100%;
+ height: 100%;
+ overflow: hidden; }
+ .anp, .anp input, .anp textarea, .anp select, .anp button {
+ font-family: "Roboto"; }
+ .anp button, .anp input, .anp textarea, .anp select {
+ font-size: 1em; }
+ .anp a[href] {
+ text-decoration: none; }
+ .anp a[href], .anp [data-role=link], .anp button, .anp [type=submit], .anp [type=button], .anp [type=reset], .anp [role=button] {
+ cursor: pointer;
+ transition-duration: 1s;
+ transition-property: color; }
+ .anp a[href]:hover, .anp [data-role=link]:hover, .anp button:hover, .anp [type=submit]:hover, .anp [type=button]:hover, .anp [type=reset]:hover, .anp [role=button]:hover {
+ transition-duration: 0.35s; }
+ .anp button, .anp [type=submit], .anp [type=button], .anp [type=reset], .anp [role=button] {
+ cursor: pointer;
+ border-width: .1em;
+ border-style: solid;
+ border-radius: 0.3em;
+ transition-property: color, border-color, box-shadow; }
+ .anp .buttons {
+ text-align: center; }
+ .anp .buttons button {
+ border-radius: 0em; }
+ .anp .buttons > :first-child {
+ border-top-left-radius: 0.3em;
+ border-bottom-left-radius: 0.3em; }
+ .anp .buttons > :last-child {
+ border-top-right-radius: 0.3em;
+ border-bottom-right-radius: 0.3em; }
+ .anp header, .anp main, .anp footer {
+ position: absolute;
+ left: 0em;
+ width: 100%; }
+ .anp header {
+ display: flex;
+ align-items: center;
+ top: 0em;
+ height: 4em;
+ z-index: 20; }
+ .anp main {
+ top: 4em;
+ bottom: 2em;
+ z-index: 10; }
+ .anp footer {
+ display: flex;
+ align-items: center;
+ bottom: 0em;
+ height: 2em;
+ z-index: 30; }
+ .anp[data-forced-gui-mode=default][data-gui-mode=default] {
+ background-color: #EFEFEF;
+ color: #222; }
+ .anp[data-forced-gui-mode=default][data-gui-mode=default] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=default] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=default] button, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button] {
+ color: #2262b0; }
+ .anp[data-forced-gui-mode=default][data-gui-mode=default] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button]:hover {
+ color: #b06222; }
+ .anp[data-forced-gui-mode=default][data-gui-mode=default] button, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button] {
+ border-color: #2262b0;
+ box-shadow: 0em 0em 0.2em inset #2262b0; }
+ .anp[data-forced-gui-mode=default][data-gui-mode=default] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=default] [role=button]:hover {
+ border-color: #b06222;
+ box-shadow: 0em 0em 0.2em inset #b06222; }
+ .anp[data-forced-gui-mode=dark], .anp[data-forced-gui-mode=default][data-gui-mode=dark] {
+ background-color: #222;
+ color: #EFEFEF; }
+ .anp[data-forced-gui-mode=dark] a[href], .anp[data-forced-gui-mode=dark] [data-role=link], .anp[data-forced-gui-mode=dark] button, .anp[data-forced-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=dark] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=dark] button, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button] {
+ color: #4b8bd9; }
+ .anp[data-forced-gui-mode=dark] a[href]:hover, .anp[data-forced-gui-mode=dark] [data-role=link]:hover, .anp[data-forced-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=dark] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button]:hover {
+ color: #d98b4b; }
+ .anp[data-forced-gui-mode=dark] button, .anp[data-forced-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=dark] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] button, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button] {
+ border-color: #4b8bd9;
+ box-shadow: 0em 0em 0.2em inset #4b8bd9; }
+ .anp[data-forced-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=dark] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=dark] [role=button]:hover {
+ border-color: #d98b4b;
+ box-shadow: 0em 0em 0.2em inset #d98b4b; }
+ .anp[data-forced-gui-mode=light], .anp[data-forced-gui-mode=default][data-gui-mode=light] {
+ background-color: #EFEFEF;
+ color: #222; }
+ .anp[data-forced-gui-mode=light] a[href], .anp[data-forced-gui-mode=light] [data-role=link], .anp[data-forced-gui-mode=light] button, .anp[data-forced-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=light] [type=button], .anp[data-forced-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=light] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] a[href], .anp[data-forced-gui-mode=default][data-gui-mode=light] [data-role=link], .anp[data-forced-gui-mode=default][data-gui-mode=light] button, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button] {
+ color: #2262b0; }
+ .anp[data-forced-gui-mode=light] a[href]:hover, .anp[data-forced-gui-mode=light] [data-role=link]:hover, .anp[data-forced-gui-mode=light] button:hover, .anp[data-forced-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=light] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] a[href]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [data-role=link]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button]:hover {
+ color: #b06222; }
+ .anp[data-forced-gui-mode=light] button, .anp[data-forced-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=light] [type=button], .anp[data-forced-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=light] [role=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] button, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button], .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset], .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button] {
+ border-color: #2262b0;
+ box-shadow: 0em 0em 0.2em inset #2262b0; }
+ .anp[data-forced-gui-mode=light] button:hover, .anp[data-forced-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=light] [role=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] button:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=submit]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=button]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [type=reset]:hover, .anp[data-forced-gui-mode=default][data-gui-mode=light] [role=button]:hover {
+ border-color: #b06222;
+ box-shadow: 0em 0em 0.2em inset #b06222; }
+ .anp .input {
+ display: inline-block;
+ position: relative;
+ width: 100%;
+ min-width: 0%; }
+ .anp .input > textarea {
+ width: 100%;
+ height: 5em;
+ min-width: 0%;
+ min-height: 0%;
+ resize: none; }
+ .anp h1 {
+ flex-grow: 0;
+ font-size: 1em;
+ margin: 0em;
+ white-space: nowrap; }
+ .anp h1 a > *, .anp h1 .image > * {
+ vertical-align: middle; }
+ .anp h1 img {
+ width: auto;
+ height: 3.2em;
+ margin-top: 0.4em;
+ margin-left: 1.4em;
+ margin-right: 0.4em; }
+ .anp h1 img + span {
+ display: none; }
+ .anp h1 .text {
+ font-size: 3.2em;
+ font-weight: 900; }
+ .anp .top-menu {
+ flex-grow: 1; }
+ .anp .top-menu > ul {
+ margin: 0em;
+ padding: 0em;
+ text-align: center;
+ list-style-type: none; }
+ .anp .top-menu > ul li {
+ display: inline-block;
+ margin: 0em 1em; }
+ .anp .sessions-mini {
+ flex-grow: 0;
+ display: relative;
+ width: 12em;
+ height: 100%; }
+ .anp .sessions-mini .image {
+ position: absolute;
+ top: 0em;
+ right: 0em;
+ width: 4em;
+ height: 4em;
+ cursor: pointer;
+ background-color: #898989;
+ overflow: hidden;
+ border-radius: 0% 0% 0% 50%;
+ transition-duration: 1s;
+ transition-property: border-radius; }
+ .anp .sessions-mini .image:hover {
+ border-radius: 0% 0% 0% 10%;
+ transition-duration: 0.35s; }
+ .anp .sessions-mini img {
+ width: auto;
+ height: 100%; }
+ .anp .sessions-mini img + span {
+ display: none; }
+ .anp .sessions-mini .info {
+ position: absolute;
+ top: .2em;
+ right: 4.5em;
+ margin: 0em;
+ padding: 0em;
+ text-align: right;
+ list-style-type: none; }
+ .anp .sessions-mini .info li {
+ font-size: .85em; }
+ .anp .sessions-mini .info [data-icon] + [data-i18n] {
+ display: none; }
+ .anp .sessions-mini nav {
+ position: absolute;
+ bottom: .5em;
+ right: 4.5em;
+ text-align: right; }
+ .anp .sessions-mini nav ul {
+ margin: 0em;
+ padding: 0em;
+ text-align: center;
+ list-style-type: none; }
+ .anp .sessions-mini nav ul li {
+ display: inline-block;
+ margin: 0em .2em; }
+ .anp .sessions-mini nav [data-icon]::before {
+ margin: 0em; }
+ .anp .sessions-mini nav [data-icon] + [data-i18n] {
+ display: none; }
+ .anp .gui-controls {
+ flex-grow: 0;
+ white-space: nowrap; }
+ .anp .gui-controls [data-icon]::before {
+ margin: 0em; }
+ .anp .gui-controls [data-icon] + [data-i18n] {
+ display: none; }
+ .anp .licenses {
+ display: flex;
+ align-items: center;
+ flex-grow: 1; }
+ .anp .licenses > * {
+ margin: 0em .3em;
+ text-align: center;
+ font-size: .7em;
+ font-weight: 900; }
+ .anp .licenses > [data-i18n=cc_by_nc_sa_4] {
+ display: flex;
+ align-items: center; }
+ .anp .licenses > [data-i18n=cc_by_nc_sa_4] span {
+ flex-grow: 1; }
+ .anp .licenses > [data-i18n=cc_by_nc_sa_4] img {
+ flex-grow: 0;
+ width: auto;
+ height: 2em;
+ margin: 0em .3em; }
+ .anp .i18n-selector {
+ flex-grow: 0;
+ width: 10em;
+ white-space: nowrap; }
+ .anp .i18n-selector ul {
+ position: absolute;
+ bottom: 0em;
+ right: 0em;
+ margin: 0em;
+ padding: 0em .3em;
+ text-align: right;
+ list-style-type: none; }
+ .anp .i18n-selector ul:hover li {
+ margin-top: 0em;
+ opacity: 1;
+ transition-duration: 0.35s; }
+ .anp .i18n-selector li {
+ position: relative;
+ margin-top: -1.28em;
+ transition-duration: 1s;
+ transition-property: margin-top, opacity; }
+ .anp .i18n-selector li > * {
+ vertical-align: middle; }
+ .anp .i18n-selector [data-selected=false] {
+ opacity: 0; }
+ .anp .i18n-selector img {
+ width: auto;
+ height: 1em;
+ margin-right: .3em; }
+
+.anp [data-icon]::before {
+ margin-right: .3em;
+ font-family: "FA6FS"; }
+.anp [data-icon=home]::before {
+ content: "\f015"; }
+.anp [data-icon=git]::before {
+ content: "\f841";
+ font-family: "FA6FB"; }
+.anp [data-icon=user]::before {
+ content: "\f007"; }
+.anp [data-icon=ip]::before {
+ content: "IP";
+ font-weight: 900;
+ font-family: "Roboto"; }
+.anp [data-icon=login]::before {
+ content: "\f090"; }
+.anp [data-icon=register]::before {
+ content: "\f234"; }
+.anp [data-icon=logout]::before {
+ content: "\f08b"; }
+.anp [data-icon=zoom]::before {
+ content: "\f002"; }
+.anp [data-icon=reset_zoom]::before {
+ content: "\f689"; }
+.anp [data-icon=gui_mode]::before {
+ content: "\f043"; }
+
+/*# sourceMappingURL=AnP.css.map */
diff --git a/Public/scss/AnP.css.map b/Public/scss/AnP.css.map
new file mode 100644
index 0000000..176f30f
--- /dev/null
+++ b/Public/scss/AnP.css.map
@@ -0,0 +1,7 @@
+{
+"version": 3,
+"mappings": "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",
+"sources": ["AnP.fonts.scss","AnP.base.scss","AnP.settings.scss","AnP.common.scss","AnP.icons.scss"],
+"names": [],
+"file": "AnP.css"
+}
diff --git a/Public/scss/AnP.fonts.scss b/Public/scss/AnP.fonts.scss
new file mode 100644
index 0000000..4c644cf
--- /dev/null
+++ b/Public/scss/AnP.fonts.scss
@@ -0,0 +1,893 @@
+@font-face {
+ font-family : "FA6FB";
+ font-style : normal;
+ font-weight : 400;
+ font-display : block;
+ src :
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.woff2") format("woff2"),
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-brands-400.ttf") format("truetype");
+}
+@font-face {
+ font-family : "FA6FR";
+ font-style : normal;
+ font-weight : 400;
+ font-display : block;
+ src :
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.woff2") format("woff2"),
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-regular-400.ttf") format("truetype");
+}
+@font-face {
+ font-family : "FA6FS";
+ font-style : normal;
+ font-weight : 900;
+ font-display : block;
+ src :
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.woff2") format("woff2"),
+ url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/webfonts/fa-solid-900.ttf") format("truetype");
+}
+
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz0dL_nz.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzQdL_nz.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzwdL_nz.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzMdL_nz.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz8dL_nz.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEz4dL_nz.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOiCnqEu92Fr1Mu51QrEzAdLw.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc3CsTKlA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc-CsTKlA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc2CsTKlA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc5CsTKlA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TjASc6CsQ.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xFIzIFKw.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xMIzIFKw.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xEIzIFKw.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xLIzIFKw.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xHIzIFKw.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xGIzIFKw.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1Mu51xIIzI.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc3CsTKlA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc-CsTKlA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc2CsTKlA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc5CsTKlA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc1CsTKlA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc0CsTKlA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51S7ACc6CsQ.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic3CsTKlA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic-CsTKlA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic2CsTKlA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic5CsTKlA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic1CsTKlA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic0CsTKlA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzBic6CsQ.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc3CsTKlA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc-CsTKlA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc2CsTKlA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc5CsTKlA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc1CsTKlA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : italic;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TLBCc6CsQ.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxFIzIFKw.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxEIzIFKw.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxLIzIFKw.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxHIzIFKw.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxGIzIFKw.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 100;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxIIzI.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCRc4EsA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fABc4EsA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCBc4EsA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBxc4EsA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fCxc4EsA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fChc4EsA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 300;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmSU5fBBc4.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu5mxKOzY.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7mxKOzY.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4WxKOzY.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7WxKOzY.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu7GxKOzY.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 400;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCRc4EsA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fABc4EsA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCBc4EsA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBxc4EsA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fCxc4EsA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 500;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmEU9fBBc4.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCRc4EsA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfABc4EsA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCBc4EsA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBxc4EsA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfCxc4EsA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfChc4EsA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBBc4.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCRc4EsA.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfABc4EsA.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCBc4EsA.woff2") format("woff2");
+ unicode-range : U+1F00-1FFF;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfBxc4EsA.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfCxc4EsA.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfChc4EsA.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto";
+ font-style : normal;
+ font-weight : 900;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmYUtfBBc4.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3CWWoKC.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3mWWoKC.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm36WWoKC.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3KWWoKC.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm3OWWoKC.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : italic;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x7DF4xlVMF-BfR8bXMIjhOm32WWg.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+/* cyrillic-ext */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhGq3-OXg.woff2") format("woff2");
+ unicode-range : U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhPq3-OXg.woff2") format("woff2");
+ unicode-range : U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* greek */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhIq3-OXg.woff2") format("woff2");
+ unicode-range : U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
+}
+/* vietnamese */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhEq3-OXg.woff2") format("woff2");
+ unicode-range : U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
+}
+/* latin-ext */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhFq3-OXg.woff2") format("woff2");
+ unicode-range : U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face{
+ font-family : "Roboto Mono";
+ font-style : normal;
+ font-weight : 100 700;
+ font-display : swap;
+ src : url("https://fonts.gstatic.com/s/robotomono/v23/L0x5DF4xlVMF-BfR8bXMIjhLq38.woff2") format("woff2");
+ unicode-range : U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.icons.scss b/Public/scss/AnP.icons.scss
new file mode 100644
index 0000000..28b34cb
--- /dev/null
+++ b/Public/scss/AnP.icons.scss
@@ -0,0 +1,16 @@
+.anp{
+ [data-icon]::before{
+ margin-right : .3em;
+ font-family : $font-icon;
+ }
+ [data-icon=home]::before{content : unicode("f015");}
+ [data-icon=git]::before{content : unicode("f841"); font-family : "FA6FB";}
+ [data-icon=user]::before{content : unicode("f007");}
+ [data-icon=ip]::before{content : "IP"; font-weight : 900; font-family : $font-normal;}
+ [data-icon=login]::before{content : unicode("f090");}
+ [data-icon=register]::before{content : unicode("f234");}
+ [data-icon=logout]::before{content : unicode("f08b");}
+ [data-icon=zoom]::before{content : unicode("f002");}
+ [data-icon=reset_zoom]::before{content : unicode("f689");}
+ [data-icon=gui_mode]::before{content : unicode("f043");}
+}
\ No newline at end of file
diff --git a/Public/scss/AnP.scss b/Public/scss/AnP.scss
new file mode 100644
index 0000000..7353f1d
--- /dev/null
+++ b/Public/scss/AnP.scss
@@ -0,0 +1 @@
+@import "AnP.fonts.scss", "AnP.settings.scss", "AnP.common.scss", "AnP.base.scss", "AnP.icons.scss";
\ No newline at end of file
diff --git a/Public/scss/AnP.settings.scss b/Public/scss/AnP.settings.scss
new file mode 100644
index 0000000..2874dd7
--- /dev/null
+++ b/Public/scss/AnP.settings.scss
@@ -0,0 +1,47 @@
+// Colors
+$color-fore : #222;
+$color-back : #EFEFEF;
+$color-primary : #2272D4;
+$color-secondary : #D47222;
+$color-full-fore : #000;
+$color-full-back : #FFF;
+$color-grey : mix($color-fore, $color-back, 50%);
+$color : (
+ light : (
+ fore : $color-fore,
+ back : $color-back,
+ primary : mix($color-primary, $color-fore, 80%),
+ secondary : mix($color-secondary, $color-fore, 80%),
+ full : $color-full-back,
+ input-back : mix($color-back, $color-full-back, 80%),
+ transparent : rgba(255, 255, 255, 0)
+ ),
+ dark : (
+ fore : $color-back,
+ back : $color-fore,
+ primary : mix($color-primary, $color-back, 80%),
+ secondary : mix($color-secondary, $color-back, 80%),
+ full : $color-full-fore,
+ input-back : mix($color-back, $color-full-fore, 80%),
+ transparent : rgba(0, 0, 0, 0)
+ )
+);
+
+// Sizes
+$header-height : 4em;
+$footer-height : 2em;
+$border-radius : .3em;
+$margin : .5em;
+
+// Fonts
+// $font-normal : Arial, Helvetica, Sans;
+// $font-mono : Consolas, "Courier New", Courier, monospace;
+// $font-icon : Arial, Helvetica, Sans;
+$font-normal : "Roboto";
+$font-mono : "Roboto Mono";
+$font-icon : "FA6FS";
+
+// Transitions
+$transition-in : .35s;
+$transition-out : 1s;
+$transition : .5s;
\ No newline at end of file
diff --git a/Python/Abstracts/ControllerAbstract.py b/Python/Abstracts/ControllerAbstract.py
new file mode 100644
index 0000000..286993f
--- /dev/null
+++ b/Python/Abstracts/ControllerAbstract.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Optional, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Abstracts.ModelAbstract import ModelAbstract
+
+class ControllerAbstract(ModelAbstract):
+
+ def __init__(self:Self,
+ anp:AnPInterface,
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> None:
+ self.anp:AnPInterface = anp
\ No newline at end of file
diff --git a/Python/Abstracts/DispatchAbstract.py b/Python/Abstracts/DispatchAbstract.py
new file mode 100644
index 0000000..c3c31b4
--- /dev/null
+++ b/Python/Abstracts/DispatchAbstract.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Optional, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Abstracts.ModelAbstract import ModelAbstract
+
+class DispatchAbstract(ModelAbstract):
+
+ def __init__(self:Self,
+ anp:AnPInterface,
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> None:
+ self.anp:AnPInterface = anp
\ No newline at end of file
diff --git a/Python/Abstracts/ModelAbstract.py b/Python/Abstracts/ModelAbstract.py
new file mode 100644
index 0000000..73ba56f
--- /dev/null
+++ b/Python/Abstracts/ModelAbstract.py
@@ -0,0 +1,4 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+class ModelAbstract:pass
\ No newline at end of file
diff --git a/Python/Abstracts/RouteAbstract.py b/Python/Abstracts/RouteAbstract.py
new file mode 100644
index 0000000..b944b42
--- /dev/null
+++ b/Python/Abstracts/RouteAbstract.py
@@ -0,0 +1,4 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+class RouteAbstract:pass
\ No newline at end of file
diff --git a/Python/Application/AnP.py b/Python/Application/AnP.py
new file mode 100644
index 0000000..beeee7b
--- /dev/null
+++ b/Python/Application/AnP.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Optional, Sequence
+import datetime
+from re import Match as REMatch
+from traceback import extract_tb as extract_traceback, format_stack as trace_format_stack
+from Managers.I18NManager import I18NManager
+from Managers.SettingsManager import SettingsManager
+from Managers.PrintTypesManager import PrintTypesManager
+from Managers.TerminalManager import TerminalManager
+from Managers.ModelsManager import ModelsManager
+from Managers.ControllersManager import ControllersManager
+from Managers.DispatchesManager import DispatchesManager
+from Managers.IndexesManager import IndexesManager
+from Managers.RoutesManager import RoutesManager
+from Drivers.HTTPDriver import HTTPDriver
+from Utils.Common import Common
+from Utils.Patterns import RE
+
+class AnP:
+
+ def __own_update(self:Self) -> None:
+ self.__end_print_types = [
+ print_type.upper() for print_type in Common.get_keys(self.settings.get("end_print_types", None, self.__end_print_types))
+ ]
+ self.__root_projects_paths = Common.get_texts(self.settings.get("root_projects_paths", None, self.__root_projects_paths))
+ self.__print_format = self.settings.get("print_format", None, self.__print_format)
+ self.__exception_format = self.settings.get("exception_format", None, self.__exception_format)
+
+ def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
+
+ self.__working:bool = True
+ self.settings:SettingsManager = SettingsManager(self, inputs)
+ self.i18n:I18NManager = I18NManager(self)
+ self.print_types:PrintTypesManager = PrintTypesManager(self)
+ self.__end_print_types:list[str] = ["UNKN", "EXCE", "ERRO"]
+ self.__root_projects_paths:list[str] = []
+ self.__print_format:str = "[{type}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{line}]{file}({method}): {message}"
+ self.__exception_format:str = " '[{line}]{file}({method})'{lines}\n\n{exception_message}"
+ self.__own_update()
+ self.terminal:TerminalManager = TerminalManager(self)
+ self.models:ModelsManager = ModelsManager(self)
+ self.controllers:ControllersManager = ControllersManager(self)
+ self.dispatches:DispatchesManager = DispatchesManager(self)
+ self.indexes:IndexesManager = IndexesManager(self)
+ self.routes:RoutesManager = RoutesManager(self)
+ self.http_server:HTTPDriver = HTTPDriver(self)
+
+ def update(self:Self) -> None:
+ self.settings.update()
+ self.i18n.update()
+ self.print_types.update()
+ self.__own_update()
+ self.terminal.update()
+ self.models.update()
+ self.controllers.update()
+ self.dispatches.update()
+ self.indexes.update()
+ self.routes.update()
+ self.http_server.update()
+
+ def reset(self:Self) -> None:
+ self.settings.reset()
+ self.i18n.reset()
+ self.print_types.reset()
+ self.__own_update()
+ self.terminal.reset()
+ self.models.reset()
+ self.controllers.reset()
+ self.dispatches.reset()
+ self.indexes.reset()
+ self.routes.reset()
+ self.http_server.reset()
+
+ def close(self:Self) -> None:
+ self.__working = False
+ self.http_server.close()
+
+ def working(self:Self) -> bool:
+ return self.__working
+
+ def print(self:Self,
+ _type:str,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:
+
+ date:datetime.datetime = datetime.datetime.now()
+ own:dict[str, Any] = {
+ "raw_type" : _type,
+ "type": self.print_types.get(_type),
+ "message": self.i18n.get(message, inputs),
+ **Common.get_dictionary(inputs),
+ **Common.get_action_data(i + 1)
+ }
+
+ for key in ("year", "month", "day", "hour", "minute", "second"):
+
+ k:str = "i" if key == "minute" else key[0]
+
+ own[k] = own[key] = getattr(date, key)
+ own[k + k] = ("00" + str(own[key]))[-2:]
+
+ own["yyyy"] = ("0000" + str(own["year"]))[-4:]
+
+ for root_path in self.__root_projects_paths:
+ if own["file"].startswith(root_path):
+ own["file"] = own["file"][len(root_path):]
+ break
+
+ if any(own["type"].startswith(end_type) for end_type in self.__end_print_types) and "end" in own:
+ own["message"] += own["end"]
+
+ print(Common.string_variables(self.__print_format, own))
+
+ def exception(self:Self,
+ exception:Exception,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:
+
+ lines:list[str] = extract_traceback(exception.__traceback__).format()
+ matches:REMatch = RE.EXCEPTION.match(lines[-1])
+ data:dict[str, Any|None] = {
+ **Common.get_dictionary(inputs),
+ "lines" : "",
+ "exception_message" : str(exception),
+ "method" : matches.group(3),
+ "line" : matches.group(2),
+ "file" : matches.group(1)
+ }
+ block:str
+ j:int
+
+ for root_path in self.__root_projects_paths:
+ if data["file"] and data["file"].startswith(root_path):
+ data["file"] = data["file"][len(root_path):]
+
+ for j, block in enumerate(trace_format_stack()[:-2] + lines):
+ if block:
+ data["lines"] += "\n " + str(j) + " - " + RE.NEW_LINE.split(block.strip())[0]
+
+ data["end"] = Common.string_variables(self.__exception_format, data)
+
+ message and self.print("exception", message, data, i + 2)
\ No newline at end of file
diff --git a/Python/Application/Event.py b/Python/Application/Event.py
new file mode 100644
index 0000000..195be72
--- /dev/null
+++ b/Python/Application/Event.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Callable, Any, Optional
+
+class Event:
+
+ def __init__(self:Self) -> None:
+ self.__events:dict[int, Callable[[Optional[Any]], Any|None]] = {}
+
+ def execute(self:Self, *inputs:Any|None) -> None:
+
+ results:dict[int, Any|None] = {}
+
+ for i, event in self.__events.items():
+ results[i] = event(*inputs)
+
+ return results
+
+ def add(self:Self, event:Callable[[Optional[Any]], Any|None]) -> int:
+
+ i:int = 0
+ l:int = len(self.__events)
+
+ while i < l:
+ if i not in self.__events:
+ break
+ i += 1
+
+ self.__events[i] = event
+
+ return i
+
+ def remove(self:Self, i:int) -> None:
+ if i in self.__events:
+ del self.__events[i]
\ No newline at end of file
diff --git a/Python/Controllers/AIController.py b/Python/Controllers/AIController.py
new file mode 100644
index 0000000..8b70f90
--- /dev/null
+++ b/Python/Controllers/AIController.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self
+from Abstracts.ModelAbstract import ModelAbstract
+from Interfaces.Application.AnPInterface import AnPInterface
+from Models.RequestModel import RequestModel
+
+class AIController(ModelAbstract):
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+ self.anp: AnPInterface = anp
+
+ def new_message(self:Self, request:RequestModel) -> None:
+ pass
\ No newline at end of file
diff --git a/Python/Drivers/HTTPDriver.py b/Python/Drivers/HTTPDriver.py
new file mode 100644
index 0000000..141d245
--- /dev/null
+++ b/Python/Drivers/HTTPDriver.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Models.RequestModel import RequestModel
+from Utils.Common import Common
+from threading import Thread
+from http.server import BaseHTTPRequestHandler, HTTPServer
+
+class HTTPDriver:
+
+ class HTTPRequestHandler(BaseHTTPRequestHandler):
+
+ def __process(self:Self, method:str) -> None:
+
+ anp:AnPInterface = self.server.anp
+ request:RequestModel = RequestModel()
+
+ request.method = method
+ request.path = self.path
+ request.headers = dict(self.headers)
+ request.client_host = self.client_address[0]
+ request.client_port = self.client_address[1]
+
+ anp.routes.go(method, self.path, request)
+
+ self.send_response(request.response_code)
+ self.send_header("Content-Type", request.response_mime)
+ self.send_header("Content-Length", str(len(request.response)))
+ self.end_headers()
+
+ self.wfile.write(
+ request.response.encode() if isinstance(request.response, str) else
+ request.response)
+
+ def do_GET(self:Self) -> None:
+ self.__process("get")
+
+ def do_POST(self:Self) -> None:
+ self.__process("post")
+
+ def do_PUT(self:Self) -> None:
+ self.__process("put")
+
+ def do_DELETE(self:Self) -> None:
+ self.__process("delete")
+
+ def do_PATCH(self:Self) -> None:
+ self.__process("patch")
+
+ def do_HEAD(self:Self) -> None:
+ self.__process("head")
+
+ def do_OPTIONS(self:Self) -> None:
+ self.__process("options")
+
+ def do_CONNECT(self:Self) -> None:
+ self.__process("connect")
+
+ def do_TRACE(self:Self) -> None:
+ self.__process("trace")
+
+ def do_COPY(self:Self) -> None:
+ self.__process("copy")
+
+ def do_MOVE(self:Self) -> None:
+ self.__process("move")
+
+ def do_PROPFIND(self:Self) -> None:
+ self.__process("propfind")
+
+ def do_PROPPATCH(self:Self) -> None:
+ self.__process("proppatch")
+
+ def do_LOCK(self:Self) -> None:
+ self.__process("lock")
+
+ def do_UNLOCK(self:Self) -> None:
+ self.__process("unlock")
+
+ def do_REPORT(self:Self) -> None:
+ self.__process("report")
+
+ def do_MKCOL(self:Self) -> None:
+ self.__process("mkcol")
+
+ def __init__(self:Self,
+ anp:AnPInterface,
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__inputs:dict[str, Any|None] = Common.get_dictionary(inputs)
+ self.__port:int = 8000
+ self.__host:str = ""
+ self.__server:HTTPServer|None = None
+ self.__thread:Thread|None = None
+
+ self.update()
+
+ def start(self:Self) -> None:
+ if self.__server is None:
+ self.__thread = Thread(target = self.__run_service)
+ self.__thread.start()
+
+ def close(self:Self) -> None:
+ if self.__server is not None:
+ self.__server.shutdown()
+ self.__server = None
+
+ def update(self:Self) -> None:
+
+ self.close()
+
+ self.__port = self.anp.settings.get(("http_server_port", "http_port", "port"), self.__inputs, self.__port)
+ self.__host = self.anp.settings.get(("http_server_host", "http_host", "host"), self.__inputs, self.__host)
+
+ self.start()
+
+ def reset(self:Self) -> None:
+
+ self.__port = 8000
+ self.__host = ""
+
+ self.update()
+
+ def __run_service(self:Self) -> None:
+ self.__server = HTTPServer((self.__host, self.__port), self.HTTPRequestHandler)
+ self.__server.anp = self.anp
+ self.__server.serve_forever()
\ No newline at end of file
diff --git a/Python/Drivers/OllamaDriver.py b/Python/Drivers/OllamaDriver.py
new file mode 100644
index 0000000..08cba74
--- /dev/null
+++ b/Python/Drivers/OllamaDriver.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+
+
+class OllamaDriver:
+
+ def __init__(self:Self,
+ anp:AnPInterface,
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> None:
+ self.anp:AnPInterface = anp
\ No newline at end of file
diff --git a/Python/Drivers/WebSocketServerDriver.py b/Python/Drivers/WebSocketServerDriver.py
new file mode 100644
index 0000000..bf9c8ea
--- /dev/null
+++ b/Python/Drivers/WebSocketServerDriver.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Sequence, Optional
+from websockets.sync.server import serve as server_serve
+from websockets import ServerConnection as WebSocketServer, ClientConnection as WebSocketClient
+from Application.Event import Event
+from Interfaces.Application.AnPInterface import AnPInterface
+
+class WebSocketServerDriver:
+
+ def __init__(self:Self, anp:AnPInterface, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
+
+ self.anp:AnPInterface = anp
+ self.on_new_client:Event = Event()
+ self.on_message:Event = Event()
+ self.on_close:Event = Event()
+ self.on_error:Event = Event()
+ self.__server:WebSocketServer
+ self.__clients:dict[str, WebSocketClient] = {}
+ self.__host:str = anp.settings.get(("web_socket_server_host", "host"), inputs, "")
+ self.__port:int = anp.settings.get(("web_socket_server_port", "port"), inputs, 8765)
+
+ with server_serve(self.__handler, self.__host, self.__port) as self.__server:
+ self.__server.serve_forever()
+
+ def close(self:Self) -> None:
+
+ id:str
+
+ for id in tuple(self.__clients.keys()):
+ self.close_client(id)
+
+ self.__server.close()
+
+ def close_client(self:Self, id:str, show_exception:bool = True) -> None:
+ if id in self.__clients:
+ try:
+ self.__clients[id].close()
+ except Exception as exception:
+ show_exception and self.anp.exception(exception, "web_socket_server_client_close_exception", {
+ "client": id,
+ "port": self.__port,
+ "host": self.__host
+ })
+ del self.__clients[id]
+
+ def __handler(self:Self, client:WebSocketClient) -> None:
+
+ id:str = str(id(client))
+ self.__clients[id] = client
+ self.on_new_client.execute(client, id)
+
+ self.anp.print("info", "web_socket_server_client_connected", {
+ "client": id,
+ "port": self.__port,
+ "host": self.__host,
+ "client_host" : client.remote_address[0],
+ "client_port" : client.remote_address[1]
+ })
+
+ try:
+ while self.anp.working():
+ message:str = client.recv()
+ if message is None:
+ break
+ self.on_message.execute(client, message)
+ except Exception as exception:
+ self.anp.exception(exception, "web_socket_server_client_exception", {
+ "client": id,
+ "port": self.__port,
+ "host": self.__host
+ })
+ self.on_error.execute(client, exception)
+ finally:
+ self.close_client(id, False)
+ self.on_close.execute(client)
+ self.anp.print("info", "web_socket_server_client_disconnected", {
+ "client": id,
+ "port": self.__port,
+ "host": self.__host
+ })
\ No newline at end of file
diff --git a/Python/Interfaces/Application/AnPInterface.py b/Python/Interfaces/Application/AnPInterface.py
new file mode 100644
index 0000000..09bd8ff
--- /dev/null
+++ b/Python/Interfaces/Application/AnPInterface.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Sequence, Self
+from abc import ABC, abstractmethod
+from Interfaces.Managers.SettingsManagerInterface import SettingsManagerInterface
+from Interfaces.Managers.I18NManagerInterface import I18NManagerInterface
+from Interfaces.Managers.PrintTypesManagerInterface import PrintTypesManagerInterface
+from Interfaces.Managers.TerminalManagerInterface import TerminalManagerInterface
+from Interfaces.Managers.ModelsManagerInterface import ModelsManagerInterface
+from Interfaces.Managers.ControllersManagerInterface import ControllersManagerInterface
+from Interfaces.Managers.DispatchesManagerInterface import DispatchesManagerInterface
+from Interfaces.Managers.IndexesManagerInterface import IndexesManagerInterface
+from Interfaces.Managers.RoutesManagerInterface import RoutesManagerInterface
+
+class AnPInterface(ABC):
+
+ def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
+ self.settings:SettingsManagerInterface = None
+ self.i18n:I18NManagerInterface = None
+ self.print_types:PrintTypesManagerInterface = None
+ self.terminal:TerminalManagerInterface = None
+ self.models:ModelsManagerInterface = None
+ self.controllers:ControllersManagerInterface = None
+ self.dispatches:DispatchesManagerInterface = None
+ self.indexes:IndexesManagerInterface = None
+ self.routes:RoutesManagerInterface = None
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def close(self:Self) -> None:pass
+
+ @abstractmethod
+ def working(self:Self) -> bool:pass
+
+ @abstractmethod
+ def print(self:Self,
+ _type:str,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:pass
+
+ @abstractmethod
+ def exception(self:Self,
+ exception:Exception,
+ message:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ i:int = 0
+ ) -> None:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/ControllersManagerInterface.py b/Python/Interfaces/Managers/ControllersManagerInterface.py
new file mode 100644
index 0000000..7e3dc81
--- /dev/null
+++ b/Python/Interfaces/Managers/ControllersManagerInterface.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self
+from abc import ABC, abstractmethod
+from Models.RequestModel import RequestModel
+
+class ControllersManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
+
+ @abstractmethod
+ def execute(self:Self, key:str, method:str, request:RequestModel) -> bool:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/DispatchesManagerInterface.py b/Python/Interfaces/Managers/DispatchesManagerInterface.py
new file mode 100644
index 0000000..d6338df
--- /dev/null
+++ b/Python/Interfaces/Managers/DispatchesManagerInterface.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self
+from abc import ABC, abstractmethod
+
+class DispatchesManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
+
+ @abstractmethod
+ def execute(self:Self, key:str, method:str, *arguments:list[Any|None]) -> bool:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/I18NManagerInterface.py b/Python/Interfaces/Managers/I18NManagerInterface.py
new file mode 100644
index 0000000..671686a
--- /dev/null
+++ b/Python/Interfaces/Managers/I18NManagerInterface.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from abc import ABC, abstractmethod
+
+class I18NManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def get(self:Self,
+ keys:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ custom_language:Optional[Any] = None
+ ) -> Any|None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/IndexesManagerInterface.py b/Python/Interfaces/Managers/IndexesManagerInterface.py
new file mode 100644
index 0000000..b0db3e0
--- /dev/null
+++ b/Python/Interfaces/Managers/IndexesManagerInterface.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any
+from abc import ABC, abstractmethod
+
+class IndexesManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None) -> None:pass
+
+ @abstractmethod
+ def get(self:Self) -> list[str]:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/ModelsManagerInterface.py b/Python/Interfaces/Managers/ModelsManagerInterface.py
new file mode 100644
index 0000000..6f5c82d
--- /dev/null
+++ b/Python/Interfaces/Managers/ModelsManagerInterface.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Sequence, TypeVar
+from abc import ABC, abstractmethod
+
+T = TypeVar("T")
+
+class ModelsManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def get(self:Self, Type:type[T], keys:str|Sequence[str]) -> T|None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/PrintTypesManagerInterface.py b/Python/Interfaces/Managers/PrintTypesManagerInterface.py
new file mode 100644
index 0000000..e376177
--- /dev/null
+++ b/Python/Interfaces/Managers/PrintTypesManagerInterface.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Sequence
+from abc import ABC, abstractmethod
+
+class PrintTypesManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def get(self:Self, keys:str|Sequence[str]) -> str:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None) -> None:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/RoutesManagerInterface.py b/Python/Interfaces/Managers/RoutesManagerInterface.py
new file mode 100644
index 0000000..80b182f
--- /dev/null
+++ b/Python/Interfaces/Managers/RoutesManagerInterface.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any
+from abc import ABC, abstractmethod
+from Models.RequestModel import RequestModel
+
+class RoutesManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def go(self:Self, method:str, path:str, request:RequestModel) -> None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
+
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/SettingsManagerInterface.py b/Python/Interfaces/Managers/SettingsManagerInterface.py
new file mode 100644
index 0000000..0da3691
--- /dev/null
+++ b/Python/Interfaces/Managers/SettingsManagerInterface.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from abc import ABC, abstractmethod
+
+class SettingsManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def get(self:Self,
+ keys:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ default:Optional[Any] = None
+ ) -> Any|None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
+
+ @abstractmethod
+ def add_secret(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
\ No newline at end of file
diff --git a/Python/Interfaces/Managers/TerminalManagerInterface.py b/Python/Interfaces/Managers/TerminalManagerInterface.py
new file mode 100644
index 0000000..ec1d711
--- /dev/null
+++ b/Python/Interfaces/Managers/TerminalManagerInterface.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any
+from abc import ABC, abstractmethod
+
+class TerminalManagerInterface(ABC):
+
+ @abstractmethod
+ def update(self:Self) -> None:pass
+
+ @abstractmethod
+ def reset(self:Self) -> None:pass
+
+ @abstractmethod
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
\ No newline at end of file
diff --git a/Python/Managers/ControllersManager.py b/Python/Managers/ControllersManager.py
new file mode 100644
index 0000000..f07cd34
--- /dev/null
+++ b/Python/Managers/ControllersManager.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Abstracts.ControllerAbstract import ControllerAbstract
+from Models.RequestModel import RequestModel
+from Utils.Common import Common
+from Utils.Checks import Check
+
+class ControllersManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__controllers:dict[str, ControllerAbstract] = {}
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_controllers_files", "controllers_files", "default_controllers", "controllers"):
+ self.add(self.anp.settings.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__controllers = {}
+
+ self.update()
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in Common.load_json(inputs, True):
+ for key, controller in subinputs.items():
+ if Common.is_mark_key(key) and controller is None:
+ continue
+
+ ControllerClass:type[ControllerAbstract]|None
+
+ if Check.is_string(controller):
+ ControllerClass = self.anp.models.get(ControllerAbstract, controller)
+ elif issubclass(controller, ControllerAbstract):
+ ControllerClass = controller
+ elif Check.is_dictionary(controller) and "type" in controller and Check.is_string(controller["type"]):
+ ControllerClass = self.anp.models.get(ControllerAbstract, controller["type"])
+ else:
+ continue
+
+ if ControllerClass and (
+ overwrite or key not in self.__controllers
+ ):
+ self.__controllers[key] = ControllerClass(
+ self.anp, controller if Check.is_dictionary(controller) else
+ None)
+
+ def execute(self:Self, key:str, method:str, request:RequestModel) -> bool:
+ if key in self.__controllers and hasattr(self.__controllers[key], method):
+ getattr(self.__controllers[key], method)(request)
+ return True
+ return False
\ No newline at end of file
diff --git a/Python/Managers/DispatchesManager.py b/Python/Managers/DispatchesManager.py
new file mode 100644
index 0000000..065311d
--- /dev/null
+++ b/Python/Managers/DispatchesManager.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self
+from Interfaces.Application.AnPInterface import AnPInterface
+from Abstracts.DispatchAbstract import DispatchAbstract
+from Utils.Common import Common
+from Utils.Checks import Check
+
+class DispatchesManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__dispatches:dict[str, DispatchAbstract] = {}
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_dispatches_files", "dispatches_files", "default_dispatches", "dispatches"):
+ self.add(self.anp.settings.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__dispatches = {}
+
+ self.update()
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in Common.load_json(inputs, True):
+ for key, dispatch in subinputs.items():
+ if Common.is_mark_key(key) and dispatch is None:
+ continue
+
+ DispatchClass:type[DispatchAbstract]|None
+
+ if Check.is_string(dispatch):
+ DispatchClass = self.anp.models.get(DispatchAbstract, dispatch)
+ elif issubclass(dispatch, DispatchAbstract):
+ DispatchClass = dispatch
+ elif Check.is_dictionary(dispatch) and "type" in dispatch and Check.is_string(dispatch["type"]):
+ DispatchClass = self.anp.models.get(DispatchAbstract, dispatch["type"])
+ else:
+ continue
+
+ if DispatchClass and (
+ overwrite or key not in self.__dispatches
+ ):
+ self.__dispatches[key] = DispatchClass(
+ self.anp, dispatch if Check.is_dictionary(dispatch) else
+ None)
+
+ def execute(self:Self, key:str, method:str, *arguments:list[Any|None]) -> bool:
+ if key in self.__dispatches and hasattr(self.__dispatches[key], method):
+ getattr(self.__dispatches[key], method)(*arguments)
+ return True
+ return False
\ No newline at end of file
diff --git a/Python/Managers/I18NManager.py b/Python/Managers/I18NManager.py
new file mode 100644
index 0000000..53d1a2a
--- /dev/null
+++ b/Python/Managers/I18NManager.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Utils.Checks import Check
+from Utils.Common import Common
+
+class I18NManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__sentences:dict[str, dict[str, str|list[str]]] = {
+ "english" : {}
+ }
+ self.__default_language:str = list(self.__sentences.keys())[0]
+ self.__language:str = self.__default_language
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_i18n_files", "i18n_files", "default_i18n", "i18n"):
+ self.add(self.anp.settings.get(key), True)
+
+ self.__default_language = self.anp.settings.get("language", None, self.__default_language)
+ self.__language = self.anp.settings.get("language", None, self.__language)
+
+ def reset(self:Self) -> None:
+
+ self.__sentences = {
+ "english" : {}
+ }
+ self.__default_language = list(self.__sentences.keys())[0]
+ self.__language = self.__default_language
+
+ self.update()
+
+ def __get_sentence(self:Self, texts:str|Sequence[str], custom_language:Optional[str] = None) -> str|list[str]|None:
+
+ keys:list[str] = Common.get_keys(texts)
+
+ if len(keys) != 0:
+
+ language:str
+ done:list[str] = []
+
+ for language in (custom_language, self.__language, self.__default_language) + tuple(self.__sentences.keys()):
+ if language not in done and language in self.__sentences:
+
+ key:str
+
+ done.append(language)
+
+ for key in keys:
+ if key in self.__sentences[language]:
+ return self.__sentences[language][key]
+ return (
+ (texts[0] if len(texts) > 0 else None) if Check.is_array(texts) else
+ texts if Check.is_string(texts) else
+ None)
+
+ def get(self:Self,
+ keys:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ custom_language:Optional[Any] = None
+ ) -> Any|None:
+
+ text:str|list[str]|None = self.__get_sentence(keys, custom_language)
+
+ return Common.string_variables((
+ text if Check.is_string(text) else
+ "".join(text) if Check.is_array(text) else
+ ""), inputs)
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in Common.load_json(inputs, True):
+
+ language:str
+ sentences:dict[str, str|list[str]]
+
+ for language, sentences in subinputs.items():
+
+ key:str
+ sentence:str|list[str]
+
+ if language not in self.__sentences:
+ self.__sentences[language] = {}
+ for key, sentence in sentences.items():
+ if Common.is_mark_key(key) and sentence is None:
+ continue
+ if overwrite or key not in self.__sentences[language]:
+ self.__sentences[language][key] = sentence
\ No newline at end of file
diff --git a/Python/Managers/IndexesManager.py b/Python/Managers/IndexesManager.py
new file mode 100644
index 0000000..b51955b
--- /dev/null
+++ b/Python/Managers/IndexesManager.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any
+from Interfaces.Application.AnPInterface import AnPInterface
+from Utils.Checks import Check
+
+class IndexesManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__indexes:list[str] = []
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_indexes", "indexes"):
+ self.add(self.anp.settings.get(key))
+
+ def reset(self:Self) -> None:
+
+ self.__indexes = []
+
+ self.update()
+
+ def add(self:Self, inputs:Any|None) -> None:
+ if Check.is_array(inputs):
+
+ item:Any|None
+
+ for item in inputs:
+ self.add(item)
+
+ elif (
+ Check.is_string(inputs) and
+ (inputs := inputs.strip()) and
+ inputs not in self.__indexes
+ ):
+ self.__indexes.append(inputs)
+
+ def get(self:Self) -> list[str]:
+ return [*self.__indexes]
\ No newline at end of file
diff --git a/Python/Managers/ModelsManager.py b/Python/Managers/ModelsManager.py
new file mode 100644
index 0000000..28fa7a0
--- /dev/null
+++ b/Python/Managers/ModelsManager.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Sequence, TypeVar
+from Interfaces.Application.AnPInterface import AnPInterface
+from Abstracts.ModelAbstract import ModelAbstract
+from Utils.Common import Common
+
+T = TypeVar("T", bound = ModelAbstract)
+
+class ModelsManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__models:dict[str, ModelAbstract] = {}
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_models_files", "models_files", "default_models", "models"):
+ self.add(self.anp.settings.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__models = {}
+
+ self.update()
+
+ def get(self:Self, Type:type[T], keys:str|Sequence[str]) -> T|None:
+
+ key:str
+
+ for key in Common.get_keys(keys):
+ if key in self.__models and isinstance(self.__models[key], Type):
+ return self.__models[key]
+ return None
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, ModelAbstract]
+
+ for subinputs in Common.load_json(inputs, True):
+ for key, model in subinputs.items():
+ if Common.is_mark_key(key) and model is None:
+ continue
+ if isinstance(model, ModelAbstract) and (
+ overwrite or key not in self.__models
+ ):
+ self.__models[key] = model
\ No newline at end of file
diff --git a/Python/Managers/PrintTypesManager.py b/Python/Managers/PrintTypesManager.py
new file mode 100644
index 0000000..587a1d6
--- /dev/null
+++ b/Python/Managers/PrintTypesManager.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Utils.Common import Common
+from Utils.Checks import Check
+
+class PrintTypesManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__print_types:list[list[str]] = [
+ ["unkn", "unknown"]
+ ]
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_print_types_files", "print_types_files", "default_print_types", "print_types"):
+ self.add(self.anp.settings.get(key))
+
+ def reset(self:Self) -> None:
+
+ self.__print_types = [
+ ["unkn", "unknown"]
+ ]
+
+ self.update()
+
+ def get(self:Self, keys:str|Sequence[str]) -> str:
+
+ key:str
+
+ for key in Common.get_keys(keys):
+
+ block:list[str]
+
+ key = key.lower()
+
+ for block in self.__print_types:
+ if key in block:
+ return block[0].upper()
+ return self.__print_types[0][0].upper()
+
+ def add(self:Self, inputs:Any|None) -> None:
+ if Check.is_array(inputs):
+ for item in inputs:
+ if all(Check.is_key(subitem) for subitem in item):
+
+ main:str = item[0].lower()
+ i:int = 0
+ l:int = len(self.__print_types)
+ subitem:str
+
+ while i < l:
+ if main == self.__print_types[i][0]:
+ break
+ i += 1
+
+ if i == l:
+ self.__print_types.append([main])
+
+ for subitem in item[1:]:
+ subitem = subitem.lower()
+ if subitem not in self.__print_types[i]:
+ self.__print_types[i].append(subitem)
+
+ else:
+
+ subinputs:Any|None
+
+ for subinputs in item:
+ self.add(Common.load_json(subinputs))
diff --git a/Python/Managers/RoutesManager.py b/Python/Managers/RoutesManager.py
new file mode 100644
index 0000000..e7cc02c
--- /dev/null
+++ b/Python/Managers/RoutesManager.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import re
+from typing import Self, Any
+from Interfaces.Application.AnPInterface import AnPInterface
+from Models.RouteModel import RouteModel
+from Models.RequestModel import RequestModel
+from Utils.Common import Common
+from Utils.Checks import Check
+
+class RoutesManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__routes:list[RouteModel] = []
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_routes_files", "routes_files", "default_routes", "routes"):
+ self.add(self.anp.settings.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__routes = []
+
+ self.update()
+
+ def go(self:Self, method:str, path:str, request:RequestModel) -> None:
+
+ route:RouteModel
+
+ for route in self.__routes:
+ if route.match(method, path, request):
+ if not request.response:
+ if route.callback:
+ route.callback(request)
+ elif route.path:
+
+ index:str
+
+ for index in [""] + self.anp.indexes.get():
+
+ path_indexed:str|None = Common.get_absolute_path(route.path + "/" + path + ("/" + index if index else ""))
+
+ if path_indexed is not None and Common.is_file(path_indexed):
+ request.set_response(Common.load_file(path_indexed, "rb"))
+ request.response_mime = Common.get_mime_from_path(path_indexed)
+ return
+ request.set_response({
+ "ok" : False,
+ "code" : 404,
+ "message" : "not_found"
+ })
+ elif route.controller and route.controller_method:
+ if not self.anp.controllers.execute(route.controller, route.controller_method, request):
+ request.set_response({
+ "ok" : False,
+ "code" : 505,
+ "message" : "not_implemented"
+ })
+ else:
+ request.set_response({
+ "ok" : True,
+ "code" : 500,
+ "message" : "internal_server_error"
+ })
+ return
+
+ request.set_response({
+ "ok" : False,
+ "code" : 404,
+ "message" : "not_found"
+ })
+
+ def __add_new_route(self:Self, new_route:RouteModel, overwrite:bool = False) -> bool:
+ if new_route.error:
+ return False
+
+ i:int
+ route:RouteModel
+
+ for i, route in enumerate(self.__routes):
+ if route.path == new_route.path and route.method == new_route.method:
+ if overwrite:
+ self.__routes[i] = new_route
+ return True
+
+ self.__routes.append(new_route)
+
+ return True
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:Any|None
+
+ for subinputs in Common.load_json(inputs, False):
+ if isinstance(subinputs, RouteModel):
+ self.__add_new_route(subinputs, overwrite)
+ elif (
+ Check.is_string(subinputs) or Check.is_array(subinputs) or Check.is_dictionary(subinputs)
+ ) and not self.__add_new_route(RouteModel(subinputs), overwrite):
+ if Check.is_string(subinputs):
+ self.add(subinputs, overwrite)
+ elif Check.is_array(subinputs):
+
+ fragment:Any|None
+
+ for fragment in subinputs:
+ if Check.is_string(fragment) and not self.__add_new_route(RouteModel(fragment), overwrite):
+ continue
+ self.add(fragment, overwrite)
+
\ No newline at end of file
diff --git a/Python/Managers/SettingsManager.py b/Python/Managers/SettingsManager.py
new file mode 100644
index 0000000..0c259e0
--- /dev/null
+++ b/Python/Managers/SettingsManager.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Self, Sequence
+from Interfaces.Application.AnPInterface import AnPInterface
+from Utils.Common import Common
+
+class SettingsManager:
+
+ DEFAULT_SETTINGS:dict[str, Any|None] = {
+ "default_settings_files" : "/JSON/AnP.settings.json",
+ }
+
+ def __init__(self:Self,
+ anp:AnPInterface,
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None
+ ) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__inputs:dict[str, Any|None] = Common.get_dictionary(inputs)
+ self.__settings:dict[str, Any|None] = {}
+ self.__secrets:dict[str, Any|None] = {}
+
+ self.update()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for key in ("default_settings_files", "settings_files", "default_settings", "settings"):
+ self.add(self.get(key), True)
+ for key in ("default_secrets_files", "secrets_files", "default_secrets", "secrets"):
+ self.add_secret(self.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__settings = {}
+ self.__secrets = {}
+
+ self.update()
+
+ def get(self:Self,
+ keys:str|Sequence[str],
+ inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None,
+ default:Optional[Any] = None
+ ) -> Any|None:
+ return Common.get_value(keys, (
+ inputs, self.__inputs, self.__secrets, self.__settings, self.DEFAULT_SETTINGS
+ ), default)
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in Common.load_json(inputs, True):
+
+ key:str
+ value:Any|None
+
+ for key, value in subinputs.items():
+ if Common.is_mark_key(key) and value is None:
+ continue
+ if overwrite or key not in self.__settings:
+ self.__settings[key] = value
+
+ def add_secret(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in Common.load_json(inputs, True):
+
+ key:str
+ value:Any|None
+
+ for key, value in subinputs.items():
+ if Common.is_mark_key(key) and value is None:
+ continue
+ if overwrite or key not in self.__secrets:
+ self.__secrets[key] = value
\ No newline at end of file
diff --git a/Python/Managers/TerminalManager.py b/Python/Managers/TerminalManager.py
new file mode 100644
index 0000000..5c3626f
--- /dev/null
+++ b/Python/Managers/TerminalManager.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Callable, Any, Optional
+from threading import Thread
+from Interfaces.Application.AnPInterface import AnPInterface
+from Utils.Checks import Check
+from Utils.Common import Common
+from Models.CommandModel import CommandModel
+
+class TerminalManager:
+
+ def __init__(self:Self, anp:AnPInterface) -> None:
+
+ self.anp:AnPInterface = anp
+ self.__commands:list[CommandModel] = []
+ self.__thread:Thread = Thread(target = self.__handler)
+
+ self.update()
+
+ self.__thread.start()
+
+ def update(self:Self) -> None:
+
+ key:str
+
+ for keys, callback in (
+ (["close", "exit", "quit", "bye"], self.__close_command),
+ (["update", "upgrade"], self.__update_command),
+ (["reset"], self.__reset_command),
+ (["help", "h", "?"], self.__help_command)
+ ):
+ if not any(name in command_model.names for command_model in self.__commands for name in keys):
+ self.__commands.append(CommandModel(keys, callback))
+
+ for key in ("default_commands_files", "commands_files", "default_commands", "commands"):
+ self.add(self.anp.settings.get(key), True)
+
+ def reset(self:Self) -> None:
+
+ self.__commands = []
+
+ self.update()
+
+ def __handler(self:Self) -> None:
+ while self.anp.working():
+ try:
+
+ order:str = input()
+ command:str = order.split(" ")[0].lower()
+ parameters:dict[str, Any|None] = {}
+ arguments:list[Any|None] = []
+ done:bool = False
+
+ for command_model in self.__commands:
+ if command in command_model.names:
+ command_model.callback(parameters, arguments)
+ done = True
+ break
+
+ if not done:
+ self.anp.print("warning", "terminal_manager_unknown_command", {
+ "command": command
+ })
+
+ except Exception as error:
+ self.anp.exception(error, "terminal_manager_exception")
+
+ def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
+ if Check.is_array(inputs):
+ if len(inputs) == 2 and (
+ Check.is_key(inputs[0]) or
+ (Check.is_array(inputs[0]) and all(Check.is_key(name) for name in inputs[0]))
+ ) and Check.is_function(inputs[1]):
+
+ i:int = 0
+ l:int = len(self.__commands)
+ keys:list[str] = Common.get_keys(inputs[0])
+
+ while i < l:
+ if any(name in self.__commands[i].names for name in keys):
+ break
+ i += 1
+
+ if i == l:
+ self.__commands.append(CommandModel(keys, inputs[1]))
+ else:
+ self.__commands[i].update(keys, inputs[1], overwrite)
+
+ def __close_command(self:Self, parameters:dict[str, Any|None], arguments:Optional[list[Any|None]]) -> None:
+ self.anp.close()
+
+ def __update_command(self:Self, parameters:dict[str, Any|None], arguments:Optional[list[Any|None]]) -> None:
+ self.anp.update()
+
+ def __reset_command(self:Self, parameters:dict[str, Any|None], arguments:Optional[list[Any|None]]) -> None:
+ self.anp.reset()
+
+ def __help_command(self:Self, parameters:dict[str, Any|None], arguments:Optional[list[Any|None]]) -> None:
+ pass
\ No newline at end of file
diff --git a/Python/Models/CommandModel.py b/Python/Models/CommandModel.py
new file mode 100644
index 0000000..9fa5e77
--- /dev/null
+++ b/Python/Models/CommandModel.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Callable, Optional, Self, Sequence
+from Utils.Common import Common
+from Utils.Checks import Check
+
+class CommandModel:
+
+ def __init__(self:Self,
+ names:str|Sequence[str],
+ callback:Callable[[dict[str, Any|None], Optional[list[Any|None]]], None]
+ ) -> None:
+ self.names:list[str] = Common.get_keys(names)
+ self.callback:Callable[[dict[str, Any|None], Optional[list[Any|None]]], None] = callback
+
+ def update(self:Self,
+ names:str|Sequence[str],
+ callback:Optional[Callable[[dict[str, Any|None], Optional[list[Any|None]]], None]] = None,
+ overwrite:bool = False
+ ) -> None:
+ self.names += [name for name in Common.get_keys(names) if name not in self.names]
+ if Check.is_function(callback) or overwrite:
+ self.callback = callback
\ No newline at end of file
diff --git a/Python/Models/RequestModel.py b/Python/Models/RequestModel.py
new file mode 100644
index 0000000..b479082
--- /dev/null
+++ b/Python/Models/RequestModel.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Callable, Sequence, Optional
+from json import dumps as json_encode
+from Abstracts.RouteAbstract import RouteAbstract
+from Utils.Common import Common
+
+class RequestModel:
+
+ def __init__(self:Self) -> None:
+ self.post_variables:dict[str, Any|None] = {}
+ self.get_variables:dict[str, Any|None] = {}
+ self.url_variables:dict[str, Any|None] = {}
+ self.variables:dict[str, Any|None] = {}
+ self.request_headers:dict[str, Any|None] = {}
+ self.method:str|None = None
+ self.route:RouteAbstract|None = None
+ self.response:str|bytes|None = None
+ self.response_mime:str|None = None
+ self.response_code:int = 0
+ self.response_headers:dict[str, Any|None] = {}
+ self.callback:Callable[[RequestModel, str|bytes|None], None]|None = None
+
+ def get(self:Self, key:str|Sequence[str], default:Optional[Any] = None) -> Any|None:
+ return Common.get_value(key, (
+ self.url_variables, self.get_variables, self.post_variables, self.variables
+ ), default)
+
+ def set_variables(self:Self, inputs:dict[str, Any|None], on:Optional[str] = None) -> None:
+ (
+ self.url_variables if on == "url" else
+ self.get_variables if on == "get" else
+ self.post_variables if on == "post" else
+ self.variables).update(Common.get_dictionary(Common.load_json(inputs)))
+
+ def set_response(self:Self, data:Any|None) -> None:
+
+ if isinstance(data, dict):
+ self.response_code = Common.get_value("code", data, 200)
+
+ if data is None or isinstance(data, (str, bytes)):
+ if not self.response_code:
+ self.response_code = 200
+ self.response = data
+ elif isinstance(data, (dict, list, tuple, set)):
+ self.response_mime = "application/json"
+ self.response = json_encode(data)
+ elif isinstance(data, bool):
+ self.response = "true" if data else "false"
+ else:
+ self.response = str(data)
+ self.callback and self.callback(self, self.response)
+
+ def set_request_headers(self:Self, inputs:Any|None) -> None:
+ self.request_headers.update(Common.get_dictionary(inputs))
+
+ def set_response_headers(self:Self, inputs:Any|None) -> None:
+ self.response_headers.update(Common.get_dictionary(inputs))
\ No newline at end of file
diff --git a/Python/Models/RouteModel.py b/Python/Models/RouteModel.py
new file mode 100644
index 0000000..a3737a9
--- /dev/null
+++ b/Python/Models/RouteModel.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Self, Any, Sequence, Callable
+from re import Match as REMatch, Pattern as REPattern, compile as re_compile
+from Abstracts.RouteAbstract import RouteAbstract
+from Utils.Common import Common
+from Utils.Checks import Check
+from Utils.Patterns import RE
+from Models.RequestModel import RequestModel
+
+class RouteModel(RouteAbstract):
+
+ def __init__(self:Self, inputs:str|dict[str, Any|None]|Sequence[Any|None]) -> None:
+
+ request:str|REPattern
+ self.method:str
+ self.request:REPattern
+ self.action:str|None = None
+ self.controller:str|None = None
+ self.path:str|None = None
+ self.callback:Callable[[RequestModel], None]|None = None
+ self.permissions:list[str] = []
+ self.variables:list[str] = []
+ self.error:int = 0
+
+ if Check.is_string(inputs):
+
+ matches:REMatch = RE.ROUTE.match(inputs.strip())
+
+ if matches is not None:
+
+ permissions:str|None
+ method:str|None
+
+ method, request, self.action, self.controller, self.path, permissions = matches.groups()
+
+ self.method = "get" if method is None else method.lower()
+ if permissions is not None and (permissions := permissions.strip()) != "":
+ self.permissions.extend(permissions.split(","))
+
+ elif Check.is_dictionary(inputs):
+
+ preaction:str|Callable[[RequestModel], None]|None = Common.get_value("action", inputs)
+
+ self.method = Common.get_value("method", inputs, "get").lower()
+ request = Common.get_value("request", inputs, "/")
+ self.controller = Common.get_value("controller", inputs)
+ self.path = Common.get_value("path", inputs)
+ self.permissions.extend(Common.get_value("permissions", inputs, []))
+
+ if Check.is_function(preaction):
+ self.callback = preaction
+ elif Check.is_key(preaction):
+ self.action = preaction
+
+ elif Check.is_array(inputs):
+
+ l:int = len(inputs)
+ preaction:str|Callable[[RequestModel], None]|None
+ i:int = 3
+
+ if l < 3:
+ self.error = 1 << 1
+ return
+
+ self.method, request, preaction = inputs[:3]
+ while l > i:
+ if Check.is_key(inputs[i]):
+ self.controller = inputs[i]
+ elif Check.is_array(inputs[i]):
+ self.permissions.extend(inputs[i])
+ i += 1
+
+ if Check.is_function(preaction):
+ self.callback = preaction
+ elif Check.is_key(preaction):
+ self.action = preaction
+
+ else:
+ self.error = 1 << 0
+
+ if not self.error and not self.path and not self.callback and (
+ not self.action or not self.controller
+ ):
+ self.error = 1 << 2
+
+ if not self.error:
+ if Check.is_string(request):
+
+ def callback(matches:REMatch) -> str:
+
+ self.variables.append(matches.group(1))
+
+ return r'([^\/]+)'
+
+ self.request = re_compile(r'^' + RE.ROUTE_KEY.sub(callback, Common.to_regular_expression(
+ request[:-1] if request[-1] == "/" else request
+ )) + (r'\/?' if self.path is None else r'(\/.*)?') + r'$')
+
+ elif Check.is_regular_expression(request):
+ request = request.pattern
+ else:
+ self.error = 1 << 3
+
+ def match(self:Self, method:str, path:str, request:RequestModel) -> bool:
+
+ if self.method == method.lower():
+
+ matches:REMatch = self.request.match(path)
+
+ if matches is not None:
+
+ i:int
+ variable:str
+
+ for i, variable in enumerate(self.variables):
+ request.variables[variable] = matches.group(i + 1)
+
+ if self.error:
+ request.set_response({
+ "ok" : False,
+ "code" : 501,
+ "message" : "invalid_route_configuration",
+ "route_error" : self.error
+ })
+
+ return True
+ return False
\ No newline at end of file
diff --git a/Python/Utils/Checks.py b/Python/Utils/Checks.py
new file mode 100644
index 0000000..0f80265
--- /dev/null
+++ b/Python/Utils/Checks.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Self, Sequence
+from re import Pattern as REPattern
+from Utils.Patterns import RE
+
+class Check:
+
+ @staticmethod
+ def is_string(item:Any|None) -> bool:
+ return isinstance(item, str)
+
+ @classmethod
+ def is_key(cls:type[Self], item:Any|None) -> bool:
+ return isinstance(item, str) and RE.KEY.match(item) is not None
+
+ @staticmethod
+ def is_array(item:Any|None) -> bool:
+ return isinstance(item, (list, tuple))
+
+ @staticmethod
+ def is_dictionary(item:Any|None) -> bool:
+ return isinstance(item, dict)
+
+ @staticmethod
+ def is_function(item:Any|None) -> bool:
+ return callable(item)
+
+ @staticmethod
+ def is_regular_expression(item:Any|None) -> bool:
+ return isinstance(item, REPattern)
\ No newline at end of file
diff --git a/Python/Utils/Common.py b/Python/Utils/Common.py
new file mode 100644
index 0000000..e8e59b2
--- /dev/null
+++ b/Python/Utils/Common.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any, Optional, Sequence, Self
+from re import Match as REMatch
+from os.path import abspath as absolute_path, dirname as directory_name, exists as path_exists, isfile as is_file
+from json import loads as json_decode
+from io import FileIO
+from mimetypes import guess_type as get_mime_by_extension
+from inspect import FrameInfo, stack as get_stack
+from Utils.Checks import Check
+from Utils.Patterns import RE
+
+ROOT_PATH:str = absolute_path(directory_name(__file__))
+SLASH:str = "/" if "/" in ROOT_PATH else "\\"
+
+class Common:
+
+ ROOT_PATH:str = ROOT_PATH
+ SLASH:str = SLASH
+ ROOTS_PATH:list[str] = [""] + [ROOT_PATH + (SLASH + "..") * i for i in range(4)]
+ SPECIAL_REGULAR_EXPRESSION_CHARACTERS:dict[str, str] = {
+ "\r" : "r",
+ "\n" : "n",
+ "\t" : "t"
+ }
+
+ @classmethod
+ def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:
+
+ keys:list[str] = []
+ item:Any|None
+
+ for item in items:
+ if Check.is_key(item):
+ item not in keys and keys.append(item)
+ elif Check.is_array(item):
+
+ key:str
+
+ for key in cls.get_keys(*item):
+ key not in keys and keys.append(key)
+
+ return keys
+
+ @classmethod
+ def get_texts(cls:type[Self], *items:list[Any|None]) -> list[str]:
+
+ texts:list[str] = []
+ item:Any|None
+
+ for item in items:
+ if Check.is_string(item):
+ texts.append(item)
+ elif Check.is_array(item):
+
+ text:str
+
+ for text in cls.get_texts(*item):
+ texts.append(text)
+
+ return texts
+
+ @classmethod
+ def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]:
+
+ dictionaries:list[dict[str, Any|None]] = []
+ item:Any|None
+
+ for item in items:
+ if Check.is_dictionary(item):
+ dictionaries.append(item)
+ elif Check.is_array(item):
+
+ dictionary:dict[str, Any|None]
+
+ for dictionary in cls.get_dictionaries(*item):
+ dictionaries.append(dictionary)
+
+ return dictionaries
+
+ @classmethod
+ def get_dictionary(cls:type[Self], inputs:Any|None, overwrite:bool = False) -> dict[str, Any|None]:
+
+ dictionary:dict[str, Any|None] = {}
+
+ if Check.is_dictionary(inputs):
+
+ key:str
+ value:Any|None
+
+ for key, value in inputs.items():
+ if overwrite or key not in dictionary:
+ dictionary[key] = value
+
+ elif Check.is_array(inputs):
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in inputs:
+
+ key:str
+ value:Any|None
+
+ for key, value in cls.get_dictionaries(subinputs).items():
+ if overwrite or key not in dictionary:
+ dictionary[key] = value
+
+ return dictionary
+
+ @classmethod
+ def get_value(cls:type[Self],
+ keys:str|Sequence[str],
+ inputs:dict[str, Any|None]|Sequence[Any|None],
+ default:Optional[Any|None] = None
+ ) -> Any|None:
+ if len(keys := cls.get_keys(keys)):
+
+ subinputs:dict[str, Any|None]
+
+ for subinputs in cls.get_dictionaries(inputs):
+
+ key:str
+
+ for key in keys:
+ if key in subinputs:
+ return subinputs[key]
+ return default
+
+ @classmethod
+ def string_variables(cls:type[Self],
+ string:str,
+ inputs:dict[str, Any|None]|Sequence[Any|None],
+ default:Optional[str] = None
+ ) -> str:
+
+ inputs = cls.get_dictionary(inputs)
+
+ def replace(matches:REMatch) -> str:
+
+ key:str = matches.group(1)
+
+ return str(
+ inputs[key] if key in inputs else
+ default if default is not None else
+ matches.group(0))
+
+ return RE.STRING_VARIABLES.sub(replace, str(string))
+
+ @classmethod
+ def fix_path(cls:type[Self], path:str) -> str:
+ return RE.SLASHES.sub(cls.SLASH, path)
+
+ @classmethod
+ def get_absolute_path(cls:type[Self], path:str) -> str|None:
+
+ root:str
+
+ for root in cls.ROOTS_PATH:
+
+ absolute_path:str = cls.fix_path((root + cls.SLASH if root else "") + path)
+
+ if absolute_path[0] == "\\":
+ absolute_path = "\\" + absolute_path
+
+ if path_exists(absolute_path):
+ return absolute_path
+ return None
+
+ @classmethod
+ def load_file(cls:type[Self], path:str, mode:str = "r") -> str|bytes|None:
+ try:
+
+ file:FileIO
+
+ if mode == "r":
+ for format in (
+ "utf8", "cp1252", "ascii", "cp850", "latin1", "iso8859_1",
+ "utf16", "utf16le", "utf16be", "utf8sig", "iso8859_15"
+ ):
+ try:
+ with open(cls.get_absolute_path(path), mode, encoding = format) as file:
+ return file.read()
+ except Exception as exception:
+ pass
+ elif mode == "rb":
+ with open(cls.get_absolute_path(path), mode) as file:
+ return file.read()
+ except Exception as exception:
+ pass
+ return None
+
+ @classmethod
+ def load_json(cls:type[Self],
+ data:str|dict[str, Any|None]|Sequence[Any|None],
+ only_dictionaries:bool = True
+ ) -> list[dict[str, Any|None]|Sequence[Any|None]]:
+
+ json:list[dict[str, Any|None]|Sequence[Any|None]] = []
+
+ if isinstance(data, dict):
+ json.append(data)
+ elif isinstance(data, str):
+
+ subdata:dict[str, Any|None]|Sequence[Any|None]|None
+
+ try:
+ if subdata := json_decode(data):
+ json.extend(cls.load_json(subdata, only_dictionaries))
+ return
+ except Exception as exception:
+ pass
+
+ try:
+ json.extend(cls.load_json(json_decode(cls.load_file(data, "r")), only_dictionaries))
+ except Exception as exception:
+ pass
+
+ elif isinstance(data, (list, tuple)):
+ if only_dictionaries:
+
+ item:Any|None
+
+ for item in data:
+ json.extend(cls.load_json(item, only_dictionaries))
+ else:
+ json.append(data)
+
+ return json
+
+ @staticmethod
+ def get_action_data(i:int = 0) -> dict[str, str|int]:
+
+ stack:FrameInfo = get_stack()[i]
+
+ return {
+ "file" : stack.filename,
+ "method" : stack.function,
+ "line" : stack.lineno
+ }
+
+ @classmethod
+ def to_regular_expression(cls:type[Self], string:str) -> str:
+
+ def callback(matches:REMatch) -> str:
+
+ character:str = matches.group(0)
+
+ return "\\" + (
+ cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS[character] if character in cls.SPECIAL_REGULAR_EXPRESSION_CHARACTERS else
+ character)
+
+ return RE.TO_REGULAR_EXPRESSION.sub(callback, string)
+
+ @staticmethod
+ def get_mime_from_path(path:str) -> str|None:
+ return get_mime_by_extension(path)[0]
+
+ @classmethod
+ def is_mark_key(cls:type[Self], key:str, marks:str|Sequence[str] = "AnP") -> bool:
+
+ mark:str
+
+ for mark in cls.get_keys(marks):
+ if key.startswith(mark + "_") and (
+ key.endswith("_start") or
+ key.endswith("_end")
+ ):
+ return True
+ return False
+
+ @staticmethod
+ def is_file(path:str) -> bool:
+ return is_file(path)
\ No newline at end of file
diff --git a/Python/Utils/Patterns.py b/Python/Utils/Patterns.py
new file mode 100644
index 0000000..4de1181
--- /dev/null
+++ b/Python/Utils/Patterns.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from re import compile as re_compile, Pattern as REPattern, IGNORECASE as RE_IGNORECASE
+
+class RE:
+
+ KEY:REPattern = re_compile(r"^[a-z_][a-z0-9_]*$", RE_IGNORECASE)
+ STRING_VARIABLES:REPattern = re_compile(r"\{([a-z_][a-z0-9_]*)\}", RE_IGNORECASE)
+ SLASHES:REPattern = re_compile(r"[\\\/]+")
+ NEW_LINES:REPattern = re_compile(r"\r\n|\r|\n")
+ ROUTE:REPattern = re_compile(r"^(?:([a-z]+)\:)?([^\s]+)\s+(?:([^\s\@]+)\@([^\s]+)|([^\s]+))(?:\s+(.+))?$", RE_IGNORECASE)
+ TO_REGULAR_EXPRESSION:REPattern = re_compile(r'[\(\)\{\}\/\\\.\-\+\*\^\$\?\|\!\<\>\r\n\t]')
+ ROUTE_KEY:REPattern = re_compile(r'\\\{([a-z_][a-z0-9_]*)\\\}', RE_IGNORECASE)
+ EXCEPTION:REPattern = re_compile(r'^\s*File "([^"]+)", line ([0-9]+), in ([^\n]+)(.*|[\r\n]*)*$')
+ NEW_LINE:REPattern = re_compile(r'\r\n|[\r\n]')
\ No newline at end of file
diff --git a/Python/run.py b/Python/run.py
new file mode 100644
index 0000000..c604fe1
--- /dev/null
+++ b/Python/run.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from typing import Any
+from Application.AnP import AnP
+from Controllers.AIController import AIController
+
+inputs:dict[str, dict[str, Any|None]] = {
+ "default_models" : {
+ "AIController" : AIController
+ }
+}
+
+try:
+
+ from secrets import secrets as custom_secrets
+
+ for key, value in dict(custom_secrets).items():
+ if key not in inputs or isinstance(inputs[key], dict):
+ inputs[key] = value
+ elif isinstance(value, dict):
+ for subkey, subvalue in value.items():
+ inputs[key][subkey] = subvalue
+
+except ImportError:
+ pass
+
+anp:AnP = AnP(inputs)
\ No newline at end of file
diff --git a/Tools/run.server.python.sh b/Tools/run.server.python.sh
new file mode 100755
index 0000000..f4738bd
--- /dev/null
+++ b/Tools/run.server.python.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+directory=`dirname $(readlink -f "$0")`
+python3 "$directory/../Python/run.py"
diff --git a/Tools/sass.sh b/Tools/sass.sh
new file mode 100755
index 0000000..bac3be8
--- /dev/null
+++ b/Tools/sass.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+directory=`dirname $(readlink -f "$0")`
+sass $directory/../Public/scss/AnP.scss ../Public/scss/AnP.css;
diff --git a/version b/version
new file mode 100644
index 0000000..8a9ecc2
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+0.0.1
\ No newline at end of file