cb0b676cd7
FossilOrigin-Name: ce5d7f4522f9b7a75ed918b58ba51473975f37d7c9e11a273f5648c481dc14d9
755 lines
17 KiB
JavaScript
755 lines
17 KiB
JavaScript
(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.redom = {}));
|
|
})(this, (function (exports) { 'use strict';
|
|
|
|
function createElement (query, ns) {
|
|
var ref = parse(query);
|
|
var tag = ref.tag;
|
|
var id = ref.id;
|
|
var className = ref.className;
|
|
var element = ns ? document.createElementNS(ns, tag) : document.createElement(tag);
|
|
|
|
if (id) {
|
|
element.id = id;
|
|
}
|
|
|
|
if (className) {
|
|
if (ns) {
|
|
element.setAttribute('class', className);
|
|
} else {
|
|
element.className = className;
|
|
}
|
|
}
|
|
|
|
return element;
|
|
}
|
|
|
|
function parse (query) {
|
|
var chunks = query.split(/([.#])/);
|
|
var className = '';
|
|
var id = '';
|
|
|
|
for (var i = 1; i < chunks.length; i += 2) {
|
|
switch (chunks[i]) {
|
|
case '.':
|
|
className += " " + (chunks[i + 1]);
|
|
break;
|
|
|
|
case '#':
|
|
id = chunks[i + 1];
|
|
}
|
|
}
|
|
|
|
return {
|
|
className: className.trim(),
|
|
tag: chunks[0] || 'div',
|
|
id: id
|
|
};
|
|
}
|
|
|
|
function unmount (parent, child) {
|
|
var parentEl = getEl(parent);
|
|
var childEl = getEl(child);
|
|
|
|
if (child === childEl && childEl.__redom_view) {
|
|
// try to look up the view if not provided
|
|
child = childEl.__redom_view;
|
|
}
|
|
|
|
if (childEl.parentNode) {
|
|
doUnmount(child, childEl, parentEl);
|
|
|
|
parentEl.removeChild(childEl);
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
function doUnmount (child, childEl, parentEl) {
|
|
var hooks = childEl.__redom_lifecycle;
|
|
|
|
if (hooksAreEmpty(hooks)) {
|
|
childEl.__redom_lifecycle = {};
|
|
return;
|
|
}
|
|
|
|
var traverse = parentEl;
|
|
|
|
if (childEl.__redom_mounted) {
|
|
trigger(childEl, 'onunmount');
|
|
}
|
|
|
|
while (traverse) {
|
|
var parentHooks = traverse.__redom_lifecycle || {};
|
|
|
|
for (var hook in hooks) {
|
|
if (parentHooks[hook]) {
|
|
parentHooks[hook] -= hooks[hook];
|
|
}
|
|
}
|
|
|
|
if (hooksAreEmpty(parentHooks)) {
|
|
traverse.__redom_lifecycle = null;
|
|
}
|
|
|
|
traverse = traverse.parentNode;
|
|
}
|
|
}
|
|
|
|
function hooksAreEmpty (hooks) {
|
|
if (hooks == null) {
|
|
return true;
|
|
}
|
|
for (var key in hooks) {
|
|
if (hooks[key]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* global Node, ShadowRoot */
|
|
|
|
var hookNames = ['onmount', 'onremount', 'onunmount'];
|
|
var shadowRootAvailable = typeof window !== 'undefined' && 'ShadowRoot' in window;
|
|
|
|
function mount (parent, child, before, replace) {
|
|
var parentEl = getEl(parent);
|
|
var childEl = getEl(child);
|
|
|
|
if (child === childEl && childEl.__redom_view) {
|
|
// try to look up the view if not provided
|
|
child = childEl.__redom_view;
|
|
}
|
|
|
|
if (child !== childEl) {
|
|
childEl.__redom_view = child;
|
|
}
|
|
|
|
var wasMounted = childEl.__redom_mounted;
|
|
var oldParent = childEl.parentNode;
|
|
|
|
if (wasMounted && (oldParent !== parentEl)) {
|
|
doUnmount(child, childEl, oldParent);
|
|
}
|
|
|
|
if (before != null) {
|
|
if (replace) {
|
|
var beforeEl = getEl(before);
|
|
|
|
if (beforeEl.__redom_mounted) {
|
|
trigger(beforeEl, 'onunmount');
|
|
}
|
|
|
|
parentEl.replaceChild(childEl, beforeEl);
|
|
} else {
|
|
parentEl.insertBefore(childEl, getEl(before));
|
|
}
|
|
} else {
|
|
parentEl.appendChild(childEl);
|
|
}
|
|
|
|
doMount(child, childEl, parentEl, oldParent);
|
|
|
|
return child;
|
|
}
|
|
|
|
function trigger (el, eventName) {
|
|
if (eventName === 'onmount' || eventName === 'onremount') {
|
|
el.__redom_mounted = true;
|
|
} else if (eventName === 'onunmount') {
|
|
el.__redom_mounted = false;
|
|
}
|
|
|
|
var hooks = el.__redom_lifecycle;
|
|
|
|
if (!hooks) {
|
|
return;
|
|
}
|
|
|
|
var view = el.__redom_view;
|
|
var hookCount = 0;
|
|
|
|
view && view[eventName] && view[eventName]();
|
|
|
|
for (var hook in hooks) {
|
|
if (hook) {
|
|
hookCount++;
|
|
}
|
|
}
|
|
|
|
if (hookCount) {
|
|
var traverse = el.firstChild;
|
|
|
|
while (traverse) {
|
|
var next = traverse.nextSibling;
|
|
|
|
trigger(traverse, eventName);
|
|
|
|
traverse = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
function doMount (child, childEl, parentEl, oldParent) {
|
|
var hooks = childEl.__redom_lifecycle || (childEl.__redom_lifecycle = {});
|
|
var remount = (parentEl === oldParent);
|
|
var hooksFound = false;
|
|
|
|
for (var i = 0, list = hookNames; i < list.length; i += 1) {
|
|
var hookName = list[i];
|
|
|
|
if (!remount) { // if already mounted, skip this phase
|
|
if (child !== childEl) { // only Views can have lifecycle events
|
|
if (hookName in child) {
|
|
hooks[hookName] = (hooks[hookName] || 0) + 1;
|
|
}
|
|
}
|
|
}
|
|
if (hooks[hookName]) {
|
|
hooksFound = true;
|
|
}
|
|
}
|
|
|
|
if (!hooksFound) {
|
|
childEl.__redom_lifecycle = {};
|
|
return;
|
|
}
|
|
|
|
var traverse = parentEl;
|
|
var triggered = false;
|
|
|
|
if (remount || (traverse && traverse.__redom_mounted)) {
|
|
trigger(childEl, remount ? 'onremount' : 'onmount');
|
|
triggered = true;
|
|
}
|
|
|
|
while (traverse) {
|
|
var parent = traverse.parentNode;
|
|
var parentHooks = traverse.__redom_lifecycle || (traverse.__redom_lifecycle = {});
|
|
|
|
for (var hook in hooks) {
|
|
parentHooks[hook] = (parentHooks[hook] || 0) + hooks[hook];
|
|
}
|
|
|
|
if (triggered) {
|
|
break;
|
|
} else {
|
|
if (traverse.nodeType === Node.DOCUMENT_NODE ||
|
|
(shadowRootAvailable && (traverse instanceof ShadowRoot)) ||
|
|
(parent && parent.__redom_mounted)
|
|
) {
|
|
trigger(traverse, remount ? 'onremount' : 'onmount');
|
|
triggered = true;
|
|
}
|
|
traverse = parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setStyle (view, arg1, arg2) {
|
|
var el = getEl(view);
|
|
|
|
if (typeof arg1 === 'object') {
|
|
for (var key in arg1) {
|
|
setStyleValue(el, key, arg1[key]);
|
|
}
|
|
} else {
|
|
setStyleValue(el, arg1, arg2);
|
|
}
|
|
}
|
|
|
|
function setStyleValue (el, key, value) {
|
|
el.style[key] = value == null ? '' : value;
|
|
}
|
|
|
|
/* global SVGElement */
|
|
|
|
var xlinkns = 'http://www.w3.org/1999/xlink';
|
|
|
|
function setAttr (view, arg1, arg2) {
|
|
setAttrInternal(view, arg1, arg2);
|
|
}
|
|
|
|
function setAttrInternal (view, arg1, arg2, initial) {
|
|
var el = getEl(view);
|
|
|
|
var isObj = typeof arg1 === 'object';
|
|
|
|
if (isObj) {
|
|
for (var key in arg1) {
|
|
setAttrInternal(el, key, arg1[key], initial);
|
|
}
|
|
} else {
|
|
var isSVG = el instanceof SVGElement;
|
|
var isFunc = typeof arg2 === 'function';
|
|
|
|
if (arg1 === 'style' && typeof arg2 === 'object') {
|
|
setStyle(el, arg2);
|
|
} else if (isSVG && isFunc) {
|
|
el[arg1] = arg2;
|
|
} else if (arg1 === 'dataset') {
|
|
setData(el, arg2);
|
|
} else if (!isSVG && (arg1 in el || isFunc) && (arg1 !== 'list')) {
|
|
el[arg1] = arg2;
|
|
} else {
|
|
if (isSVG && (arg1 === 'xlink')) {
|
|
setXlink(el, arg2);
|
|
return;
|
|
}
|
|
if (initial && arg1 === 'class') {
|
|
arg2 = el.className + ' ' + arg2;
|
|
}
|
|
if (arg2 == null) {
|
|
el.removeAttribute(arg1);
|
|
} else {
|
|
el.setAttribute(arg1, arg2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function setXlink (el, arg1, arg2) {
|
|
if (typeof arg1 === 'object') {
|
|
for (var key in arg1) {
|
|
setXlink(el, key, arg1[key]);
|
|
}
|
|
} else {
|
|
if (arg2 != null) {
|
|
el.setAttributeNS(xlinkns, arg1, arg2);
|
|
} else {
|
|
el.removeAttributeNS(xlinkns, arg1, arg2);
|
|
}
|
|
}
|
|
}
|
|
|
|
function setData (el, arg1, arg2) {
|
|
if (typeof arg1 === 'object') {
|
|
for (var key in arg1) {
|
|
setData(el, key, arg1[key]);
|
|
}
|
|
} else {
|
|
if (arg2 != null) {
|
|
el.dataset[arg1] = arg2;
|
|
} else {
|
|
delete el.dataset[arg1];
|
|
}
|
|
}
|
|
}
|
|
|
|
function text (str) {
|
|
return document.createTextNode((str != null) ? str : '');
|
|
}
|
|
|
|
function parseArgumentsInternal (element, args, initial) {
|
|
for (var i = 0, list = args; i < list.length; i += 1) {
|
|
var arg = list[i];
|
|
|
|
if (arg !== 0 && !arg) {
|
|
continue;
|
|
}
|
|
|
|
var type = typeof arg;
|
|
|
|
if (type === 'function') {
|
|
arg(element);
|
|
} else if (type === 'string' || type === 'number') {
|
|
element.appendChild(text(arg));
|
|
} else if (isNode(getEl(arg))) {
|
|
mount(element, arg);
|
|
} else if (arg.length) {
|
|
parseArgumentsInternal(element, arg, initial);
|
|
} else if (type === 'object') {
|
|
setAttrInternal(element, arg, null, initial);
|
|
}
|
|
}
|
|
}
|
|
|
|
function ensureEl (parent) {
|
|
return typeof parent === 'string' ? html(parent) : getEl(parent);
|
|
}
|
|
|
|
function getEl (parent) {
|
|
return (parent.nodeType && parent) || (!parent.el && parent) || getEl(parent.el);
|
|
}
|
|
|
|
function isNode (arg) {
|
|
return arg && arg.nodeType;
|
|
}
|
|
|
|
function html (query) {
|
|
var args = [], len = arguments.length - 1;
|
|
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
|
|
|
|
var element;
|
|
|
|
var type = typeof query;
|
|
|
|
if (type === 'string') {
|
|
element = createElement(query);
|
|
} else if (type === 'function') {
|
|
var Query = query;
|
|
element = new (Function.prototype.bind.apply( Query, [ null ].concat( args) ));
|
|
} else {
|
|
throw new Error('At least one argument required');
|
|
}
|
|
|
|
parseArgumentsInternal(getEl(element), args, true);
|
|
|
|
return element;
|
|
}
|
|
|
|
var el = html;
|
|
var h = html;
|
|
|
|
html.extend = function extendHtml () {
|
|
var args = [], len = arguments.length;
|
|
while ( len-- ) args[ len ] = arguments[ len ];
|
|
|
|
return html.bind.apply(html, [ this ].concat( args ));
|
|
};
|
|
|
|
function setChildren (parent) {
|
|
var children = [], len = arguments.length - 1;
|
|
while ( len-- > 0 ) children[ len ] = arguments[ len + 1 ];
|
|
|
|
var parentEl = getEl(parent);
|
|
var current = traverse(parent, children, parentEl.firstChild);
|
|
|
|
while (current) {
|
|
var next = current.nextSibling;
|
|
|
|
unmount(parent, current);
|
|
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
function traverse (parent, children, _current) {
|
|
var current = _current;
|
|
|
|
var childEls = Array(children.length);
|
|
|
|
for (var i = 0; i < children.length; i++) {
|
|
childEls[i] = children[i] && getEl(children[i]);
|
|
}
|
|
|
|
for (var i$1 = 0; i$1 < children.length; i$1++) {
|
|
var child = children[i$1];
|
|
|
|
if (!child) {
|
|
continue;
|
|
}
|
|
|
|
var childEl = childEls[i$1];
|
|
|
|
if (childEl === current) {
|
|
current = current.nextSibling;
|
|
continue;
|
|
}
|
|
|
|
if (isNode(childEl)) {
|
|
var next = current && current.nextSibling;
|
|
var exists = child.__redom_index != null;
|
|
var replace = exists && next === childEls[i$1 + 1];
|
|
|
|
mount(parent, child, current, replace);
|
|
|
|
if (replace) {
|
|
current = next;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (child.length != null) {
|
|
current = traverse(parent, child, current);
|
|
}
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
function listPool (View, key, initData) {
|
|
return new ListPool(View, key, initData);
|
|
}
|
|
|
|
var ListPool = function ListPool (View, key, initData) {
|
|
this.View = View;
|
|
this.initData = initData;
|
|
this.oldLookup = {};
|
|
this.lookup = {};
|
|
this.oldViews = [];
|
|
this.views = [];
|
|
|
|
if (key != null) {
|
|
this.key = typeof key === 'function' ? key : propKey(key);
|
|
}
|
|
};
|
|
|
|
ListPool.prototype.update = function update (data, context) {
|
|
var ref = this;
|
|
var View = ref.View;
|
|
var key = ref.key;
|
|
var initData = ref.initData;
|
|
var keySet = key != null;
|
|
|
|
var oldLookup = this.lookup;
|
|
var newLookup = {};
|
|
|
|
var newViews = Array(data.length);
|
|
var oldViews = this.views;
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
var item = data[i];
|
|
var view = (void 0);
|
|
|
|
if (keySet) {
|
|
var id = key(item);
|
|
|
|
view = oldLookup[id] || new View(initData, item, i, data);
|
|
newLookup[id] = view;
|
|
view.__redom_id = id;
|
|
} else {
|
|
view = oldViews[i] || new View(initData, item, i, data);
|
|
}
|
|
view.update && view.update(item, i, data, context);
|
|
|
|
var el = getEl(view.el);
|
|
|
|
el.__redom_view = view;
|
|
newViews[i] = view;
|
|
}
|
|
|
|
this.oldViews = oldViews;
|
|
this.views = newViews;
|
|
|
|
this.oldLookup = oldLookup;
|
|
this.lookup = newLookup;
|
|
};
|
|
|
|
function propKey (key) {
|
|
return function (item) {
|
|
return item[key];
|
|
};
|
|
}
|
|
|
|
function list (parent, View, key, initData) {
|
|
return new List(parent, View, key, initData);
|
|
}
|
|
|
|
var List = function List (parent, View, key, initData) {
|
|
this.View = View;
|
|
this.initData = initData;
|
|
this.views = [];
|
|
this.pool = new ListPool(View, key, initData);
|
|
this.el = ensureEl(parent);
|
|
this.keySet = key != null;
|
|
};
|
|
|
|
List.prototype.update = function update (data, context) {
|
|
if ( data === void 0 ) data = [];
|
|
|
|
var ref = this;
|
|
var keySet = ref.keySet;
|
|
var oldViews = this.views;
|
|
|
|
this.pool.update(data, context);
|
|
|
|
var ref$1 = this.pool;
|
|
var views = ref$1.views;
|
|
var lookup = ref$1.lookup;
|
|
|
|
if (keySet) {
|
|
for (var i = 0; i < oldViews.length; i++) {
|
|
var oldView = oldViews[i];
|
|
var id = oldView.__redom_id;
|
|
|
|
if (lookup[id] == null) {
|
|
oldView.__redom_index = null;
|
|
unmount(this, oldView);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var i$1 = 0; i$1 < views.length; i$1++) {
|
|
var view = views[i$1];
|
|
|
|
view.__redom_index = i$1;
|
|
}
|
|
|
|
setChildren(this, views);
|
|
|
|
if (keySet) {
|
|
this.lookup = lookup;
|
|
}
|
|
this.views = views;
|
|
};
|
|
|
|
List.extend = function extendList (parent, View, key, initData) {
|
|
return List.bind(List, parent, View, key, initData);
|
|
};
|
|
|
|
list.extend = List.extend;
|
|
|
|
/* global Node */
|
|
|
|
function place (View, initData) {
|
|
return new Place(View, initData);
|
|
}
|
|
|
|
var Place = function Place (View, initData) {
|
|
this.el = text('');
|
|
this.visible = false;
|
|
this.view = null;
|
|
this._placeholder = this.el;
|
|
|
|
if (View instanceof Node) {
|
|
this._el = View;
|
|
} else if (View.el instanceof Node) {
|
|
this._el = View;
|
|
this.view = View;
|
|
} else {
|
|
this._View = View;
|
|
}
|
|
|
|
this._initData = initData;
|
|
};
|
|
|
|
Place.prototype.update = function update (visible, data) {
|
|
var placeholder = this._placeholder;
|
|
var parentNode = this.el.parentNode;
|
|
|
|
if (visible) {
|
|
if (!this.visible) {
|
|
if (this._el) {
|
|
mount(parentNode, this._el, placeholder);
|
|
unmount(parentNode, placeholder);
|
|
|
|
this.el = getEl(this._el);
|
|
this.visible = visible;
|
|
} else {
|
|
var View = this._View;
|
|
var view = new View(this._initData);
|
|
|
|
this.el = getEl(view);
|
|
this.view = view;
|
|
|
|
mount(parentNode, view, placeholder);
|
|
unmount(parentNode, placeholder);
|
|
}
|
|
}
|
|
this.view && this.view.update && this.view.update(data);
|
|
} else {
|
|
if (this.visible) {
|
|
if (this._el) {
|
|
mount(parentNode, placeholder, this._el);
|
|
unmount(parentNode, this._el);
|
|
|
|
this.el = placeholder;
|
|
this.visible = visible;
|
|
|
|
return;
|
|
}
|
|
mount(parentNode, placeholder, this.view);
|
|
unmount(parentNode, this.view);
|
|
|
|
this.el = placeholder;
|
|
this.view = null;
|
|
}
|
|
}
|
|
this.visible = visible;
|
|
};
|
|
|
|
/* global Node */
|
|
|
|
function router (parent, Views, initData) {
|
|
return new Router(parent, Views, initData);
|
|
}
|
|
|
|
var Router = function Router (parent, Views, initData) {
|
|
this.el = ensureEl(parent);
|
|
this.Views = Views;
|
|
this.initData = initData;
|
|
};
|
|
|
|
Router.prototype.update = function update (route, data) {
|
|
if (route !== this.route) {
|
|
var Views = this.Views;
|
|
var View = Views[route];
|
|
|
|
this.route = route;
|
|
|
|
if (View && (View instanceof Node || View.el instanceof Node)) {
|
|
this.view = View;
|
|
} else {
|
|
this.view = View && new View(this.initData, data);
|
|
}
|
|
|
|
setChildren(this.el, [this.view]);
|
|
}
|
|
this.view && this.view.update && this.view.update(data, route);
|
|
};
|
|
|
|
var ns = 'http://www.w3.org/2000/svg';
|
|
|
|
function svg (query) {
|
|
var args = [], len = arguments.length - 1;
|
|
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
|
|
|
|
var element;
|
|
|
|
var type = typeof query;
|
|
|
|
if (type === 'string') {
|
|
element = createElement(query, ns);
|
|
} else if (type === 'function') {
|
|
var Query = query;
|
|
element = new (Function.prototype.bind.apply( Query, [ null ].concat( args) ));
|
|
} else {
|
|
throw new Error('At least one argument required');
|
|
}
|
|
|
|
parseArgumentsInternal(getEl(element), args, true);
|
|
|
|
return element;
|
|
}
|
|
|
|
var s = svg;
|
|
|
|
svg.extend = function extendSvg () {
|
|
var args = [], len = arguments.length;
|
|
while ( len-- ) args[ len ] = arguments[ len ];
|
|
|
|
return svg.bind.apply(svg, [ this ].concat( args ));
|
|
};
|
|
|
|
svg.ns = ns;
|
|
|
|
exports.List = List;
|
|
exports.ListPool = ListPool;
|
|
exports.Place = Place;
|
|
exports.Router = Router;
|
|
exports.el = el;
|
|
exports.h = h;
|
|
exports.html = html;
|
|
exports.list = list;
|
|
exports.listPool = listPool;
|
|
exports.mount = mount;
|
|
exports.place = place;
|
|
exports.router = router;
|
|
exports.s = s;
|
|
exports.setAttr = setAttr;
|
|
exports.setChildren = setChildren;
|
|
exports.setData = setData;
|
|
exports.setStyle = setStyle;
|
|
exports.setXlink = setXlink;
|
|
exports.svg = svg;
|
|
exports.text = text;
|
|
exports.unmount = unmount;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
}));
|