UNPKG

bd-core

Version:

a JavaScript library for building browser-hosted user interfaces

1,462 lines (1,338 loc) 107 kB
let _global = 0; let watchers = []; function global() { return _global; } function setGlobal(theGlobal) { if (!_global) { _global = theGlobal; watchers.forEach(handler => handler(theGlobal)); watchers = null; } else { throw new Error('illegal to mutate global space'); } } function adviseGlobal(handler) { if (_global) { handler(_global); } else { watchers.push(handler); } } const postProcessingFuncs = Object.create(null); function insPostProcessingFunction(name, transform, func) { if (typeof transform === 'string') { // transform is an alias for name if (!postProcessingFuncs[name]) { throw Error(`cannot alias to a non-existing post-processing function: ${name}`); } postProcessingFuncs[transform] = postProcessingFuncs[name]; return; } if (arguments.length === 3) { if (typeof transform !== 'function') { transform = (prop, value) => prop ? {[prop]: value} : value; } } else { func = transform; transform = (prop, value) => value; } func.bdTransform = transform; if (postProcessingFuncs[name]) { throw Error(`duplicate postprocessing function name: ${name}`); } postProcessingFuncs[name] = func; } function replacePostProcessingFunction(name, func) { postProcessingFuncs[name] = func; } function getPostProcessingFunction(name) { return postProcessingFuncs[name]; } function noop() { // do nothing } class Destroyable { constructor(proc, container, onEmpty) { const result = this; result.proc = proc; if (container) { result.destroy = () => { result.destroy = result.proc = noop; const index = container.indexOf(result); if (index !== -1) { container.splice(index, 1); } !container.length && onEmpty && onEmpty(); }; container.push(result); } else { result.destroy = () => { result.destroy = result.proc = noop; }; } } static destroyAll(container) { // deterministic and robust algorithm to destroy handles: // * deterministic even when handle destructors insert handles (though the new handles will not be destroyed) // * robust even when handle destructors cause other handles to be destroyed if (Array.isArray(container)) { container.slice().forEach(h => h.destroy()); }// else container was likely falsy and never used } } const STAR = Symbol('bd-star'); const eqlComparators = new Map(); function eql(refValue, otherValue) { if (!refValue) { return otherValue === refValue; } if (refValue instanceof Object) { const comparator = eqlComparators.get(refValue.constructor); if (comparator) { return comparator(refValue, otherValue); } } if (otherValue instanceof Object) { const comparator = eqlComparators.get(otherValue.constructor); if (comparator) { return comparator(otherValue, refValue); } } return refValue === otherValue; } const watcherCatalog = new WeakMap(); const OWNER = Symbol('bd-owner'); const OWNER_NULL = Symbol('bd-owner-null'); const PROP = Symbol('bd-prop'); const UNKNOWN_OLD_VALUE = { value: 'UNKNOWN_OLD_VALUE' }; const pWatchableWatchers = Symbol('bd-pWatchableWatchers'); const pWatchableHandles = Symbol('bd-pWatchableHandles'); const pWatchableSetup = Symbol('bd-pWatchableSetup'); class WatchableRef { constructor(referenceObject, prop, formatter) { if (typeof prop === 'function') { // no prop,...star watcher formatter = prop; prop = STAR; } Object.defineProperty(this, 'value', { enumerable: true, // eslint-disable-next-line func-names get: ((function () { if (formatter) { if (prop === STAR) { return () => formatter(referenceObject); } else { return () => formatter(referenceObject[prop]); } } else if (prop === STAR) { return () => referenceObject; } else { return () => referenceObject[prop]; } })()) }); // if (referenceObject[OWNER] && prop === STAR), then we cValue===newValue===referenceObject... // therefore can't detect internal mutations to referenceObject, so don't try const cannotDetectMutations = prop === STAR && referenceObject[OWNER]; this[pWatchableWatchers] = []; let cValue; const callback = (newValue, oldValue, target, referenceProp) => { if (formatter) { oldValue = oldValue === UNKNOWN_OLD_VALUE ? oldValue : formatter(oldValue); newValue = formatter(newValue); } if (cannotDetectMutations || oldValue === UNKNOWN_OLD_VALUE || !eql(cValue, newValue)) { this[pWatchableWatchers].slice().forEach( destroyable => destroyable.proc((cValue = newValue), oldValue, target, referenceProp) ); } }; this[pWatchableSetup] = () => { cValue = this.value; if (referenceObject[OWNER]) { this[pWatchableHandles] = [watch(referenceObject, prop, (newValue, oldValue, receiver, _prop) => { if (prop === STAR) { callback(referenceObject, UNKNOWN_OLD_VALUE, referenceObject, _prop); } else { callback(newValue, oldValue, referenceObject, _prop); } })]; } else if (referenceObject.watch) { this[pWatchableHandles] = [ referenceObject.watch(prop, (newValue, oldValue, target) => { callback(newValue, oldValue, target, prop); if (this[pWatchableHandles].length === 2) { this[pWatchableHandles].pop().destroy(); } if (newValue && newValue[OWNER]) { // value is a watchable // eslint-disable-next-line no-shadow this[pWatchableHandles].push(watch(newValue, (newValue, oldValue, receiver, referenceProp) => { callback(receiver, UNKNOWN_OLD_VALUE, referenceObject, referenceProp); })); } }) ]; const value = referenceObject[prop]; if (value && value[OWNER]) { // value is a watchable this[pWatchableHandles].push(watch(value, (newValue, oldValue, receiver, referenceProp) => { callback(receiver, UNKNOWN_OLD_VALUE, referenceObject, referenceProp); })); } referenceObject.own && referenceObject.own(this); } else { throw new Error("don't know how to watch referenceObject"); } }; } destroy() { Destroyable.destroyAll(this[pWatchableWatchers]); } watch(watcher) { this[pWatchableHandles] || this[pWatchableSetup](); return new Destroyable(watcher, this[pWatchableWatchers], () => { Destroyable.destroyAll(this[pWatchableHandles]); delete this[pWatchableHandles]; }); } } WatchableRef.pWatchableWatchers = pWatchableWatchers; WatchableRef.pWatchableHandles = pWatchableHandles; WatchableRef.pWatchableSetup = pWatchableSetup; WatchableRef.UNKNOWN_OLD_VALUE = UNKNOWN_OLD_VALUE; WatchableRef.STAR = STAR; function getWatchableRef(referenceObject, referenceProp, formatter) { // (referenceObject, referenceProp, formatter) // (referenceObject, referenceProp) // (referenceObject, formatter) => (referenceObject, STAR, formatter) // (referenceObject) => (referenceObject, STAR) if (typeof referenceProp === 'function') { // no referenceProp,...star watcher formatter = referenceProp; referenceProp = STAR; } return new WatchableRef(referenceObject, referenceProp || STAR, formatter); } function watch(watchable, name, watcher) { if (typeof name === 'function') { watcher = name; name = STAR; } let variables = watcherCatalog.get(watchable); if (!variables) { watcherCatalog.set(watchable, (variables = {})); } // eslint-disable-next-line no-shadow const insWatcher = (name, watcher) => new Destroyable(watcher, variables[name] || (variables[name] = [])); if (!watcher) { const hash = name; // eslint-disable-next-line no-shadow return Reflect.ownKeys(hash).map(name => insWatcher(name, hash[name])); } else if (Array.isArray(name)) { // eslint-disable-next-line no-shadow return name.map(name => insWatcher(name, watcher)); } else { return insWatcher(name, watcher); } } let holdStarNotifications = false; function applyWatchers(newValue, oldValue, receiver, name) { const catalog = watcherCatalog.get(receiver); if (catalog) { if (name === STAR) { const watchers = catalog[STAR]; watchers && watchers.slice().forEach(destroyable => destroyable.proc(receiver, oldValue, receiver, [STAR])); } else { const prop = name[0]; let watchers = catalog[prop]; watchers && watchers.slice().forEach(destroyable => destroyable.proc(receiver[prop], oldValue, receiver, name)); if (!holdStarNotifications) { (watchers = catalog[STAR]) && watchers.slice().forEach( destroyable => destroyable.proc(receiver, oldValue, receiver, name) ); } } } if (watch.log) { // eslint-disable-next-line no-console console.log(name, newValue); } if (!holdStarNotifications && receiver[OWNER] !== OWNER_NULL) { name.unshift(receiver[PROP]); applyWatchers(receiver, UNKNOWN_OLD_VALUE, receiver[OWNER], name); } } let pauseWatchers = false; const moving = false; let _silentSet = false; function set(target, prop, value, receiver) { if (_silentSet) { target[prop] = value; return true; } else { const oldValue = target[prop]; if (value instanceof Object) { const holdPauseWatchers = pauseWatchers; try { pauseWatchers = true; value = moving ? value : createWatchable(value, receiver, prop); pauseWatchers = holdPauseWatchers; } catch (e) { pauseWatchers = holdPauseWatchers; throw e; } } // we would like to set and applyWatchers iff target[prop] !== value. Unfortunately, sometimes target[prop] === value // even though we haven't seen the mutation before, e.g., length in an Array instance const result = Reflect.set(target, prop, value, receiver); !pauseWatchers && applyWatchers(value, oldValue, receiver, [prop]); return result; } } const objectProxyHandler = { set }; const SWAP_OLD_LENGTH = Symbol('SWAP_OLD_LENGTH'); const OLD_LENGTH = Symbol('old-length'); const NO_CHANGE = Symbol('splice-no-change'); const QUICK_COPY = Symbol('slice-quick-copy'); const BEFORE_ADVICE = Symbol('BEFORE_ADVICE'); const noop$1 = () => { // do nothing }; const arrayProxyHandler = { set(target, prop, value, receiver) { if (prop === 'length') { const result = Reflect.set(target, prop, value, receiver); const oldValue = target[SWAP_OLD_LENGTH](value); !pauseWatchers && !_silentSet && applyWatchers(value, oldValue, receiver, ['length']); return result; } else { return set(target, prop, value, receiver); } } }; function getAdvice(owner, method) { const advice = owner[BEFORE_ADVICE] && owner[BEFORE_ADVICE][method]; return advice && advice.map(f => f()); } class WatchableArray extends Array { // note: we can make all of these much more efficient, particularly shift and unshift. // But, it's probably rare that it will matter, so we'll do it when the need arises [SWAP_OLD_LENGTH](newLength) { const result = this[OLD_LENGTH]; this[OLD_LENGTH] = newLength; return result; } before(method, proc) { let beforeAdvice = this[BEFORE_ADVICE]; if (!beforeAdvice) { Object.defineProperty(this, BEFORE_ADVICE, {value: {}}); beforeAdvice = this[BEFORE_ADVICE]; } const stack = beforeAdvice[method] || (beforeAdvice[method] = []); stack.push(proc); const handle = { destroy() { handle.destroy = noop$1; const index = stack.indexOf(proc); if (index !== -1) stack.splice(index, 1); } }; return handle; } _splice(...args) { const oldValues = this.slice(QUICK_COPY); const changeSet = []; let result; try { _silentSet = true; result = super.splice(...args); for (let i = 0, end = Math.max(oldValues.length, this.length); i < end; i++) { let value = this[i]; if (value instanceof Object) { if (value[OWNER]) { if (value[OWNER] !== this) { // a new item that came from another watchable value = this[i] = createWatchable(value, this, i); changeSet.push(value); // eslint-disable-next-line eqeqeq } else if (value[PROP] != i) { // INTENTIONAL != // an item that was moved within this value[PROP] = i; changeSet.push(value); } else { changeSet.push(NO_CHANGE); } } else { // a new item that is not already a watchable value = this[i] = createWatchable(value, this, i); changeSet.push(value); } } else if (value !== oldValues[i]) { changeSet.push(value); } else { changeSet.push(NO_CHANGE); } } _silentSet = false; } catch (e) { try { oldValues.forEach((value, i) => (this[i] = value)); } catch (e) { // eslint-disable-next-line no-console console.error(e); } _silentSet = false; throw e; } try { holdStarNotifications = true; let change = false; changeSet.forEach((value, i) => { if (value !== NO_CHANGE) { change = true; applyWatchers(value, oldValues[i], this, [i]); } }); if (this.length !== oldValues.length) { change = true; applyWatchers(this.length, oldValues.length, this, ['length']); } holdStarNotifications = false; if (change) { applyWatchers(this, UNKNOWN_OLD_VALUE, this, STAR); } } catch (e) { holdStarNotifications = false; throw e; } return result; } splice(...args) { const advice = getAdvice(this, 'splice'); const result = this._splice(...args); advice && advice.map(f => f && f(result)); return result; } pop() { const advice = getAdvice(this, 'pop'); const result = fromWatchable(super.pop()); advice && advice.map(f => f && f(result)); return result; } shift() { const advice = getAdvice(this, 'shift'); const result = fromWatchable(this._splice(0, 1)[0]); advice && advice.map(f => f && f(result)); return result; } slice(...args) { return args[0] === QUICK_COPY ? super.slice() : fromWatchable(super.slice(...args)); } unshift(...args) { const advice = getAdvice(this, 'unshift'); this._splice(0, 0, ...args); advice && advice.map(f => f && f()); } reverse() { const advice = getAdvice(this, 'reverse'); const oldValues = this.slice(QUICK_COPY); try { _silentSet = true; for (let i = 0, j = this.length - 1; i < j; i++, j--) { const temp = this[i]; this[i] = this[j]; this[i][PROP] = i; this[j] = temp; temp[PROP] = j; } _silentSet = false; } catch (e) { _silentSet = false; throw e; } try { holdStarNotifications = true; if (this.length % 2) { for (let i = 0, end = Math.floor(this.length / 2); i < end; i++) { applyWatchers(this[i], oldValues[i], this, [i]); } for (let i = Math.ceil(this.length / 2), end = this.length; i < end; i++) { applyWatchers(this[i], oldValues[i], this, [i]); } } else { this.forEach((value, i) => applyWatchers(value, oldValues[i], this, [i])); } holdStarNotifications = false; if (this.length > 1) { applyWatchers(this, UNKNOWN_OLD_VALUE, this, STAR); } } catch (e) { holdStarNotifications = false; throw e; } advice && advice.map(f => f && f(this)); return this; } _reorder(proc) { const oldValues = this.slice(QUICK_COPY); const changeSet = Array(this.length).fill(false); let changes = false; try { _silentSet = true; proc(this); this.forEach((value, i) => { // eslint-disable-next-line eqeqeq if (value[PROP] != i) { // INTENTIONAL != value[PROP] = i; changeSet[i] = true; changes = true; } }); _silentSet = false; } catch (e) { _silentSet = false; throw e; } try { holdStarNotifications = true; changeSet.forEach((value, i) => { if (value) { applyWatchers(this[i], oldValues[i], this, [i]); } }); holdStarNotifications = false; if (changes) { applyWatchers(this, UNKNOWN_OLD_VALUE, this, STAR); } } catch (e) { holdStarNotifications = false; throw e; } } reorder(proc) { const advice = getAdvice(this, 'reorder'); this._reorder(proc); advice && advice.map(f => f && f(this)); return this; } sort(...args) { const advice = getAdvice(this, 'sort'); this._reorder(theArray => super.sort.apply(theArray, args)); advice && advice.map(f => f && f(this)); return this; } } function silentSet(watchable, prop, value, enumerable, configurable) { try { _silentSet = true; if (value === undefined) { delete watchable[prop]; } else if (enumerable !== undefined && !enumerable && !watchable.hasOwnProperty(prop)) { Object.defineProperty(watchable, prop, { writable: true, configurable: configurable !== undefined ? configurable : true, value }); } else { watchable[prop] = value; } _silentSet = false; } catch (e) { _silentSet = false; throw e; } } function createWatchable(src, owner, prop) { const keys = Reflect.ownKeys(src); const isArray = Array.isArray(src); const result = isArray ? new Proxy(new WatchableArray(), arrayProxyHandler) : new Proxy({}, objectProxyHandler); if (isArray) { keys.forEach(k => k !== 'length' && (result[k] = src[k])); Object.defineProperty(result, OLD_LENGTH, {writable: true, value: result.length}); } else { keys.forEach(k => (result[k] = src[k])); } const silentHold = _silentSet; _silentSet = true; Object.defineProperty(result, OWNER, {writable: true, value: owner}); prop !== undefined && Object.defineProperty(result, PROP, {writable: true, value: prop}); _silentSet = silentHold; return result; } function toWatchable(data) { if (!(data instanceof Object)) { throw new Error('scalar values are not watchable'); } try { pauseWatchers = true; data = createWatchable(data, OWNER_NULL, false); pauseWatchers = false; return data; } catch (e) { pauseWatchers = false; throw e; } } function fromWatchable(data) { if (data instanceof Object) { if (!data) { return data; } if (!data[OWNER]) { return data; } const result = Array.isArray(data) ? Array(data.length) : {}; Reflect.ownKeys(data).forEach(k => { if (k !== OWNER && k !== PROP && k !== OLD_LENGTH) { result[k] = fromWatchable(data[k]); } }); return result; } else { return data; } } const onMutateBeforeNames = {}; const onMutateNames = {}; function mutate(owner, name, privateName, newValue) { const oldValue = owner[privateName]; if (eql(oldValue, newValue)) { return false; } else { let onMutateBeforeName, onMutateName; if (typeof name !== 'symbol') { onMutateBeforeName = onMutateBeforeNames[name]; if (!onMutateBeforeName) { const suffix = name.substring(0, 1).toUpperCase() + name.substring(1); onMutateBeforeName = onMutateBeforeNames[name] = `onMutateBefore${suffix}`; onMutateName = onMutateNames[name] = `onMutate${suffix}`; } else { onMutateName = onMutateNames[name]; } } if (onMutateBeforeName && owner[onMutateBeforeName]) { if (owner[onMutateBeforeName](newValue, oldValue) === false) { // the proposed mutation is illegal return false; } } if (owner.hasOwnProperty(privateName)) { owner[privateName] = newValue; } else { // not enumerable or configurable Object.defineProperty(owner, privateName, {writable: true, value: newValue}); } onMutateName && owner[onMutateName] && owner[onMutateName](newValue, oldValue); return [name, newValue, oldValue]; } } function getWatcher(owner, watcher) { return typeof watcher === 'function' ? watcher : owner[watcher].bind(owner); } function watchHub(superClass) { return class extends (superClass || class { }) { // protected interface... bdMutateNotify(name, newValue, oldValue) { const variables = watcherCatalog.get(this); if (!variables) { return; } if (Array.isArray(name)) { // each element in name is either a triple ([name, oldValue, newValue]) or false let doStar = false; name.forEach(p => { if (p) { doStar = true; const watchers = variables[p[0]]; if (watchers) { newValue = p[1]; oldValue = p[2]; watchers.slice().forEach(destroyable => destroyable.proc(newValue, oldValue, this, name)); } } }); if (doStar) { const watchers = variables[STAR]; if (watchers) { watchers.slice().forEach(destroyable => destroyable.proc(this, oldValue, this, name)); } } } else { let watchers = variables[name]; if (watchers) { watchers.slice().forEach(destroyable => destroyable.proc(newValue, oldValue, this, name)); } watchers = variables[STAR]; if (watchers) { watchers.slice().forEach(destroyable => destroyable.proc(newValue, oldValue, this, name)); } } } bdMutate(name, privateName, newValue) { if (arguments.length > 3) { let i = 0; const results = []; let mutateOccurred = false; while (i < arguments.length) { // eslint-disable-next-line prefer-rest-params const mutateResult = mutate(this, arguments[i++], arguments[i++], arguments[i++]); mutateOccurred = mutateOccurred || mutateResult; results.push(mutateResult); } if (mutateOccurred) { this.bdMutateNotify(results); return results; } return false; } else { const result = mutate(this, name, privateName, newValue); if (result) { this.bdMutateNotify(...result); return result; } return false; } } // public interface... get isBdWatchHub() { return true; } watch(...args) { // possible sigs: // 1: name, watcher // 2: name[], watcher // 3: hash: name -> watcher // 4: watchable, name, watcher // 5: watchable, name[], watcher // 6: watchable, hash: name -> watcher // 7: watchable, watcher // STAR watcher if (arguments.length === 1) { // sig 3 const hash = args[0]; return Reflect.ownKeys(hash).map(name => this.watch(name, hash[name])); } if (args[0][OWNER]) { // sig 4-6 let result; if (arguments.length === 2) { if (typeof args[1] === 'object') { // sig 6 const hash = args[1]; Reflect.ownKeys(hash).map(name => (hash[name] = getWatcher(this, hash[name]))); result = watch(args[0], hash); } else { // sig 7 result = watch(args[0], STAR, getWatcher(this, args[1])); } } else { // sig 4 or 5 result = watch(args[0], args[1], getWatcher(this, args[2])); } this.own && this.own(result); return result; } if (Array.isArray(args[0])) { // sig 2 return args[0].map(name => this.watch(name, getWatcher(this, args[1]))); } // sig 1 const name = args[0]; const watcher = getWatcher(this, args[1]); let variables = watcherCatalog.get(this); if (!variables) { watcherCatalog.set(this, (variables = {})); } const result = new Destroyable(watcher, variables[name] || (variables[name] = [])); this.own && this.own(result); return result; } destroyWatch(name) { const variables = watcherCatalog.get(this); function destroyList(list) { if (list) { while (list.length) list.shift().destroy(); } } if (variables) { if (name) { destroyList(variables[name]); delete variables[name]; } else { Reflect.ownKeys(variables).forEach(k => destroyList(variables[k])); watcherCatalog.delete(this); } } } getWatchableRef(name, formatter) { const result = new WatchableRef(this, name, formatter); this.own && this.own(result); return result; } }; } const WatchHub = watchHub(); function isWatchable(target) { return target && target[OWNER]; } function withWatchables(superClass, ...args) { const publicPropNames = []; function def(name) { let pname; if (Array.isArray(name)) { pname = name[1]; name = name[0]; } else { pname = `_${name}`; } publicPropNames.push(name); // eslint-disable-next-line no-use-before-define Object.defineProperty(prototype, name, { enumerable: true, get() { return this[pname]; }, set(value) { this.bdMutate(name, pname, value); } }); } function init(owner, kwargs) { publicPropNames.forEach(name => { if (kwargs.hasOwnProperty(name)) { owner[name] = kwargs[name]; } }); } const result = class extends superClass { constructor(kwargs) { kwargs = kwargs || {}; super(kwargs); init(this, kwargs); } }; const prototype = result.prototype; args.forEach(def); result.watchables = publicPropNames.concat(superClass.watchables || []); return result; } function bind(src, srcProp, dest, destProp) { dest[destProp] = src[srcProp]; if (src.isBdWatchHub) { return src.watch(srcProp, newValue => (dest[destProp] = newValue)); } else if (src[OWNER]) { return watch(srcProp, newValue => (dest[destProp] = newValue)); } else { throw new Error('src is not watchable'); } } function biBind(src1, prop1, src2, prop2) { src2[prop2] = src1[prop1]; return [bind(src1, prop1, src2, prop2), bind(src2, prop2, src1, prop1)]; } let window$1 = 0; let document = 0; adviseGlobal(global => { window$1 = global; document = window$1.document; }); function getAttributeValueFromEvent(e, attributeName, stopNode) { let node = e.target; while (node && node !== stopNode) { if (node.getAttributeNode(attributeName)) { return node.getAttribute(attributeName); } node = node.parentNode; } return undefined; } function setAttr(node, name, value) { if (arguments.length === 2) { // name is a hash Object.keys(name).forEach(n => setAttr(node, n, name[n])); } else if (name === 'style') { setStyle(node, value); } else if (name === 'innerHTML' || (name in node && node instanceof HTMLElement)) { node[name] = value; } else { node.setAttribute(name, value); } } function getAttr(node, name) { if (name in node && node instanceof HTMLElement) { return node[name]; } else { return node.getAttribute(name); } } let lastComputedStyleNode = 0; let lastComputedStyle = 0; function getComputedStyle(node) { if (lastComputedStyleNode !== node) { lastComputedStyle = window$1.getComputedStyle((lastComputedStyleNode = node)); } return lastComputedStyle; } function getStyle(node, property) { if (lastComputedStyleNode !== node) { lastComputedStyle = window$1.getComputedStyle((lastComputedStyleNode = node)); } const result = lastComputedStyle[property]; return (typeof result === 'string' && /px$/.test(result)) ? parseFloat(result) : result; } function getStyles(node, ...styleNames) { if (lastComputedStyleNode !== node) { lastComputedStyle = window$1.getComputedStyle((lastComputedStyleNode = node)); } let styles = []; styleNames.forEach(styleName => { if (Array.isArray(styleName)) { styles = styles.concat(styleName); } else if (typeof styleName === 'string') { styles.push(styleName); } else { // styleName is a hash Object.keys(styleName).forEach(p => styles.push(p)); } }); const result = {}; styles.forEach(property => { const value = lastComputedStyle[property]; result[property] = (typeof value === 'string' && /px$/.test(value)) ? parseFloat(value) : value; }); return result; } function setStyle(node, property, value) { if (arguments.length === 2) { if (typeof property === 'string') { node.style = property; } else { // property is a hash Object.keys(property).forEach(p => { node.style[p] = property[p]; }); } } else { node.style[property] = value; } } function getPosit(node) { const result = node.getBoundingClientRect(); result.t = result.top; result.b = result.bottom; result.l = result.left; result.r = result.right; result.h = result.height; result.w = result.width; return result; } function positStyle(v) { return v === false ? '' : `${v}px`; } function setPosit(node, posit) { // eslint-disable-next-line guard-for-in,no-restricted-syntax Object.keys(posit).forEach(p => { switch (p) { case 't': node.style.top = positStyle(posit.t); break; case 'b': node.style.bottom = positStyle(posit.b); break; case 'l': node.style.left = positStyle(posit.l); break; case 'r': node.style.right = positStyle(posit.r); break; case 'h': node.style.height = positStyle(posit.h); break; case 'w': node.style.width = positStyle(posit.w); break; case 'maxH': node.style.maxHeight = positStyle(posit.maxH); break; case 'maxW': node.style.maxWidth = positStyle(posit.maxW); break; case 'minH': node.style.minHeight = positStyle(posit.minH); break; case 'minW': node.style.minWidth = positStyle(posit.minW); break; case 'z': node.style.zIndex = posit.z === false ? '' : posit.z; break; // ignore...this allows clients to stuff other properties into posit for other reasons } }); } function insertBefore(node, refNode) { refNode.parentNode.insertBefore(node, refNode); } function insertAfter(node, refNode) { const parent = refNode.parentNode; if (parent.lastChild === refNode) { parent.appendChild(node); } else { parent.insertBefore(node, refNode.nextSibling); } } function insert(node, refNode, position) { if (position === undefined || position === 'last') { // short circuit the common case refNode.appendChild(node); } else { switch (position) { case 'before': insertBefore(node, refNode); break; case 'after': insertAfter(node, refNode); break; case 'replace': refNode.parentNode.replaceChild(node, refNode); return (refNode); case 'only': { const result = []; while (refNode.firstChild) { result.push(refNode.removeChild(refNode.firstChild)); } refNode.appendChild(node); return result; } case 'first': if (refNode.firstChild) { insertBefore(node, refNode.firstChild); } else { refNode.appendChild(node); } break; default: if (typeof position === 'number') { const children = refNode.childNodes; if (!children.length || children.length <= position) { refNode.appendChild(node); } else { insertBefore(node, children[position < 0 ? Math.max(0, children.length + position) : position]); } } else { throw new Error('illegal position'); } } } return 0; } function create(tag, props) { const result = Array.isArray(tag) ? document.createElementNS(`${tag[0]}`, tag[1]) : document.createElement(tag); if (props) { Reflect.ownKeys(props).forEach(p => setAttr(result, p, props[p])); } return result; } const DATA_BD_HIDE_SAVED_VALUE = 'data-bd-hide-saved-value'; function hide(...nodes) { nodes.forEach(node => { if (node) { if (!node.hasAttribute(DATA_BD_HIDE_SAVED_VALUE)) { node.setAttribute(DATA_BD_HIDE_SAVED_VALUE, node.style.display); node.style.display = 'none'; }// else, ignore, multiple calls to hide } }); } function show(...nodes) { nodes.forEach(node => { if (node) { let displayValue = ''; if (node.hasAttribute(DATA_BD_HIDE_SAVED_VALUE)) { displayValue = node.getAttribute(DATA_BD_HIDE_SAVED_VALUE); node.removeAttribute(DATA_BD_HIDE_SAVED_VALUE); } node.style.display = displayValue; } }); } function getMaxZIndex(parent) { const children = parent.childNodes; const end = children.length; let node, cs, max = 0, i = 0; while (i < end) { node = children[i++]; cs = node && node.nodeType === 1 && getComputedStyle(node); max = Math.max(max, (cs && cs.zIndex && Number(cs.zIndex)) || 0); } return max; } function destroyDomChildren(node) { let childNode; while ((childNode = node.lastChild)) { node.removeChild(childNode); } } function destroyDomNode(node) { node && node.parentNode && node.parentNode.removeChild(node); } function connect(target, type, listener, useCapture) { let destroyed = false; useCapture = !!useCapture; target.addEventListener(type, listener, useCapture); return { destroy() { if (!destroyed) { destroyed = true; target.removeEventListener(type, listener, useCapture); } } }; } function stopEvent(event) { if (event && event.preventDefault) { event.preventDefault(); event.stopPropagation(); } } function animate(node, className, onComplete) { const h = connect(node, 'animationend', e => { if (e.animationName === className) { h.destroy(); node.classList.remove(className); if (onComplete) { onComplete.destroy ? onComplete.destroy() : onComplete(); } } }); node.classList.add(className); } function flattenChildren(children) { // children can be falsey, single children (of type Element or string), or arrays of single children, arbitrarily deep const result = []; function flatten_(child) { if (Array.isArray(child)) { child.forEach(flatten_); } else if (child) { result.push(child); } } flatten_(children); return result; } class Element { constructor(type, props, ...children) { if (type instanceof Element) { // copy constructor this.type = type.type; type.isComponentType && (this.isComponentType = type.isComponentType); type.ctorProps && (this.ctorProps = type.ctorProps); type.ppFuncs && (this.ppFuncs = type.ppFuncs); type.children && (this.children = type.children); } else { // type must either be a constructor (a function) or a string; guarantee that as follows... if (type instanceof Function) { this.isComponentType = true; this.type = type; } else if (type) { // leave this.isComponentType === undefined this.type = Array.isArray(type) ? type : `${type}`; } else { throw new Error('type is required'); } // if the second arg is an Object and not an Element or and Array, then it is props... if (props) { if (props instanceof Element || Array.isArray(props)) { children.unshift(props); this.ctorProps = {}; } else if (props instanceof Object) { const ctorProps = {}; const ppFuncs = {}; let ppFuncsExist = false; let match, ppf; const setPpFuncs = (ppKey, value) => { if (ppFuncs[ppKey]) { const dest = ppFuncs[ppKey]; Reflect.ownKeys(value).forEach(k => (dest[k] = value[k])); } else { ppFuncsExist = true; ppFuncs[ppKey] = value; } }; Reflect.ownKeys(props).forEach(k => { if ((ppf = getPostProcessingFunction(k))) { const value = ppf.bdTransform(null, props[k]); setPpFuncs(k, value); } else if ((match = k.match(/^([A-Za-z0-9$]+)_(.+)$/)) && (ppf = getPostProcessingFunction(match[1]))) { const ppKey = match[1]; const value = ppf.bdTransform(match[2], props[k]); setPpFuncs(ppKey, value); } else { ctorProps[k] = props[k]; } }); this.ctorProps = Object.freeze(ctorProps); if (ppFuncsExist) { this.ppFuncs = Object.freeze(ppFuncs); } } else { children.unshift(props); this.ctorProps = {}; } } else { this.ctorProps = {}; } const flattenedChildren = flattenChildren(children); if (flattenedChildren.length === 1) { const child = flattenedChildren[0]; this.children = child instanceof Element ? child : `${child}`; } else if (flattenedChildren.length) { this.children = flattenedChildren.map(child => (child instanceof Element ? child : `${child}`)); Object.freeze(this.children); }// else children.length===0; therefore, no children } Object.freeze(this); } } function element(type, props, ...children) { // make elements without having to use new return new Element(type, props, children); } element.addElementType = function addElementType(type) { // type is either a constructor (a function) or a string if (typeof type === 'function') { if (type.name in element) { // eslint-disable-next-line no-console console.error(type.name, 'already in element'); } else { element[type.name] = (props, ...children) => new Element(type, props, children); } } else { // eslint-disable-next-line no-lonely-if if (type in element) { // eslint-disable-next-line no-console console.error(type, 'already in element'); } else { element[type] = (props, ...children) => new Element(type, props, children); } } }; 'a.abbr.address.area.article.aside.audio.base.bdi.bdo.blockquote.br.button.canvas.caption.cite.code.col.colgroup.data.datalist.dd.del.details.dfn.div.dl.dt.em.embed.fieldset.figcaption.figure.footer.form.h1.h2.h3.h4.h5.h6.h7.head.header.hr.html.i.iframe.img.input.ins.kbd.label.legend.li.link.main.map.mark.meta.meter.nav.noscript.object.ol.optgroup.option.output.p.param.picture.pre.progress.q.rb.rp.rt.rtc.ruby.s.samp.script.section.select.slot.small.source.span.strong.style.sub.summary.sup.table.tbody.td.template.textarea.tfoot.th.thead.time.title.tr.track.u.ul.var.video.wbr'.split('.').forEach(element.addElementType); function div(props, ...children) { // eslint-disable-next-line no-console console.warn('deprecated: use e.div'); return new Element('div', props, children); } const SVG = Object.create(null, { toString: { value: () => 'http://www.w3.org/2000/svg' } }); Object.freeze(SVG); function svg(type, props, ...children) { if (typeof type !== 'string') { children.unshift(props); props = type; type = 'svg'; } return new Element([SVG, type], props, children); } 'altGlyph.altGlyphDef.altGlyphItem.animate.animateColor.animateMotion.animateTransform.circle.clipPath.colorprofile.cursor.defs.desc.ellipse.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feDistantLight.feFlood.feFuncA.feFuncB.feFuncG.feFuncR.feGaussianBlur.feImage.feMerge.feMergeNode.feMorphology.feOffset.fePointLight.feSpecularLighting.feSpotLight.feTile.feTurbulence.filter.font.fontface.fontfaceformat.fontfacename.fontfacesrc.fontfaceuri.foreignObject.g.glyph.glyphRef.hkern.image.line.linearGradient.marker.mask.metadata.missingglyph.mpath.path.pattern.polygon.polyline.radialGradient.rect.script.set.stop.style.svg.switch.symbol.text.textPath.title.tref.tspan.use.view.vkern'.split('.').forEach(tag => { svg[tag] = (props, ...children) => svg(tag, props, children); }); const listenerCatalog = new WeakMap(); function eventHub(superClass) { return class extends (superClass || class { }) { // protected interface... bdNotify(e) { const events = listenerCatalog.get(this); if (!events) { return; } let handlers; if (e instanceof Event) { handlers = events[e.type]; } else { // eslint-disable-next-line no-lonely-if if (e.type) { handlers = events[e.type]; e.target = this; } else if (!e.name) { handlers = events[e]; e = {type: e, name: e, target: this}; } else { // eslint-disable-next-line no-console console.warn('event.name is deprecated; use event.type'); handlers = events[e.name]; e.type = e.name; e.target = this; } } if (handlers) { handlers.slice().forEach(theDestroyable => theDestroyable.proc(e)); } if ((handlers = events[STAR])) { handlers.slice().forEach(theDestroyable => theDestroyable.proc(e)); } } // public interface... get isBdEventHub() { return true; } advise(eventName, handler) { if (!handler) { const hash = eventName; return Reflect.ownKeys(hash).map(key => this.advise(key, hash[key])); } else if (Array.isArray(eventName)) { return eventName.map(name => this.advise(name, handler)); } else { let events = listenerCatalog.get(this); if (!events) { listenerCatalog.set(this, (events = {})); } const result = new Destroyable(handler, events[eventName] || (events[eventName] = [])); this.own && this.own(result); return result; } } destroyAdvise(eventName) { const events = listenerCatalog.get(this); if (!events) { return; } if (eventName) { const handlers = events[eventName]; if (handlers) { handlers.forEach(h => h.destroy()); delete events[eventName]; } } else { // eslint-disable-next-line no-shadow Reflect.ownKeys(events).forEach(eventName => { events[eventName].forEach(h => h.destroy()); }); listenerCatalog.delete(this); } } }; } const EventHub = eventHub(); let document$1 = 0; adviseGlobal(window => { document$1 = window.document; }); function cleanClassName(s) { return s.re