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