mettle
Version:
A approachable, fast, flexible JavaScript library for building user interfaces.
1,442 lines (1,438 loc) • 67 kB
JavaScript
/*!
* Mettle.js v1.7.6
* (c) 2021-2025 maomincoding
* Released under the MIT License.
*/
(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.Mettle = {}));
})(this, (function (exports) { 'use strict';
const MODE_SLASH = 0;
const MODE_TEXT = 1;
const MODE_WHITESPACE = 2;
const MODE_TAGNAME = 3;
const MODE_COMMENT = 4;
const MODE_PROP_SET = 5;
const MODE_PROP_APPEND = 6;
const CHILD_APPEND = 0;
const CHILD_RECURSE = 2;
const TAG_SET = 3;
const PROPS_ASSIGN = 4;
const PROP_SET = MODE_PROP_SET;
const PROP_APPEND = MODE_PROP_APPEND;
const evaluate = (h, built, fields, args) => {
let tmp;
built[0] = 0;
for (let i = 1; i < built.length; i++) {
const type = built[i++];
const value = built[i] ? ((built[0] |= type ? 1 : 2), fields[built[i++]]) : built[++i];
if (type === TAG_SET) {
args[0] = value;
}
else if (type === PROPS_ASSIGN) {
args[1] = Object.assign(args[1] || {}, value);
}
else if (type === PROP_SET) {
(args[1] = args[1] || {})[built[++i]] = value;
}
else if (type === PROP_APPEND) {
args[1][built[++i]] += value + '';
}
else if (type) {
tmp = h.apply(value, evaluate(h, value, fields, ['', null]));
args.push(tmp);
if (value[0]) {
built[0] |= 2;
}
else {
built[i - 2] = CHILD_APPEND;
built[i] = tmp;
}
}
else {
args.push(value);
}
}
return args;
};
const build = function (statics) {
let mode = MODE_TEXT;
let buffer = '';
let quote = '';
let current = [0];
let char, propName;
const commit = (field) => {
if (mode === MODE_TEXT && (field || (buffer = buffer.replace(/^\s*\n\s*|\s*\n\s*$/g, '')))) {
current.push(CHILD_APPEND, field, buffer);
}
else if (mode === MODE_TAGNAME && (field || buffer)) {
current.push(TAG_SET, field, buffer);
mode = MODE_WHITESPACE;
}
else if (mode === MODE_WHITESPACE && buffer === '...' && field) {
current.push(PROPS_ASSIGN, field, 0);
}
else if (mode === MODE_WHITESPACE && buffer && !field) {
current.push(PROP_SET, 0, true, buffer);
}
else if (mode >= MODE_PROP_SET) {
if (buffer || (!field && mode === MODE_PROP_SET)) {
current.push(mode, 0, buffer, propName);
mode = MODE_PROP_APPEND;
}
if (field) {
current.push(mode, field, 0, propName);
mode = MODE_PROP_APPEND;
}
}
buffer = '';
};
for (let i = 0; i < statics.length; i++) {
if (i) {
if (mode === MODE_TEXT) {
commit();
}
commit(i);
}
for (let j = 0; j < statics[i].length; j++) {
char = statics[i][j];
if (mode === MODE_TEXT) {
if (char === '<') {
commit();
current = [current];
mode = MODE_TAGNAME;
}
else {
buffer += char;
}
}
else if (mode === MODE_COMMENT) {
if (buffer === '--' && char === '>') {
mode = MODE_TEXT;
buffer = '';
}
else {
buffer = char + buffer[0];
}
}
else if (quote) {
if (char === quote) {
quote = '';
}
else {
buffer += char;
}
}
else if (char === '"' || char === "'") {
quote = char;
}
else if (char === '>') {
commit();
mode = MODE_TEXT;
}
else if (!mode) ;
else if (char === '=') {
mode = MODE_PROP_SET;
propName = buffer;
buffer = '';
}
else if (char === '/' && (mode < MODE_PROP_SET || statics[i][j + 1] === '>')) {
commit();
if (mode === MODE_TAGNAME) {
current = current[0];
}
mode = current;
(current = current[0]).push(CHILD_RECURSE, 0, mode);
mode = MODE_SLASH;
}
else if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
commit();
mode = MODE_WHITESPACE;
}
else {
buffer += char;
}
if (mode === MODE_TAGNAME && buffer === '!--') {
mode = MODE_COMMENT;
current = current[0];
}
}
}
commit();
return current;
};
const CACHES = new Map();
const regular = function (statics) {
let tmp = CACHES.get(this);
if (!tmp) {
tmp = new Map();
CACHES.set(this, tmp);
}
tmp = evaluate(this, tmp.get(statics) || (tmp.set(statics, (tmp = build(statics))), tmp), arguments, []);
return tmp.length > 1 ? tmp : tmp[0];
};
const createVNode = function (tag, props, child) {
let key = null;
let el = null;
let i = null;
let children = null;
for (i in props) {
if (i === 'key')
key = props[i];
}
if (arguments.length > 2) {
children = arguments.length > 3 ? Array.prototype.slice.call(arguments, 2) : child;
}
// Vnode
return {
tag,
props,
children,
key,
el,
};
};
const html = regular.bind(createVNode);
const BRAND_SYMBOL = Symbol.for('signals');
const RUNNING = 1 << 0;
const NOTIFIED = 1 << 1;
const OUTDATED = 1 << 2;
const DISPOSED = 1 << 3;
const HAS_ERROR = 1 << 4;
const TRACKING = 1 << 5;
// Batch
function startBatch() {
batchDepth++;
}
function endBatch() {
if (batchDepth > 1) {
batchDepth--;
return;
}
let error;
let hasError = false;
while (batchedEffect !== undefined) {
let effect = batchedEffect;
batchedEffect = undefined;
batchIteration++;
while (effect !== undefined) {
const next = effect._nextBatchedEffect;
effect._nextBatchedEffect = undefined;
effect._flags &= ~NOTIFIED;
if (!(effect._flags & DISPOSED) && needsToRecompute(effect)) {
try {
effect._callback();
}
catch (err) {
if (!hasError) {
error = err;
hasError = true;
}
}
}
effect = next;
}
}
batchIteration = 0;
batchDepth--;
if (hasError) {
throw error;
}
}
function batch(fn) {
if (batchDepth > 0) {
return fn();
}
/*@__INLINE__**/ startBatch();
try {
return fn();
}
finally {
endBatch();
}
}
// Currently evaluated computed or effect.
let evalContext = undefined;
function untracked(fn) {
const prevContext = evalContext;
evalContext = undefined;
try {
return fn();
}
finally {
evalContext = prevContext;
}
}
let batchedEffect = undefined;
let batchDepth = 0;
let batchIteration = 0;
let globalVersion = 0;
function addDependency(signal) {
if (evalContext === undefined) {
return undefined;
}
let node = signal._node;
if (node === undefined || node._target !== evalContext) {
node = {
_version: 0,
_source: signal,
_prevSource: evalContext._sources,
_nextSource: undefined,
_target: evalContext,
_prevTarget: undefined,
_nextTarget: undefined,
_rollbackNode: node,
};
if (evalContext._sources !== undefined) {
evalContext._sources._nextSource = node;
}
evalContext._sources = node;
signal._node = node;
if (evalContext._flags & TRACKING) {
signal._subscribe(node);
}
return node;
}
else if (node._version === -1) {
node._version = 0;
if (node._nextSource !== undefined) {
node._nextSource._prevSource = node._prevSource;
if (node._prevSource !== undefined) {
node._prevSource._nextSource = node._nextSource;
}
node._prevSource = evalContext._sources;
node._nextSource = undefined;
evalContext._sources._nextSource = node;
evalContext._sources = node;
}
return node;
}
return undefined;
}
/** @internal */
// @ts-ignore: "Cannot redeclare exported variable 'Signal'."
function Signal(value, options) {
this._value = value;
this._version = 0;
this._node = undefined;
this._targets = undefined;
this._watched = options?.watched;
this._unwatched = options?.unwatched;
this.name = options?.name;
}
Signal.prototype.brand = BRAND_SYMBOL;
Signal.prototype._refresh = function () {
return true;
};
Signal.prototype._subscribe = function (node) {
const targets = this._targets;
if (targets !== node && node._prevTarget === undefined) {
node._nextTarget = targets;
this._targets = node;
if (targets !== undefined) {
targets._prevTarget = node;
}
else {
untracked(() => {
this._watched?.call(this);
});
}
}
};
Signal.prototype._unsubscribe = function (node) {
if (this._targets !== undefined) {
const prev = node._prevTarget;
const next = node._nextTarget;
if (prev !== undefined) {
prev._nextTarget = next;
node._prevTarget = undefined;
}
if (next !== undefined) {
next._prevTarget = prev;
node._nextTarget = undefined;
}
if (node === this._targets) {
this._targets = next;
if (next === undefined) {
untracked(() => {
this._unwatched?.call(this);
});
}
}
}
};
Signal.prototype.subscribe = function (fn) {
return effect(() => {
const value = this.value;
const prevContext = evalContext;
evalContext = undefined;
try {
fn(value);
}
finally {
evalContext = prevContext;
}
}, { name: 'sub' });
};
Signal.prototype.valueOf = function () {
return this.value;
};
Signal.prototype.toString = function () {
return this.value + '';
};
Signal.prototype.toJSON = function () {
return this.value;
};
Signal.prototype.peek = function () {
const prevContext = evalContext;
evalContext = undefined;
try {
return this.value;
}
finally {
evalContext = prevContext;
}
};
Object.defineProperty(Signal.prototype, 'value', {
get() {
const node = addDependency(this);
if (node !== undefined) {
node._version = this._version;
}
return this._value;
},
set(value) {
if (value !== this._value) {
if (batchIteration > 100) {
throw new Error('Cycle detected');
}
this._value = value;
this._version++;
globalVersion++;
/**@__INLINE__*/ startBatch();
try {
for (let node = this._targets; node !== undefined; node = node._nextTarget) {
node._target._notify();
}
}
finally {
endBatch();
}
}
},
});
function signal(value, options) {
return new Signal(value, options);
}
function needsToRecompute(target) {
for (let node = target._sources; node !== undefined; node = node._nextSource) {
if (node._source._version !== node._version ||
!node._source._refresh() ||
node._source._version !== node._version) {
return true;
}
}
return false;
}
function prepareSources(target) {
for (let node = target._sources; node !== undefined; node = node._nextSource) {
const rollbackNode = node._source._node;
if (rollbackNode !== undefined) {
node._rollbackNode = rollbackNode;
}
node._source._node = node;
node._version = -1;
if (node._nextSource === undefined) {
target._sources = node;
break;
}
}
}
function cleanupSources(target) {
let node = target._sources;
let head = undefined;
while (node !== undefined) {
const prev = node._prevSource;
if (node._version === -1) {
node._source._unsubscribe(node);
if (prev !== undefined) {
prev._nextSource = node._nextSource;
}
if (node._nextSource !== undefined) {
node._nextSource._prevSource = prev;
}
}
else {
head = node;
}
node._source._node = node._rollbackNode;
if (node._rollbackNode !== undefined) {
node._rollbackNode = undefined;
}
node = prev;
}
target._sources = head;
}
/** @internal */
function Computed(fn, options) {
Signal.call(this, undefined);
this._fn = fn;
this._sources = undefined;
this._globalVersion = globalVersion - 1;
this._flags = OUTDATED;
this._watched = options?.watched;
this._unwatched = options?.unwatched;
this.name = options?.name;
}
Computed.prototype = new Signal();
Computed.prototype._refresh = function () {
this._flags &= ~NOTIFIED;
if (this._flags & RUNNING) {
return false;
}
if ((this._flags & (OUTDATED | TRACKING)) === TRACKING) {
return true;
}
this._flags &= ~OUTDATED;
if (this._globalVersion === globalVersion) {
return true;
}
this._globalVersion = globalVersion;
this._flags |= RUNNING;
if (this._version > 0 && !needsToRecompute(this)) {
this._flags &= ~RUNNING;
return true;
}
const prevContext = evalContext;
try {
prepareSources(this);
evalContext = this;
const value = this._fn();
if (this._flags & HAS_ERROR || this._value !== value || this._version === 0) {
this._value = value;
this._flags &= ~HAS_ERROR;
this._version++;
}
}
catch (err) {
this._value = err;
this._flags |= HAS_ERROR;
this._version++;
}
evalContext = prevContext;
cleanupSources(this);
this._flags &= ~RUNNING;
return true;
};
Computed.prototype._subscribe = function (node) {
if (this._targets === undefined) {
this._flags |= OUTDATED | TRACKING;
for (let node = this._sources; node !== undefined; node = node._nextSource) {
node._source._subscribe(node);
}
}
Signal.prototype._subscribe.call(this, node);
};
Computed.prototype._unsubscribe = function (node) {
if (this._targets !== undefined) {
Signal.prototype._unsubscribe.call(this, node);
if (this._targets === undefined) {
this._flags &= ~TRACKING;
for (let node = this._sources; node !== undefined; node = node._nextSource) {
node._source._unsubscribe(node);
}
}
}
};
Computed.prototype._notify = function () {
if (!(this._flags & NOTIFIED)) {
this._flags |= OUTDATED | NOTIFIED;
for (let node = this._targets; node !== undefined; node = node._nextTarget) {
node._target._notify();
}
}
};
Object.defineProperty(Computed.prototype, 'value', {
get() {
if (this._flags & RUNNING) {
throw new Error('Cycle detected');
}
const node = addDependency(this);
this._refresh();
if (node !== undefined) {
node._version = this._version;
}
if (this._flags & HAS_ERROR) {
throw this._value;
}
return this._value;
},
});
function computed(fn, options) {
return new Computed(fn, options);
}
// Effect
function cleanupEffect(effect) {
const cleanup = effect._cleanup;
effect._cleanup = undefined;
if (typeof cleanup === 'function') {
/*@__INLINE__**/ startBatch();
const prevContext = evalContext;
evalContext = undefined;
try {
cleanup();
}
catch (err) {
effect._flags &= ~RUNNING;
effect._flags |= DISPOSED;
disposeEffect(effect);
throw err;
}
finally {
evalContext = prevContext;
endBatch();
}
}
}
function disposeEffect(effect) {
for (let node = effect._sources; node !== undefined; node = node._nextSource) {
node._source._unsubscribe(node);
}
effect._fn = undefined;
effect._sources = undefined;
cleanupEffect(effect);
}
function endEffect(prevContext) {
if (evalContext !== this) {
throw new Error('Out-of-order effect');
}
cleanupSources(this);
evalContext = prevContext;
this._flags &= ~RUNNING;
if (this._flags & DISPOSED) {
disposeEffect(this);
}
endBatch();
}
/** @internal */
function Effect(fn, options) {
this._fn = fn;
this._cleanup = undefined;
this._sources = undefined;
this._nextBatchedEffect = undefined;
this._flags = TRACKING;
this.name = options?.name;
}
Effect.prototype._callback = function () {
const finish = this._start();
try {
if (this._flags & DISPOSED)
return;
if (this._fn === undefined)
return;
const cleanup = this._fn();
if (typeof cleanup === 'function') {
this._cleanup = cleanup;
}
}
finally {
finish();
}
};
Effect.prototype._start = function () {
if (this._flags & RUNNING) {
throw new Error('Cycle detected');
}
this._flags |= RUNNING;
this._flags &= ~DISPOSED;
cleanupEffect(this);
prepareSources(this);
/*@__INLINE__**/ startBatch();
const prevContext = evalContext;
evalContext = this;
return endEffect.bind(this, prevContext);
};
Effect.prototype._notify = function () {
if (!(this._flags & NOTIFIED)) {
this._flags |= NOTIFIED;
this._nextBatchedEffect = batchedEffect;
batchedEffect = this;
}
};
Effect.prototype._dispose = function () {
this._flags |= DISPOSED;
if (!(this._flags & RUNNING)) {
disposeEffect(this);
}
};
Effect.prototype.dispose = function () {
this._dispose();
};
function effect(fn, options) {
const effect = new Effect(fn, options);
try {
effect._callback();
}
catch (err) {
effect._dispose();
throw err;
}
const dispose = effect._dispose.bind(effect);
// @ts-ignore
dispose[Symbol.dispose] = dispose;
return dispose;
}
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
const SVG_TAGS = 'svg,animate,circle,clippath,cursor,image,defs,desc,ellipse,filter,font-face,' +
'foreignobject,g,glyph,line,marker,mask,missing-glyph,path,pattern,' +
'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view,' +
'feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feFlood,feGaussianBlur,' +
'feImage,feMerge,feMorphology,feOffset,feSpecularLighting,feTile,feTurbulence,feDistantLight,fePointLight,feSpotLight,' +
'linearGradient,stop,radialGradient,' +
'animateTransform,animateMotion';
function makeMap(str) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return function (val) {
return map[val];
};
}
const isSVG = /*#__PURE__*/ makeMap(SVG_TAGS);
const namespaceMap = {
svg: 'http://www.w3.org/2000/svg',
math: 'http://www.w3.org/1998/Math/MathML',
};
function getTagNamespace(tag) {
if (isSVG(tag)) {
return 'svg';
}
if (tag === 'math') {
return 'math';
}
return undefined;
}
function createElementNS(namespace, tagName) {
return document.createElementNS(namespaceMap[namespace], tagName);
}
const hasOwnProperty$1 = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty$1.call(val, key);
const isObject = (val) => val !== null && typeof val === 'object';
const isUndef = (v) => v === undefined || v === null;
const checkSameVnode = (o, n) => o.tag === n.tag && o.key === n.key;
const notTagComponent = (oNode, nNode) => typeof oNode.tag !== 'function' && typeof nNode.tag !== 'function';
const isVnode = (vnode) => vnode != null && (typeof vnode === 'object' ? 'tag' in vnode : false);
const isTextChildren = (children) => !isVnode(children) && !Array.isArray(children);
function warn(msg) {
console.warn(`[Mettle.js warn]: ${msg}`);
}
function setStyleProp(el, prototype) {
Object.assign(el.style, prototype);
}
function addEventListener(el, name, listener) {
if (typeof listener !== 'function')
return;
const eventName = name.slice(2).toLowerCase();
el.addEventListener(eventName, listener);
}
function removeEventListener(el, name, listener) {
if (typeof listener !== 'function')
return;
const eventName = name.slice(2).toLowerCase();
el.removeEventListener(eventName, listener);
}
const XLINK_NS = 'http://www.w3.org/1999/xlink';
const BOOLEAN_ATTRS = new Set([
'checked',
'disabled',
'readonly',
'selected',
'multiple',
'hidden',
]);
function setAttribute(el, key, value) {
if (BOOLEAN_ATTRS.has(key)) {
value ? el.setAttribute(key, '') : el.removeAttribute(key);
return;
}
if (key.startsWith('xlink:')) {
el.setAttributeNS(XLINK_NS, key, value);
return;
}
el.setAttribute(key, value);
}
function removeAttribute(el, key) {
if (key.startsWith('xlink:')) {
el.removeAttributeNS(XLINK_NS, key);
return;
}
el.removeAttribute(key);
}
const CREATE_ELEMENT = document.createElement.bind(document);
const CREATE_FRAGMENT = document.createDocumentFragment.bind(document);
const CREATE_COMMENT = document.createComment.bind(document);
function createNode(tag) {
if (tag === 'fragment')
return CREATE_FRAGMENT();
if (tag === 'comment' || tag === 'null')
return CREATE_COMMENT('');
if (isSVG(tag))
return createElementNS(getTagNamespace(tag), tag);
return CREATE_ELEMENT(tag);
}
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
function getSequence(arr) {
const p = arr.slice();
const result = [0];
let i, j, u, v, c;
const len = arr.length;
for (i = 0; i < len; i++) {
const arrI = arr[i];
if (arrI !== 0) {
j = result[result.length - 1];
if (arr[j] < arrI) {
p[i] = j;
result.push(i);
continue;
}
u = 0;
v = result.length - 1;
while (u < v) {
c = ((u + v) / 2) | 0;
if (arr[result[c]] < arrI) {
u = c + 1;
}
else {
v = c;
}
}
if (arrI < arr[result[u]]) {
if (u > 0) {
p[i] = result[u - 1];
}
result[u] = i;
}
}
}
u = result.length;
v = result[u - 1];
while (u-- > 0) {
result[u] = v;
v = p[v];
}
return result;
}
// Create el for the child elements of the element marked with $memo
function memoCreateEl(oNode, nNode) {
const oldChildren = oNode.children;
const newChildren = nNode.children;
const childrenArrType = Array.isArray(newChildren);
if (childrenArrType) {
for (let index = 0; index < newChildren.length; index++) {
const newChild = newChildren[index];
const oldChild = oldChildren[index];
if (isVnode(newChild)) {
newChild.el = oldChild.el;
memoCreateEl(oldChild, newChild);
}
}
}
else if (isObject(newChildren)) {
newChildren.el = oldChildren.el;
}
}
// version
const version = '1.7.6';
// Flag
const isFlag = /* @__PURE__ */ makeMap('$ref,$once,$memo');
// Component
let componentMap = new WeakMap();
// DomInfo
const domInfo = new WeakMap();
// Memo
let memoMap = new WeakMap();
// Update text node
function updateTextNode(val, el) {
el.textContent = val;
}
// Convert virtual dom to real dom
function mount(vnode, container, anchor) {
const { tag, props, children } = vnode;
if (isUndef(tag))
return;
// tag
if (typeof tag === 'string') {
const el = createNode(tag);
vnode.el = el;
// props
if (!isUndef(props)) {
const keys = Object.keys(props);
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
const propValue = props[key];
const propTypeObj = isObject(propValue);
if (key.startsWith('on')) {
addEventListener(el, key, propValue);
}
if (typeof propValue !== 'function' && key !== 'key' && !isFlag(key)) {
setAttribute(el, key, propValue);
}
if (key === 'style' && propTypeObj) {
setStyleProp(el, propValue);
}
// domInfo
if (key === '$ref' && propTypeObj) {
domInfo.set(propValue, el);
}
}
}
// children
if (!isUndef(children)) {
if (isTextChildren(children)) {
if (el) {
updateTextNode(children, el);
}
}
else {
const childrenObjType = isObject(children);
if (Array.isArray(children)) {
for (let index = 0; index < children.length; index++) {
const child = children[index];
if (isVnode(child)) {
mount(child, el);
}
}
}
else if (childrenObjType) {
mount(children, el);
}
}
}
if (anchor) {
container.insertBefore(el, anchor);
}
else if (container) {
container.appendChild(el);
}
else {
return el;
}
}
else if (typeof tag === 'function') {
const template = tag.call(tag, props, tag, memo.bind(tag));
const newTree = effectFn(template, tag);
componentMap.set(tag, newTree);
mount(newTree, container);
}
}
// Diff
function patch(oNode, nNode, memoFlag) {
const oldProps = oNode.props || {};
// $once
if (hasOwn(oldProps, '$once')) {
return;
}
if (!notTagComponent(oNode, nNode)) {
return;
}
if (!checkSameVnode(oNode, nNode)) {
const parent = oNode.el.parentNode;
const anchor = oNode.el.nextSibling;
parent.removeChild(oNode.el);
mount(nNode, parent, anchor);
}
else {
const el = (nNode.el = oNode.el);
// props
const oldProps = oNode.props || {};
const newProps = nNode.props || {};
const allKeys = {};
const newKeys = Object.keys(newProps);
const oldKeys = Object.keys(oldProps);
const newKeySet = new Set(newKeys);
for (let i = 0; i < newKeys.length; i++) {
allKeys[newKeys[i]] = true;
}
for (let i = 0; i < oldKeys.length; i++) {
allKeys[oldKeys[i]] = true;
}
const keys = Object.keys(allKeys);
for (let index = 0; index < keys.length; index++) {
const key = keys[index];
const newValue = newProps[key];
const oldValue = oldProps[key];
if (newValue === oldValue)
continue;
if (isUndef(newValue)) {
removeAttribute(el, key);
continue;
}
const newObjType = isObject(newValue);
const isFunc = typeof newValue === 'function';
const isStyle = key === 'style';
if (isFunc) {
if (newValue.toString() !== oldValue.toString()) {
removeEventListener(el, key, oldValue);
addEventListener(el, key, newValue);
}
continue;
}
if (isStyle && newObjType) {
setStyleProp(el, newValue);
continue;
}
const isRegularAttr = key !== 'key' && !isFlag(key);
if (isRegularAttr && !isFunc) {
setAttribute(el, key, newValue);
}
}
for (let index = 0; index < oldKeys.length; index++) {
const key = oldKeys[index];
if (!newKeySet.has(key)) {
removeAttribute(el, key);
}
}
// $memo
if (hasOwn(oldProps, '$memo')) {
const memo = oldProps.$memo;
if (memoFlag === memo[1] && !memo[0]) {
memo[2] && memoCreateEl(oNode, nNode);
return;
}
}
// children
const oc = oNode.children;
const nc = nNode.children;
if (Array.isArray(oc) && Array.isArray(nc)) {
patchKeyChildren(oc, nc, el, memoFlag);
}
else if (isVnode(oc) && isVnode(nc)) {
patch(oc, nc, memoFlag);
}
else if (isTextChildren(oc) && isTextChildren(nc) && oc !== nc) {
updateTextNode(nc, el);
}
}
}
// can be all-keyed or mixed
function patchKeyChildren(n1, n2, parentElm, memoFlag) {
const l2 = n2.length;
let i = 0;
let e1 = n1.length - 1;
let e2 = l2 - 1;
while (i <= e1 && i <= e2) {
if (checkSameVnode(n1[i], n2[i])) {
patch(n1[i], n2[i], memoFlag);
}
else {
break;
}
i++;
}
while (i <= e1 && i <= e2) {
if (checkSameVnode(n1[e1], n2[e2])) {
patch(n1[e1], n2[e2], memoFlag);
}
else {
break;
}
e1--;
e2--;
}
if (i > e1) {
if (i <= e2) {
const nextPos = e2 + 1;
const anchor = nextPos < l2 ? n2[nextPos].el : null;
while (i <= e2) {
parentElm.insertBefore(mount(n2[i]), anchor);
i++;
}
}
}
else if (i > e2) {
while (i <= e1) {
parentElm.removeChild(n1[i].el);
i++;
}
}
else {
const s1 = i;
const s2 = i;
const keyToNewIndexMap = new Map();
for (i = s2; i <= e2; i++) {
const nextChild = n2[i];
if (nextChild.key != null) {
keyToNewIndexMap.set(nextChild.key, i);
}
}
let j;
let patched = 0;
const toBePatched = e2 - s2 + 1;
let moved = false;
let maxIndexSoFar = 0;
const newIndexToOldIndexMap = new Array(toBePatched);
for (i = 0; i < toBePatched; i++)
newIndexToOldIndexMap[i] = 0;
for (let i = s1; i <= e1; i++) {
if (patched >= toBePatched) {
parentElm.removeChild(n1[i].el);
continue;
}
let newIndex;
if (n1[i].key !== null) {
newIndex = keyToNewIndexMap.get(n1[i].key);
}
else {
for (j = s2; j <= e2; j++) {
if (newIndexToOldIndexMap[j - s2] === 0 && checkSameVnode(n1[i], n2[j])) {
newIndex = j;
break;
}
}
}
if (newIndex === undefined) {
parentElm.removeChild(n1[i].el);
}
else {
newIndexToOldIndexMap[newIndex - s2] = i + 1;
if (newIndex > maxIndexSoFar) {
maxIndexSoFar = newIndex;
}
else {
moved = true;
}
patch(n1[i], n2[newIndex], memoFlag);
patched++;
}
}
const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : [];
j = increasingNewIndexSequence.length - 1;
for (let i = toBePatched - 1; i >= 0; i--) {
const nextIndex = i + s2;
const anchor = nextIndex + 1 < l2 ? n2[nextIndex + 1].el : null;
if (newIndexToOldIndexMap[i] === 0) {
parentElm.insertBefore(mount(n2[nextIndex]), anchor);
}
else if (moved) {
if (j < 0 || i !== increasingNewIndexSequence[j]) {
parentElm.insertBefore(n2[nextIndex].el, anchor);
}
else {
j--;
}
}
}
}
}
// Change data
function setData(target, newTree, memoFlag) {
try {
const oldTree = componentMap.get(target);
patch(oldTree, newTree, memoFlag);
componentMap.set(target, newTree);
}
catch (err) {
warn(err);
}
}
// Memo
function memo(fn, name) {
memoMap.set(this, name);
if (typeof fn === 'function') {
fn();
}
}
// Effect
function effectFn(template, target) {
let initMode = true;
effect(() => {
target.template = template;
const newTree = template();
if (initMode) {
initMode = null;
}
else {
const memoFlag = memoMap.get(target);
setData(target, newTree, memoFlag);
if (memoMap.has(target)) {
memoMap = new WeakMap();
}
}
});
return template();
}
// Normalize Container
function normalizeContainer(container) {
if (typeof container === 'string') {
const res = document.querySelector(container);
if (!res) {
let elem = null;
if (container.startsWith('#')) {
elem = document.createElement('div');
elem.setAttribute('id', container.substring(1, container.length));
}
else if (container.startsWith('.')) {
elem = document.createElement('div');
elem.setAttribute('class', container.substring(1, container.length));
}
else {
warn(`Failed to mount app: mount target selector "${container}" returned null.`);
}
document.body.insertAdjacentElement('afterbegin', elem);
return elem;
}
return res;
}
else if (container instanceof HTMLElement) {
return container;
}
else if (window.ShadowRoot &&
container instanceof window.ShadowRoot &&
container.mode === 'closed') {
warn('mounting on a ShadowRoot with `{mode: "closed"}` may lead to unpredictable bugs.');
return null;
}
else {
return null;
}
}
let _el = Object.create(null);
// Create Mettle application
function createApp(root, container) {
const rootContent = root.tag;
const template = rootContent.call(rootContent, root.props, rootContent, memo.bind(rootContent));
const newTree = effectFn(template, rootContent);
const mountNodeEl = normalizeContainer(container);
mount(newTree, mountNodeEl);
componentMap.set(rootContent, newTree);
_el = mountNodeEl;
bindMounted();
}
// onMounted
let mountHook = [];
let unMountedHookCount = 0;
let oldunMountedHookCount = 0;
function onMounted(fn = null) {
if (fn === null)
return;
if (typeof fn !== 'function') {
warn('The parameter of onMounted is not a function!');
return;
}
mountHook.push(fn);
}
function bindMounted() {
if (mountHook.length > 0) {
for (let i = 0, j = mountHook.length; i < j; i++) {
mountHook[i] && mountHook[i]();
}
}
oldunMountedHookCount = unMountedHookCount;
unMountedHookCount = 0;
mountHook = [];
}
// onUnmounted
let unMountedHook = [];
function onUnmounted(fn = null) {
if (fn === null)
return;
if (typeof fn !== 'function') {
warn('The parameter of onUnmounted is not a function!');
return;
}
unMountedHookCount += 1;
unMountedHook.push(fn);
}
function bindUnmounted() {
if (unMountedHook.length > 0) {
for (let i = 0; i < oldunMountedHookCount; i++) {
unMountedHook[i] && unMountedHook[i]();
}
unMountedHook.splice(0, oldunMountedHookCount);
}
oldunMountedHookCount = unMountedHookCount;
}
// Reset view
function resetView(view, routerContainer) {
bindUnmounted();
const routerContainerEl = routerContainer ? normalizeContainer(routerContainer) : _el;
routerContainerEl.innerHTML = '';
const template = view.call(view, view, memo.bind(view));
const newTree = effectFn(template, view);
mount(newTree, routerContainerEl);
componentMap.set(view, newTree);
bindMounted();
}
var NOTHING = Symbol.for('immer-nothing');
var DRAFTABLE = Symbol.for('immer-draftable');
var DRAFT_STATE = Symbol.for('immer-state');
function die(error, ...args) {
throw new Error(`Error`);
}
var getPrototypeOf = Object.getPrototypeOf;
function isDraft(value) {
return !!value && !!value[DRAFT_STATE];
}
function isDraftable(value) {
if (!value)
return false;
return (isPlainObject(value) ||
Array.isArray(value) ||
!!value[DRAFTABLE] ||
!!value.constructor?.[DRAFTABLE] ||
isMap(value) ||
isSet(value));
}
var objectCtorString = Object.prototype.constructor.toString();
function isPlainObject(value) {
if (!value || typeof value !== 'object')
return false;
const proto = getPrototypeOf(value);
if (proto === null) {
return true;
}
const Ctor = Object.hasOwnProperty.call(proto, 'constructor') && proto.constructor;
if (Ctor === Object)
return true;
return typeof Ctor == 'function' && Function.toString.call(Ctor) === objectCtorString;
}
function each(obj, iter) {
if (getArchtype(obj) === 0 /* Object */) {
Reflect.ownKeys(obj).forEach((key) => {
iter(key, obj[key], obj);
});
}
else {
obj.forEach((entry, index) => iter(index, entry, obj));
}
}
function getArchtype(thing) {
const state = thing[DRAFT_STATE];
return state
? state.type_
: Array.isArray(thing)
? 1 /* Array */
: isMap(thing)
? 2 /* Map */
: isSet(thing)
? 3 /* Set */
: 0 /* Object */;
}
function has(thing, prop) {
return getArchtype(thing) === 2 /* Map */
? thing.has(prop)
: Object.prototype.hasOwnProperty.call(thing, prop);
}
function set(thing, propOrOldValue, value) {
const t = getArchtype(thing);
if (t === 2 /* Map */)
thing.set(propOrOldValue, value);
else if (t === 3 /* Set */) {
thing.add(value);
}
else
thing[propOrOldValue] = value;
}
function is(x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
}
else {
return x !== x && y !== y;
}
}
function isMap(target) {
return target instanceof Map;
}
function isSet(target) {
return target instanceof Set;
}
function latest(state) {
return state.copy_ || state.base_;
}
function shallowCopy(base, strict) {
if (isMap(base)) {
return new Map(base);
}
if (isSet(base)) {
return new Set(base);
}
if (Array.isArray(base))
return Array.prototype.slice.call(base);
const isPlain = isPlainObject(base);
if (strict === true || (strict === 'class_only' && !isPlain)) {
const descriptors = Object.getOwnPropertyDescriptors(base);
delete descriptors[DRAFT_STATE];
let keys = Reflect.ownKeys(descriptors);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const desc = descriptors[key];
if (desc.writable === false) {
desc.writable = true;
desc.configurable = true;
}
if (desc.get || desc.set)
descriptors[key] = {
configurable: true,
writable: true,
enumerable: desc.enumerable,
value: base[key],
};
}
return Object.create(getPrototypeOf(base), descriptors);
}
else {
const proto = getPrototypeOf(base);
if (proto !== null && isPlain) {
return { ...base };
}
const obj = Object.create(proto);
return Object.assign(obj, base);
}
}
function freeze(obj, deep = false) {
if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj))
return obj;
if (getArchtype(obj) > 1) {
obj.set = obj.add = obj.clear = obj.delete = dontMutateFrozenCollections;
}
Object.freeze(obj);
if (deep)
Object.entries(obj).forEach(([key, value]) => freeze(value, true));
return obj;
}
function dontMutateFrozenCollections() {
die();
}
function isFrozen(obj) {
return Object.isFrozen(obj);
}
var plugins = {};
function getPlugin(pluginKey) {
const plugin = plugins[pluginKey];
if (!plugin) {
die(0, pluginKey);
}
return plugin;
}
var currentScope;
function getCurrentScope() {
return currentScope;
}
function createScope(parent_, immer_) {
return {
drafts_: [],
parent_,
immer_,
canAutoFreeze_: true,
unfinalizedDrafts_: 0,
};
}
function usePatchesInScope(scope, patchListener) {
if (patchListener) {
getPlugin('Patches');
scope.patches_ = [];
scope.inversePatc