You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1983 lines
57 KiB

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Pillar = factory());
}(this, (function () { 'use strict';
/** Virtual DOM Node */
function VNode() {}
/** Global options
* @public
* @namespace options {Object}
*/
var options = {
/** If `true`, `prop` changes trigger synchronous component updates.
* @name syncComponentUpdates
* @type Boolean
* @default true
*/
//syncComponentUpdates: true,
/** Processes all created VNodes.
* @param {VNode} vnode A newly-created VNode to normalize/process
*/
//vnode(vnode) { }
/** Hook invoked after a component is mounted. */
// afterMount(component) { }
/** Hook invoked after the DOM is updated with a component's latest render. */
// afterUpdate(component) { }
/** Hook invoked immediately before a component is unmounted. */
// beforeUnmount(component) { }
};
var stack = [];
var EMPTY_CHILDREN = [];
/** JSX/hyperscript reviver
* Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0
* @see http://jasonformat.com/wtf-is-jsx
* @public
*/
function h(nodeName, attributes) {
var children = EMPTY_CHILDREN,
lastSimple,
child,
simple,
i;
for (i = arguments.length; i-- > 2;) {
stack.push(arguments[i]);
}
if (attributes && attributes.children != null) {
if (!stack.length) stack.push(attributes.children);
delete attributes.children;
}
while (stack.length) {
if ((child = stack.pop()) && child.pop !== undefined) {
for (i = child.length; i--;) {
stack.push(child[i]);
}
} else {
if (typeof child === 'boolean') child = null;
if (simple = typeof nodeName !== 'function') {
if (child == null) child = '';else if (typeof child === 'number') child = String(child);else if (typeof child !== 'string') simple = false;
}
if (simple && lastSimple) {
children[children.length - 1] += child;
} else if (children === EMPTY_CHILDREN) {
children = [child];
} else {
children.push(child);
}
lastSimple = simple;
}
}
var p = new VNode();
p.nodeName = nodeName;
p.children = children;
p.attributes = attributes == null ? undefined : attributes;
p.key = attributes == null ? undefined : attributes.key;
// if a "vnode hook" is defined, pass every created VNode to it
if (options.vnode !== undefined) options.vnode(p);
return p;
}
/** Copy own-properties from `props` onto `obj`.
* @returns obj
* @private
*/
function extend(obj, props) {
for (var i in props) {
obj[i] = props[i];
}return obj;
}
/** Call a function asynchronously, as soon as possible.
* @param {Function} callback
*/
var defer = typeof Promise == 'function' ? Promise.resolve().then.bind(Promise.resolve()) : setTimeout;
function cloneElement(vnode, props) {
return h(vnode.nodeName, extend(extend({}, vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
}
// DOM properties that should NOT have "px" added when numeric
var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
/** Managed queue of dirty components to be re-rendered */
var items = [];
function enqueueRender(component) {
if (!component._dirty && (component._dirty = true) && items.push(component) == 1) {
(options.debounceRendering || defer)(rerender);
}
}
function rerender() {
var p,
list = items;
items = [];
while (p = list.pop()) {
if (p._dirty) renderComponent(p);
}
}
/** Check if two nodes are equivalent.
* @param {Element} node
* @param {VNode} vnode
* @private
*/
function isSameNodeType(node, vnode, hydrating) {
if (typeof vnode === 'string' || typeof vnode === 'number') {
return node.splitText !== undefined;
}
if (typeof vnode.nodeName === 'string') {
return !node._componentConstructor && isNamedNode(node, vnode.nodeName);
}
return hydrating || node._componentConstructor === vnode.nodeName;
}
/** Check if an Element has a given normalized name.
* @param {Element} node
* @param {String} nodeName
*/
function isNamedNode(node, nodeName) {
return node.normalizedNodeName === nodeName || node.nodeName.toLowerCase() === nodeName.toLowerCase();
}
/**
* Reconstruct Component-style `props` from a VNode.
* Ensures default/fallback values from `defaultProps`:
* Own-properties of `defaultProps` not present in `vnode.attributes` are added.
* @param {VNode} vnode
* @returns {Object} props
*/
function getNodeProps(vnode) {
var props = extend({}, vnode.attributes);
props.children = vnode.children;
var defaultProps = vnode.nodeName.defaultProps;
if (defaultProps !== undefined) {
for (var i in defaultProps) {
if (props[i] === undefined) {
props[i] = defaultProps[i];
}
}
}
return props;
}
/** Create an element with the given nodeName.
* @param {String} nodeName
* @param {Boolean} [isSvg=false] If `true`, creates an element within the SVG namespace.
* @returns {Element} node
*/
function createNode(nodeName, isSvg) {
var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
node.normalizedNodeName = nodeName;
return node;
}
/** Remove a child node from its parent if attached.
* @param {Element} node The node to remove
*/
function removeNode(node) {
var parentNode = node.parentNode;
if (parentNode) parentNode.removeChild(node);
}
/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
* If `value` is `null`, the attribute/handler will be removed.
* @param {Element} node An element to mutate
* @param {string} name The name/key to set, such as an event or attribute name
* @param {any} old The last value that was set for this name/node pair
* @param {any} value An attribute value, such as a function to be used as an event handler
* @param {Boolean} isSvg Are we currently diffing inside an svg?
* @private
*/
function setAccessor(node, name, old, value, isSvg) {
if (name === 'className') name = 'class';
if (name === 'key') {
// ignore
} else if (name === 'ref') {
if (old) old(null);
if (value) value(node);
} else if (name === 'class' && !isSvg) {
node.className = value || '';
} else if (name === 'style') {
if (!value || typeof value === 'string' || typeof old === 'string') {
node.style.cssText = value || '';
}
if (value && typeof value === 'object') {
if (typeof old !== 'string') {
for (var i in old) {
if (!(i in value)) node.style[i] = '';
}
}
for (var i in value) {
node.style[i] = typeof value[i] === 'number' && IS_NON_DIMENSIONAL.test(i) === false ? value[i] + 'px' : value[i];
}
}
} else if (name === 'dangerouslySetInnerHTML') {
if (value) node.innerHTML = value.__html || '';
} else if (name[0] == 'o' && name[1] == 'n') {
var useCapture = name !== (name = name.replace(/Capture$/, ''));
name = name.toLowerCase().substring(2);
if (value) {
if (!old) node.addEventListener(name, eventProxy, useCapture);
} else {
node.removeEventListener(name, eventProxy, useCapture);
}
(node._listeners || (node._listeners = {}))[name] = value;
} else if (name !== 'list' && name !== 'type' && !isSvg && name in node) {
setProperty(node, name, value == null ? '' : value);
if (value == null || value === false) node.removeAttribute(name);
} else {
var ns = isSvg && name !== (name = name.replace(/^xlink\:?/, ''));
if (value == null || value === false) {
if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase());else node.removeAttribute(name);
} else if (typeof value !== 'function') {
if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', name.toLowerCase(), value);else node.setAttribute(name, value);
}
}
}
/** Attempt to set a DOM property to the given value.
* IE & FF throw for certain property-value combinations.
*/
function setProperty(node, name, value) {
try {
node[name] = value;
} catch (e) {}
}
/** Proxy an event to hooked event handlers
* @private
*/
function eventProxy(e) {
return this._listeners[e.type](options.event && options.event(e) || e);
}
/** Queue of components that have been mounted and are awaiting componentDidMount */
var mounts = [];
/** Diff recursion count, used to track the end of the diff cycle. */
var diffLevel = 0;
/** Global flag indicating if the diff is currently within an SVG */
var isSvgMode = false;
/** Global flag indicating if the diff is performing hydration */
var hydrating = false;
/** Invoke queued componentDidMount lifecycle methods */
function flushMounts() {
var c;
while (c = mounts.pop()) {
if (options.afterMount) options.afterMount(c);
if (c.componentDidMount) c.componentDidMount();
}
}
/** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
* @param {Element} [dom=null] A DOM node to mutate into the shape of the `vnode`
* @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure
* @returns {Element} dom The created/mutated element
* @private
*/
function diff(dom, vnode, context, mountAll, parent, componentRoot) {
// diffLevel having been 0 here indicates initial entry into the diff (not a subdiff)
if (!diffLevel++) {
// when first starting the diff, check if we're diffing an SVG or within an SVG
isSvgMode = parent != null && parent.ownerSVGElement !== undefined;
// hydration is indicated by the existing element to be diffed not having a prop cache
hydrating = dom != null && !('__preactattr_' in dom);
}
var ret = idiff(dom, vnode, context, mountAll, componentRoot);
// append the element if its a new parent
if (parent && ret.parentNode !== parent) parent.appendChild(ret);
// diffLevel being reduced to 0 means we're exiting the diff
if (! --diffLevel) {
hydrating = false;
// invoke queued componentDidMount lifecycle methods
if (!componentRoot) flushMounts();
}
return ret;
}
/** Internals of `diff()`, separated to allow bypassing diffLevel / mount flushing. */
function idiff(dom, vnode, context, mountAll, componentRoot) {
var out = dom,
prevSvgMode = isSvgMode;
// empty values (null, undefined, booleans) render as empty Text nodes
if (vnode == null || typeof vnode === 'boolean') vnode = '';
// Fast case: Strings & Numbers create/update Text nodes.
if (typeof vnode === 'string' || typeof vnode === 'number') {
// update if it's already a Text node:
if (dom && dom.splitText !== undefined && dom.parentNode && (!dom._component || componentRoot)) {
/* istanbul ignore if */ /* Browser quirk that can't be covered: https://github.com/developit/preact/commit/fd4f21f5c45dfd75151bd27b4c217d8003aa5eb9 */
if (dom.nodeValue != vnode) {
dom.nodeValue = vnode;
}
} else {
// it wasn't a Text node: replace it with one and recycle the old Element
out = document.createTextNode(vnode);
if (dom) {
if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
recollectNodeTree(dom, true);
}
}
out['__preactattr_'] = true;
return out;
}
// If the VNode represents a Component, perform a component diff:
var vnodeName = vnode.nodeName;
if (typeof vnodeName === 'function') {
return buildComponentFromVNode(dom, vnode, context, mountAll);
}
// Tracks entering and exiting SVG namespace when descending through the tree.
isSvgMode = vnodeName === 'svg' ? true : vnodeName === 'foreignObject' ? false : isSvgMode;
// If there's no existing element or it's the wrong type, create a new one:
vnodeName = String(vnodeName);
if (!dom || !isNamedNode(dom, vnodeName)) {
out = createNode(vnodeName, isSvgMode);
if (dom) {
// move children into the replacement node
while (dom.firstChild) {
out.appendChild(dom.firstChild);
} // if the previous Element was mounted into the DOM, replace it inline
if (dom.parentNode) dom.parentNode.replaceChild(out, dom);
// recycle the old element (skips non-Element node types)
recollectNodeTree(dom, true);
}
}
var fc = out.firstChild,
props = out['__preactattr_'],
vchildren = vnode.children;
if (props == null) {
props = out['__preactattr_'] = {};
for (var a = out.attributes, i = a.length; i--;) {
props[a[i].name] = a[i].value;
}
}
// Optimization: fast-path for elements containing a single TextNode:
if (!hydrating && vchildren && vchildren.length === 1 && typeof vchildren[0] === 'string' && fc != null && fc.splitText !== undefined && fc.nextSibling == null) {
if (fc.nodeValue != vchildren[0]) {
fc.nodeValue = vchildren[0];
}
}
// otherwise, if there are existing or new children, diff them:
else if (vchildren && vchildren.length || fc != null) {
innerDiffNode(out, vchildren, context, mountAll, hydrating || props.dangerouslySetInnerHTML != null);
}
// Apply attributes/props from VNode to the DOM Element:
diffAttributes(out, vnode.attributes, props);
// restore previous SVG mode: (in case we're exiting an SVG namespace)
isSvgMode = prevSvgMode;
return out;
}
/** Apply child and attribute changes between a VNode and a DOM Node to the DOM.
* @param {Element} dom Element whose children should be compared & mutated
* @param {Array} vchildren Array of VNodes to compare to `dom.childNodes`
* @param {Object} context Implicitly descendant context object (from most recent `getChildContext()`)
* @param {Boolean} mountAll
* @param {Boolean} isHydrating If `true`, consumes externally created elements similar to hydration
*/
function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
var originalChildren = dom.childNodes,
children = [],
keyed = {},
keyedLen = 0,
min = 0,
len = originalChildren.length,
childrenLen = 0,
vlen = vchildren ? vchildren.length : 0,
j,
c,
f,
vchild,
child;
// Build up a map of keyed children and an Array of unkeyed children:
if (len !== 0) {
for (var i = 0; i < len; i++) {
var _child = originalChildren[i],
props = _child['__preactattr_'],
key = vlen && props ? _child._component ? _child._component.__key : props.key : null;
if (key != null) {
keyedLen++;
keyed[key] = _child;
} else if (props || (_child.splitText !== undefined ? isHydrating ? _child.nodeValue.trim() : true : isHydrating)) {
children[childrenLen++] = _child;
}
}
}
if (vlen !== 0) {
for (var i = 0; i < vlen; i++) {
vchild = vchildren[i];
child = null;
// attempt to find a node based on key matching
var key = vchild.key;
if (key != null) {
if (keyedLen && keyed[key] !== undefined) {
child = keyed[key];
keyed[key] = undefined;
keyedLen--;
}
}
// attempt to pluck a node of the same type from the existing children
else if (!child && min < childrenLen) {
for (j = min; j < childrenLen; j++) {
if (children[j] !== undefined && isSameNodeType(c = children[j], vchild, isHydrating)) {
child = c;
children[j] = undefined;
if (j === childrenLen - 1) childrenLen--;
if (j === min) min++;
break;
}
}
}
// morph the matched/found/created DOM child to match vchild (deep)
child = idiff(child, vchild, context, mountAll);
f = originalChildren[i];
if (child && child !== dom && child !== f) {
if (f == null) {
dom.appendChild(child);
} else if (child === f.nextSibling) {
removeNode(f);
} else {
dom.insertBefore(child, f);
}
}
}
}
// remove unused keyed children:
if (keyedLen) {
for (var i in keyed) {
if (keyed[i] !== undefined) recollectNodeTree(keyed[i], false);
}
}
// remove orphaned unkeyed children:
while (min <= childrenLen) {
if ((child = children[childrenLen--]) !== undefined) recollectNodeTree(child, false);
}
}
/** Recursively recycle (or just unmount) a node and its descendants.
* @param {Node} node DOM node to start unmount/removal from
* @param {Boolean} [unmountOnly=false] If `true`, only triggers unmount lifecycle, skips removal
*/
function recollectNodeTree(node, unmountOnly) {
var component = node._component;
if (component) {
// if node is owned by a Component, unmount that component (ends up recursing back here)
unmountComponent(component);
} else {
// If the node's VNode had a ref function, invoke it with null here.
// (this is part of the React spec, and smart for unsetting references)
if (node['__preactattr_'] != null && node['__preactattr_'].ref) node['__preactattr_'].ref(null);
if (unmountOnly === false || node['__preactattr_'] == null) {
removeNode(node);
}
removeChildren(node);
}
}
/** Recollect/unmount all children.
* - we use .lastChild here because it causes less reflow than .firstChild
* - it's also cheaper than accessing the .childNodes Live NodeList
*/
function removeChildren(node) {
node = node.lastChild;
while (node) {
var next = node.previousSibling;
recollectNodeTree(node, true);
node = next;
}
}
/** Apply differences in attributes from a VNode to the given DOM Element.
* @param {Element} dom Element with attributes to diff `attrs` against
* @param {Object} attrs The desired end-state key-value attribute pairs
* @param {Object} old Current/previous attributes (from previous VNode or element's prop cache)
*/
function diffAttributes(dom, attrs, old) {
var name;
// remove attributes no longer present on the vnode by setting them to undefined
for (name in old) {
if (!(attrs && attrs[name] != null) && old[name] != null) {
setAccessor(dom, name, old[name], old[name] = undefined, isSvgMode);
}
}
// add new & update changed attributes
for (name in attrs) {
if (name !== 'children' && name !== 'innerHTML' && (!(name in old) || attrs[name] !== (name === 'value' || name === 'checked' ? dom[name] : old[name]))) {
setAccessor(dom, name, old[name], old[name] = attrs[name], isSvgMode);
}
}
}
/** Retains a pool of Components for re-use, keyed on component name.
* Note: since component names are not unique or even necessarily available, these are primarily a form of sharding.
* @private
*/
var components = {};
/** Reclaim a component for later re-use by the recycler. */
function collectComponent(component) {
var name = component.constructor.name;
(components[name] || (components[name] = [])).push(component);
}
/** Create a component. Normalizes differences between PFC's and classful Components. */
function createComponent(Ctor, props, context) {
var list = components[Ctor.name],
inst;
if (Ctor.prototype && Ctor.prototype.render) {
inst = new Ctor(props, context);
Component.call(inst, props, context);
} else {
inst = new Component(props, context);
inst.constructor = Ctor;
inst.render = doRender;
}
if (list) {
for (var i = list.length; i--;) {
if (list[i].constructor === Ctor) {
inst.nextBase = list[i].nextBase;
list.splice(i, 1);
break;
}
}
}
return inst;
}
/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
return this.constructor(props, context);
}
/** Set a component's `props` (generally derived from JSX attributes).
* @param {Object} props
* @param {Object} [opts]
* @param {boolean} [opts.renderSync=false] If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
* @param {boolean} [opts.render=true] If `false`, no render will be triggered.
*/
function setComponentProps(component, props, opts, context, mountAll) {
if (component._disable) return;
component._disable = true;
if (component.__ref = props.ref) delete props.ref;
if (component.__key = props.key) delete props.key;
if (!component.base || mountAll) {
if (component.componentWillMount) component.componentWillMount();
} else if (component.componentWillReceiveProps) {
component.componentWillReceiveProps(props, context);
}
if (context && context !== component.context) {
if (!component.prevContext) component.prevContext = component.context;
component.context = context;
}
if (!component.prevProps) component.prevProps = component.props;
component.props = props;
component._disable = false;
if (opts !== 0) {
if (opts === 1 || options.syncComponentUpdates !== false || !component.base) {
renderComponent(component, 1, mountAll);
} else {
enqueueRender(component);
}
}
if (component.__ref) component.__ref(component);
}
/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
* @param {Component} component
* @param {Object} [opts]
* @param {boolean} [opts.build=false] If `true`, component will build and store a DOM node if not already associated with one.
* @private
*/
function renderComponent(component, opts, mountAll, isChild) {
if (component._disable) return;
var props = component.props,
state = component.state,
context = component.context,
previousProps = component.prevProps || props,
previousState = component.prevState || state,
previousContext = component.prevContext || context,
isUpdate = component.base,
nextBase = component.nextBase,
initialBase = isUpdate || nextBase,
initialChildComponent = component._component,
skip = false,
rendered,
inst,
cbase;
// if updating
if (isUpdate) {
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (opts !== 2 && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) {
skip = true;
} else if (component.componentWillUpdate) {
component.componentWillUpdate(props, state, context);
}
component.props = props;
component.state = state;
component.context = context;
}
component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
component._dirty = false;
if (!skip) {
rendered = component.render(props, state, context);
// context to pass to the child, can be updated via (grand-)parent component
if (component.getChildContext) {
context = extend(extend({}, context), component.getChildContext());
}
var childComponent = rendered && rendered.nodeName,
toUnmount,
base;
if (typeof childComponent === 'function') {
// set up high order component link
var childProps = getNodeProps(rendered);
inst = initialChildComponent;
if (inst && inst.constructor === childComponent && childProps.key == inst.__key) {
setComponentProps(inst, childProps, 1, context, false);
} else {
toUnmount = inst;
component._component = inst = createComponent(childComponent, childProps, context);
inst.nextBase = inst.nextBase || nextBase;
inst._parentComponent = component;
setComponentProps(inst, childProps, 0, context, false);
renderComponent(inst, 1, mountAll, true);
}
base = inst.base;
} else {
cbase = initialBase;
// destroy high order component link
toUnmount = initialChildComponent;
if (toUnmount) {
cbase = component._component = null;
}
if (initialBase || opts === 1) {
if (cbase) cbase._component = null;
base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
}
}
if (initialBase && base !== initialBase && inst !== initialChildComponent) {
var baseParent = initialBase.parentNode;
if (baseParent && base !== baseParent) {
baseParent.replaceChild(base, initialBase);
if (!toUnmount) {
initialBase._component = null;
recollectNodeTree(initialBase, false);
}
}
}
if (toUnmount) {
unmountComponent(toUnmount);
}
component.base = base;
if (base && !isChild) {
var componentRef = component,
t = component;
while (t = t._parentComponent) {
(componentRef = t).base = base;
}
base._component = componentRef;
base._componentConstructor = componentRef.constructor;
}
}
if (!isUpdate || mountAll) {
mounts.unshift(component);
} else if (!skip) {
// Ensure that pending componentDidMount() hooks of child components
// are called before the componentDidUpdate() hook in the parent.
// Note: disabled as it causes duplicate hooks, see https://github.com/developit/preact/issues/750
// flushMounts();
if (component.componentDidUpdate) {
component.componentDidUpdate(previousProps, previousState, previousContext);
}
if (options.afterUpdate) options.afterUpdate(component);
}
if (component._renderCallbacks != null) {
while (component._renderCallbacks.length) {
component._renderCallbacks.pop().call(component);
}
}
if (!diffLevel && !isChild) flushMounts();
}
/** Apply the Component referenced by a VNode to the DOM.
* @param {Element} dom The DOM node to mutate
* @param {VNode} vnode A Component-referencing VNode
* @returns {Element} dom The created/mutated element
* @private
*/
function buildComponentFromVNode(dom, vnode, context, mountAll) {
var c = dom && dom._component,
originalComponent = c,
oldDom = dom,
isDirectOwner = c && dom._componentConstructor === vnode.nodeName,
isOwner = isDirectOwner,
props = getNodeProps(vnode);
while (c && !isOwner && (c = c._parentComponent)) {
isOwner = c.constructor === vnode.nodeName;
}
if (c && isOwner && (!mountAll || c._component)) {
setComponentProps(c, props, 3, context, mountAll);
dom = c.base;
} else {
if (originalComponent && !isDirectOwner) {
unmountComponent(originalComponent);
dom = oldDom = null;
}
c = createComponent(vnode.nodeName, props, context);
if (dom && !c.nextBase) {
c.nextBase = dom;
// passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
oldDom = null;
}
setComponentProps(c, props, 1, context, mountAll);
dom = c.base;
if (oldDom && dom !== oldDom) {
oldDom._component = null;
recollectNodeTree(oldDom, false);
}
}
return dom;
}
/** Remove a component from the DOM and recycle it.
* @param {Component} component The Component instance to unmount
* @private
*/
function unmountComponent(component) {
if (options.beforeUnmount) options.beforeUnmount(component);
var base = component.base;
component._disable = true;
if (component.componentWillUnmount) component.componentWillUnmount();
component.base = null;
// recursively tear down & recollect high-order component children:
var inner = component._component;
if (inner) {
unmountComponent(inner);
} else if (base) {
if (base['__preactattr_'] && base['__preactattr_'].ref) base['__preactattr_'].ref(null);
component.nextBase = base;
removeNode(base);
collectComponent(component);
removeChildren(base);
}
if (component.__ref) component.__ref(null);
}
/** Base Component class.
* Provides `setState()` and `forceUpdate()`, which trigger rendering.
* @public
*
* @example
* class MyFoo extends Component {
* render(props, state) {
* return <div />;
* }
* }
*/
function Component(props, context) {
this._dirty = true;
/** @public
* @type {object}
*/
this.context = context;
/** @public
* @type {object}
*/
this.props = props;
/** @public
* @type {object}
*/
this.state = this.state || {};
}
extend(Component.prototype, {
/** Returns a `boolean` indicating if the component should re-render when receiving the given `props` and `state`.
* @param {object} nextProps
* @param {object} nextState
* @param {object} nextContext
* @returns {Boolean} should the component re-render
* @name shouldComponentUpdate
* @function
*/
/** Update component state by copying properties from `state` to `this.state`.
* @param {object} state A hash of state properties to update with new values
* @param {function} callback A function to be called once component state is updated
*/
setState: function setState(state, callback) {
var s = this.state;
if (!this.prevState) this.prevState = extend({}, s);
extend(s, typeof state === 'function' ? state(s, this.props) : state);
if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);
enqueueRender(this);
},
/** Immediately perform a synchronous re-render of the component.
* @param {function} callback A function to be called after component is re-rendered.
* @private
*/
forceUpdate: function forceUpdate(callback) {
if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);
renderComponent(this, 2);
},
/** Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
* Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
* @param {object} props Props (eg: JSX attributes) received from parent element/component
* @param {object} state The component's current state
* @param {object} context Context object (if a parent component has provided context)
* @returns VNode
*/
render: function render() {}
});
/** Render JSX into a `parent` Element.
* @param {VNode} vnode A (JSX) VNode to render
* @param {Element} parent DOM element to render into
* @param {Element} [merge] Attempt to re-use an existing DOM tree rooted at `merge`
* @public
*
* @example
* // render a div into <body>:
* render(<div id="hello">hello!</div>, document.body);
*
* @example
* // render a "Thing" component into #foo:
* const Thing = ({ name }) => <span>{ name }</span>;
* render(<Thing name="one" />, document.querySelector('#foo'));
*/
function render(vnode, parent, merge) {
return diff(merge, vnode, {}, false, parent, false);
}
var preact = {
h: h,
createElement: h,
cloneElement: cloneElement,
Component: Component,
render: render,
rerender: rerender,
options: options
};
var preact_esm = Object.freeze({
h: h,
createElement: h,
cloneElement: cloneElement,
Component: Component,
render: render,
rerender: rerender,
options: options,
default: preact
});
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
* to provide information about what broke and what you were
* expecting.
*
* The invariant message will be stripped in production, but the invariant
* will remain to ensure logic does not differ in production.
*/
var validateFormat = function validateFormat(format) {};
{
validateFormat = function validateFormat(format) {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
};
}
function invariant(condition, format, a, b, c, d, e, f) {
validateFormat(format);
if (!condition) {
var error;
if (format === undefined) {
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
} else {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(format.replace(/%s/g, function () {
return args[argIndex++];
}));
error.name = 'Invariant Violation';
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
var invariant_1 = invariant;
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var require$$0 = ( preact_esm && preact ) || preact_esm;
var preactMarkup = createCommonjsModule(function (module, exports) {
(function (global, factory) {
module.exports = factory(require$$0);
}(commonjsGlobal, function (preact) { var parserDoc = void 0;
function parseMarkup(markup, type) {
var doc = void 0,
mime = type === 'html' ? 'text/html' : 'application/xml',
parserError = void 0,
wrappedMarkup = void 0,
tag = void 0;
if (type === 'html') {
tag = 'body';
wrappedMarkup = '<!DOCTYPE html>\n<html><body>' + markup + '</body></html>';
} else {
tag = 'xml';
wrappedMarkup = '<?xml version="1.0" encoding="UTF-8"?>\n<xml>' + markup + '</xml>';
}
try {
doc = new DOMParser().parseFromString(wrappedMarkup, mime);
} catch (err) {
parserError = err;
}
if (!doc && type === 'html') {
doc = parserDoc || (parserDoc = buildParserFrame());
doc.open();
doc.write(wrappedMarkup);
doc.close();
}
if (!doc) return;
var out = doc.getElementsByTagName(tag)[0],
fc = out.firstChild;
if (markup && !fc) {
out.error = 'Document parse failed.';
}
if (fc && String(fc.nodeName).toLowerCase() === 'parsererror') {
fc.removeChild(fc.firstChild);
fc.removeChild(fc.lastChild);
out.error = fc.textContent || fc.nodeValue || parserError || 'Unknown error';
out.removeChild(fc);
}
return out;
}
function buildParserFrame() {
if (document.implementation && document.implementation.createHTMLDocument) {
return document.implementation.createHTMLDocument('');
}
var frame = document.createElement('iframe');
frame.style.cssText = 'position:absolute; left:0; top:-999em; width:1px; height:1px; overflow:hidden;';
frame.setAttribute('sandbox', 'allow-forms');
document.body.appendChild(frame);
return frame.contentWindow.document;
}
var EMPTY_OBJ$1 = {};
function toVdom(node, visitor, h, options) {
walk.visitor = visitor;
walk.h = h;
walk.options = options || EMPTY_OBJ$1;
return walk(node);
}
function walk(n, index, arr) {
if (n.nodeType === 3) {
var text = 'textContent' in n ? n.textContent : n.nodeValue || '';
if (walk.options.trim !== false) {
var isFirstOrLast = index === 0 || index === arr.length - 1;
if (text.match(/^[\s\n]+$/g) && walk.options.trim !== 'all') {
text = ' ';
} else {
text = text.replace(/(^[\s\n]+|[\s\n]+$)/g, walk.options.trim === 'all' || isFirstOrLast ? '' : ' ');
}
if ((!text || text === ' ') && arr.length > 1 && isFirstOrLast) return null;
}
return text;
}
if (n.nodeType !== 1) return null;
var nodeName = String(n.nodeName).toLowerCase();
if (nodeName === 'script' && !walk.options.allowScripts) return null;
var out = walk.h(nodeName, getProps(n.attributes), walkChildren(n.childNodes));
if (walk.visitor) walk.visitor(out);
return out;
}
function getProps(attrs) {
var len = attrs && attrs.length;
if (!len) return null;
var props = {};
for (var i = 0; i < len; i++) {
var _attrs$i = attrs[i];
var name = _attrs$i.name;
var value = _attrs$i.value;
if (value === '') value = true;
if (name.substring(0, 2) === 'on' && walk.options.allowEvents) {
value = new Function(value);
}
props[name] = value;
}
return props;
}
function walkChildren(children) {
var c = children && Array.prototype.map.call(children, walk).filter(exists);
return c && c.length ? c : null;
}
var exists = function (x) {
return x;
};
var EMPTY_OBJ = {};
function markupToVdom(markup, type, reviver, map, options) {
var dom = parseMarkup(markup, type);
if (dom && dom.error) {
throw new Error(dom.error);
}
var body = dom && dom.body || dom;
visitor.map = map || EMPTY_OBJ;
var vdom = body && toVdom(body, visitor, reviver, options);
visitor.map = null;
return vdom && vdom.children || null;
}
function toCamelCase(name) {
return name.replace(/-(.)/g, function (match, letter) {
return letter.toUpperCase();
});
}
function visitor(node) {
var name = node.nodeName.toLowerCase(),
map = visitor.map;
if (map && map.hasOwnProperty(name)) {
node.nodeName = map[name];
node.attributes = Object.keys(node.attributes || {}).reduce(function (attrs, attrName) {
attrs[toCamelCase(attrName)] = node.attributes[attrName];
return attrs;
}, {});
} else {
node.nodeName = name.replace(/[^a-z0-9-]/i, '');
}
}
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var objectWithoutProperties = function (obj, keys) {
var target = {};
for (var i in obj) {
if (keys.indexOf(i) >= 0) continue;
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
target[i] = obj[i];
}
return target;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var customReviver = void 0;
var Markup = function (_Component) {
inherits(Markup, _Component);
function Markup() {
classCallCheck(this, Markup);
return possibleConstructorReturn(this, _Component.apply(this, arguments));
}
Markup.setReviver = function setReviver(h) {
customReviver = h;
};
Markup.prototype.shouldComponentUpdate = function shouldComponentUpdate(_ref) {
var wrap = _ref.wrap;
var type = _ref.type;
var markup = _ref.markup;
var p = this.props;
return wrap !== p.wrap || type !== p.type || markup !== p.markup;
};
Markup.prototype.setComponents = function setComponents(components) {
this.map = {};
if (components) {
for (var i in components) {
if (components.hasOwnProperty(i)) {
var name = i.replace(/([A-Z]+)([A-Z][a-z0-9])|([a-z0-9]+)([A-Z])/g, '$1$3-$2$4').toLowerCase();
this.map[name] = components[i];
}
}
}
};
Markup.prototype.render = function render(_ref2) {
var _ref2$wrap = _ref2.wrap;
var wrap = _ref2$wrap === undefined ? true : _ref2$wrap;
var type = _ref2.type;
var markup = _ref2.markup;
var components = _ref2.components;
var reviver = _ref2.reviver;
var onError = _ref2.onError;
var allowScripts = _ref2['allow-scripts'];
var allowEvents = _ref2['allow-events'];
var trim = _ref2.trim;
var props = objectWithoutProperties(_ref2, ['wrap', 'type', 'markup', 'components', 'reviver', 'onError', 'allow-scripts', 'allow-events', 'trim']);
var h = reviver || this.reviver || this.constructor.prototype.reviver || customReviver || preact.h,
vdom = void 0;
this.setComponents(components);
var options = {
allowScripts: allowScripts,
allowEvents: allowEvents,
trim: trim
};
try {
vdom = markupToVdom(markup, type, h, this.map, options);
} catch (error) {
if (onError) {
onError({ error: error });
} else if (typeof console !== 'undefined' && console.error) {
console.error('preact-markup: ' + error);
}
}
if (wrap === false) return vdom && vdom[0] || null;
var c = props.hasOwnProperty('className') ? 'className' : 'class',
cl = props[c];
if (!cl) props[c] = 'markup';else if (cl.splice) cl.splice(0, 0, 'markup');else if (typeof cl === 'string') props[c] += ' markup';else if (typeof cl === 'object') cl.markup = true;
return h('div', props, vdom || null);
};
return Markup;
}(preact.Component);
return Markup;
}));
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
/**
* Minimal implementation of Mustache templates
* Taken from https://github.com/aishikaty/tiny-mustache
*/
/* eslint-disable */
function mustache(template, self, parent, invert) {
var render = mustache;
var output = '';
var i = void 0;
function get$$1(ctx, path) {
path = path.pop ? path : path.split('.');
ctx = ctx[path.shift()] || '';
return 0 in path ? get$$1(ctx, path) : ctx;
}
self = Array.isArray(self) ? self : self ? [self] : [];
self = invert ? 0 in self ? [] : [1] : self;
for (i = 0; i < self.length; i++) {
var childCode = '';
var depth = 0;
var inverted;
var ctx = _typeof(self[i]) === 'object' ? self[i] : {};
ctx = Object.assign({}, parent, ctx);
ctx[''] = { '': self[i] };
template.replace(/([\s\S]*?)({{((\/)|(\^)|#)(.*?)}}|$)/g, function (match, code, y, z, close, invert, name) {
if (!depth) {
output += code.replace(/{{{(.*?)}}}|{{(!?)(&?)(>?)(.*?)}}/g, function (match, raw, comment, isRaw, partial, name) {
return raw ? get$$1(ctx, raw) : isRaw ? get$$1(ctx, name) : partial ? render(get$$1(ctx, name), ctx) : !comment ? new Option(get$$1(ctx, name)).innerHTML : '';
});
inverted = invert;
} else {
childCode += depth && !close || depth > 1 ? match : code;
}
if (close) {
if (! --depth) {
name = get$$1(ctx, name);
if (/^f/.test(typeof name === 'undefined' ? 'undefined' : _typeof(name))) {
output += name.call(ctx, childCode, function (template) {
return render(template, ctx);
});
} else {
output += render(childCode, name, ctx, inverted);
}
childCode = '';
}
} else {
++depth;
}
});
}
return output;
}
// This is used for emptying VDOM
var Null = function Null() {
return null;
};
/* eslint-disable no-underscore-dangle */
var PillarBaseElement = function (_HTMLElement) {
inherits(PillarBaseElement, _HTMLElement);
function PillarBaseElement() {
var _ref;
var _temp, _this, _ret;
classCallCheck(this, PillarBaseElement);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = possibleConstructorReturn(this, (_ref = PillarBaseElement.__proto__ || Object.getPrototypeOf(PillarBaseElement)).call.apply(_ref, [this].concat(args))), _this), _this.props = {
flags: {}
}, _temp), possibleConstructorReturn(_this, _ret);
}
createClass(PillarBaseElement, [{
key: 'convertToVdom',
/**
* Convert to VDOM
*
* @description Converts given HTML node into a Virtual DOM node
* @param {HTMLElement} element - Element to convert
* @param {VNode} [node] - Virtual dom node
* @returns {PillarConnectedComponent}
* @memberof Pillar
*/
value: function convertToVdom(element, node) {
if (element.nodeType === 3) return element.nodeValue;
if (element.nodeType !== 1) return null;
var attributes = element.attributes,
childNodes = element.childNodes;
var children = [];
this.mapAttributesToProps(attributes);
// If we're not using children as placeholder, go through child nodes and parse them too
if (!this.props.flags.ignoreChildren) {
for (var i = 0; i < childNodes.length; i += 1) {
children[i] = this.convertToVdom(childNodes[i], null, true);
}
}
// @REFACTOR? This feels dirty. If not using Shadow DOM,
// existing children are appended to, instead of being overwritten by VDOM render.
// This works fine, but we need to investigate for a better way to do it.
if (!this.useShadow) {
for (var _i = 0; _i < childNodes.length; _i += 1) {
element.removeChild(childNodes[_i]);
}
}
var ConnectedComponent = void 0;
if (node) {
ConnectedComponent = this.connectAttrsToProps(node);
} else {
ConnectedComponent = element.nodeName.toLowerCase();
}
var componentProps = node ? this.props : null;
return h(
ConnectedComponent,
componentProps,
children
);
}
/**
* Map attributes to props
*
* @description Goes through element's attributes and maps them to `this.props` and `this.flags`
* @param {NamedNodeMap} attributes
* @memberof PillarBaseElement
*/
}, {
key: 'mapAttributesToProps',
value: function mapAttributesToProps(attributes) {
for (var i = 0; i < attributes.length; i += 1) {
invariant_1(attributes[i].name !== 'flags', 'Attribute "flags" is reserved');
if (attributes[i].name.indexOf('p:') === -1) {
// Map attributes to props
var name = PillarBaseElement.dashToCamel(attributes[i].name);
this.props[name] = attributes[i].value;
} else {
// Map flags separately
var flagName = PillarBaseElement.dashToCamel(attributes[i].name.replace('p:', ''));
this.props.flags[flagName] = attributes[i].value || true; // UIKit-style implicit boolean
}
}
}
// Fires when custom element creates
}, {
key: 'connectedCallback',
value: function connectedCallback() {
this.initialize();
}
// Fires when custom element is destroyed
}, {
key: 'disconnectedCallback',
value: function disconnectedCallback() {
render(h(Null, null), this.shadow || this, this.root);
}
/**
* Initialize
* @description Initialize Pillar web component
* @memberof Pillar
*/
}, {
key: 'initialize',
value: function initialize() {
var _this2 = this;
this.observer = new MutationObserver(function (mutations) {
if (_this2.props.flags.protected) {
// Lock external mutations
invariant_1(false, 'Attempting to change attributes of protected component.');
// @TODO Revert attributes?
} else {
var newProps = [];
mutations.forEach(function (mutation) {
var attributeName = mutation.attributeName;
newProps.push({
name: attributeName,
value: _this2.attributes[attributeName] ? _this2.attributes[attributeName].value : null
});
});
_this2.mapAttributesToProps(newProps);
if (_this2.passPropsToVdom) {
_this2.passPropsToVdom(_this2.props);
if (_this2.__pillar__) {
// Wait till it's for sure rendered, then pass it to dev tools
setTimeout(function () {
return _this2.hookToDevTools(_this2.__pillar__);
}, 200);
}
}
}
});
// Activate observer
this.observer.observe(this, { attributes: true, attributeOldValue: true });
if (this.useShadow) {
invariant_1(this.useShadow === 'open' || this.useShadow === 'closed', 'Shadow DOM mode is expected to be "open" or "closed", but got %s', this.useShadow);
this.shadow = this.attachShadow({ mode: this.useShadow });
}
var snapshot = void 0;
try {
snapshot = this.vdomComponent(this.props);
} catch (e) {
// VDOM component is a class, so moving on
}
if (this.vdomComponent.prototype.render || (typeof snapshot === 'undefined' ? 'undefined' : _typeof(snapshot)) === 'object') {
this.renderComponent();
} else if (typeof snapshot === 'string') {
if (snapshot.charAt(0) === '#') {
this.renderTemplate(snapshot.slice(1));
} else {
this.renderString();
}
} else {
invariant_1(false, 'Component must return a JSX element, HTML string or template ID.');
}
}
/**
* Connect element attributes to props
*
* @description Creates a higher order component that makes element's attributes
* available in wrapped component's 'this.props', allowing reacting
* to their changes via 'componentWillReceiveProps'.
* @param {PillarComponent} WrappedComponent
* @returns {PillarConnectedComponent}
* @memberof PillarBaseElement
*/
}, {
key: 'connectAttrsToProps',
value: function connectAttrsToProps(WrappedComponent) {
var element = this;
// Higher order component to link props to the outside world
return function (_Component) {
inherits(PillarConnectedComponent, _Component);
function PillarConnectedComponent() {
classCallCheck(this, PillarConnectedComponent);
var _this3 = possibleConstructorReturn(this, (PillarConnectedComponent.__proto__ || Object.getPrototypeOf(PillarConnectedComponent)).call(this));
element.passPropsToVdom = _this3.reinsertProps.bind(_this3);
return _this3;
}
/**
* Reinsert props
*
* @description Updates props from attributes and triggers re-render of wrapped component
* @param {object} attrProps
*/
createClass(PillarConnectedComponent, [{
key: 'reinsertProps',
value: function reinsertProps(attrProps) {
this.setState(_extends({}, this.state, {
attrProps: attrProps
}));
}
}, {
key: 'render',
value: function render$$1() {
return h(WrappedComponent, _extends({}, this.props, this.state.attrProps));
}
}]);
return PillarConnectedComponent;
}(Component);
}
/**
* DevTools Hook
*
* @description Hook created element to Pillar DevTools plugin if it exists
* @memberof PillarBaseElement
*/
}, {
key: 'hookToDevTools',
value: function hookToDevTools(existingId) {
if (window.__PILLAR_DEVTOOLS_HOOK__) {
console.log('HOOK', existingId, this.__pillar__);
var id = existingId || Math.random().toString(36).slice(2);
window.__PILLAR_DEVTOOLS_HOOK__.elements[id] = {
name: this.localName,
props: this.props,
useShadow: this.useShadow,
innerHTML: this.useShadow ? this.shadow.innerHTML : this.innerHTML,
outerHTML: this.useShadow ? this.shadow.outerHTML : this.outerHTML
};
if (!window.__PILLAR_DEVTOOLS_HOOK__.attached) {
window.__PILLAR_DEVTOOLS_HOOK__.attached = true;
}
if (!existingId) {
this.__pillar__ = id;
} else {
window.postMessage({ type: 'PILLAR_UPDATE' }, '*');
}
}
}
/**
* Render component
*
* @description Render JSX component. NOTE: This method is only called once.
* @memberof PillarBaseElement
*/
}, {
key: 'renderComponent',
value: function renderComponent() {
this.root = render(this.convertToVdom(this, this.vdomComponent), this.shadow || this, this.root);
this.hookToDevTools();
}
/**
* Render string
*
* @description Render contents of an HTML string. NOTE: This method is only called once.
* @memberof PillarBaseElement
*/
}, {
key: 'renderString',
value: function renderString() {
var _this4 = this;
this.mapAttributesToProps(this.attributes);
this.innerHTML = null;
this.root = render(h(preactMarkup, { markup: this.vdomComponent(this.props) }), this.shadow || this);
this.passPropsToVdom = function (props) {
render(h(preactMarkup, { markup: _this4.vdomComponent(props) }), _this4.shadow || _this4, _this4.root);
};
this.hookToDevTools();
}
/**
* Render Template
*
*
* @description Render template tag's content. NOTE: This method is only called once.
* @param {any} id - ID of a template tag
* @memberof PillarBaseElement
*/
}, {
key: 'renderTemplate',
value: function renderTemplate(id) {
var _this5 = this;
// Get an HTML string for running through Mustache
var template = document.getElementById(id).innerHTML;
this.mapAttributesToProps(this.attributes);
this.innerHTML = null;
this.root = render(h(preactMarkup, { markup: mustache(template, this.props), 'allow-scripts': this.allowScripts }), this.shadow || this);
this.passPropsToVdom = function (props) {
render(h(preactMarkup, { markup: mustache(template, props), 'allow-scripts': _this5.allowScripts }), _this5.shadow || _this5, _this5.root);
};
this.hookToDevTools();
}
}], [{
key: 'dashToCamel',
/**
* Dash to Camel case
*
* @description Convert dash-cased-string to camelCasedString
* @static
* @param {string} dash - string to convert
* @returns {string}
* @memberof Pillar
*/
value: function dashToCamel(dash) {
return dash.indexOf('-') < 0 ? dash : dash.toLowerCase().replace(/-[aA-zZ]/g, function (m) {
return m[1].toUpperCase();
});
}
/**
* Camel to Dash case
*
* @description Convert camelCasedString to dash-cased-string
* @static
* @param {string} camel - string to convert
* @returns {string}
* @memberof Pillar
*/
}, {
key: 'camelToDash',
value: function camelToDash(camel) {
return camel.replace(/([A-Z])/g, '-$1').toLowerCase();
}
}]);
return PillarBaseElement;
}(HTMLElement);
var Pillar = function (_Component) {
inherits(Pillar, _Component);
createClass(Pillar, null, [{
key: 'register',
/**
* Register tag
*
* @description Register custom element as an HTML tag with the name specified
* @static
* @param {any} tagName - Name of an HTML tag to register
* @param {Pillar} TargetComponent - Component to register as a tag
* @memberof Pillar
*/
value: function register(TargetComponent, tagName) {
var elementOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (typeof TargetComponent === 'string') {
Pillar.invariant(tagName, 'When using a template tag, you need to explicitly define tag name to register');
}
var elementName = tagName || PillarBaseElement.camelToDash(TargetComponent.name).slice(1);
Pillar.invariant(!window.customElements.get(elementName), 'Element "' + tagName + '" has already been defined. You can only define element once.');
Pillar.invariant(TargetComponent, 'You need to pass the component to create element from.');
// Define custom element
window.customElements.define(elementName, Pillar.createElement(TargetComponent, elementOptions));
}
}, {
key: 'createElement',
value: function createElement(TargetComponent, elementOptions) {
return function (_PillarBaseElement) {
inherits(PillarElement, _PillarBaseElement);
function PillarElement() {
classCallCheck(this, PillarElement);
var _this2 = possibleConstructorReturn(this, (PillarElement.__proto__ || Object.getPrototypeOf(PillarElement)).call(this));
Object.assign(_this2, elementOptions); // Extend "this" with options
Pillar.invariant(typeof TargetComponent === 'string' || typeof TargetComponent === 'function', 'Register method requires a component or template tag ID, but found unexpected %s', typeof TargetComponent === 'undefined' ? 'undefined' : _typeof(TargetComponent));
if (typeof TargetComponent === 'string') {
_this2.vdomComponent = function () {
return TargetComponent;
};
} else {
_this2.vdomComponent = TargetComponent;
}
return _this2;
}
return PillarElement;
}(PillarBaseElement);
}
}]);
function Pillar(props, state) {
classCallCheck(this, Pillar);
var _this = possibleConstructorReturn(this, (Pillar.__proto__ || Object.getPrototypeOf(Pillar)).call(this, props, state));
_this.validateRender();
_this.flags = _this.props ? _this.props.flags : {};
if (_this.componentWillReceiveProps) {
var originalHandler = _this.componentWillReceiveProps.bind(_this);
_this.componentWillReceiveProps = function (nextProps, nextState) {
_this.flags = nextProps.flags;
originalHandler(nextProps, nextState);
};
} else {
_this.componentWillReceiveProps = function (nextProps) {
_this.flags = nextProps.flags;
};
}
return _this;
}
/**
* Validate render method
* @description Makes sure there's only one render method and it's actually a method
* @memberof Pillar
*/
createClass(Pillar, [{
key: 'validateRender',
value: function validateRender() {
Pillar.invariant(typeof this.render === 'function', 'render() is expected to be a function, but got a type "%s"', _typeof(this.render));
}
}]);
return Pillar;
}(Component);
Pillar.invariant = invariant_1;
Pillar.h = h;
Pillar.register(function (_ref) {
var text = _ref.text;
return h(
'h1',
null,
text
);
}, 'custom-header');
return Pillar;
})));