bd-core
Version:
a JavaScript library for building browser-hosted user interfaces
1,340 lines (1,230 loc) • 120 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.bd = {}));
}(this, (function (exports) { 'use strict';
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) {
// es