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("