malevic
Version:
Malevič.js - minimalistic reactive UI library
1,331 lines (1,303 loc) • 48.8 kB
JavaScript
/* malevic@0.20.2 - Aug 10, 2024 */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.Malevic = global.Malevic || {}, global.Malevic.DOM = {})));
})(this, (function (exports) { 'use strict';
function createPluginsStore() {
var plugins = [];
return {
add: function (plugin) {
plugins.push(plugin);
return this;
},
apply: function (props) {
var result;
var plugin;
var usedPlugins = new Set();
for (var i = plugins.length - 1; i >= 0; i--) {
plugin = plugins[i];
if (usedPlugins.has(plugin)) {
continue;
}
result = plugin(props);
if (result != null) {
return result;
}
usedPlugins.add(plugin);
}
return null;
},
delete: function (plugin) {
for (var i = plugins.length - 1; i >= 0; i--) {
if (plugins[i] === plugin) {
plugins.splice(i, 1);
break;
}
}
return this;
},
empty: function () {
return plugins.length === 0;
},
};
}
function iterateComponentPlugins(type, pairs, iterator) {
pairs
.filter(function (_a) {
var key = _a[0];
return type[key];
})
.forEach(function (_a) {
var key = _a[0], plugins = _a[1];
return type[key].forEach(function (plugin) { return iterator(plugins, plugin); });
});
}
function addComponentPlugins(type, pairs) {
iterateComponentPlugins(type, pairs, function (plugins, plugin) {
return plugins.add(plugin);
});
}
function deleteComponentPlugins(type, pairs) {
iterateComponentPlugins(type, pairs, function (plugins, plugin) {
return plugins.delete(plugin);
});
}
function createPluginsAPI(key) {
var api = {
add: function (type, plugin) {
if (!type[key]) {
type[key] = [];
}
type[key].push(plugin);
return api;
},
};
return api;
}
var XHTML_NS = 'http://www.w3.org/1999/xhtml';
var SVG_NS = 'http://www.w3.org/2000/svg';
var PLUGINS_CREATE_ELEMENT = Symbol();
var pluginsCreateElement = createPluginsStore();
function createElement(spec, parent) {
var result = pluginsCreateElement.apply({ spec: spec, parent: parent });
if (result) {
return result;
}
var tag = spec.type;
if (tag === 'svg') {
return document.createElementNS(SVG_NS, 'svg');
}
var namespace = parent.namespaceURI;
if (namespace === XHTML_NS || namespace == null) {
return document.createElement(tag);
}
return document.createElementNS(namespace, tag);
}
function classes() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var classes = [];
var process = function (c) {
if (!c)
return;
if (typeof c === 'string') {
classes.push(c);
}
else if (Array.isArray(c)) {
c.forEach(process);
}
else if (typeof c === 'object') {
classes.push.apply(classes, Object.keys(c).filter(function (key) { return Boolean(c[key]); }));
}
};
args.forEach(process);
return classes.join(' ');
}
function setInlineCSSPropertyValue(element, prop, $value) {
if ($value != null && $value !== '') {
var value = String($value);
var important = '';
if (value.endsWith('!important')) {
value = value.substring(0, value.length - 10);
important = 'important';
}
element.style.setProperty(prop, value, important);
}
else {
element.style.removeProperty(prop);
}
}
function isObject(value) {
return value != null && typeof value === 'object';
}
var eventListeners = new WeakMap();
function addEventListener(element, event, listener) {
var listeners;
if (eventListeners.has(element)) {
listeners = eventListeners.get(element);
}
else {
listeners = new Map();
eventListeners.set(element, listeners);
}
if (listeners.get(event) !== listener) {
if (listeners.has(event)) {
element.removeEventListener(event, listeners.get(event));
}
element.addEventListener(event, listener);
listeners.set(event, listener);
}
}
function removeEventListener(element, event) {
if (!eventListeners.has(element)) {
return;
}
var listeners = eventListeners.get(element);
element.removeEventListener(event, listeners.get(event));
listeners.delete(event);
}
function setClassObject(element, classObj) {
var cls = Array.isArray(classObj)
? classes.apply(void 0, classObj) : classes(classObj);
if (cls) {
element.setAttribute('class', cls);
}
else {
element.removeAttribute('class');
}
}
function mergeValues(obj, old) {
var values = new Map();
var newProps = new Set(Object.keys(obj));
var oldProps = Object.keys(old);
oldProps
.filter(function (prop) { return !newProps.has(prop); })
.forEach(function (prop) { return values.set(prop, null); });
newProps.forEach(function (prop) { return values.set(prop, obj[prop]); });
return values;
}
function setStyleObject(element, styleObj, prev) {
var prevObj;
if (isObject(prev)) {
prevObj = prev;
}
else {
prevObj = {};
element.removeAttribute('style');
}
var declarations = mergeValues(styleObj, prevObj);
declarations.forEach(function ($value, prop) {
return setInlineCSSPropertyValue(element, prop, $value);
});
}
function setEventListener(element, event, listener) {
if (typeof listener === 'function') {
addEventListener(element, event, listener);
}
else {
removeEventListener(element, event);
}
}
var specialAttrs = new Set([
'key',
'oncreate',
'onupdate',
'onrender',
'onremove',
]);
var PLUGINS_SET_ATTRIBUTE = Symbol();
var pluginsSetAttribute = createPluginsStore();
function getPropertyValue(obj, prop) {
return obj && obj.hasOwnProperty(prop) ? obj[prop] : null;
}
function syncAttrs(element, attrs, prev) {
var values = mergeValues(attrs, prev || {});
values.forEach(function (value, attr) {
if (!pluginsSetAttribute.empty()) {
var result = pluginsSetAttribute.apply({
element: element,
attr: attr,
value: value,
get prev() {
return getPropertyValue(prev, attr);
},
});
if (result != null) {
return;
}
}
if (attr === 'class' && isObject(value)) {
setClassObject(element, value);
}
else if (attr === 'style' && isObject(value)) {
var prevValue = getPropertyValue(prev, attr);
setStyleObject(element, value, prevValue);
}
else if (attr.startsWith('on')) {
var event_1 = attr.substring(2);
setEventListener(element, event_1, value);
}
else if (specialAttrs.has(attr)) ;
else if (value == null || value === false) {
element.removeAttribute(attr);
}
else {
element.setAttribute(attr, value === true ? '' : String(value));
}
});
}
var LinkedList = (function () {
function LinkedList() {
var items = [];
for (var _i = 0; _i < arguments.length; _i++) {
items[_i] = arguments[_i];
}
var _this = this;
this.nexts = new WeakMap();
this.prevs = new WeakMap();
this.first = null;
this.last = null;
items.forEach(function (item) { return _this.push(item); });
}
LinkedList.prototype.empty = function () {
return this.first == null;
};
LinkedList.prototype.push = function (item) {
if (this.empty()) {
this.first = item;
this.last = item;
}
else {
this.nexts.set(this.last, item);
this.prevs.set(item, this.last);
this.last = item;
}
};
LinkedList.prototype.insertBefore = function (newItem, refItem) {
var prev = this.before(refItem);
this.prevs.set(newItem, prev);
this.nexts.set(newItem, refItem);
this.prevs.set(refItem, newItem);
prev && this.nexts.set(prev, newItem);
refItem === this.first && (this.first = newItem);
};
LinkedList.prototype.delete = function (item) {
var prev = this.before(item);
var next = this.after(item);
prev && this.nexts.set(prev, next);
next && this.prevs.set(next, prev);
item === this.first && (this.first = next);
item === this.last && (this.last = prev);
};
LinkedList.prototype.before = function (item) {
return this.prevs.get(item) || null;
};
LinkedList.prototype.after = function (item) {
return this.nexts.get(item) || null;
};
LinkedList.prototype.loop = function (iterator) {
if (this.empty()) {
return;
}
var current = this.first;
do {
if (iterator(current)) {
break;
}
} while ((current = this.after(current)));
};
LinkedList.prototype.copy = function () {
var list = new LinkedList();
this.loop(function (item) {
list.push(item);
return false;
});
return list;
};
LinkedList.prototype.forEach = function (iterator) {
this.loop(function (item) {
iterator(item);
return false;
});
};
LinkedList.prototype.find = function (iterator) {
var result = null;
this.loop(function (item) {
if (iterator(item)) {
result = item;
return true;
}
return false;
});
return result;
};
LinkedList.prototype.map = function (iterator) {
var results = [];
this.loop(function (item) {
results.push(iterator(item));
return false;
});
return results;
};
return LinkedList;
}());
function matchChildren(vnode, old) {
var oldChildren = old.children();
var oldChildrenByKey = new Map();
var oldChildrenWithoutKey = [];
oldChildren.forEach(function (v) {
var key = v.key();
if (key == null) {
oldChildrenWithoutKey.push(v);
}
else {
oldChildrenByKey.set(key, v);
}
});
var children = vnode.children();
var matches = [];
var unmatched = new Set(oldChildren);
var keys = new Set();
children.forEach(function (v) {
var match = null;
var guess = null;
var key = v.key();
if (key != null) {
if (keys.has(key)) {
throw new Error('Duplicate key');
}
keys.add(key);
if (oldChildrenByKey.has(key)) {
guess = oldChildrenByKey.get(key);
}
}
else if (oldChildrenWithoutKey.length > 0) {
guess = oldChildrenWithoutKey.shift();
}
if (v.matches(guess)) {
match = guess;
}
matches.push([v, match]);
if (match) {
unmatched.delete(match);
}
});
return { matches: matches, unmatched: unmatched };
}
function execute(vnode, old, vdom) {
var didMatch = vnode && old && vnode.matches(old);
if (didMatch && vnode.parent() === old.parent()) {
vdom.replaceVNode(old, vnode);
}
else if (vnode) {
vdom.addVNode(vnode);
}
var context = vdom.getVNodeContext(vnode);
var oldContext = vdom.getVNodeContext(old);
if (old && !didMatch) {
old.detach(oldContext);
old.children().forEach(function (v) { return execute(null, v, vdom); });
old.detached(oldContext);
}
if (vnode && !didMatch) {
vnode.attach(context);
vnode.children().forEach(function (v) { return execute(v, null, vdom); });
vnode.attached(context);
}
if (didMatch) {
var result = vnode.update(old, context);
if (result !== vdom.LEAVE) {
var _a = matchChildren(vnode, old), matches = _a.matches, unmatched = _a.unmatched;
unmatched.forEach(function (v) { return execute(null, v, vdom); });
matches.forEach(function (_a) {
var v = _a[0], o = _a[1];
return execute(v, o, vdom);
});
vnode.updated(context);
}
}
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
function m(tagOrComponent, props) {
var children = [];
for (var _i = 2; _i < arguments.length; _i++) {
children[_i - 2] = arguments[_i];
}
props = props || {};
if (typeof tagOrComponent === 'string') {
var tag = tagOrComponent;
return { type: tag, props: props, children: children };
}
if (typeof tagOrComponent === 'function') {
var component = tagOrComponent;
return { type: component, props: props, children: children };
}
throw new Error('Unsupported spec type');
}
function isSpec(x) {
return isObject(x) && x.type != null && x.nodeType == null;
}
function isNodeSpec(x) {
return isSpec(x) && typeof x.type === 'string';
}
function isComponentSpec(x) {
return isSpec(x) && typeof x.type === 'function';
}
var VNodeBase = (function () {
function VNodeBase(parent) {
this.parentVNode = parent;
}
VNodeBase.prototype.key = function () {
return null;
};
VNodeBase.prototype.parent = function (vnode) {
if (vnode) {
this.parentVNode = vnode;
return;
}
return this.parentVNode;
};
VNodeBase.prototype.children = function () {
return [];
};
VNodeBase.prototype.attach = function (context) { };
VNodeBase.prototype.detach = function (context) { };
VNodeBase.prototype.update = function (old, context) {
return null;
};
VNodeBase.prototype.attached = function (context) { };
VNodeBase.prototype.detached = function (context) { };
VNodeBase.prototype.updated = function (context) { };
return VNodeBase;
}());
function nodeMatchesSpec(node, spec) {
return (node instanceof Element &&
((node.namespaceURI === XHTML_NS &&
spec.type === node.tagName.toLocaleLowerCase()) ||
(node.namespaceURI !== XHTML_NS && spec.type === node.tagName)));
}
var refinedElements = new WeakMap();
function markElementAsRefined(element, vdom) {
var refined;
if (refinedElements.has(vdom)) {
refined = refinedElements.get(vdom);
}
else {
refined = new WeakSet();
refinedElements.set(vdom, refined);
}
refined.add(element);
}
function isElementRefined(element, vdom) {
return refinedElements.has(vdom) && refinedElements.get(vdom).has(element);
}
var ElementVNode = (function (_super) {
__extends(ElementVNode, _super);
function ElementVNode(spec, parent) {
var _this = _super.call(this, parent) || this;
_this.spec = spec;
return _this;
}
ElementVNode.prototype.matches = function (other) {
return (other instanceof ElementVNode && this.spec.type === other.spec.type);
};
ElementVNode.prototype.key = function () {
return this.spec.props.key;
};
ElementVNode.prototype.children = function () {
return [this.child];
};
ElementVNode.prototype.getExistingElement = function (context) {
var parent = context.parent;
var existing = context.node;
var element;
if (nodeMatchesSpec(existing, this.spec)) {
element = existing;
}
else if (!isElementRefined(parent, context.vdom) &&
context.vdom.isDOMNodeCaptured(parent)) {
var sibling = context.sibling;
var guess = sibling
? sibling.nextElementSibling
: parent.firstElementChild;
if (guess && !context.vdom.isDOMNodeCaptured(guess)) {
if (nodeMatchesSpec(guess, this.spec)) {
element = guess;
}
else {
parent.removeChild(guess);
}
}
}
return element;
};
ElementVNode.prototype.attach = function (context) {
var element;
var existing = this.getExistingElement(context);
if (existing) {
element = existing;
}
else {
element = createElement(this.spec, context.parent);
markElementAsRefined(element, context.vdom);
}
syncAttrs(element, this.spec.props, null);
this.child = createDOMVNode(element, this.spec.children, this, false);
};
ElementVNode.prototype.update = function (prev, context) {
var prevContext = context.vdom.getVNodeContext(prev);
var element = prevContext.node;
syncAttrs(element, this.spec.props, prev.spec.props);
this.child = createDOMVNode(element, this.spec.children, this, false);
};
ElementVNode.prototype.attached = function (context) {
var _a = this.spec.props, oncreate = _a.oncreate, onrender = _a.onrender;
if (oncreate) {
oncreate(context.node);
}
if (onrender) {
onrender(context.node);
}
};
ElementVNode.prototype.detached = function (context) {
var onremove = this.spec.props.onremove;
if (onremove) {
onremove(context.node);
}
};
ElementVNode.prototype.updated = function (context) {
var _a = this.spec.props, onupdate = _a.onupdate, onrender = _a.onrender;
if (onupdate) {
onupdate(context.node);
}
if (onrender) {
onrender(context.node);
}
};
return ElementVNode;
}(VNodeBase));
var symbols = {
CREATED: Symbol(),
REMOVED: Symbol(),
UPDATED: Symbol(),
RENDERED: Symbol(),
ACTIVE: Symbol(),
DEFAULTS_ASSIGNED: Symbol(),
};
var domPlugins = [
[PLUGINS_CREATE_ELEMENT, pluginsCreateElement],
[PLUGINS_SET_ATTRIBUTE, pluginsSetAttribute],
];
var ComponentVNode = (function (_super) {
__extends(ComponentVNode, _super);
function ComponentVNode(spec, parent) {
var _this = _super.call(this, parent) || this;
_this.lock = false;
_this.spec = spec;
_this.prev = null;
_this.store = {};
_this.store[symbols.ACTIVE] = _this;
return _this;
}
ComponentVNode.prototype.matches = function (other) {
return (other instanceof ComponentVNode &&
this.spec.type === other.spec.type);
};
ComponentVNode.prototype.key = function () {
return this.spec.props.key;
};
ComponentVNode.prototype.children = function () {
return [this.child];
};
ComponentVNode.prototype.createContext = function (context) {
var parent = context.parent;
var _a = this, spec = _a.spec, prev = _a.prev, store = _a.store;
return {
spec: spec,
prev: prev,
store: store,
get node() {
return context.node;
},
get nodes() {
return context.nodes;
},
parent: parent,
onCreate: function (fn) { return (store[symbols.CREATED] = fn); },
onUpdate: function (fn) { return (store[symbols.UPDATED] = fn); },
onRemove: function (fn) { return (store[symbols.REMOVED] = fn); },
onRender: function (fn) { return (store[symbols.RENDERED] = fn); },
refresh: function () {
var activeVNode = store[symbols.ACTIVE];
activeVNode.refresh(context);
},
leave: function () { return context.vdom.LEAVE; },
getStore: function (defaults) {
if (defaults && !store[symbols.DEFAULTS_ASSIGNED]) {
Object.entries(defaults).forEach(function (_a) {
var prop = _a[0], value = _a[1];
store[prop] = value;
});
store[symbols.DEFAULTS_ASSIGNED] = true;
}
return store;
},
};
};
ComponentVNode.prototype.unbox = function (context) {
var Component = this.spec.type;
var props = this.spec.props;
var children = this.spec.children;
this.lock = true;
var prevContext = ComponentVNode.context;
ComponentVNode.context = this.createContext(context);
var unboxed = null;
try {
unboxed = Component.apply(void 0, __spreadArray([props], children, false));
}
finally {
ComponentVNode.context = prevContext;
this.lock = false;
}
return unboxed;
};
ComponentVNode.prototype.refresh = function (context) {
if (this.lock) {
throw new Error('Calling refresh during unboxing causes infinite loop');
}
this.prev = this.spec;
var latestContext = context.vdom.getVNodeContext(this);
var unboxed = this.unbox(latestContext);
if (unboxed === context.vdom.LEAVE) {
return;
}
var prevChild = this.child;
this.child = createVNode(unboxed, this);
context.vdom.execute(this.child, prevChild);
this.updated(context);
};
ComponentVNode.prototype.addPlugins = function () {
addComponentPlugins(this.spec.type, domPlugins);
};
ComponentVNode.prototype.deletePlugins = function () {
deleteComponentPlugins(this.spec.type, domPlugins);
};
ComponentVNode.prototype.attach = function (context) {
this.addPlugins();
var unboxed = this.unbox(context);
var childSpec = unboxed === context.vdom.LEAVE ? null : unboxed;
this.child = createVNode(childSpec, this);
};
ComponentVNode.prototype.update = function (prev, context) {
this.store = prev.store;
this.prev = prev.spec;
this.store[symbols.ACTIVE] = this;
var prevContext = context.vdom.getVNodeContext(prev);
this.addPlugins();
var unboxed = this.unbox(prevContext);
var result = null;
if (unboxed === context.vdom.LEAVE) {
result = unboxed;
this.child = prev.child;
context.vdom.adoptVNode(this.child, this);
}
else {
this.child = createVNode(unboxed, this);
}
return result;
};
ComponentVNode.prototype.handle = function (event, context) {
var fn = this.store[event];
if (fn) {
var nodes = context.nodes.length === 0 ? [null] : context.nodes;
fn.apply(void 0, nodes);
}
};
ComponentVNode.prototype.attached = function (context) {
this.deletePlugins();
this.handle(symbols.CREATED, context);
this.handle(symbols.RENDERED, context);
};
ComponentVNode.prototype.detached = function (context) {
this.handle(symbols.REMOVED, context);
};
ComponentVNode.prototype.updated = function (context) {
this.deletePlugins();
this.handle(symbols.UPDATED, context);
this.handle(symbols.RENDERED, context);
};
ComponentVNode.context = null;
return ComponentVNode;
}(VNodeBase));
function getComponentContext() {
return ComponentVNode.context;
}
var TextVNode = (function (_super) {
__extends(TextVNode, _super);
function TextVNode(text, parent) {
var _this = _super.call(this, parent) || this;
_this.text = text;
return _this;
}
TextVNode.prototype.matches = function (other) {
return other instanceof TextVNode;
};
TextVNode.prototype.children = function () {
return [this.child];
};
TextVNode.prototype.getExistingNode = function (context) {
var parent = context.parent;
var node;
if (context.node instanceof Text) {
node = context.node;
}
else if (!isElementRefined(parent, context.vdom) &&
context.vdom.isDOMNodeCaptured(parent)) {
var sibling = context.sibling;
var guess = sibling ? sibling.nextSibling : parent.firstChild;
if (guess &&
!context.vdom.isDOMNodeCaptured(guess) &&
guess instanceof Text) {
node = guess;
}
}
return node;
};
TextVNode.prototype.attach = function (context) {
var existing = this.getExistingNode(context);
var node;
if (existing) {
node = existing;
node.textContent = this.text;
}
else {
node = document.createTextNode(this.text);
}
this.child = createVNode(node, this);
};
TextVNode.prototype.update = function (prev, context) {
var prevContext = context.vdom.getVNodeContext(prev);
var node = prevContext.node;
if (this.text !== prev.text) {
node.textContent = this.text;
}
this.child = createVNode(node, this);
};
return TextVNode;
}(VNodeBase));
var InlineFunctionVNode = (function (_super) {
__extends(InlineFunctionVNode, _super);
function InlineFunctionVNode(fn, parent) {
var _this = _super.call(this, parent) || this;
_this.fn = fn;
return _this;
}
InlineFunctionVNode.prototype.matches = function (other) {
return other instanceof InlineFunctionVNode;
};
InlineFunctionVNode.prototype.children = function () {
return [this.child];
};
InlineFunctionVNode.prototype.call = function (context) {
var fn = this.fn;
var inlineFnContext = {
parent: context.parent,
get node() {
return context.node;
},
get nodes() {
return context.nodes;
},
};
var result = fn(inlineFnContext);
this.child = createVNode(result, this);
};
InlineFunctionVNode.prototype.attach = function (context) {
this.call(context);
};
InlineFunctionVNode.prototype.update = function (prev, context) {
var prevContext = context.vdom.getVNodeContext(prev);
this.call(prevContext);
};
return InlineFunctionVNode;
}(VNodeBase));
var NullVNode = (function (_super) {
__extends(NullVNode, _super);
function NullVNode() {
return _super !== null && _super.apply(this, arguments) || this;
}
NullVNode.prototype.matches = function (other) {
return other instanceof NullVNode;
};
return NullVNode;
}(VNodeBase));
var DOMVNode = (function (_super) {
__extends(DOMVNode, _super);
function DOMVNode(node, childSpecs, parent, isNative) {
var _this = _super.call(this, parent) || this;
_this.node = node;
_this.childSpecs = childSpecs;
_this.isNative = isNative;
return _this;
}
DOMVNode.prototype.matches = function (other) {
return other instanceof DOMVNode && this.node === other.node;
};
DOMVNode.prototype.wrap = function () {
var _this = this;
this.childVNodes = this.childSpecs.map(function (spec) {
return createVNode(spec, _this);
});
};
DOMVNode.prototype.insertNode = function (context) {
var parent = context.parent, sibling = context.sibling;
var shouldInsert = !(parent === this.node.parentElement &&
sibling === this.node.previousSibling);
if (shouldInsert) {
var target = sibling ? sibling.nextSibling : parent.firstChild;
parent.insertBefore(this.node, target);
}
};
DOMVNode.prototype.attach = function (context) {
this.wrap();
this.insertNode(context);
};
DOMVNode.prototype.detach = function (context) {
context.parent.removeChild(this.node);
};
DOMVNode.prototype.update = function (prev, context) {
this.wrap();
this.insertNode(context);
};
DOMVNode.prototype.cleanupDOMChildren = function (context) {
var element = this.node;
for (var current = element.lastChild; current != null;) {
if (context.vdom.isDOMNodeCaptured(current)) {
current = current.previousSibling;
}
else {
var prev = current.previousSibling;
element.removeChild(current);
current = prev;
}
}
};
DOMVNode.prototype.refine = function (context) {
if (!this.isNative) {
this.cleanupDOMChildren(context);
}
var element = this.node;
markElementAsRefined(element, context.vdom);
};
DOMVNode.prototype.attached = function (context) {
var node = this.node;
if (node instanceof Element &&
!isElementRefined(node, context.vdom) &&
context.vdom.isDOMNodeCaptured(node)) {
this.refine(context);
}
};
DOMVNode.prototype.children = function () {
return this.childVNodes;
};
return DOMVNode;
}(VNodeBase));
function isDOMVNode(v) {
return v instanceof DOMVNode;
}
function createDOMVNode(node, childSpecs, parent, isNative) {
return new DOMVNode(node, childSpecs, parent, isNative);
}
var ArrayVNode = (function (_super) {
__extends(ArrayVNode, _super);
function ArrayVNode(items, key, parent) {
var _this = _super.call(this, parent) || this;
_this.items = items;
_this.id = key;
return _this;
}
ArrayVNode.prototype.matches = function (other) {
return other instanceof ArrayVNode;
};
ArrayVNode.prototype.key = function () {
return this.id;
};
ArrayVNode.prototype.children = function () {
return this.childVNodes;
};
ArrayVNode.prototype.wrap = function () {
var _this = this;
this.childVNodes = this.items.map(function (spec) { return createVNode(spec, _this); });
};
ArrayVNode.prototype.attach = function () {
this.wrap();
};
ArrayVNode.prototype.update = function () {
this.wrap();
};
return ArrayVNode;
}(VNodeBase));
function createVNode(spec, parent) {
if (isNodeSpec(spec)) {
return new ElementVNode(spec, parent);
}
if (isComponentSpec(spec)) {
if (spec.type === Array) {
return new ArrayVNode(spec.children, spec.props.key, parent);
}
return new ComponentVNode(spec, parent);
}
if (typeof spec === 'string') {
return new TextVNode(spec, parent);
}
if (spec == null) {
return new NullVNode(parent);
}
if (typeof spec === 'function') {
return new InlineFunctionVNode(spec, parent);
}
if (spec instanceof Node) {
return createDOMVNode(spec, [], parent, true);
}
if (Array.isArray(spec)) {
return new ArrayVNode(spec, null, parent);
}
throw new Error('Unable to create virtual node for spec');
}
function createVDOM(rootNode) {
var contexts = new WeakMap();
var hubs = new WeakMap();
var parentNodes = new WeakMap();
var passingLinks = new WeakMap();
var linkedParents = new WeakSet();
var LEAVE = Symbol();
function execute$1(vnode, old) {
execute(vnode, old, vdom);
}
function creatVNodeContext(vnode) {
var parentNode = parentNodes.get(vnode);
contexts.set(vnode, {
parent: parentNode,
get node() {
var linked = passingLinks
.get(vnode)
.find(function (link) { return link.node != null; });
return linked ? linked.node : null;
},
get nodes() {
return passingLinks
.get(vnode)
.map(function (link) { return link.node; })
.filter(function (node) { return node; });
},
get sibling() {
if (parentNode === rootNode.parentElement) {
return passingLinks.get(vnode).first.node.previousSibling;
}
var hub = hubs.get(parentNode);
var current = passingLinks.get(vnode).first;
while ((current = hub.links.before(current))) {
if (current.node) {
return current.node;
}
}
return null;
},
vdom: vdom,
});
}
function createRootVNodeLinks(vnode) {
var parentNode = rootNode.parentElement || document.createDocumentFragment();
var node = rootNode;
var links = new LinkedList({
parentNode: parentNode,
node: node,
});
passingLinks.set(vnode, links.copy());
parentNodes.set(vnode, parentNode);
hubs.set(parentNode, {
node: parentNode,
links: links,
});
}
function createVNodeLinks(vnode) {
var parent = vnode.parent();
var isBranch = linkedParents.has(parent);
var parentNode = isDOMVNode(parent)
? parent.node
: parentNodes.get(parent);
parentNodes.set(vnode, parentNode);
var vnodeLinks = new LinkedList();
passingLinks.set(vnode, vnodeLinks);
if (isBranch) {
var newLink = {
parentNode: parentNode,
node: null,
};
var current = vnode;
do {
passingLinks.get(current).push(newLink);
current = current.parent();
} while (current && !isDOMVNode(current));
hubs.get(parentNode).links.push(newLink);
}
else {
linkedParents.add(parent);
var links = isDOMVNode(parent)
? hubs.get(parentNode).links
: passingLinks.get(parent);
links.forEach(function (link) { return vnodeLinks.push(link); });
}
}
function connectDOMVNode(vnode) {
if (isDOMVNode(vnode)) {
var node_1 = vnode.node;
hubs.set(node_1, {
node: node_1,
links: new LinkedList({
parentNode: node_1,
node: null,
}),
});
passingLinks.get(vnode).forEach(function (link) { return (link.node = node_1); });
}
}
function addVNode(vnode) {
var parent = vnode.parent();
if (parent == null) {
createRootVNodeLinks(vnode);
}
else {
createVNodeLinks(vnode);
}
connectDOMVNode(vnode);
creatVNodeContext(vnode);
}
function getVNodeContext(vnode) {
return contexts.get(vnode);
}
function getAncestorsLinks(vnode) {
var parentNode = parentNodes.get(vnode);
var hub = hubs.get(parentNode);
var allLinks = [];
var current = vnode;
while ((current = current.parent()) && !isDOMVNode(current)) {
allLinks.push(passingLinks.get(current));
}
allLinks.push(hub.links);
return allLinks;
}
function replaceVNode(old, vnode) {
if (vnode.parent() == null) {
addVNode(vnode);
return;
}
var oldContext = contexts.get(old);
var parentNode = oldContext.parent;
parentNodes.set(vnode, parentNode);
var oldLinks = passingLinks.get(old);
var newLink = {
parentNode: parentNode,
node: null,
};
getAncestorsLinks(vnode).forEach(function (links) {
var nextLink = links.after(oldLinks.last);
oldLinks.forEach(function (link) { return links.delete(link); });
if (nextLink) {
links.insertBefore(newLink, nextLink);
}
else {
links.push(newLink);
}
});
var vnodeLinks = new LinkedList(newLink);
passingLinks.set(vnode, vnodeLinks);
creatVNodeContext(vnode);
}
function adoptVNode(vnode, parent) {
var vnodeLinks = passingLinks.get(vnode);
var parentLinks = passingLinks.get(parent).copy();
vnode.parent(parent);
getAncestorsLinks(vnode).forEach(function (links) {
vnodeLinks.forEach(function (link) {
return links.insertBefore(link, parentLinks.first);
});
parentLinks.forEach(function (link) { return links.delete(link); });
});
}
function isDOMNodeCaptured(node) {
return hubs.has(node) && node !== rootNode.parentElement;
}
var vdom = {
execute: execute$1,
addVNode: addVNode,
getVNodeContext: getVNodeContext,
replaceVNode: replaceVNode,
adoptVNode: adoptVNode,
isDOMNodeCaptured: isDOMNodeCaptured,
LEAVE: LEAVE,
};
return vdom;
}
var roots = new WeakMap();
var vdoms = new WeakMap();
function realize(node, vnode) {
var old = roots.get(node) || null;
roots.set(node, vnode);
var vdom;
if (vdoms.has(node)) {
vdom = vdoms.get(node);
}
else {
vdom = createVDOM(node);
vdoms.set(node, vdom);
}
vdom.execute(vnode, old);
return vdom.getVNodeContext(vnode);
}
function render(element, spec) {
var vnode = createDOMVNode(element, Array.isArray(spec) ? spec : [spec], null, false);
realize(element, vnode);
return element;
}
function sync(node, spec) {
var vnode = createVNode(spec, null);
var context = realize(node, vnode);
var nodes = context.nodes;
if (nodes.length !== 1 || nodes[0] !== node) {
throw new Error('Spec does not match the node');
}
return nodes[0];
}
function teardown(node) {
roots.delete(node);
vdoms.delete(node);
}
function createComponent(fn) {
var component = function (props) {
var children = [];
for (var _i = 1; _i < arguments.length; _i++) {
children[_i - 1] = arguments[_i];
}
var context = getComponentContext();
return fn.apply(void 0, __spreadArray([context, props], children, false));
};
return function (props) {
var children = [];
for (var _i = 1; _i < arguments.length; _i++) {
children[_i - 1] = arguments[_i];
}
return m.apply(void 0, __spreadArray([component, props], children, false));
};
}
function normalize(attrsOrChild) {
var otherChildren = [];
for (var _i = 1; _i < arguments.length; _i++) {
otherChildren[_i - 1] = arguments[_i];
}
var attrs = isObject(attrsOrChild) && !isSpec(attrsOrChild) ? attrsOrChild : null;
var children = attrs == null
? [attrsOrChild].concat(otherChildren)
: otherChildren;
return { attrs: attrs, children: children };
}
function createTagFunction(tag) {
return function (attrsOrChild) {
var otherChildren = [];
for (var _i = 1; _i < arguments.length; _i++) {
otherChildren[_i - 1] = arguments[_i];
}
var _a = normalize(attrsOrChild, otherChildren), attrs = _a.attrs, children = _a.children;
return m(tag, attrs, children);
};
}
var tags = new Proxy({}, {
get: function (_, tag) {
return createTagFunction(tag);
},
});
function specFromNode(node) {
return walkNode(node);
}
function walkNode(node) {
if (node instanceof Text) {
return node.textContent
.trim()
.replaceAll(/\r/g, '')
.replaceAll(/\s*?\n\s*/g, '\n');
}
if (!(node instanceof Element)) {
return null;
}
var tag = node.namespaceURI === XHTML_NS
? node.tagName.toLocaleLowerCase()
: node.tagName;
var attrs = {};
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes[i];
attrs[attr.name] = String(attr.value);
}
var children = Array.from(node.childNodes)
.map(walkNode)
.filter(function (c) {
return c === null ||
(typeof c === 'string' && c !== '') ||
typeof c === 'object';
});
return m.apply(void 0, __spreadArray([tag, attrs], children, false));
}
var plugins = {
createElement: createPluginsAPI(PLUGINS_CREATE_ELEMENT),
setAttribute: createPluginsAPI(PLUGINS_SET_ATTRIBUTE),
};
exports.component = createComponent;
exports.getContext = getComponentContext;
exports.plugins = plugins;
exports.render = render;
exports.specFromNode = specFromNode;
exports.sync = sync;
exports.tag = createTagFunction;
exports.tags = tags;
exports.teardown = teardown;
}));