AnP/Public/ecma/Application/AnP.ecma.js

291 lines
10 KiB
JavaScript

"use strict";
import {SettingsManager} from "../Managers/SettingsManager.ecma.js";
import {I18NManager} from "../Managers/I18NManager.ecma.js";
import {URLPathDriver} from "../Drivers/URLPathDriver.ecma.js";
import {ThreadsManager} from "../Managers/ThreadsManager.ecma.js";
import {Attributes} from "./Attributes.ecma.js";
import {Components} from "./Components.ecma.js";
import {Check} from "../Utils/Check.ecma.js";
import {Utils} from "../Utils/Utils.ecma.js";
import {Patterns} from "../Utils/Patterns.ecma.js";
/**
* @callback anp_preload_callback
* @param {?HTMLElement} item
* @param {!boolean} asynchronous
* @param {!boolean} ok
* @returns {void}
*/
/**
* @callback anp_settings_get_callback
* @param {!(string|Array.<string>)} keys
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @param {?any} [_default = null]
* @returns {any|null}
*/
/**
* @callback anp_start_callback
* @param {!boolean} ok
* @returns {boolean}
*/
/**
* @callback anp_autostart_callback
* @param {!AnP} anp
* @returns {void}
*/
/**
* @class
* @constructor
* @param {?(Object.<string, any|null>|Array.<any|null>)} inputs
* @param {?anp_autostart_callback} [autostart_callback = null]
* @returns {void}
* @access public
*/
export const AnP = (function(){
/**
* @constructs AnP
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @param {?anp_autostart_callback} [autostart_callback = null]
* @returns {void}
* @access private
*/
const AnP = function(inputs = null, autostart_callback = null){
/** @type {AnP} */
const self = this,
/** @type {Array.<string>} */
random_chains = [],
// http://detectmobilebrowsers.com/
/** @type {boolean} */
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),
// https://stackoverflow.com/questions/56393880/how-do-i-detect-dark-mode-using-javascript#answer-57795495
/** @type {boolean} */
is_dark_mode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
/** @type {boolean} */
let started = false,
/** @type {number} */
preload_timeout = 2000,
/** @type {string} */
chain_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
/** @type {number} */
chain_length = 11;
/** @type {SettingsManager} */
this.settings = null;
/** @type {I18NManager} */
this.i18n = null;
/** @type {URLPathDriver} */
this.request = null;
/** @type {ThreadsManager} */
this.threads = null;
/** @type {Attributes} */
this.attributes = this.attr = null;
/** @type {Components} */
this.components = this.comp = null;
/** @type {string|null} */
this.object_name = null;
/** @type {HTMLElement|Document} */
this.item_self = document;
/** @type {string|null} */
this.hash_self = null;
/**
* @returns {void}
* @access private
*/
const set_basics = () => {
/** @type {anp_settings_get_callback} */
const get = self.settings ? self.settings.get : Utils.get_value;
preload_timeout = get(["preload_timeout", "timeout"], inputs, preload_timeout);
chain_alphabet = get(["random_chain_alphabet", "chain_alphabet", "alphabet"], inputs, chain_alphabet);
chain_length = get(["random_chain_length", "chain_length", "length"], inputs, chain_length);
};
/**
* @returns {void}
* @access private
*/
const execute_constructor = () => {
set_basics();
self.settings = new SettingsManager(self, inputs);
self.object_name = self.settings.get("object_name");
set_basics();
self.i18n = new I18NManager(self);
self.request = new URLPathDriver(self);
self.threads = new ThreadsManager(self);
self.attributes = self.attr = new Attributes(self);
self.components = self.comp = new Components(self);
set_basics();
self.settings.get("autostart") && self.start();
}
/**
* @returns {void}
* @access private
*/
const constructor = () => {
if(Utils.get_value("debug_mode")){
try{
execute_constructor();
}catch(exception){
console.error(exception);
};
}else
execute_constructor();
};
/**
* @param {?anp_start_callback} callback
* @returns {boolean}
* @access public
*/
this.start = (callback = null) => {
/** @type {!anp_start_callback} */
const end = ok => {
Utils.execute(callback, ok);
ok && Utils.execute(autostart_callback, self);
return ok;
};
if(started)
return end(false);
started = true;
Utils.execute_items([
"settings", "i18n", "request", "threads", "attributes", "components"
], (key, next) => {
self[key].start(ok => {
next();
});
}, () => {
end(true);
});
return true;
};
/**
* @param {!(string|HTMLElement)} selector
* @param {!anp_preload_callback} callback
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {void}
* @access public
*/
this.preload = (selector, callback, inputs = null) => {
if(!Check.is_function(callback))
return;
if(Check.is_html_item(selector)){
callback(selector, false, true);
return;
};
if(Check.is_string(selector)){
/** @type {HTMLElement|null} */
let item;
try{
if(item = self.item_self.querySelector(selector)){
callback(item, false, true);
return;
};
}catch(exception){
callback(null, false, false);
return;
};
/** @type {number} */
const date = Date.now(),
/** @type {number} */
timeout = Utils.get_value(["preload_timeout", "timeout"], inputs, preload_timeout);
self.threads.add(thread => {
if(item = self.item_self.querySelector(selector)){
self.threads.remove(thread);
callback(item, true, true);
}else if(Date.now() - date > timeout){
self.threads.remove(thread);
callback(null, true, false);
};
}, {
/** @type {boolean} */
bucle : true
});
}else
callback(null, false, false);
};
/**
* @returns {string}
* @access public
*/
this.random_chain = () => {
/** @type {string} */
let chain;
/** @type {number} */
const l = chain_alphabet.length;
do{
chain = "";
while((chain += chain_alphabet[Math.random() * l >> 0]).length < chain_length);
}while(
random_chains.includes(chain) ||
!Patterns.RE_RIGHT_RANDOM_CHAIN.test(chain) ||
document.querySelector("." + chain + ",#" + chain + ",[name=" + chain + "]")
);
random_chains.push(chain);
return chain;
};
/**
* @param {!string} chain
* @returns {void}
* @access public
*/
this.remove_random_chain = chain => {
random_chains.includes(chain) &&
random_chains.splice(random_chains.indexOf(chain), 1);
};
/**
* @returns {boolean}
* @access public
*/
this.is_mobile = () => is_mobile;
/**
* @returns {boolean}
* @access public
*/
this.is_dark_mode = () => is_dark_mode;
constructor();
};
return AnP
})();