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

260 lines
8.5 KiB
JavaScript

"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.<string>} */
without_values = [
"disabled", "readonly"
],
/** @type {Array.<string>} */
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.<string, any|null>|Array.<any|null>)} attributes
* @param {!Array.<string>} included
* @param {!Array.<string>} excluded
* @param {!anp_attributes_name_callback} callback
* @param {!Object.<string, any|null>} 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.<string, any|null>|Array.<any|null>)} attributes
* @param {?(string|Array.<string>)} included
* @param {?(string|Array.<string>)} 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.<string, any|null>|Array.<any|null>)} attributes
* @param {?(string|Array.<string>)} [included = null]
* @param {?(string|Array.<string>)} [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.<string, any|null>|Array.<any|null>)} attributes
* @param {?(string|Array.<string>)} [included = null]
* @param {?(string|Array.<string>)} [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.<string>)} classes
* @returns {Array.<string>}
* @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.<string>)} classes
* @param {?(Object.<string, any|null>|Array.<any|null>)} attributes
* @returns {Array.<string>}
* @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.<string, any|null>|HTMLElement)} item
* @param {!(string|Array.<string>)} names
* @returns {Object.<string, any|null>|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;
})();