"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; })();