Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
package.dist.module.mikado.js Maven / Gradle / Ivy
Go to download
Web's fastest template library to build user interfaces.
/**!
* Mikado.js
* Copyright 2019-2024 Nextapps GmbH
* Author: Thomas Wilkerling
* Licence: Apache-2.0
* https://github.com/nextapps-de/mikado
*/
import { TemplateDOM, Template, MikadoOptions, MikadoCallbacks, NodeCache, ProxyCache } from "./type.js";
import Observer from "./array.js";
import { create_path, construct } from "./factory.js";
import proxy_create from "./proxy.js";
/** @const {Object>} */
export const includes = Object.create(null);
/**
* Abbrevations:
* _mkd: _dom
* _mkl: _live
* _mki: _instance
*/
/**
* NOTE: Using prototype enables conditional instance member functions.
* @param {!string|Template} template
* @param {MikadoOptions=} options
* @constructor
*/
export default function Mikado(template, options = /** @type MikadoOptions */{}) {
if (!(this instanceof Mikado)) {
return new Mikado(template, options);
}
if ("string" == typeof template) {
const tpl = includes[template];
if (tpl instanceof Mikado) {
return tpl;
}
template = /** @type Template */tpl[0];
options || (options = /** @type MikadoOptions */tpl[1]);
}
/** @type {Array} */
this.dom = [];
/** @type {number} */
this.length = 0;
/** @type {?Element} */
this.root = options.root || options.mount || null;
/** @const {boolean} */
this.recycle = !!options.recycle;
/** @type {*} */
this.state = options.state || {};
/** @type {boolean} */
this.shadow = options.shadow || !!template.cmp;
/** @const {string} */
this.key = template.key || "";
/**
* @private
* @dict {Object}
*/
this.live = {};
/** @type {Array} */
const fn = template.fn;
// make a copy to make this template re-usable when consumed
// this should just have been copied when it is a root template!
template.fc || fn && (template.fc = fn.slice());
/**
* The compiler unshift includes, so we can use faster arr.pop() here, because it needs reverse direction.
* Let consume the nested template functions by using .pop() is pretty much simpler than using an index.
* @private {Function}
*/
this.apply = fn && fn.pop();
/** @type {Template|null} */
this.tpl = template;
/** @const {string} */
this.name = template.name;
/*
* Includes are filled with Mikado instances when factory is constructed
* @const {Array}
*/
this.inc = [];
const cacheable = this.recycle || !!this.key;
// Pool sizes are automatically being handled,
// non-keyed pools does not need boundary,
// keyed pools start at size 1 and increase to max items per view
/** @type {number} */
this.pool = cacheable && options.pool ? 1 : 0;
/** @private {Array} */
this.pool_shared = [];
/** @private */
this.pool_keyed = new Map();
/** @const {boolean} */
this.cache = cacheable && (template.cache || !!options.cache);
/** @type {boolean} */
this.async = !!options.async;
/** @private {number} */
this.timer = 0;
/** @private {MikadoCallbacks|null} */
this.on = options.on || null;
{
/**
* @type {Object>}
*/
this.proxy = null;
/** @type {number} */
this.fullproxy = 0;
/** @type {Observer|undefined} */
const store = options.observe;
if (store) {
new Observer(store).mount(this);
}
}
if (this.root) {
this.mount(this.root, options.hydrate);
} else {
/** @private */
this.factory = null;
}
}
/**
* This function is also automatically called when loading es5 templates.
* @param {string|Template} tpl
* @param {MikadoOptions=} options
*/
export function register(tpl, options) {
let name, re_assign;
if ("string" == typeof tpl) {
name = re_assign = tpl;
tpl = /** @type {string|Template} */includes[name];
tpl instanceof Mikado || (tpl = tpl[0]);
} else {
name = tpl.name;
}
// Just collect template definitions. The instantiation starts when .mount() is
// internally called for the first time.
includes[name] = [
/** @type {Template} */tpl,
/** @type {MikadoOptions} */options];
return Mikado;
}
/**
* @param {string|Template} name
*/
export function unregister(name) {
if ("object" == typeof name) {
name = /** @type {string} */name.name;
}
const mikado = includes[name];
if (mikado) {
mikado instanceof Mikado && mikado.destroy();
includes[name] = null;
}
return Mikado;
}
/*
Example: Swap Mount
A[DOM] B[DOM]
A[TPL] A[TPL]
A[DOM] A[DOM]
A[TPL] B[TPL]
*/
/**
* @param {Element} target
* @param {boolean=} hydrate
* @returns {Mikado}
* @const
*/
Mikado.prototype.mount = function (target, hydrate) {
// cancel async render task if scheduled
this.timer && this.cancel();
if (this.shadow) {
// Actually the MIKADO_CLASS will append to the templates root element,
// but it would be better to have it on the mounting element
// TODO improve getting the root withing components by assigning MIKADO_ROOT to the mounting element
const cmp = /** @type {Array} */this.tpl.cmp;
// also when cmp: [] has no definitions at top level scope
target = target.shadowRoot || target.attachShadow({ mode: "open" });
if (cmp && cmp.length) {
// the root is always the last element
const tmp = target.lastElementChild;
if (tmp /*&& tmp.tagName === "ROOT"*/) {
target = tmp;
} else {
// push root as the last element
cmp.push({ tag: "root" });
/** @type {TemplateDOM} */
for (let i = 0, node; i < cmp.length; i++) {
node = construct(this, /** @type {TemplateDOM} */cmp[i], [], "");
target.append(node);
if (i === cmp.length - 1) {
// the root element is the last one
target = /** @type {Element} */node;
}
}
}
}
}
const target_instance = target._mki,
root_changed = this.root !== target;
if (target_instance === this) {
// same template, same root
if (!root_changed) return this;
// same template, different root
this.dom = target._mkd;
this.length = this.dom.length;
} else if (target_instance) {
target_instance.clear();
// different template
target._mkd = this.dom = [];
this.length = 0;
if (target.firstChild) {
target.textContent = "";
}
const callback = this.on && this.on.unmount;
callback && callback(target, target_instance);
} else {
// initial mount
if (hydrate) {
this.dom = collection_to_array(target.children);
this.length = this.dom.length;
} else {
this.dom = [];
this.length = 0;
if (target.firstChild) {
target.textContent = "";
}
}
target._mkd = this.dom;
}
if (this.key) {
// handle live pool
if (root_changed && this.root) {
this.root._mkl = this.live;
}
if (target_instance === this) {
this.live = target._mkl;
} else {
const live = {};
if (!target_instance && hydrate && this.length) {
for (let i = 0, node, key; i < this.length; i++) {
node = this.dom[i];
// server-side rendering needs to export the key as attribute
key = node.getAttribute("key");
node._mkk = key;
live[key] = node;
}
}
target._mkl = this.live = live;
}
}
target._mki = this;
this.root = target;
if (!this.factory) {
if (hydrate && this.length) {
/** @private */
this.factory = this.dom[0].cloneNode(!0);
construct(this, /** @type {TemplateDOM} */this.tpl.tpl, [], "", this.factory) && finishFactory(this);
}
// also when falls back on hydration if structure was incompatible:
if (this.tpl) {
/** @private */
this.factory = construct(this, /** @type {TemplateDOM} */this.tpl.tpl, [], "");
finishFactory(this);
}
}
const callback = this.on && this.on.mount;
callback && callback(target, this);
return this;
};
/**
* @param {Mikado} self
*/
function finishFactory(self) {
if (self.tpl.fc) {
// self.tpl.fn could have further template functions
self.tpl.fn = self.tpl.fc;
self.tpl.fc = null;
}
self.tpl = null;
}
/**
* @param {NodeList} collection
* @return {Array}
*/
function collection_to_array(collection) {
const length = collection.length,
array = Array(length);
for (let i = 0; i < length; i++) {
array[i] = collection[i];
}
return array;
}
/**
* @param {Element|ShadowRoot} root
* @param {Template} template
* @param {Array|Object|Function|boolean=} data
* @param {*|Function|boolean=} state
* @param {Function|boolean=} callback
* @const
*/
export function once(root, template, data, state, callback) {
let mikado;
if ("function" == typeof data || !0 === data) {
callback = data;
data = null;
} else if ("function" == typeof state || !0 === state) {
callback = state;
state = null;
}
if (callback) {
return new Promise(function (resolve) {
requestAnimationFrame(function () {
once(root, template, data, state);
if ("function" == typeof callback) callback();
resolve();
});
});
}
const is_shadow = template.cmp,
is_component = is_shadow && is_shadow.length;
if (is_shadow && !is_component) {
// switch to shadow root
root = root.shadowRoot || root.attachShadow({ mode: "open" });
}
if (!data && !is_component && !template.fn) {
// static non-looped templates
// uses the low-level template factory constructor
const node = construct(
/** @type {!Mikado} */{},
/** @type {TemplateDOM} */template.tpl, [], "", null, 1);
root.append(node);
} else {
mikado = new Mikado(template);
if (is_component) {
// full declared web components needs to be mounted
root = mikado.mount(root).root;
}
if (data && Array.isArray(data)) {
// looped templates
for (let i = 0; i < data.length; i++) {
root.append(mikado.create(data[i], state, i));
}
} else {
// dynamic non-looped templates + web components
root.append(mikado.create(data, state));
}
mikado.destroy();
}
return Mikado;
}
/**
* @param {!*} data
* @param {*=} state
* @param {Function|boolean=} callback
* @param {boolean|number=} _skip_async
* @returns {Mikado|Promise}
* @const
*/
Mikado.prototype.render = function (data, state, callback, _skip_async) {
if (!_skip_async) {
let has_fn;
if (state && (has_fn = "function" == typeof state) || !0 === state) {
callback = /** @type {Function|boolean} */state;
state = null;
}
if (this.timer) {
this.cancel();
}
if (this.async || callback) {
const self = this;
has_fn || (has_fn = "function" == typeof callback);
self.timer = requestAnimationFrame(function () {
self.timer = 0;
self.render(data, state, null, 1);
/*has_fn &&*/ /** @type {Function} */callback();
});
return has_fn ? this : new Promise(function (resolve) {
callback = resolve;
});
}
}
//profiler_start("render");
let length = this.length;
// a template could have just expressions without accessing data
if (!data) {
if (!this.apply) {
this.dom[0] || this.add();
return this;
}
}
let count;
if (Array.isArray(data) || data instanceof Observer) {
count = data.length;
if (!count) {
return this.remove(0, length);
}
} else {
data = [data];
count = 1;
}
const key = this.key,
proxy = this.proxy;
if (length && !key && !this.recycle) {
this.remove(0, length);
length = 0;
}
let min = length < count ? length : count,
x = 0;
// update
if (x < min) {
for (let node, item; x < min; x++) {
node = this.dom[x];
item = data[x];
if (key && node._mkk !== item[key]) {
return this.reconcile( /** @type {Array} */data, state, x);
} else {
this.update(node, item, state, x);
}
if (proxy && /* (!key && !this.recycle) || */!item._mkx) {
data[x] = apply_proxy(this, node, item);
}
}
}
// add
if (x < count) {
// when recycle is disabled the proxy needs to be updated
const recycle = key || this.recycle;
for (; x < count; x++) {
const item = data[x];
this.add(item, state /*, x*/);
if (proxy && (!recycle || !item._mkx)) {
data[x] = apply_proxy(this, this.dom[x], item);
}
}
}
// remove
else if (count < length) {
this.remove(count, length - count);
}
//profiler_end("render");
return this;
};
/**
* @param {!Element|number} node
* @param {*=} data
* @param {*=} state
* @param {number=} index
* @const
*/
Mikado.prototype.replace = function (node, data, state, index) {
//profiler_start("replace");
if ("undefined" == typeof index) {
if ("number" == typeof node) {
index = 0 > node ? this.length + node : node;
node = this.dom[index];
} else {
index = this.index(node);
}
}
node = /** @type {!Element} */node;
let tmp, update;
// The main difference of replace() and update() is that replace() will also handle the keyed live pool.
if (this.key) {
const key = data[this.key];
if (tmp = this.live[key]) {
if (tmp !== node) {
const index_old = this.index(tmp),
node_a = index_old < index ? tmp : node,
node_b = index_old < index ? node : tmp;
let next = this.dom[index_old < index ? index_old + 1 : index + 1];
this.dom[index] = tmp;
this.dom[index_old] = node;
// if(next === node_b){
//
// this.root.insertBefore(node_b, node_a);
// }
// else{
if (next !== node_b) {
this.root.insertBefore(node_a, node_b);
} else {
next = node_a;
}
this.root.insertBefore(node_b, next);
//}
}
} else if (this.pool && (tmp = this.pool_keyed.get(key))) {
this.pool_keyed.delete(key);
this.checkout(node);
this.dom[index] = tmp;
node.replaceWith(tmp);
}
update = tmp;
} else if (this.recycle) {
update = node;
}
if (update) {
this.apply && (this.fullproxy && data._mkx || this.apply(data, state || this.state, index, update._mkp || create_path(update, this.factory._mkp, this.cache)));
} else {
const clone = this.create(data, state, index, 1);
if (this.key || this.pool) {
this.checkout(node);
}
this.dom[index] = clone;
node.replaceWith(clone);
}
const callback = this.on && this.on.replace;
callback && callback(node, this);
//profiler_end("replace");
return this;
};
/**
* @param {Element|number} node
* @param {*=} data
* @param {*=} state
* @param {number=} index
* @const
*/
Mikado.prototype.update = function (node, data, state, index) {
//profiler_start("update");
if (!this.apply) {
return this;
}
if (this.fullproxy && data._mkx) {
return this;
}
if ("undefined" == typeof index) {
if ("number" == typeof node) {
index = 0 > node ? this.length + node - 1 : node;
node = this.dom[index];
} else {
index = this.index(node);
}
}
node = /** @type {Element} */node;
// Is keyed handling also needed in update?
// .replace() = .update() + keyed handling
/*
if(!_skip_check){
let replace;
if(SUPPORT_KEYED && this.key){
const ref = node[MIKADO_TPL_KEY];
const tmp = data[this.key];
if(ref !== tmp){
if(this.recycle){
this.live[ref] = null;
this.live[tmp] = node;
node[MIKADO_TPL_KEY] = tmp;
}
else{
replace = true;
}
}
}
else if(!this.recycle){
replace = true;
}
if(replace){
return this.replace(node, data, state, index);
}
}
*/
this.apply(data, state || this.state, index, node._mkp || create_path(node, this.factory._mkp, this.cache));
//profiler_end("update");
const callback = this.on && this.on.update;
callback && callback(node, this);
return this;
};
/** @const */
Mikado.prototype.cancel = function () {
//if(this.timer){
cancelAnimationFrame(this.timer);
this.timer = 0;
//}
return this;
};
/**
* @param {*=} data
* @param {*=} state
* @param {number=} index
* @param {boolean|number=} _update_pool
* @return {!Element}
* @const
*/
Mikado.prototype.create = function (data, state, index, _update_pool) {
const keyed = this.key,
key = keyed && data[keyed];
let node, pool, factory, found;
if (this.pool) {
// 1. shared keyed pool
if (keyed) {
if ((pool = this.pool_keyed) && (node = pool.get(key))) {
pool.delete(key);
found = 1;
}
}
// 2. shared non-keyed pool
else if ((pool = this.pool_shared) && pool.length) {
node = pool.pop();
}
}
if (!node) {
node = factory = this.factory;
if (!factory) {
/** @private */
this.factory = node = factory = construct(this, /** @type {TemplateDOM} */this.tpl.tpl, [], "");
finishFactory(this);
}
}
let cache;
if (this.apply) {
const vpath = node._mkp || create_path(node, this.factory._mkp, !!factory || this.cache);
cache = factory && this.cache && /** @type {Array} */Array(vpath.length);
this.apply(data, state || this.state, index, vpath, !!factory, cache);
}
if (factory) {
node = factory.cloneNode(!0);
if (cache && !0 !== cache) {
node._mkc = cache;
}
node._mkr = 1;
}
if (keyed) {
if (!found) node._mkk = key;
if (_update_pool) this.live[key] = node;
}
const callback = this.on && this.on[factory ? "create" : "recycle"];
callback && callback(node, this);
return node;
};
/**
* @param {*=} data
* @param {*|number=} state
* @param {number|null=} index
* @returns {Mikado}
* @const
*/
Mikado.prototype.add = function (data, state, index) {
//profiler_start("add");
let has_index;
if ("number" == typeof state) {
index = 0 > state ? this.length + state : state;
state = null;
has_index = index < this.length;
} else if ("number" == typeof index) {
if (0 > index) index += this.length;
has_index = index < this.length;
} else {
index = this.length;
}
const node = this.create(data, state, index, 1);
if (has_index) {
this.root.insertBefore(node, this.dom[index]);
splice(this.dom, this.length - 1, index, node);
//this.dom.splice(length, 0, node);
this.length++;
} else {
this.root.appendChild(node);
this.dom[this.length++] = node;
}
if (this.key && !0 && this.pool) {
// adjust keyed pool size
if (this.pool < this.length) this.pool = this.length;
}
const callback = this.on && this.on.insert;
callback && callback(node, this);
//profiler_end("add");
return this;
};
/**
* @param {Mikado} self
* @param {Element} node
* @param {Object} data
* @return {Proxy}
*/
export function apply_proxy(self, node, data) {
//TODO inject the full path on first recycle
return proxy_create(data, node._mkp || create_path(node, self.factory._mkp, self.cache), self.proxy);
}
// Since there don't exist a native transaction feature for DOM changes, all changes applies incrementally.
// For a full render task there are a "dirty" intermediate state when moving one node.
// This state will resolve after running through the whole reconcile().
// Since we don't use an extra loop running upfront to calculate the diff,
// Mikado uses a smart algorithm which can find the shortest path in one loop.
// That also means, during reconcile there is no look-ahead to the data (just to the live pool).
// For this reason the keyed live pool needs to be in sync with the vdom array.
// The reconcile runs like a resizable "window function" on where unmatched things
// will move further to the end until the process cursor reach this index.
/**
* Reconcile based on "longest distance" strategy by Thomas Wilkerling
* @param {Array=} b
* @param {*=} state
* @param {number=} x
* @returns {Mikado}
* @const
*/
Mikado.prototype.reconcile = function (b, state, x) {
const a = this.dom,
live = this.live,
key = this.key;
let end_b = b.length,
end_a = a.length,
max_end = end_a > end_b ? end_a : end_b,
shift = 0;
for (x || (x = 0); x < max_end; x++) {
let found;
if (x < end_b) {
const b_x = b[x],
ended = x >= end_a;
let a_x, b_x_key, a_x_key, proxy;
if (this.proxy) {
if (b_x._mkx) {
proxy = this.fullproxy;
} else {
b[x] = apply_proxy(this, a[x], b_x);
}
}
if (!ended) {
a_x = a[x];
b_x_key = b_x[key];
a_x_key = a_x._mkk;
if (a_x_key === b_x_key) {
proxy || this.update(a_x, b_x, state, x);
continue;
}
}
if (ended || !live[b_x_key]) {
// without pool enabled .add() is better than .replace()
if (ended || !this.pool) {
end_a++;
max_end = end_a > end_b ? end_a : end_b;
this.add(b_x, state, x);
} else {
// TODO replace iteratively performs pool size adjustment
this.replace( /** @type {!Element} */a_x, b_x, state, x);
}
continue;
}
let idx_a, idx_b;
for (let y = x + 1; y < max_end; y++) {
// determine longest distance
if (!idx_a && y < end_a && a[y]._mkk === b_x_key) idx_a = y + 1;
if (!idx_b && y < end_b && b[y][key] === a_x_key) idx_b = y + 1;
if (idx_a && idx_b) {
// shift up (move target => current)
if (idx_a >= idx_b + shift) {
const tmp_a = a[idx_a - 1];
// when distance is 1 it will always move before, no predecessor check necessary
this.root.insertBefore( /** @type {Node} */tmp_a, /** @type {Node} */a_x);
proxy || this.update(tmp_a, b_x, state, x);
// fast path optimization when distance is equal (skips finding on next turn)
if (idx_a === idx_b) {
if (1 < y - x) {
this.root.insertBefore( /** @type {Node} */a_x, /** @type {Node} */a[idx_a]);
}
a[x] = a[y];
a[y] = /** @type {!Element} */a_x;
} else {
splice(a, idx_a - 1, x);
//a.splice(x, 0, a.splice(idx_a - 1, 1)[0]);
shift++;
}
}
// shift down (move current => target)
else {
const index = idx_b - 1 + shift;
// distance is always greater than 1, no predecessor check necessary
this.root.insertBefore( /** @type {Node} */a_x, /** @type {Node} */a[index] || null);
splice(a, x, (index > end_a ? end_a : index) - 1);
//a.splice(/* one is removed: */ index - 1, 0, a.splice(x, 1)[0]);
shift--;
x--;
}
found = 1;
break;
}
}
}
if (!found) {
this.remove(x);
end_a--;
max_end = end_a > end_b ? end_a : end_b;
x--;
}
}
return this;
};
/**
* @param {Array} arr
* @param {number} pos_old
* @param {number} pos_new
* @param {*=} insert
* @const
*/
function splice(arr, pos_old, pos_new, insert) {
const tmp = insert || arr[pos_old];
if (insert) {
pos_old++;
}
if (pos_old < pos_new) {
for (; pos_old < pos_new; pos_old++) {
arr[pos_old] = arr[pos_old + 1];
}
} else /*if(pos_old > pos_new)*/{
for (; pos_old > pos_new; pos_old--) {
arr[pos_old] = arr[pos_old - 1];
}
}
arr[pos_new] = tmp;
}
/**
* @param {*=} data
* @param {*=} state
* @param {number=} index
* @const
*/
Mikado.prototype.append = function (data, state, index) {
//profiler_start("append");
let has_index;
if ("number" == typeof state) {
index = 0 > state ? this.length + state : state;
state = null;
has_index = 1;
} else if ("number" == typeof index) {
if (0 > index) index += this.length;
has_index = 1;
}
const count = data.length;
for (let x = 0; x < count; x++) {
this.add(data[x], state, has_index ? index++ : null);
}
//profiler_end("append");
return this;
};
/**
* @returns {Mikado}
* @const
*/
Mikado.prototype.clear = function () {
if (this.length) {
this.remove(0, this.length);
}
return this;
};
/**
* @param {!Element|number} index
* @param {number=} count
* @returns {Mikado}
* @const
*/
Mikado.prototype.remove = function (index, count) {
//profiler_start("remove");
let length = this.length;
if (length && index) {
if ("number" != typeof index) {
index = this.index(index);
} else if (0 > index) {
index = length + index;
}
}
if (!length || index >= length) {
//profiler_end("remove");
return this;
}
if (count) {
if (0 > count) {
index -= count + 1;
if (0 > index) {
index = 0;
}
count *= -1;
}
} else {
count = 1;
}
let nodes;
if (!index && count >= length) {
nodes = this.dom;
count = nodes.length;
this.root.textContent = "";
this.root._mkd = this.dom = [];
length = 0;
} else {
nodes = this.dom.splice( /** @type {number} */index, count);
length -= count;
}
// Reverse is applied in order to use push/pop rather than shift/unshift.
// When no keyed pool is used a proper order of items will:
// 1. optimize the pagination of content (forward, backward)
// 2. optimize toggling the count of items per page (list resizing)
// 3. optimize folding of content (show more, show less)
// 4. optimize filtering state of content (filter on, filter off)
// 5. optimize initializing with the last view state (close view, open view)
const reverse = this.pool && !this.key,
checkout = this.key || this.pool,
callback = this.on && this.on.remove;
let adjust_pool = this.key && this.pool;
if (adjust_pool && count >= adjust_pool) {
this.pool_keyed = new Map();
//this.pool_keyed.clear();
adjust_pool = 0;
}
for (let x = 0, node; x < count; x++) {
node = nodes[reverse ? count - x - 1 : x];
length && node.remove();
checkout && this.checkout(node, /* skip pool resize */1);
callback && callback(node, this);
}
if (adjust_pool && 0 < (adjust_pool = this.pool_keyed.size - adjust_pool)) {
const keys = this.pool_keyed.keys();
while (adjust_pool--) {
this.pool_keyed.delete(keys.next().value);
}
}
this.length = length;
//profiler_end("remove");
return this;
};
/**
* @param {Element} node
* @return {number}
* @const
*/
Mikado.prototype.index = function (node) {
return node ? this.dom.indexOf(node) : -1;
};
/**
* @param {number} index
* @return {Element}
* @const
*/
Mikado.prototype.node = function (index) {
return this.dom[index];
};
/**
* @param {Element} node
* @param {?number=} _skip_resize
* @private
* @const
*/
Mikado.prototype.checkout = function (node, _skip_resize) {
let key;
if (this.key) {
key = node._mkk;
// remove from live-pool
this.live[key] = null;
}
if (this.pool) {
if (key) {
// always adding to keyed shared-pool increases the probability of matching keys
// but requires resizing of limited pools
this.pool_keyed.set(key, node);
if (!_skip_resize && /*this.pool !== true &&*/this.pool_keyed.size > this.pool) {
this.pool_keyed.delete( /*this.pool_keyed._keys ||*/this.pool_keyed.keys().next().value);
}
} else {
const length = this.pool_shared.length;
//if(this.pool === true || (length < this.pool)){
// add to non-keyed shared-pool
this.pool_shared[length] = node;
//}
}
}
};
Mikado.prototype.flush = function () {
this.pool_shared = [];
this.pool_keyed = new Map();
return this;
};
/**
* @const
*/
Mikado.prototype.destroy = function () {
for (let i = 0, inc; i < this.inc.length; i++) {
inc = this.inc[i];
includes[inc.name] || inc.destroy();
}
if (this.key) {
this.root && (this.root._mkl = null);
this.live = null;
}
if (this.root) {
this.root._mkd = this.root._mki = null;
}
/** @suppress {checkTypes} */this.dom = this.root = this.tpl = this.apply = this.inc = this.state = this.factory = null;
this.pool_shared = null;
this.pool_keyed = null;
this.on = null;
this.proxy = null;
};