AnP/Public/ecma/Utils/Common.ecma.js

471 lines
14 KiB
JavaScript

"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.<string, Object.<string, string>>} */
Common.REGULAR_EXPRESSION = {
TO : {
"\n" : "n",
"\r" : "r",
"\t" : "t"
},
FROM : {
n : "\n",
r : "\r",
t : "\t"
}
};
/**
* @param {...(any|null)} items
* @returns {Array.<string>}
* @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.<string>}
* @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.<Object.<string, any|null>>}
* @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.<string>} [except = []]
* @returns {Object.<string, any|null>}
* @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.<any|null>}
* @access public
* @static
*/
Common.get_array = items => Check.is_array(items) ? items : [items];
/**
* @param {!(string|Array.<string>)} keys
* @param {!(Object.<string, any|null>|Array<any|null>)} 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.<string, any|null>|Array<any|null>)} 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.<any|null>} 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.<any|null>} 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.<any|null>} 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.<string, any|null>} 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.<any|null>)} inputs
* @returns {Array.<HTMLElement>}
* @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.<string, any|null>|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.<string, any|null>|Array.<any|null>|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.<string, any|null>|Array.<any|null>)} data
* @returns {string}
* @access public
* @static
*/
Common.data_encode = data => Common.base64_encode(Common.json_encode(data));
/**
* @param {!string} data
* @returns {(Object.<string, any|null>|Array.<any|null>)}
* @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;
})();