add `.rep()` & `.replaceChildren()` function update `.app()` & `.append()` to support rest params update build target to ES6 fix comment color on demo page add (more) tests
187 lines
5.7 KiB
JavaScript
187 lines
5.7 KiB
JavaScript
// @license magnet:?xt=urn:btih:723febf9f6185544f57f0660a41489c7d6b4931b&dn=wtfpl.txt
|
|
const isFluentDomObject = (value) => !!value.fluentDom;
|
|
const isHTMLElement = (value) => !!value.nodeType;
|
|
const isNumber = (value) => typeof value === "number";
|
|
const isString = (value) => typeof value === "string";
|
|
/**
|
|
* fluent-dom-esm v2.3.0
|
|
*
|
|
* Fluent DOM Manipulation, adapted to ESM and cranked up to v2.3(.0).
|
|
*
|
|
* https://git.itsericwoodward.com/eric/fluent-dom-esm
|
|
*
|
|
* v2.3.0 Copyright (c) 2025 Eric Woodward
|
|
* Original copyright (c) 2009 Tommy Montgomery (https://glacius.tmont.com/articles/fluent-dom-manipulation-in-javascript)
|
|
*
|
|
* Released under the WTFPL (Do What the Fuck You Want to Public License)
|
|
*
|
|
* @author Eric Woodward (v2+)
|
|
* @author Tommy Montgomery (original)
|
|
* @license http://sam.zoy.org/wtfpl/
|
|
*/
|
|
const APP_VERSION = "2.3.0";
|
|
const fluentDomEsm = (function() {
|
|
const FluentDom = function(nodeOrQuerySelector) {
|
|
if (typeof nodeOrQuerySelector !== "string")
|
|
return new FluentDomInternal(nodeOrQuerySelector);
|
|
const f = new FluentDomInternal();
|
|
f.querySelector(nodeOrQuerySelector);
|
|
return f;
|
|
};
|
|
FluentDom.c = FluentDom.create = function(tagName) {
|
|
const f = new FluentDomInternal();
|
|
f.create(tagName);
|
|
return f;
|
|
};
|
|
FluentDom.v = FluentDom.version = function() {
|
|
return APP_VERSION;
|
|
};
|
|
const FluentDomInternal = function(node) {
|
|
let root = node || null;
|
|
this.fluentDom = APP_VERSION;
|
|
this.a = this.attr = function(name, value) {
|
|
if (!root) {
|
|
throw new Error("Cannot set an attribute on a non-element");
|
|
}
|
|
if (typeof name === "undefined") {
|
|
throw new Error("Cannot set an attribute without a name");
|
|
}
|
|
root.setAttribute(name, value);
|
|
return this;
|
|
};
|
|
this.app = this.append = function(...content) {
|
|
if (!root || !root?.appendChild) {
|
|
throw new Error("Cannot append to a non-element");
|
|
}
|
|
if (!content || content.length < 1) {
|
|
throw new Error("Cannot append without a value");
|
|
}
|
|
content.forEach((value) => {
|
|
const type = typeof value;
|
|
if (type === "object") {
|
|
if (isFluentDomObject(value)) {
|
|
const domVal = value.toDom();
|
|
if (domVal !== null) root?.appendChild(domVal);
|
|
} else if (isHTMLElement(value)) {
|
|
root?.appendChild(value);
|
|
} else {
|
|
throw new Error(
|
|
"Invalid argument: not an HTMLElement or FluentDom object"
|
|
);
|
|
}
|
|
} else if (isNumber(value) || isString(value)) {
|
|
root?.appendChild(document.createTextNode(`${value}`));
|
|
} else {
|
|
throw new Error(
|
|
`Invalid argument: not a valid type (${typeof value})`
|
|
);
|
|
}
|
|
});
|
|
return this;
|
|
};
|
|
this.c = this.create = function(tagName) {
|
|
root = document.createElement(tagName);
|
|
return this;
|
|
};
|
|
this.className = this.cls = function(value) {
|
|
return this.attr("class", value);
|
|
};
|
|
this.clear = function() {
|
|
root = null;
|
|
return this;
|
|
};
|
|
this.h = this.href = function(url) {
|
|
return this.attr("href", url);
|
|
};
|
|
this.html = function(content) {
|
|
if (!root) {
|
|
throw new Error("Cannot set innerHTML for a non-element");
|
|
}
|
|
root.innerHTML = content;
|
|
return this;
|
|
};
|
|
this.id = function(value) {
|
|
return this.attr("id", value);
|
|
};
|
|
this.l = this.listen = function(...props) {
|
|
if (!root || !root.addEventListener) {
|
|
throw new Error("Cannot addEventListener to a non-element");
|
|
}
|
|
root.addEventListener(...props);
|
|
return this;
|
|
};
|
|
this.q = this.querySelector = function(selector) {
|
|
root = document.querySelector(selector);
|
|
return this;
|
|
};
|
|
this.rep = this.replaceChildren = function(...content) {
|
|
if (!root) {
|
|
throw new Error("Cannot replaceChildren on a non-element");
|
|
}
|
|
if (!root.replaceChildren) {
|
|
if (!content) return this.html("");
|
|
return this.html("").append(...content);
|
|
}
|
|
if (content.length && content[0] && isFluentDomObject(content[0])) {
|
|
const domValues = content.map(
|
|
(val) => val.toDom()
|
|
);
|
|
root.replaceChildren(
|
|
...domValues.filter((val) => val !== null)
|
|
);
|
|
}
|
|
root.replaceChildren(...content);
|
|
return this;
|
|
};
|
|
function styleFunction(prop, value) {
|
|
if (!root) {
|
|
throw new Error("Cannot get or set style for a non-element");
|
|
}
|
|
if (typeof prop === "string" && typeof value !== "undefined") {
|
|
root.style[prop] = value;
|
|
return this;
|
|
}
|
|
if (typeof prop !== "object") {
|
|
throw new Error(
|
|
`Invalid argument: "prop" must be string or object (found ${typeof prop})`
|
|
);
|
|
}
|
|
Object.keys(prop).forEach((key) => {
|
|
root.style[key] = prop[key];
|
|
});
|
|
return this;
|
|
}
|
|
this.s = this.style = styleFunction;
|
|
this.t = this.text = function(text) {
|
|
if (!root) {
|
|
throw new Error("Cannot set innerText for a non-element");
|
|
}
|
|
return this.append(text);
|
|
};
|
|
this.title = function(title) {
|
|
if (!root) {
|
|
throw new Error("Cannot get or set title for a non-element");
|
|
}
|
|
return this.attr("title", title);
|
|
};
|
|
this.toDom = this.toHTMLElement = function() {
|
|
return root;
|
|
};
|
|
this.unlisten = function(...props) {
|
|
if (!root || !root.removeEventListener) {
|
|
throw new Error("Cannot removeEventListener on a non-element");
|
|
}
|
|
root.removeEventListener(...props);
|
|
return this;
|
|
};
|
|
this.v = this.version = function() {
|
|
return this.fluentDom;
|
|
};
|
|
};
|
|
return FluentDom;
|
|
})();
|
|
export {
|
|
fluentDomEsm as default
|
|
};
|
|
// @license-end
|