Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Has been cancelled
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Has been cancelled
Execution Tests / test (macos-latest) (push) Has been cancelled
Execution Tests / test (ubuntu-latest) (push) Has been cancelled
Execution Tests / test (windows-latest) (push) Has been cancelled
Test server launches without errors / test (push) Has been cancelled
Unit Tests / test (macos-latest) (push) Has been cancelled
Unit Tests / test (ubuntu-latest) (push) Has been cancelled
Unit Tests / test (windows-2022) (push) Has been cancelled
Includes 30 custom nodes committed directly, 7 Civitai-exclusive loras stored via Git LFS, and a setup script that installs all dependencies and downloads HuggingFace-hosted models on vast.ai. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
593 lines
25 KiB
JavaScript
593 lines
25 KiB
JavaScript
import * as dom from "./utils_dom.js";
|
|
import { getObjectValue } from "./shared_utils.js";
|
|
const CONFIG_DEFAULT = {
|
|
attrBind: "data-bind",
|
|
attrIf: "data-if",
|
|
attrIfIs: "data-if-is",
|
|
};
|
|
const CONFIG = Object.assign({}, CONFIG_DEFAULT, {
|
|
attrBind: "bind",
|
|
attrIf: "if",
|
|
attrIfIs: "if-is",
|
|
});
|
|
const RGX_COMPARISON = (() => {
|
|
let value = "((?:\\!*)[_a-z0-9\\.\\-\\[\\]'\"]+)";
|
|
let comparison = "((?:<|>|==|\\!=)=?)";
|
|
return new RegExp(`^(?:\\!*)\\(?${value}\\s*${comparison}\\s*${value}\\)?$`, "i");
|
|
})();
|
|
const RGXPART_BIND_FN_TEMPLATE_STRING = "template|tpl";
|
|
const RGXPART_BIND_FN_ELEMENT_STRING = "element|el";
|
|
const RGX_BIND_FN_TEMPLATE = new RegExp(`^(?:${RGXPART_BIND_FN_TEMPLATE_STRING})\\(([^\\)]+)\\)`, "i");
|
|
const RGX_BIND_FN_ELEMENT = new RegExp(`^(?:${RGXPART_BIND_FN_ELEMENT_STRING})\\(([^\\)]+)\\)`, "i");
|
|
const RGX_BIND_FN_TEMPLATE_OR_ELEMENT = new RegExp(`^(?:${RGXPART_BIND_FN_TEMPLATE_STRING}|${RGXPART_BIND_FN_ELEMENT_STRING})\\(([^\\)]+)\\)`, "i");
|
|
const RGX_BIND_FN_LENGTH = /^(?:length|len|size)\(([^\)]+)\)/i;
|
|
const RGX_BIND_FN_FORMAT = /^(?:format|fmt)\(([^\,]+),([^\)]+)\)/i;
|
|
const RGX_BIND_FN_CALL = /^([^\(]+)\(([^\)]*)\)/i;
|
|
const EMPTY_PREPROCESS_FN = (data) => data;
|
|
const RGX_BIND_DECLARATIONS = /\s*(\!*(?:[\$_a-z0-9-\.\'\"]|\?\?|\|\||\&\&|(?:(?:<|>|==|\!=)=?))+(?:\`[^\`]+\`)?(?:\([^\)]*\))?)(?::(.*?))?(\s|$)/gi;
|
|
function localAssertNotFalsy(input, errorMsg = `Input is not of type.`) {
|
|
if (input == null) {
|
|
throw new Error(errorMsg);
|
|
}
|
|
return input;
|
|
}
|
|
function cleanKey(key) {
|
|
return key.toLowerCase().trim().replace(/\s/g, "");
|
|
}
|
|
function toArray(value) {
|
|
if (Array.isArray(value)) {
|
|
return value;
|
|
}
|
|
if (value instanceof Set) {
|
|
return Array.from(value);
|
|
}
|
|
if (typeof value === "object" && typeof value.length === "number") {
|
|
return [].slice.call(value);
|
|
}
|
|
return [value];
|
|
}
|
|
function flattenArray(arr) {
|
|
return toArray(arr).reduce((acc, val) => {
|
|
return acc.concat(Array.isArray(val) ? flattenArray(val) : val);
|
|
}, []);
|
|
}
|
|
function getObjValue(lookup, obj) {
|
|
let booleanMatch = lookup.match(/^(\!+)(.+?)$/i) || [];
|
|
let booleanNots = [];
|
|
if (booleanMatch[1] && booleanMatch[2]) {
|
|
booleanNots = booleanMatch[1].split("");
|
|
lookup = booleanMatch[2];
|
|
}
|
|
let value = getObjectValue(obj, lookup);
|
|
while (booleanNots.length) {
|
|
value = !value;
|
|
booleanNots.shift();
|
|
}
|
|
return value;
|
|
}
|
|
function getPrimitiveOrObjValue(stringValue, data) {
|
|
let value;
|
|
if (stringValue == null) {
|
|
return stringValue;
|
|
}
|
|
let negate = getNegates(stringValue);
|
|
if (negate != null) {
|
|
stringValue = stringValue.replace(/^\!+/, "");
|
|
}
|
|
try {
|
|
const cleanedStringValue = stringValue.replace(/^'(.*)'$/, '"$1"');
|
|
value = JSON.parse(cleanedStringValue);
|
|
}
|
|
catch (e) {
|
|
value = getObjValue(stringValue, data);
|
|
}
|
|
value = negate !== null ? (negate === 1 ? !value : !!value) : value;
|
|
return value;
|
|
}
|
|
function getNegates(stringValue) {
|
|
let negate = null;
|
|
let negateMatches = stringValue.match(/^(\!+)(.*)/);
|
|
if (negateMatches && negateMatches.length >= 3) {
|
|
negate = negateMatches[1].length % 2;
|
|
}
|
|
return negate;
|
|
}
|
|
function getStringComparisonExpression(bindingPropName, data) {
|
|
let comparisonMatches = bindingPropName.match(RGX_COMPARISON);
|
|
if (!(comparisonMatches === null || comparisonMatches === void 0 ? void 0 : comparisonMatches.length)) {
|
|
return null;
|
|
}
|
|
let a = getPrimitiveOrObjValue(comparisonMatches[1], data);
|
|
let b = getPrimitiveOrObjValue(comparisonMatches[3], data);
|
|
let c = comparisonMatches[2];
|
|
let value = (() => {
|
|
switch (c) {
|
|
case "===":
|
|
return a === b;
|
|
case "==":
|
|
return a == b;
|
|
case "<=":
|
|
return a <= b;
|
|
case ">=":
|
|
return a >= b;
|
|
case "!==":
|
|
return a !== b;
|
|
case "!=":
|
|
return a != b;
|
|
case "<":
|
|
return a < b;
|
|
case ">":
|
|
return a > b;
|
|
default:
|
|
return undefined;
|
|
}
|
|
})();
|
|
return value;
|
|
}
|
|
function replaceTplElementWithChildren(tplEl, fragOrElOrEls) {
|
|
const els = Array.isArray(fragOrElOrEls) ? fragOrElOrEls : [fragOrElOrEls];
|
|
tplEl.replaceWith(...els);
|
|
const numOfChildren = Array.isArray(fragOrElOrEls)
|
|
? fragOrElOrEls.length
|
|
: fragOrElOrEls.childElementCount;
|
|
if (numOfChildren === 1) {
|
|
const firstChild = Array.isArray(fragOrElOrEls)
|
|
? fragOrElOrEls[0]
|
|
: fragOrElOrEls.firstElementChild;
|
|
if (firstChild instanceof Element) {
|
|
if (tplEl.className.length) {
|
|
firstChild.className += ` ${tplEl.className}`;
|
|
}
|
|
let attr = tplEl.getAttribute("data");
|
|
if (attr) {
|
|
firstChild.setAttribute("data", attr);
|
|
}
|
|
attr = tplEl.getAttribute(CONFIG.attrBind);
|
|
if (attr) {
|
|
firstChild.setAttribute(CONFIG.attrBind, attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function getValueForBinding(bindingPropName, context) {
|
|
console.log("getValueForBinding", bindingPropName, context);
|
|
const data = context.data;
|
|
let stringTemplate = null;
|
|
let stringTemplates = /^(.*?)\`([^\`]*)\`$/.exec(bindingPropName.trim());
|
|
if ((stringTemplates === null || stringTemplates === void 0 ? void 0 : stringTemplates.length) === 3) {
|
|
bindingPropName = stringTemplates[1];
|
|
stringTemplate = stringTemplates[2];
|
|
}
|
|
let value = null;
|
|
let hadALogicalOp = false;
|
|
const opsToValidation = new Map([
|
|
[/\s*\?\?\s*/, (v) => v != null],
|
|
[/\s*\|\|\s*/, (v) => !!v],
|
|
[/\s*\&\&\s*/, (v) => !v],
|
|
]);
|
|
for (const [op, fn] of opsToValidation.entries()) {
|
|
if (bindingPropName.match(op)) {
|
|
hadALogicalOp = true;
|
|
const bindingPropNames = bindingPropName.split(op);
|
|
for (const propName of bindingPropNames) {
|
|
value = getValueForBindingPropName(propName, context);
|
|
if (fn(value)) {
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!hadALogicalOp) {
|
|
value = getValueForBindingPropName(bindingPropName, context);
|
|
}
|
|
return stringTemplate && value != null
|
|
? stringTemplate.replace(/\$\{value\}/g, String(value))
|
|
: value;
|
|
}
|
|
function getValueForBindingPropName(bindingPropName, context) {
|
|
var _a, _b, _c;
|
|
const data = context.data;
|
|
let negate = getNegates(bindingPropName);
|
|
if (negate != null) {
|
|
bindingPropName = bindingPropName.replace(/^\!+/, "");
|
|
}
|
|
let value;
|
|
RGX_COMPARISON.lastIndex = 0;
|
|
if (RGX_COMPARISON.test(bindingPropName)) {
|
|
value = getStringComparisonExpression(bindingPropName, data);
|
|
}
|
|
else if (RGX_BIND_FN_LENGTH.test(bindingPropName)) {
|
|
bindingPropName = RGX_BIND_FN_LENGTH.exec(bindingPropName)[1];
|
|
value = getPrimitiveOrObjValue(bindingPropName, data);
|
|
value = (value && value.length) || 0;
|
|
}
|
|
else if (RGX_BIND_FN_FORMAT.test(bindingPropName)) {
|
|
let matches = RGX_BIND_FN_FORMAT.exec(bindingPropName);
|
|
bindingPropName = matches[1];
|
|
value = getPrimitiveOrObjValue(bindingPropName, data);
|
|
value = matches[2].replace(/^['"]/, "").replace(/['"]$/, "").replace(/\$1/g, value);
|
|
}
|
|
else if (RGX_BIND_FN_CALL.test(bindingPropName)) {
|
|
console.log("-----");
|
|
console.log(bindingPropName);
|
|
let matches = RGX_BIND_FN_CALL.exec(bindingPropName);
|
|
const functionName = matches[1];
|
|
const maybeDataName = (_a = matches[2]) !== null && _a !== void 0 ? _a : null;
|
|
value = getPrimitiveOrObjValue(maybeDataName, data);
|
|
console.log(functionName, maybeDataName, value);
|
|
if (typeof (value === null || value === void 0 ? void 0 : value[functionName]) === "function") {
|
|
value = value[functionName](value, data, context.currentElement, context.contextElement);
|
|
}
|
|
else if (typeof (data === null || data === void 0 ? void 0 : data[functionName]) === "function") {
|
|
value = data[functionName](value, data, context.currentElement, context.contextElement);
|
|
}
|
|
else if (typeof ((_b = context.currentElement) === null || _b === void 0 ? void 0 : _b[functionName]) === "function") {
|
|
value = context.currentElement[functionName](value, data, context.currentElement, context.contextElement);
|
|
}
|
|
else if (typeof ((_c = context.contextElement) === null || _c === void 0 ? void 0 : _c[functionName]) === "function") {
|
|
value = context.contextElement[functionName](value, data, context.currentElement, context.contextElement);
|
|
}
|
|
else {
|
|
console.error(`No method named ${functionName} on data or element instance. Just calling regular value.`);
|
|
value = getPrimitiveOrObjValue(bindingPropName, data);
|
|
}
|
|
}
|
|
else {
|
|
value = getPrimitiveOrObjValue(bindingPropName, data);
|
|
}
|
|
if (value !== undefined) {
|
|
value = negate !== null ? (negate === 1 ? !value : !!value) : value;
|
|
}
|
|
return value;
|
|
}
|
|
function removeBindingAttributes(elOrEls, deep = false) {
|
|
flattenArray(elOrEls || []).forEach((el) => {
|
|
el.removeAttribute(CONFIG.attrBind);
|
|
const innerBinds = dom.queryAll(`:scope [${CONFIG.attrBind}]`, el);
|
|
const innerTplBinds = deep ? [] : dom.queryAll(`:scope [data-tpl] [${CONFIG.attrBind}]`);
|
|
innerBinds.forEach((el) => {
|
|
if (deep || !innerTplBinds.includes(el)) {
|
|
el.removeAttribute(CONFIG.attrBind);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
const templateCache = {};
|
|
export function checkKey(key) {
|
|
return !!templateCache[cleanKey(key)];
|
|
}
|
|
export function register(key, htmlOrElement = null, preProcessScript) {
|
|
key = cleanKey(key);
|
|
if (templateCache[key]) {
|
|
return templateCache[key];
|
|
}
|
|
let fragment = null;
|
|
if (typeof htmlOrElement === "string") {
|
|
const frag = document.createDocumentFragment();
|
|
if (htmlOrElement.includes("<")) {
|
|
const html = htmlOrElement.trim();
|
|
const htmlParentTag = (html.startsWith("<tr") && "tbody") ||
|
|
(/^<t(body|head|foot)/i.test(html) && "table") ||
|
|
(/^<t(d|h)/i.test(html) && "tr") ||
|
|
"div";
|
|
const temp = document.createElement(htmlParentTag);
|
|
temp.innerHTML = html;
|
|
for (const child of temp.children) {
|
|
frag.appendChild(child);
|
|
}
|
|
}
|
|
else {
|
|
frag.appendChild(dom.createElement(htmlOrElement));
|
|
}
|
|
fragment = frag;
|
|
}
|
|
else if (htmlOrElement instanceof Element) {
|
|
const element = htmlOrElement;
|
|
const tag = element.nodeName.toLowerCase();
|
|
if (tag === "template" && element.content) {
|
|
fragment = element.content;
|
|
}
|
|
else {
|
|
throw Error("Non-template element not handled");
|
|
}
|
|
}
|
|
else if (!htmlOrElement) {
|
|
let element = dom.query(`template[id="${key}"],template[data-id="${key}"]`);
|
|
if (element && element.content) {
|
|
fragment = element.content;
|
|
}
|
|
else {
|
|
throw Error("Non-template element not handled");
|
|
}
|
|
}
|
|
if (fragment) {
|
|
templateCache[key] = {
|
|
fragment,
|
|
preProcessScript: preProcessScript || EMPTY_PREPROCESS_FN,
|
|
};
|
|
}
|
|
return templateCache[key] || null;
|
|
}
|
|
export function getPreProcessScript(keyOrEl) {
|
|
var _a;
|
|
if (typeof keyOrEl === "string") {
|
|
if (!templateCache[keyOrEl]) {
|
|
throw Error(`Template key does not exist ${keyOrEl}`);
|
|
}
|
|
return templateCache[keyOrEl].preProcessScript;
|
|
}
|
|
if (keyOrEl instanceof Element) {
|
|
const tpl = keyOrEl.getAttribute("data-tpl") || "";
|
|
return ((_a = templateCache[tpl]) === null || _a === void 0 ? void 0 : _a.preProcessScript) || EMPTY_PREPROCESS_FN;
|
|
}
|
|
return EMPTY_PREPROCESS_FN;
|
|
}
|
|
export function getTemplateFragment(key) {
|
|
key = cleanKey(key);
|
|
if (!checkKey(key)) {
|
|
register(key);
|
|
}
|
|
let templateData = templateCache[key];
|
|
if (templateData && templateData.fragment) {
|
|
let imported;
|
|
if (document.importNode) {
|
|
imported = document.importNode(templateData.fragment, true);
|
|
}
|
|
else {
|
|
imported = templateData.fragment.cloneNode(true);
|
|
}
|
|
imported.__templateid__ = key;
|
|
return imported;
|
|
}
|
|
else {
|
|
throw new Error("Ain't no template called " + key + " (" + typeof templateCache[key] + ")");
|
|
}
|
|
}
|
|
export function inflate(nodeOrKey, templateData = null, inflateOptions = {}) {
|
|
let node = nodeOrKey;
|
|
if (typeof node === "string") {
|
|
node = getTemplateFragment(node);
|
|
}
|
|
if (node) {
|
|
const els = dom.queryAll("[data-template], [data-templateid], [template]", node);
|
|
for (const child of els) {
|
|
let className = child.className || null;
|
|
let childTemplateId = localAssertNotFalsy(child.getAttribute("data-template") ||
|
|
child.getAttribute("data-templateid") ||
|
|
child.getAttribute("template"), "No child template id provided.");
|
|
const dataAttribute = child.getAttribute("data") || "";
|
|
const childData = (dataAttribute && getObjValue(dataAttribute, templateData)) || templateData;
|
|
const tplsInflateOptions = Object.assign({}, inflateOptions);
|
|
if (tplsInflateOptions.skipInit != null) {
|
|
tplsInflateOptions.skipInit = true;
|
|
}
|
|
let tpls = localAssertNotFalsy(inflate(childTemplateId, childData, tplsInflateOptions), `No template inflated from ${childTemplateId}.`);
|
|
tpls = !Array.isArray(tpls) ? [tpls] : tpls;
|
|
if (className) {
|
|
for (const tpl of tpls) {
|
|
tpl.classList.add(className);
|
|
}
|
|
}
|
|
if (child.nodeName.toUpperCase() === "TPL") {
|
|
replaceTplElementWithChildren(child, tpls);
|
|
}
|
|
else {
|
|
child.append(...tpls);
|
|
}
|
|
child.remove();
|
|
}
|
|
let children = [];
|
|
for (const child of node.children) {
|
|
let tplAttributes = (child.getAttribute("data-tpl") || "").split(" ");
|
|
if (!tplAttributes.includes(node.__templateid__)) {
|
|
tplAttributes.push(node.__templateid__);
|
|
}
|
|
child.setAttribute("data-tpl", tplAttributes.join(" ").trim());
|
|
children.push(child);
|
|
}
|
|
let childOrChildren = children.length === 1 ? children[0] : children;
|
|
if (!inflateOptions.skipInit) {
|
|
init(childOrChildren, templateData, inflateOptions.bindOptions);
|
|
}
|
|
return childOrChildren;
|
|
}
|
|
return null;
|
|
}
|
|
export function inflateSingle(nodeOrKey, scriptData = null, bindOptions = {}) {
|
|
const inflated = localAssertNotFalsy(inflate(nodeOrKey, scriptData, bindOptions));
|
|
return Array.isArray(inflated) ? inflated[0] : inflated;
|
|
}
|
|
export function inflateOnce(nodeOrKey, templateData = null, inflateOptions = {}) {
|
|
let children = inflate(nodeOrKey, templateData, inflateOptions);
|
|
children && removeBindingAttributes(children, false);
|
|
return children;
|
|
}
|
|
export function inflateSingleOnce(nodeOrKey, scriptData = null, bindOptions = {}) {
|
|
const inflated = inflate(nodeOrKey, scriptData, bindOptions) || [];
|
|
removeBindingAttributes(inflated, false);
|
|
return Array.isArray(inflated) ? inflated[0] : inflated;
|
|
}
|
|
export function init(els, data, bindOptions = {}) {
|
|
(!els ? [] : els instanceof Element ? [els] : els).forEach((el) => {
|
|
const dataTplAttr = el.getAttribute("data-tpl");
|
|
if (dataTplAttr) {
|
|
const tpls = dataTplAttr.split(" ");
|
|
tpls.forEach((tpl) => {
|
|
const dataAttribute = el.getAttribute("data") || "";
|
|
const childData = (dataAttribute && getObjValue(dataAttribute, data)) || data;
|
|
bind(el, childData, bindOptions);
|
|
});
|
|
}
|
|
else {
|
|
bind(el, data, bindOptions);
|
|
}
|
|
});
|
|
}
|
|
export function bind(elOrEls, data = {}, bindOptions = {}) {
|
|
var _a;
|
|
if (elOrEls instanceof HTMLElement) {
|
|
data = getPreProcessScript(elOrEls)({ ...data });
|
|
}
|
|
if (typeof data !== "object") {
|
|
data = { value: data };
|
|
if (elOrEls instanceof HTMLElement &&
|
|
elOrEls.children.length === 0 &&
|
|
!elOrEls.getAttribute(CONFIG.attrBind)) {
|
|
dom.setAttributes(elOrEls, { [CONFIG.attrBind]: "value" });
|
|
}
|
|
}
|
|
let passedEls = !Array.isArray(elOrEls) ? [elOrEls] : elOrEls;
|
|
for (const el of passedEls) {
|
|
const conditionEls = toArray(dom.queryAll(`[${CONFIG.attrIf}]`, el));
|
|
const contextElement = (_a = bindOptions.contextElement) !== null && _a !== void 0 ? _a : (el instanceof ShadowRoot ? el.host : el);
|
|
for (const conditionEl of conditionEls) {
|
|
getValueForBindingPropName;
|
|
let isTrue = getValueForBinding(conditionEl.getAttribute(CONFIG.attrIf), {
|
|
data,
|
|
contextElement: contextElement,
|
|
currentElement: conditionEl,
|
|
});
|
|
conditionEl.setAttribute(CONFIG.attrIfIs, String(!!isTrue));
|
|
}
|
|
let toBindEls = toArray(dom.queryAll(`:not([${CONFIG.attrIfIs}="false"]) [${CONFIG.attrBind}]:not([data-tpl]):not([${CONFIG.attrIfIs}="false"])`, el));
|
|
if (el instanceof HTMLElement && el.getAttribute(CONFIG.attrBind)) {
|
|
toBindEls.unshift(el);
|
|
}
|
|
if (toBindEls.length) {
|
|
let innerBindsElements = dom.queryAll(`:scope [data-tpl] [${CONFIG.attrBind}], :scope [data-autobind="false"] [${CONFIG.attrBind}]`, el);
|
|
toBindEls = toBindEls.filter((maybeBind) => !innerBindsElements.includes(maybeBind));
|
|
toBindEls.forEach((child) => {
|
|
RGX_BIND_DECLARATIONS.lastIndex = 0;
|
|
let bindings = [];
|
|
let bindingMatch;
|
|
while ((bindingMatch = RGX_BIND_DECLARATIONS.exec(child.getAttribute(CONFIG.attrBind).replace(/\s+/, " ").trim())) !== null) {
|
|
bindings.push([bindingMatch[1], bindingMatch[2]]);
|
|
}
|
|
bindings.forEach((bindings) => {
|
|
let bindingDataProperty = localAssertNotFalsy(bindings.shift());
|
|
let bindingFields = ((bindings.length && bindings[0]) || "default")
|
|
.trim()
|
|
.replace(/^\[(.*?)\]$/i, "$1")
|
|
.split(",");
|
|
let value = getValueForBinding(bindingDataProperty, {
|
|
data,
|
|
contextElement: contextElement,
|
|
currentElement: child,
|
|
});
|
|
if (value === undefined) {
|
|
if (bindOptions.onlyDefined === true) {
|
|
return;
|
|
}
|
|
else {
|
|
value = null;
|
|
}
|
|
}
|
|
bindingFields.forEach((field) => {
|
|
if (field.startsWith("style.")) {
|
|
let stringVal = String(value);
|
|
if (value &&
|
|
!stringVal.includes("url(") &&
|
|
stringVal !== "none" &&
|
|
(field.includes("background-image") || stringVal.startsWith("http"))) {
|
|
value = `url(${value})`;
|
|
}
|
|
dom.setStyle(child, field.replace("style.", ""), value);
|
|
}
|
|
else if (field.startsWith("el.")) {
|
|
if (field === "el.remove") {
|
|
if (value === true) {
|
|
child.remove();
|
|
}
|
|
}
|
|
else if (field === "el.toggle") {
|
|
dom.setStyle(child, "display", value === true ? "" : "none");
|
|
}
|
|
else if (field.startsWith("el.classList.toggle")) {
|
|
const cssClass = field.replace(/el.classList.toggle\(['"]?(.*?)['"]?\)/, "$1");
|
|
child.classList.toggle(cssClass, !!value);
|
|
}
|
|
}
|
|
else if (RGX_BIND_FN_TEMPLATE_OR_ELEMENT.test(field)) {
|
|
dom.empty(child);
|
|
let elementOrTemplateName = RGX_BIND_FN_TEMPLATE_OR_ELEMENT.exec(field)[1];
|
|
if (Array.isArray(value) || value instanceof Set) {
|
|
const arrayVals = toArray(value);
|
|
let isElement = RGX_BIND_FN_ELEMENT.test(field);
|
|
let frag = document.createDocumentFragment();
|
|
arrayVals.forEach((item, index) => {
|
|
let itemData;
|
|
if (typeof item === "object") {
|
|
itemData = Object.assign({ $index: index }, item);
|
|
}
|
|
else {
|
|
itemData = { $index: index, value: item };
|
|
}
|
|
const els = bindToElOrTemplate(elementOrTemplateName, itemData);
|
|
frag.append(...els);
|
|
});
|
|
if (child.nodeName.toUpperCase() === "TPL") {
|
|
replaceTplElementWithChildren(child, frag);
|
|
}
|
|
else {
|
|
dom.empty(child).appendChild(frag);
|
|
}
|
|
}
|
|
else if (value) {
|
|
const els = bindToElOrTemplate(elementOrTemplateName, value);
|
|
if (child.nodeName.toUpperCase() === "TPL") {
|
|
replaceTplElementWithChildren(child, els);
|
|
}
|
|
else {
|
|
child.append(...els);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
dom.setAttributes(child, { [field]: value });
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
if (bindOptions.singleScoped !== true) {
|
|
let toInitEls = toArray(el.querySelectorAll(":scope *[data-tpl]"));
|
|
if (toInitEls.length) {
|
|
let innerInits = dom.queryAll(':scope *[data-tpl] *[data-tpl], :scope [data-autobind="false"] [data-tpl]', el);
|
|
toInitEls = toInitEls.filter((maybeInitEl) => {
|
|
if (innerInits.includes(maybeInitEl)) {
|
|
return false;
|
|
}
|
|
let tplKey = maybeInitEl.getAttribute("data-tpl");
|
|
if (data && (data[tplKey] || data[tplKey.replace("tpl:", "")])) {
|
|
return true;
|
|
}
|
|
return maybeInitEl.getAttribute("data-autobind") !== "false";
|
|
});
|
|
toInitEls.forEach((toInitEl) => {
|
|
var tplKey = toInitEl.getAttribute("data-tpl");
|
|
init(toInitEl, (data && (data[tplKey] || data[tplKey.replace("tpl:", "")])) || data);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function bindToElOrTemplate(elementOrTemplateName, data) {
|
|
let el = getTemplateFragment(elementOrTemplateName);
|
|
if (!el) {
|
|
el = dom.createElement(elementOrTemplateName, data);
|
|
}
|
|
else {
|
|
el = inflateOnce(el, data, { skipInit: false });
|
|
}
|
|
const els = (Array.isArray(el) ? el : [el]).filter((el) => !!el);
|
|
els.forEach((el) => {
|
|
el.removeAttribute("data-tpl");
|
|
let toBindEls = dom.queryAll("[data-tpl]", el);
|
|
let innerBindsElements = dom.queryAll(`:scope [data-tpl] [${CONFIG.attrBind}], :scope [data-autobind="false"] [${CONFIG.attrBind}]`, el);
|
|
toBindEls = toBindEls.filter((maybeBind) => !innerBindsElements.includes(maybeBind));
|
|
toBindEls.forEach((c) => c.removeAttribute("data-tpl"));
|
|
});
|
|
return els;
|
|
}
|