"use strict"; import {Check} from "../Utils/Check.ecma.js"; import {Utils} from "../Utils/Utils.ecma.js"; import {Patterns} from "../Utils/Patterns.ecma.js"; import {EventsManager} from "../Managers/EventsManager.ecma.js"; /** * @typedef {import("./AnP.ecma.js").AnP} AnP */ /** * @callback anp_attributes_name_callback * @param {!string} name * @param {?string} value * @returns {void} */ /** * @callback anp_attributes_remove_callback * @param {!string} name * @returns {void} */ /** * @callback anp_attributes_load_callback * @param {!HTMLElement} item * @returns {void} */ /** * @class * @constructor * @param {!AnP} anp * @returns {void} * @access public */ export const Attributes = (function(){ /** * @constructs Attributes * @param {!AnP} anp * @returns {void} * @access private */ const Attributes = function(anp){ /** @type {Attributes} */ const self = this, /** @type {Array.} */ without_values = [ "disabled", "readonly" ], /** @type {Array.} */ normals = [ "src", "class", "title", "id", "alt", "target", "href", "style", "lang", "type", "name", "placeholder", "min", "max", "value", "step", "disabled", "readonly" ]; /** @type {boolean} */ let started = false; /** * @returns {void} * @access private */ const 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); return ok; }; return end(started ? false : started = true); }; /** * @param {!(Object.|Array.)} attributes * @param {!Array.} included * @param {!Array.} excluded * @param {!anp_attributes_name_callback} callback * @param {!Object.} parameters * @returns {void} * @access private */ const process_fixed = (attributes, included, excluded, callback, parameters) => { if(Check.is_dictionary(attributes)){ /** @type {boolean} */ const has_events_loader = !!parameters.on_load_for_events; for(let name in attributes){ /** @type {any|null} */ const value = attributes[name], /** @type {string} */ pascal = name.replace(Patterns.RE_ATTRIBUTE_BAD_SET_CHARACTERS, "-").toLowerCase(); if( (!included.length || included.includes(pascal)) && (!excluded.length || !excluded.includes(pascal)) ){ if(pascal.slice(0, 2) == "on" && Check.is_function(value)){ parameters.on_load_for_events || (parameters.on_load_for_events = new EventsManager()); if(name == "on_load_execute") parameters.on_load_execute = value; else parameters.on_load_for_events.add(item => { item.addEventListener(pascal.replace(Patterns.RE_CLEAN_EVENT_NAME, ""), event => value(item, event)); }); continue; }; callback( (( normals.includes(pascal) || ["data-", "aria-"].includes(pascal.slice(0, 5)) ) ? "" : "data-") + pascal, ( without_values.includes(pascal) ? null : Check.is_bool(value) ? value ? "true" : "false" : Check.is_json_object(value) ? Utils.variables_encode(value) : "" + value) ); }; }; if(!has_events_loader && parameters.on_load_for_events){ /** @type {string} */ const chain = anp.random_chain(); callback("data-anp-events-loader", chain); anp.preload("[data-anp-events-loader=" + chain + "]", (item, asynchronous, ok) => { anp.remove_random_chain(chain); if(ok){ item.removeAttribute("data-anp-events-loader"); parameters.on_load_for_events.execute(item); parameters.on_load_execute && parameters.on_load_execute(item); }; }); }; }else if(Check.is_array(attributes)) attributes.forEach(group => { process_fixed(group, included, excluded, callback, parameters); }); }; /** * @param {!(Object.|Array.)} attributes * @param {?(string|Array.)} included * @param {?(string|Array.)} excluded * @param {!anp_attributes_name_callback} callback * @returns {void} * @access private */ const process = (attributes, included, excluded, callback) => { process_fixed(attributes, Utils.get_pascal_keys(included), Utils.get_pascal_keys(excluded), callback, {}); }; /** * @param {!(Object.|Array.)} attributes * @param {?(string|Array.)} [included = null] * @param {?(string|Array.)} [excluded = null] * @returns {string} * @access public */ this.create = (attributes, included = null, excluded = null) => { /** @type {string} */ let html = ``; process(attributes, included, excluded, (name, value) => { html += ` ` + name + (value === null ? `` : `="` + value + `"`) }); return html; }; /** * @param {!(string|HTMLElement)} item * @param {!(Object.|Array.)} attributes * @param {?(string|Array.)} [included = null] * @param {?(string|Array.)} [excluded = null] * @returns {void} * @access public */ this.set = (item, attributes, included, excluded) => { anp.preload(item, (item, asynchronous, ok) => { ok && process(attributes, included, excluded, (name, value) => { item.setAttribute(name, value); }); }); }; constructor(); }; /** * @param {!(string|Array.)} classes * @returns {Array.} * @access public * @static */ Attributes.get_classes = classes => Utils.get_pascal_keys( Check.is_array(classes) ? classes : Check.is_string(classes) ? classes.split(Patterns.RE_SPACES) : []); /** * @param {!(string|Array.)} classes * @param {?(Object.|Array.)} attributes * @returns {Array.} * @access public * @static */ Attributes.join_classes = (...sets) => Utils.get_pascal_keys( sets.reduce((set, new_classes) => set.concat(Attributes.get_classes(new_classes)), []) ).join(" "); /** * @param {!(Object.|HTMLElement)} item * @param {!(string|Array.)} names * @returns {Object.|void} * @access public * @static */ Attributes.remove = (item, names) => { /** @type {anp_attributes_remove_callback|null} */ var action = ( Check.is_dictionary(item) ? name => { if(item[name] !== undefined) delete item[name]; } : Check.is_html_item(item) ? name => { item.hasAttribute(name) && item.removeAttribute(name) } : null); action && Utils.get_pascal_keys(names).forEach(action); if(Check.is_dictionary(item)) return item; }; return Attributes; })();