/* Description: DOM Management and HTML builder Date: 2025//3/6 Creator: Clemens Schwaighofer */ export { HtmlElementCreator, // deprecated name HtmlElementCreator as DomManagement }; import { deepCopyFunction, isObject } from './JavaScriptHelpers.mjs'; class HtmlElementCreator { /** * reates object for DOM element creation flow * @param {String} tag must set tag (div, span, etc) * @param {String} [id=''] optional set for id, if input, select will be used for name * @param {String} [content=''] text content inside, is skipped if sub elements exist * @param {Array} [css=[]] array for css tags * @param {Object} [options={}] anything else (value, placeholder, OnClick, style) * @return {Object} created element as an object */ cel(tag, id = '', content = '', css = [], options = {}) { return { tag: tag, id: id, // override name if set, else id is used. Only for input/button name: options.name, content: content, css: css, options: options, sub: [] }; } /** * attach a cel created object to another to create a basic DOM tree * @param {Object} base object where to attach/search * @param {Object} attach the object to be attached * @param {String} [id=''] optional id, if given search in base for this id and attach there * @return {Object} "none", technically there is no return needed as it is global attach */ ael(base, attach, id = '') { if (id) { // base id match already if (base.id == id) { base.sub.push(deepCopyFunction(attach)); } else { // sub check if (isObject(base.sub) && base.sub.length > 0) { for (var i = 0; i < base.sub.length; i ++) { // recursive call to sub element this.ael(base.sub[i], attach, id); } } } } else { base.sub.push(deepCopyFunction(attach)); } return base; } /** * directly attach n elements to one master base element * this type does not support attach with optional id * @param {Object} base object to where we attach the elements * @param {...Object} attach attach 1..n: attach directly to the base element those attachments * @return {Object} "none", technically there is no return needed, global attach */ aelx(base, ...attach) { for (var i = 0; i < attach.length; i ++) { base.sub.push(deepCopyFunction(attach[i])); } return base; } /** * same as aelx, but instead of using objects as parameters * get an array of objects to attach * @param {Object} base object to where we attach the elements * @param {Array} attach array of objects to attach * @return {Object} "none", technically there is no return needed, global attach */ aelxar(base, attach) { for (var i = 0; i < attach.length; i ++) { base.sub.push(deepCopyFunction(attach[i])); } return base; } /** * resets the sub elements of the base element given * @param {Object} base cel created element * @return {Object} returns reset base element */ rel(base) { base.sub = []; return base; } /** * searches and removes style from css array * @param {Object} _element element to work one * @param {String} css style sheet to remove (name) * @return {Object} returns full element */ rcssel(_element, css) { var css_index = _element.css.indexOf(css); if (css_index > -1) { _element.css.splice(css_index, 1); } return _element; } /** * adds a new style sheet to the element given * @param {Object} _element element to work on * @param {String} css style sheet to add (name) * @return {Object} returns full element */ acssel(_element, css) { var css_index = _element.css.indexOf(css); if (css_index == -1) { _element.css.push(css); } return _element; } /** * removes one css and adds another * is a wrapper around rcssel/acssel * @param {Object} _element element to work on * @param {String} rcss style to remove (name) * @param {String} acss style to add (name) * @return {Object} returns full element */ scssel(_element, rcss, acss) { this.rcssel(_element, rcss); this.acssel(_element, acss); return _element; } /** * parses the object tree created with cel/ael and converts it into an HTML string * that can be inserted into the page * @param {Object} tree object tree with dom element declarations * @return {String} HTML string that can be used as innerHTML */ phfo(tree) { let name_elements = [ 'button', 'fieldset', 'form', 'iframe', 'input', 'map', 'meta', 'object', 'output', 'param', 'select', 'textarea', ]; let skip_options = [ 'id', 'name', 'class', ]; let no_close = [ 'input', 'br', 'img', 'hr', 'area', 'col', 'keygen', 'wbr', 'track', 'source', 'param', 'command', // only in header 'base', 'meta', 'link', 'embed', ]; // holds the elements var content = []; // main part line var line = '<' + tree.tag; var i; // first id, if set if (tree.id) { line += ' id="' + tree.id + '"'; // if anything input (input, textarea, select then add name too) if (name_elements.includes(tree.tag)) { line += ' name="' + (tree.name ? tree.name : tree.id) + '"'; } } // second CSS if (isObject(tree.css) && tree.css.length > 0) { line += ' class="'; for (i = 0; i < tree.css.length; i ++) { line += tree.css[i] + ' '; } // strip last space line = line.slice(0, -1); line += '"'; } // options is anything key = "data" if (isObject(tree.options)) { // ignores id, name, class as key for (const [key, item] of Object.entries(tree.options)) { if (!skip_options.includes(key)) { line += ' ' + key + '="' + item + '"'; } } } // finish open tag line += '>'; // push finished line content.push(line); // dive into sub tree to attach sub nodes // NOTES: we can have content (text) AND sub nodes at the same level // CONTENT (TEXT) takes preference over SUB NODE in order if (isObject(tree.sub) && tree.sub.length > 0) { if (tree.content) { content.push(tree.content); } for (i = 0; i < tree.sub.length; i ++) { content.push(this.phfo(tree.sub[i])); } } else if (tree.content) { content.push(tree.content); } // if not input, image or br, then close if ( !no_close.includes(tree.tag) ) { content.push(''); } // combine to string return content.join(''); } /** * Create HTML elements from array list * as a flat element without master object file * Is like tree.sub call * @param {Array} list Array of cel created objects * @return {String} HTML String */ phfa(list) { var content = []; for (var i = 0; i < list.length; i ++) { content.push(this.phfo(list[i])); } return content.join(''); } } // __EMD__