jails-js
Version:
Jails - Elegant and Minimalistic Javascript Application Library
1,195 lines (1,194 loc) • 40.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
let textarea;
const g = {
scope: {}
};
const decodeHTML = (text) => {
textarea = textarea || document.createElement("textarea");
textarea.innerHTML = text;
return textarea.value;
};
const uuid = () => {
return Math.random().toString(36).substring(2, 9);
};
const dup = (o) => {
return JSON.parse(JSON.stringify(o));
};
const safe = (execute, val) => {
try {
const value = execute();
return value !== void 0 && value !== null ? value : val || "";
} catch (err) {
return val || "";
}
};
var Idiomorph = function() {
const noOp = () => {
};
const defaults = {
morphStyle: "outerHTML",
callbacks: {
beforeNodeAdded: noOp,
afterNodeAdded: noOp,
beforeNodeMorphed: noOp,
afterNodeMorphed: noOp,
beforeNodeRemoved: noOp,
afterNodeRemoved: noOp,
beforeAttributeUpdated: noOp
},
head: {
style: "merge",
shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true",
shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true",
shouldRemove: noOp,
afterHeadMorphed: noOp
},
restoreFocus: true
};
function morph(oldNode, newContent, config2 = {}) {
oldNode = normalizeElement(oldNode);
const newNode = normalizeParent(newContent);
const ctx = createMorphContext(oldNode, newNode, config2);
const morphedNodes = saveAndRestoreFocus(ctx, () => {
return withHeadBlocking(
ctx,
oldNode,
newNode,
/** @param {MorphContext} ctx */
(ctx2) => {
if (ctx2.morphStyle === "innerHTML") {
morphChildren(ctx2, oldNode, newNode);
return Array.from(oldNode.childNodes);
} else {
return morphOuterHTML(ctx2, oldNode, newNode);
}
}
);
});
ctx.pantry.remove();
return morphedNodes;
}
function morphOuterHTML(ctx, oldNode, newNode) {
const oldParent = normalizeParent(oldNode);
morphChildren(
ctx,
oldParent,
newNode,
// these two optional params are the secret sauce
oldNode,
// start point for iteration
oldNode.nextSibling
// end point for iteration
);
return Array.from(oldParent.childNodes);
}
function saveAndRestoreFocus(ctx, fn) {
var _a;
if (!ctx.config.restoreFocus) return fn();
let activeElement = (
/** @type {HTMLInputElement|HTMLTextAreaElement|null} */
document.activeElement
);
if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) {
return fn();
}
const { id: activeElementId, selectionStart, selectionEnd } = activeElement;
const results = fn();
if (activeElementId && activeElementId !== ((_a = document.activeElement) == null ? void 0 : _a.id)) {
activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`);
activeElement == null ? void 0 : activeElement.focus();
}
if (activeElement && !activeElement.selectionEnd && selectionEnd) {
activeElement.setSelectionRange(selectionStart, selectionEnd);
}
return results;
}
const morphChildren = /* @__PURE__ */ function() {
function morphChildren2(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {
if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {
oldParent = oldParent.content;
newParent = newParent.content;
}
insertionPoint || (insertionPoint = oldParent.firstChild);
for (const newChild of newParent.childNodes) {
if (insertionPoint && insertionPoint != endPoint) {
const bestMatch = findBestMatch(
ctx,
newChild,
insertionPoint,
endPoint
);
if (bestMatch) {
if (bestMatch !== insertionPoint) {
removeNodesBetween(ctx, insertionPoint, bestMatch);
}
morphNode(bestMatch, newChild, ctx);
insertionPoint = bestMatch.nextSibling;
continue;
}
}
if (newChild instanceof Element && ctx.persistentIds.has(newChild.id)) {
const movedChild = moveBeforeById(
oldParent,
newChild.id,
insertionPoint,
ctx
);
morphNode(movedChild, newChild, ctx);
insertionPoint = movedChild.nextSibling;
continue;
}
const insertedNode = createNode(
oldParent,
newChild,
insertionPoint,
ctx
);
if (insertedNode) {
insertionPoint = insertedNode.nextSibling;
}
}
while (insertionPoint && insertionPoint != endPoint) {
const tempNode = insertionPoint;
insertionPoint = insertionPoint.nextSibling;
removeNode(ctx, tempNode);
}
}
function createNode(oldParent, newChild, insertionPoint, ctx) {
if (ctx.callbacks.beforeNodeAdded(newChild) === false) return null;
if (ctx.idMap.has(newChild)) {
const newEmptyChild = document.createElement(
/** @type {Element} */
newChild.tagName
);
oldParent.insertBefore(newEmptyChild, insertionPoint);
morphNode(newEmptyChild, newChild, ctx);
ctx.callbacks.afterNodeAdded(newEmptyChild);
return newEmptyChild;
} else {
const newClonedChild = document.importNode(newChild, true);
oldParent.insertBefore(newClonedChild, insertionPoint);
ctx.callbacks.afterNodeAdded(newClonedChild);
return newClonedChild;
}
}
const findBestMatch = /* @__PURE__ */ function() {
function findBestMatch2(ctx, node, startPoint, endPoint) {
let softMatch = null;
let nextSibling = node.nextSibling;
let siblingSoftMatchCount = 0;
let cursor = startPoint;
while (cursor && cursor != endPoint) {
if (isSoftMatch(cursor, node)) {
if (isIdSetMatch(ctx, cursor, node)) {
return cursor;
}
if (softMatch === null) {
if (!ctx.idMap.has(cursor)) {
softMatch = cursor;
}
}
}
if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) {
siblingSoftMatchCount++;
nextSibling = nextSibling.nextSibling;
if (siblingSoftMatchCount >= 2) {
softMatch = void 0;
}
}
if (cursor.contains(document.activeElement)) break;
cursor = cursor.nextSibling;
}
return softMatch || null;
}
function isIdSetMatch(ctx, oldNode, newNode) {
let oldSet = ctx.idMap.get(oldNode);
let newSet = ctx.idMap.get(newNode);
if (!newSet || !oldSet) return false;
for (const id of oldSet) {
if (newSet.has(id)) {
return true;
}
}
return false;
}
function isSoftMatch(oldNode, newNode) {
const oldElt = (
/** @type {Element} */
oldNode
);
const newElt = (
/** @type {Element} */
newNode
);
return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && // If oldElt has an `id` with possible state and it doesn't match newElt.id then avoid morphing.
// We'll still match an anonymous node with an IDed newElt, though, because if it got this far,
// its not persistent, and new nodes can't have any hidden state.
(!oldElt.id || oldElt.id === newElt.id);
}
return findBestMatch2;
}();
function removeNode(ctx, node) {
var _a;
if (ctx.idMap.has(node)) {
moveBefore(ctx.pantry, node, null);
} else {
if (ctx.callbacks.beforeNodeRemoved(node) === false) return;
(_a = node.parentNode) == null ? void 0 : _a.removeChild(node);
ctx.callbacks.afterNodeRemoved(node);
}
}
function removeNodesBetween(ctx, startInclusive, endExclusive) {
let cursor = startInclusive;
while (cursor && cursor !== endExclusive) {
let tempNode = (
/** @type {Node} */
cursor
);
cursor = cursor.nextSibling;
removeNode(ctx, tempNode);
}
return cursor;
}
function moveBeforeById(parentNode, id, after, ctx) {
const target = (
/** @type {Element} - will always be found */
ctx.target.id === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`)
);
removeElementFromAncestorsIdMaps(target, ctx);
moveBefore(parentNode, target, after);
return target;
}
function removeElementFromAncestorsIdMaps(element, ctx) {
const id = element.id;
while (element = element.parentNode) {
let idSet = ctx.idMap.get(element);
if (idSet) {
idSet.delete(id);
if (!idSet.size) {
ctx.idMap.delete(element);
}
}
}
}
function moveBefore(parentNode, element, after) {
if (parentNode.moveBefore) {
try {
parentNode.moveBefore(element, after);
} catch (e) {
parentNode.insertBefore(element, after);
}
} else {
parentNode.insertBefore(element, after);
}
}
return morphChildren2;
}();
const morphNode = /* @__PURE__ */ function() {
function morphNode2(oldNode, newContent, ctx) {
if (ctx.ignoreActive && oldNode === document.activeElement) {
return null;
}
if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) {
return oldNode;
}
if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) ;
else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") {
handleHeadElement(
oldNode,
/** @type {HTMLHeadElement} */
newContent,
ctx
);
} else {
morphAttributes(oldNode, newContent, ctx);
if (!ignoreValueOfActiveElement(oldNode, ctx)) {
morphChildren(ctx, oldNode, newContent);
}
}
ctx.callbacks.afterNodeMorphed(oldNode, newContent);
return oldNode;
}
function morphAttributes(oldNode, newNode, ctx) {
let type = newNode.nodeType;
if (type === 1) {
const oldElt = (
/** @type {Element} */
oldNode
);
const newElt = (
/** @type {Element} */
newNode
);
const oldAttributes = oldElt.attributes;
const newAttributes = newElt.attributes;
for (const newAttribute of newAttributes) {
if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) {
continue;
}
if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) {
oldElt.setAttribute(newAttribute.name, newAttribute.value);
}
}
for (let i = oldAttributes.length - 1; 0 <= i; i--) {
const oldAttribute = oldAttributes[i];
if (!oldAttribute) continue;
if (!newElt.hasAttribute(oldAttribute.name)) {
if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) {
continue;
}
oldElt.removeAttribute(oldAttribute.name);
}
}
if (!ignoreValueOfActiveElement(oldElt, ctx)) {
syncInputValue(oldElt, newElt, ctx);
}
}
if (type === 8 || type === 3) {
if (oldNode.nodeValue !== newNode.nodeValue) {
oldNode.nodeValue = newNode.nodeValue;
}
}
}
function syncInputValue(oldElement, newElement, ctx) {
if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") {
let newValue = newElement.value;
let oldValue = oldElement.value;
syncBooleanAttribute(oldElement, newElement, "checked", ctx);
syncBooleanAttribute(oldElement, newElement, "disabled", ctx);
if (!newElement.hasAttribute("value")) {
if (!ignoreAttribute("value", oldElement, "remove", ctx)) {
oldElement.value = "";
oldElement.removeAttribute("value");
}
} else if (oldValue !== newValue) {
if (!ignoreAttribute("value", oldElement, "update", ctx)) {
oldElement.setAttribute("value", newValue);
oldElement.value = newValue;
}
}
} else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) {
syncBooleanAttribute(oldElement, newElement, "selected", ctx);
} else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) {
let newValue = newElement.value;
let oldValue = oldElement.value;
if (ignoreAttribute("value", oldElement, "update", ctx)) {
return;
}
if (newValue !== oldValue) {
oldElement.value = newValue;
}
if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) {
oldElement.firstChild.nodeValue = newValue;
}
}
}
function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) {
const newLiveValue = newElement[attributeName], oldLiveValue = oldElement[attributeName];
if (newLiveValue !== oldLiveValue) {
const ignoreUpdate = ignoreAttribute(
attributeName,
oldElement,
"update",
ctx
);
if (!ignoreUpdate) {
oldElement[attributeName] = newElement[attributeName];
}
if (newLiveValue) {
if (!ignoreUpdate) {
oldElement.setAttribute(attributeName, "");
}
} else {
if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) {
oldElement.removeAttribute(attributeName);
}
}
}
}
function ignoreAttribute(attr, element, updateType, ctx) {
if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) {
return true;
}
return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false;
}
function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
}
return morphNode2;
}();
function withHeadBlocking(ctx, oldNode, newNode, callback) {
if (ctx.head.block) {
const oldHead = oldNode.querySelector("head");
const newHead = newNode.querySelector("head");
if (oldHead && newHead) {
const promises = handleHeadElement(oldHead, newHead, ctx);
return Promise.all(promises).then(() => {
const newCtx = Object.assign(ctx, {
head: {
block: false,
ignore: true
}
});
return callback(newCtx);
});
}
}
return callback(ctx);
}
function handleHeadElement(oldHead, newHead, ctx) {
let added = [];
let removed = [];
let preserved = [];
let nodesToAppend = [];
let srcToNewHeadNodes = /* @__PURE__ */ new Map();
for (const newHeadChild of newHead.children) {
srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
}
for (const currentHeadElt of oldHead.children) {
let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
if (inNewContent || isPreserved) {
if (isReAppended) {
removed.push(currentHeadElt);
} else {
srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
preserved.push(currentHeadElt);
}
} else {
if (ctx.head.style === "append") {
if (isReAppended) {
removed.push(currentHeadElt);
nodesToAppend.push(currentHeadElt);
}
} else {
if (ctx.head.shouldRemove(currentHeadElt) !== false) {
removed.push(currentHeadElt);
}
}
}
}
nodesToAppend.push(...srcToNewHeadNodes.values());
let promises = [];
for (const newNode of nodesToAppend) {
let newElt = (
/** @type {ChildNode} */
document.createRange().createContextualFragment(newNode.outerHTML).firstChild
);
if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) {
let resolve;
let promise = new Promise(function(_resolve) {
resolve = _resolve;
});
newElt.addEventListener("load", function() {
resolve();
});
promises.push(promise);
}
oldHead.appendChild(newElt);
ctx.callbacks.afterNodeAdded(newElt);
added.push(newElt);
}
}
for (const removedElement of removed) {
if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
oldHead.removeChild(removedElement);
ctx.callbacks.afterNodeRemoved(removedElement);
}
}
ctx.head.afterHeadMorphed(oldHead, {
added,
kept: preserved,
removed
});
return promises;
}
const createMorphContext = /* @__PURE__ */ function() {
function createMorphContext2(oldNode, newContent, config2) {
const { persistentIds, idMap } = createIdMaps(oldNode, newContent);
const mergedConfig = mergeDefaults(config2);
const morphStyle = mergedConfig.morphStyle || "outerHTML";
if (!["innerHTML", "outerHTML"].includes(morphStyle)) {
throw `Do not understand how to morph style ${morphStyle}`;
}
return {
target: oldNode,
newContent,
config: mergedConfig,
morphStyle,
ignoreActive: mergedConfig.ignoreActive,
ignoreActiveValue: mergedConfig.ignoreActiveValue,
restoreFocus: mergedConfig.restoreFocus,
idMap,
persistentIds,
pantry: createPantry(),
callbacks: mergedConfig.callbacks,
head: mergedConfig.head
};
}
function mergeDefaults(config2) {
let finalConfig = Object.assign({}, defaults);
Object.assign(finalConfig, config2);
finalConfig.callbacks = Object.assign(
{},
defaults.callbacks,
config2.callbacks
);
finalConfig.head = Object.assign({}, defaults.head, config2.head);
return finalConfig;
}
function createPantry() {
const pantry = document.createElement("div");
pantry.hidden = true;
document.body.insertAdjacentElement("afterend", pantry);
return pantry;
}
function findIdElements(root) {
let elements = Array.from(root.querySelectorAll("[id]"));
if (root.id) {
elements.push(root);
}
return elements;
}
function populateIdMapWithTree(idMap, persistentIds, root, elements) {
for (const elt of elements) {
if (persistentIds.has(elt.id)) {
let current = elt;
while (current) {
let idSet = idMap.get(current);
if (idSet == null) {
idSet = /* @__PURE__ */ new Set();
idMap.set(current, idSet);
}
idSet.add(elt.id);
if (current === root) break;
current = current.parentElement;
}
}
}
}
function createIdMaps(oldContent, newContent) {
const oldIdElements = findIdElements(oldContent);
const newIdElements = findIdElements(newContent);
const persistentIds = createPersistentIds(oldIdElements, newIdElements);
let idMap = /* @__PURE__ */ new Map();
populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements);
const newRoot = newContent.__idiomorphRoot || newContent;
populateIdMapWithTree(idMap, persistentIds, newRoot, newIdElements);
return { persistentIds, idMap };
}
function createPersistentIds(oldIdElements, newIdElements) {
let duplicateIds = /* @__PURE__ */ new Set();
let oldIdTagNameMap = /* @__PURE__ */ new Map();
for (const { id, tagName } of oldIdElements) {
if (oldIdTagNameMap.has(id)) {
duplicateIds.add(id);
} else {
oldIdTagNameMap.set(id, tagName);
}
}
let persistentIds = /* @__PURE__ */ new Set();
for (const { id, tagName } of newIdElements) {
if (persistentIds.has(id)) {
duplicateIds.add(id);
} else if (oldIdTagNameMap.get(id) === tagName) {
persistentIds.add(id);
}
}
for (const id of duplicateIds) {
persistentIds.delete(id);
}
return persistentIds;
}
return createMorphContext2;
}();
const { normalizeElement, normalizeParent } = /* @__PURE__ */ function() {
const generatedByIdiomorph = /* @__PURE__ */ new WeakSet();
function normalizeElement2(content) {
if (content instanceof Document) {
return content.documentElement;
} else {
return content;
}
}
function normalizeParent2(newContent) {
if (newContent == null) {
return document.createElement("div");
} else if (typeof newContent === "string") {
return normalizeParent2(parseContent(newContent));
} else if (generatedByIdiomorph.has(
/** @type {Element} */
newContent
)) {
return (
/** @type {Element} */
newContent
);
} else if (newContent instanceof Node) {
if (newContent.parentNode) {
return (
/** @type {any} */
new SlicedParentNode(newContent)
);
} else {
const dummyParent = document.createElement("div");
dummyParent.append(newContent);
return dummyParent;
}
} else {
const dummyParent = document.createElement("div");
for (const elt of [...newContent]) {
dummyParent.append(elt);
}
return dummyParent;
}
}
class SlicedParentNode {
/** @param {Node} node */
constructor(node) {
this.originalNode = node;
this.realParentNode = /** @type {Element} */
node.parentNode;
this.previousSibling = node.previousSibling;
this.nextSibling = node.nextSibling;
}
/** @returns {Node[]} */
get childNodes() {
const nodes = [];
let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild;
while (cursor && cursor != this.nextSibling) {
nodes.push(cursor);
cursor = cursor.nextSibling;
}
return nodes;
}
/**
* @param {string} selector
* @returns {Element[]}
*/
querySelectorAll(selector) {
return this.childNodes.reduce(
(results, node) => {
if (node instanceof Element) {
if (node.matches(selector)) results.push(node);
const nodeList = node.querySelectorAll(selector);
for (let i = 0; i < nodeList.length; i++) {
results.push(nodeList[i]);
}
}
return results;
},
/** @type {Element[]} */
[]
);
}
/**
* @param {Node} node
* @param {Node} referenceNode
* @returns {Node}
*/
insertBefore(node, referenceNode) {
return this.realParentNode.insertBefore(node, referenceNode);
}
/**
* @param {Node} node
* @param {Node} referenceNode
* @returns {Node}
*/
moveBefore(node, referenceNode) {
return this.realParentNode.moveBefore(node, referenceNode);
}
/**
* for later use with populateIdMapWithTree to halt upwards iteration
* @returns {Node}
*/
get __idiomorphRoot() {
return this.originalNode;
}
}
function parseContent(newContent) {
let parser = new DOMParser();
let contentWithSvgsRemoved = newContent.replace(
/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,
""
);
if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
let content = parser.parseFromString(newContent, "text/html");
if (contentWithSvgsRemoved.match(/<\/html>/)) {
generatedByIdiomorph.add(content);
return content;
} else {
let htmlElement = content.firstChild;
if (htmlElement) {
generatedByIdiomorph.add(htmlElement);
}
return htmlElement;
}
} else {
let responseDoc = parser.parseFromString(
"<body><template>" + newContent + "</template></body>",
"text/html"
);
let content = (
/** @type {HTMLTemplateElement} */
responseDoc.body.querySelector("template").content
);
generatedByIdiomorph.add(content);
return content;
}
}
return { normalizeElement: normalizeElement2, normalizeParent: normalizeParent2 };
}();
return {
morph,
defaults
};
}();
const topics = {};
const _async = {};
const publish = (name, params) => {
_async[name] = isObject(params) ? Object.assign({}, _async[name], params) : params;
if (topics[name]) {
topics[name].forEach((topic) => topic(params));
}
};
const subscribe = (name, method) => {
topics[name] = topics[name] || [];
topics[name].push(method);
if (name in _async) {
method(_async[name]);
}
return () => {
topics[name] = topics[name].filter((fn) => fn != method);
};
};
const isObject = (value) => {
return typeof value === "object" && value !== null && !Array.isArray(value);
};
const Component = ({ name, module, dependencies, node, templates: templates2, signal, register: register2 }) => {
var _a;
let tick;
let preserve = [];
let observer = null;
let observables = [];
const _model = module.model || {};
const initialState = new Function(`return ${node.getAttribute("html-model") || "{}"}`)();
const tplid = node.getAttribute("tplid");
const scopeid = node.getAttribute("html-scopeid");
const tpl = templates2[tplid];
const scope = g.scope[scopeid];
const model = dup(((_a = module == null ? void 0 : module.model) == null ? void 0 : _a.apply) ? _model({ elm: node, initialState }) : _model);
const state = Object.assign({}, scope, model, initialState);
const view = module.view ? module.view : (data) => data;
const base = {
name,
model,
elm: node,
template: tpl.template,
dependencies,
publish,
subscribe,
main(fn) {
node.addEventListener(":mount", fn);
},
/**
* @State
*/
state: {
protected(list) {
if (list) {
preserve = list;
} else {
return preserve;
}
},
save(data) {
if (data.constructor === Function) {
data(state);
} else {
Object.assign(state, data);
}
},
set(data) {
if (!document.body.contains(node)) {
return;
}
if (data.constructor === Function) {
data(state);
} else {
Object.assign(state, data);
}
const newstate = Object.assign({}, state, scope);
return new Promise((resolve) => {
render(newstate, () => resolve(newstate));
});
},
get() {
return Object.assign({}, state);
}
},
dataset(target, name2) {
const el = name2 ? target : node;
const key = name2 ? name2 : target;
const value = el.dataset[key];
if (value === "true") return true;
if (value === "false") return false;
if (!isNaN(value) && value.trim() !== "") return Number(value);
try {
return new Function("return (" + value + ")")();
} catch (e) {
}
try {
return JSON.parse(value);
} catch (e) {
}
return value;
},
/**
* @Events
*/
on(ev, selectorOrCallback, callback) {
const attribute = ev.match(/\[(.*)\]/);
if (attribute) {
observables.push({
target: callback ? selectorOrCallback : null,
callback: callback || selectorOrCallback
});
if (!observer) {
observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "attributes") {
const attrname = mutation.attributeName;
if (attrname === attribute[1]) {
observables.forEach((item) => {
const target = item.target ? node.querySelectorAll(item.target) : [node];
target.forEach((target2) => {
if (target2 == mutation.target) {
item.callback({
target: mutation.target,
attribute: attrname,
value: mutation.target.getAttribute(attrname)
});
}
});
});
}
}
}
});
observer.observe(node, {
attributes: true,
subtree: true
});
node.addEventListener(":unmount", () => {
observables = [];
observer.disconnect();
});
}
return;
}
if (callback) {
callback.handler = (e) => {
const detail = e.detail || {};
let parent = e.target;
while (parent) {
if (parent.matches(selectorOrCallback)) {
e.delegateTarget = parent;
callback.apply(node, [e].concat(detail.args));
}
if (parent === node) break;
parent = parent.parentNode;
}
};
node.addEventListener(ev, callback.handler, {
signal,
capture: ev == "focus" || ev == "blur" || ev == "mouseenter" || ev == "mouseleave"
});
} else {
selectorOrCallback.handler = (e) => {
e.delegateTarget = node;
selectorOrCallback.apply(node, [e].concat(e.detail.args));
};
node.addEventListener(ev, selectorOrCallback.handler, { signal });
}
},
off(ev, callback) {
if (callback.handler) {
node.removeEventListener(ev, callback.handler);
}
},
trigger(ev, selectorOrCallback, data) {
if (selectorOrCallback.constructor === String) {
Array.from(node.querySelectorAll(selectorOrCallback)).forEach((children) => {
children.dispatchEvent(new CustomEvent(ev, { bubbles: true, detail: { args: data } }));
});
} else {
node.dispatchEvent(new CustomEvent(ev, { bubbles: true, detail: { args: data } }));
}
},
emit(ev, data) {
node.dispatchEvent(new CustomEvent(ev, { bubbles: true, detail: { args: data } }));
},
unmount(fn) {
node.addEventListener(":unmount", fn);
},
innerHTML(target, html_) {
const element = html_ ? target : node;
const clone = element.cloneNode();
const html = html_ ? html_ : target;
clone.innerHTML = html;
Idiomorph.morph(element, clone);
}
};
const render = (data, callback = () => {
}) => {
clearTimeout(tick);
tick = setTimeout(() => {
const html = tpl.render.call(__spreadValues(__spreadValues({}, data), view(data)), node, safe, g);
Idiomorph.morph(node, html, IdiomorphOptions(node, register2));
Promise.resolve().then(() => {
node.querySelectorAll("[tplid]").forEach((element) => {
const child = register2.get(element);
if (!child) return;
child.state.protected().forEach((key) => delete data[key]);
child.state.set(data);
});
Promise.resolve().then(() => {
g.scope = {};
callback();
});
});
});
};
render(state);
register2.set(node, base);
return module.default(base);
};
const IdiomorphOptions = (parent, register2) => ({
callbacks: {
beforeNodeMorphed(node) {
if (node.nodeType === 1) {
if ("html-static" in node.attributes) {
return false;
}
if (register2.get(node) && node !== parent) {
return false;
}
}
}
}
});
const register$1 = /* @__PURE__ */ new WeakMap();
const Element$1 = ({ component, templates: templates2, start: start2 }) => {
const { name, module, dependencies } = component;
return class extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.abortController = new AbortController();
if (!this.getAttribute("tplid")) {
start2(this.parentNode);
}
const rtrn = Component({
node: this,
name,
module,
dependencies,
templates: templates2,
signal: this.abortController.signal,
register: register$1
});
if (rtrn && rtrn.constructor === Promise) {
rtrn.then(() => {
this.dispatchEvent(new CustomEvent(":mount"));
});
} else {
this.dispatchEvent(new CustomEvent(":mount"));
}
}
disconnectedCallback() {
this.dispatchEvent(new CustomEvent(":unmount"));
this.abortController.abort();
}
};
};
const config = {
tags: ["{{", "}}"]
};
const templates = {};
const booleanAttrs = /html-(allowfullscreen|async|autofocus|autoplay|checked|controls|default|defer|disabled|formnovalidate|inert|ismap|itemscope|loop|multiple|muted|nomodule|novalidate|open|playsinline|readonly|required|reversed|selected)="(.*?)"/g;
const htmlAttr = /html-([^\s]*?)="(.*?)"/g;
const tagExpr = () => new RegExp(`\\${config.tags[0]}(.+?)\\${config.tags[1]}`, "g");
const templateConfig$1 = (newconfig) => {
Object.assign(config, newconfig);
};
const template = (target, { components }) => {
tagElements(target, [...Object.keys(components), "[html-if]", "template"], components);
const clone = target.cloneNode(true);
transformTemplate(clone);
removeTemplateTagsRecursively(clone);
setTemplates(clone, components);
return templates;
};
const compile = (html) => {
const parsedHtml = JSON.stringify(html);
return new Function("$element", "safe", "$g", `
var $data = this;
with( $data ){
var output=${parsedHtml.replace(/%%_=(.+?)_%%/g, function(_, variable) {
return '"+safe(function(){return ' + decodeHTML(variable) + ';})+"';
}).replace(/%%_(.+?)_%%/g, function(_, variable) {
return '";' + decodeHTML(variable) + '\noutput+="';
})};return output;
}
`);
};
const tagElements = (target, keys, components) => {
const isComponent = (key) => key in components;
const selector = keys.join(",");
target.querySelectorAll(selector).forEach((node) => {
if (node.localName === "template") {
tagElements(node.content, keys, components);
return;
}
if (node.hasAttribute("html-if") && !node.id) {
node.id = uuid();
}
if (isComponent(node.localName)) {
node.setAttribute("tplid", uuid());
}
});
};
const transformAttributes = (html) => {
return html.replace(/jails___scope-id/g, "%%_=$scopeid_%%").replace(tagExpr(), "%%_=$1_%%").replace(booleanAttrs, `%%_if(safe(function(){ return $2 })){_%%$1%%_}_%%`).replace(htmlAttr, (all, key, value) => {
if (["key", "model", "scopeid"].includes(key)) return all;
if (value) {
value = value.replace(/^{|}$/g, "");
return `${key}="%%_=safe(function(){ return ${value} })_%%"`;
}
return all;
});
};
const transformTemplate = (clone) => {
clone.querySelectorAll("template, [html-for], [html-if], [html-inner], [html-class]").forEach((element) => {
const htmlFor = element.getAttribute("html-for");
const htmlIf = element.getAttribute("html-if");
const htmlInner = element.getAttribute("html-inner");
const htmlClass = element.getAttribute("html-class");
if (htmlFor) {
element.removeAttribute("html-for");
const split = htmlFor.match(/(.*)\sin\s(.*)/) || "";
const varname = split[1];
const object = split[2];
const objectname = object.split(/\./).shift();
const open = document.createTextNode(`%%_ ;(function(){ var $index = 0; for(var $key in safe(function(){ return ${object} }) ){ var $scopeid = Math.random().toString(36).substring(2, 9); var ${varname} = ${object}[$key]; $g.scope[$scopeid] = Object.assign({}, { ${objectname}: ${objectname} }, { ${varname} :${varname}, $index: $index, $key: $key }); _%%`);
const close = document.createTextNode(`%%_ $index++; } })() _%%`);
wrap(open, element, close);
}
if (htmlIf) {
element.removeAttribute("html-if");
const open = document.createTextNode(`%%_ if ( safe(function(){ return ${htmlIf} }) ){ _%%`);
const close = document.createTextNode(`%%_ } _%%`);
wrap(open, element, close);
}
if (htmlInner) {
element.removeAttribute("html-inner");
element.innerHTML = `%%_=${htmlInner}_%%`;
}
if (htmlClass) {
element.removeAttribute("html-class");
element.className = (element.className + ` %%_=${htmlClass}_%%`).trim();
}
if (element.localName === "template") {
transformTemplate(element.content);
}
});
};
const setTemplates = (clone, components) => {
Array.from(clone.querySelectorAll("[tplid]")).reverse().forEach((node) => {
const tplid = node.getAttribute("tplid");
const name = node.localName;
node.setAttribute("html-scopeid", "jails___scope-id");
if (name in components && components[name].module.template) {
const children = node.innerHTML;
const html2 = components[name].module.template({ elm: node, children });
node.innerHTML = html2;
transformTemplate(node);
removeTemplateTagsRecursively(node);
}
const html = transformAttributes(node.outerHTML);
templates[tplid] = {
template: html,
render: compile(html)
};
});
};
const removeTemplateTagsRecursively = (node) => {
const templates2 = node.querySelectorAll("template");
templates2.forEach((template2) => {
if (template2.getAttribute("html-if") || template2.getAttribute("html-inner")) {
return;
}
removeTemplateTagsRecursively(template2.content);
const parent = template2.parentNode;
if (parent) {
const content = template2.content;
while (content.firstChild) {
parent.insertBefore(content.firstChild, template2);
}
parent.removeChild(template2);
}
});
};
const wrap = (open, node, close) => {
var _a, _b;
(_a = node.parentNode) == null ? void 0 : _a.insertBefore(open, node);
(_b = node.parentNode) == null ? void 0 : _b.insertBefore(close, node.nextSibling);
};
const templateConfig = (options) => {
templateConfig$1(options);
};
globalThis.__jails__ = globalThis.__jails__ || { components: {} };
const register = (name, module, dependencies) => {
const { components } = globalThis.__jails__;
components[name] = { name, module, dependencies };
};
const start = (target) => {
if (typeof window === "undefined") {
return;
}
target = target || document.body;
const { components } = globalThis.__jails__;
const templates2 = template(target, { components });
Object.values(components).forEach(({ name, module, dependencies }) => {
if (!customElements.get(name)) {
customElements.define(name, Element$1({ component: { name, module, dependencies }, templates: templates2, start }));
}
});
};
export {
publish,
register,
start,
subscribe,
templateConfig
};
//# sourceMappingURL=index.js.map