UNPKG

33.3 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3 typeof define === 'function' && define.amd ? define(['exports'], factory) :
4 (factory((global.preact = global.preact || {})));
5}(this, function (exports) {
6
7 function VNode(nodeName, attributes, children) {
8 /** @type {string|function} */
9 this.nodeName = nodeName;
10
11 /** @type {object<string>|undefined} */
12 this.attributes = attributes;
13
14 /** @type {array<VNode>|undefined} */
15 this.children = children;
16
17 /** Reference to the given key. */
18 this.key = attributes && attributes.key;
19 }
20
21 var options = {
22
23 /** If `true`, `prop` changes trigger synchronous component updates.
24 * @name syncComponentUpdates
25 * @type Boolean
26 * @default true
27 */
28 //syncComponentUpdates: true,
29
30 /** Processes all created VNodes.
31 * @param {VNode} vnode A newly-created VNode to normalize/process
32 */
33 //vnode(vnode) { }
34
35 /** Hook invoked after a component is mounted. */
36 // afterMount(component) { }
37
38 /** Hook invoked after the DOM is updated with a component's latest render. */
39 // afterUpdate(component) { }
40
41 /** Hook invoked immediately before a component is unmounted. */
42 // beforeUnmount(component) { }
43 };
44
45 var stack = [];
46
47 /** JSX/hyperscript reviver
48 * Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0
49 * @see http://jasonformat.com/wtf-is-jsx
50 * @public
51 * @example
52 * /** @jsx h *\/
53 * import { render, h } from 'preact';
54 * render(<span>foo</span>, document.body);
55 */
56
57 function h(nodeName, attributes) {
58 var children = [],
59 lastSimple = undefined,
60 child = undefined,
61 simple = undefined,
62 i = undefined;
63 for (i = arguments.length; i-- > 2;) {
64 stack.push(arguments[i]);
65 }
66 if (attributes && attributes.children) {
67 if (!stack.length) stack.push(attributes.children);
68 delete attributes.children;
69 }
70 while (stack.length) {
71 if ((child = stack.pop()) instanceof Array) {
72 for (i = child.length; i--;) stack.push(child[i]);
73 } else if (child != null && child !== false) {
74 if (typeof child == 'number' || child === true) child = String(child);
75 simple = typeof child == 'string';
76 if (simple && lastSimple) {
77 children[children.length - 1] += child;
78 } else {
79 children.push(child);
80 lastSimple = simple;
81 }
82 }
83 }
84
85 var p = new VNode(nodeName, attributes || undefined, children);
86
87 // if a "vnode hook" is defined, pass every created VNode to it
88 if (options.vnode) options.vnode(p);
89
90 return p;
91 }
92
93 function extend(obj, props) {
94 if (props) {
95 for (var i in props) {
96 obj[i] = props[i];
97 }
98 }
99 return obj;
100 }
101
102 /** Fast clone. Note: does not filter out non-own properties.
103 * @see https://esbench.com/bench/56baa34f45df6895002e03b6
104 */
105
106 function clone(obj) {
107 return extend({}, obj);
108 }
109
110 /** Get a deep property value from the given object, expressed in dot-notation.
111 * @private
112 */
113
114 function delve(obj, key) {
115 for (var p = key.split('.'), i = 0; i < p.length && obj; i++) {
116 obj = obj[p[i]];
117 }
118 return obj;
119 }
120
121 /** @private is the given object a Function? */
122
123 function isFunction(obj) {
124 return 'function' === typeof obj;
125 }
126
127 /** @private is the given object a String? */
128
129 function isString(obj) {
130 return 'string' === typeof obj;
131 }
132
133 /** Convert a hashmap of CSS classes to a space-delimited className string
134 * @private
135 */
136
137 function hashToClassName(c) {
138 var str = '';
139 for (var prop in c) {
140 if (c[prop]) {
141 if (str) str += ' ';
142 str += prop;
143 }
144 }
145 return str;
146 }
147
148 /** Just a memoized String#toLowerCase */
149 var lcCache = {};
150 var toLowerCase = function toLowerCase(s) {
151 return lcCache[s] || (lcCache[s] = s.toLowerCase());
152 };
153
154 /** Call a function asynchronously, as soon as possible.
155 * @param {Function} callback
156 */
157 var resolved = typeof Promise !== 'undefined' && Promise.resolve();
158 var defer = resolved ? function (f) {
159 resolved.then(f);
160 } : setTimeout;
161
162 function cloneElement(vnode, props) {
163 return h(vnode.nodeName, extend(clone(vnode.attributes), props), arguments.length > 2 ? [].slice.call(arguments, 2) : vnode.children);
164 }
165
166 var NO_RENDER = 0;
167 var SYNC_RENDER = 1;
168 var FORCE_RENDER = 2;
169 var ASYNC_RENDER = 3;
170
171 var EMPTY = {};
172
173 var ATTR_KEY = typeof Symbol !== 'undefined' ? Symbol['for']('preactattr') : '__preactattr_';
174
175 // DOM properties that should NOT have "px" added when numeric
176 var NON_DIMENSION_PROPS = {
177 boxFlex: 1, boxFlexGroup: 1, columnCount: 1, fillOpacity: 1, flex: 1, flexGrow: 1,
178 flexPositive: 1, flexShrink: 1, flexNegative: 1, fontWeight: 1, lineClamp: 1, lineHeight: 1,
179 opacity: 1, order: 1, orphans: 1, strokeOpacity: 1, widows: 1, zIndex: 1, zoom: 1
180 };
181
182 // DOM event types that do not bubble and should be attached via useCapture
183 var NON_BUBBLING_EVENTS = { blur: 1, error: 1, focus: 1, load: 1, resize: 1, scroll: 1 };
184
185 /** Create an Event handler function that sets a given state property.
186 * @param {Component} component The component whose state should be updated
187 * @param {string} key A dot-notated key path to update in the component's state
188 * @param {string} eventPath A dot-notated key path to the value that should be retrieved from the Event or component
189 * @returns {function} linkedStateHandler
190 * @private
191 */
192
193 function createLinkedState(component, key, eventPath) {
194 var path = key.split('.');
195 return function (e) {
196 var t = e && e.target || this,
197 state = {},
198 obj = state,
199 v = isString(eventPath) ? delve(e, eventPath) : t.nodeName ? t.type.match(/^che|rad/) ? t.checked : t.value : e,
200 i = 0;
201 for (; i < path.length - 1; i++) {
202 obj = obj[path[i]] || (obj[path[i]] = !i && component.state[path[i]] || {});
203 }
204 obj[path[i]] = v;
205 component.setState(state);
206 };
207 }
208
209 /** Managed queue of dirty components to be re-rendered */
210
211 // items/itemsOffline swap on each rerender() call (just a simple pool technique)
212 var items = [];
213
214 function enqueueRender(component) {
215 if (!component._dirty && (component._dirty = true) && items.push(component) == 1) {
216 (options.debounceRendering || defer)(rerender);
217 }
218 }
219
220 function rerender() {
221 var p = undefined,
222 list = items;
223 items = [];
224 while (p = list.pop()) {
225 if (p._dirty) renderComponent(p);
226 }
227 }
228
229 /** Check if a VNode is a reference to a stateless functional component.
230 * A function component is represented as a VNode whose `nodeName` property is a reference to a function.
231 * If that function is not a Component (ie, has no `.render()` method on a prototype), it is considered a stateless functional component.
232 * @param {VNode} vnode A VNode
233 * @private
234 */
235
236 function isFunctionalComponent(vnode) {
237 var nodeName = vnode && vnode.nodeName;
238 return nodeName && isFunction(nodeName) && !(nodeName.prototype && nodeName.prototype.render);
239 }
240
241 /** Construct a resultant VNode from a VNode referencing a stateless functional component.
242 * @param {VNode} vnode A VNode with a `nodeName` property that is a reference to a function.
243 * @private
244 */
245
246 function buildFunctionalComponent(vnode, context) {
247 return vnode.nodeName(getNodeProps(vnode), context || EMPTY);
248 }
249
250 /** Check if two nodes are equivalent.
251 * @param {Element} node
252 * @param {VNode} vnode
253 * @private
254 */
255
256 function isSameNodeType(node, vnode) {
257 if (isString(vnode)) {
258 return node instanceof Text;
259 }
260 if (isString(vnode.nodeName)) {
261 return !node._componentConstructor && isNamedNode(node, vnode.nodeName);
262 }
263 if (isFunction(vnode.nodeName)) {
264 return node._componentConstructor === vnode.nodeName || isFunctionalComponent(vnode);
265 }
266 }
267
268 function isNamedNode(node, nodeName) {
269 return node.normalizedNodeName === nodeName || toLowerCase(node.nodeName) === toLowerCase(nodeName);
270 }
271
272 /**
273 * Reconstruct Component-style `props` from a VNode.
274 * Ensures default/fallback values from `defaultProps`:
275 * Own-properties of `defaultProps` not present in `vnode.attributes` are added.
276 * @param {VNode} vnode
277 * @returns {Object} props
278 */
279
280 function getNodeProps(vnode) {
281 var props = clone(vnode.attributes);
282 props.children = vnode.children;
283
284 var defaultProps = vnode.nodeName.defaultProps;
285 if (defaultProps) {
286 for (var i in defaultProps) {
287 if (props[i] === undefined) {
288 props[i] = defaultProps[i];
289 }
290 }
291 }
292
293 return props;
294 }
295
296 /** Removes a given DOM Node from its parent. */
297
298 function removeNode(node) {
299 var p = node.parentNode;
300 if (p) p.removeChild(node);
301 }
302
303 /** Set a named attribute on the given Node, with special behavior for some names and event handlers.
304 * If `value` is `null`, the attribute/handler will be removed.
305 * @param {Element} node An element to mutate
306 * @param {string} name The name/key to set, such as an event or attribute name
307 * @param {any} value An attribute value, such as a function to be used as an event handler
308 * @param {any} previousValue The last value that was set for this name/node pair
309 * @private
310 */
311
312 function setAccessor(node, name, old, value, isSvg) {
313
314 if (name === 'className') name = 'class';
315
316 if (name === 'class' && value && typeof value === 'object') {
317 value = hashToClassName(value);
318 }
319
320 if (name === 'key') {
321 // ignore
322 } else if (name === 'class' && !isSvg) {
323 node.className = value || '';
324 } else if (name === 'style') {
325 if (!value || isString(value) || isString(old)) {
326 node.style.cssText = value || '';
327 }
328 if (value && typeof value === 'object') {
329 if (!isString(old)) {
330 for (var i in old) {
331 if (!(i in value)) node.style[i] = '';
332 }
333 }
334 for (var i in value) {
335 node.style[i] = typeof value[i] === 'number' && !NON_DIMENSION_PROPS[i] ? value[i] + 'px' : value[i];
336 }
337 }
338 } else if (name === 'dangerouslySetInnerHTML') {
339 node.innerHTML = value && value.__html || '';
340 } else if (name[0] == 'o' && name[1] == 'n') {
341 var l = node._listeners || (node._listeners = {});
342 name = toLowerCase(name.substring(2));
343 // @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
344 // if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
345 if (value) {
346 if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
347 } else if (l[name]) {
348 node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
349 }
350 l[name] = value;
351 } else if (name !== 'list' && name !== 'type' && !isSvg && name in node) {
352 setProperty(node, name, value == null ? '' : value);
353 if (value == null || value === false) node.removeAttribute(name);
354 } else {
355 var ns = isSvg && name.match(/^xlink\:?(.+)/);
356 if (value == null || value === false) {
357 if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));else node.removeAttribute(name);
358 } else if (typeof value !== 'object' && !isFunction(value)) {
359 if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), value);else node.setAttribute(name, value);
360 }
361 }
362 }
363
364 /** Attempt to set a DOM property to the given value.
365 * IE & FF throw for certain property-value combinations.
366 */
367 function setProperty(node, name, value) {
368 try {
369 node[name] = value;
370 } catch (e) {}
371 }
372
373 /** Proxy an event to hooked event handlers
374 * @private
375 */
376 function eventProxy(e) {
377 return this._listeners[e.type](options.event && options.event(e) || e);
378 }
379
380 /** DOM node pool, keyed on nodeName. */
381
382 var nodes = {};
383
384 function collectNode(node) {
385 removeNode(node);
386
387 if (node instanceof Element) {
388 node._component = node._componentConstructor = null;
389
390 var _name = node.normalizedNodeName || toLowerCase(node.nodeName);
391 (nodes[_name] || (nodes[_name] = [])).push(node);
392 }
393 }
394
395 function createNode(nodeName, isSvg) {
396 var name = toLowerCase(nodeName),
397 node = nodes[name] && nodes[name].pop() || (isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName));
398 node.normalizedNodeName = name;
399 return node;
400 }
401
402 /** Diff recursion count, used to track the end of the diff cycle. */
403 var mounts = [];
404
405 /** Diff recursion count, used to track the end of the diff cycle. */
406 var diffLevel = 0;
407
408 var isSvgMode = false;
409
410 var hydrating = false;
411
412 function flushMounts() {
413 var c = undefined;
414 while (c = mounts.pop()) {
415 if (options.afterMount) options.afterMount(c);
416 if (c.componentDidMount) c.componentDidMount();
417 }
418 }
419
420 /** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
421 * @param {Element} [dom=null] A DOM node to mutate into the shape of the `vnode`
422 * @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure
423 * @returns {Element} dom The created/mutated element
424 * @private
425 */
426
427 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
428 if (! diffLevel++) {
429 isSvgMode = parent instanceof SVGElement;
430 hydrating = dom && !(ATTR_KEY in dom);
431 }
432 var ret = idiff(dom, vnode, context, mountAll);
433 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
434 if (! --diffLevel) {
435 hydrating = false;
436 if (!componentRoot) flushMounts();
437 }
438 return ret;
439 }
440
441 function idiff(dom, vnode, context, mountAll) {
442 var originalAttributes = vnode && vnode.attributes;
443
444 while (isFunctionalComponent(vnode)) {
445 vnode = buildFunctionalComponent(vnode, context);
446 }
447
448 if (vnode == null) vnode = '';
449
450 if (isString(vnode)) {
451
452 if (dom && dom instanceof Text) {
453 if (dom.nodeValue != vnode) {
454 dom.nodeValue = vnode;
455 }
456 } else {
457 if (dom) recollectNodeTree(dom);
458 dom = document.createTextNode(vnode);
459 }
460 dom[ATTR_KEY] = true;
461 return dom;
462 }
463
464 if (isFunction(vnode.nodeName)) {
465 return buildComponentFromVNode(dom, vnode, context, mountAll);
466 }
467
468 var out = dom,
469 nodeName = String(vnode.nodeName),
470 prevSvgMode = isSvgMode,
471 vchildren = vnode.children;
472
473 isSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode;
474
475 if (!dom) {
476 out = createNode(nodeName, isSvgMode);
477 } else if (!isNamedNode(dom, nodeName)) {
478 out = createNode(nodeName, isSvgMode);
479 // move children into the replacement node
480 while (dom.firstChild) out.appendChild(dom.firstChild);
481 // reclaim element nodes
482 recollectNodeTree(dom);
483 }
484
485 var fc = out.firstChild,
486 props = out[ATTR_KEY];
487 if (!props) {
488 out[ATTR_KEY] = props = {};
489 for (var a = out.attributes, i = a.length; i--;) {
490 props[a[i].name] = a[i].value;
491 }
492 }
493
494 diffAttributes(out, vnode.attributes, props);
495
496 // fast-path for elements containing a single TextNode:
497 if (!hydrating && vchildren && vchildren.length === 1 && typeof vchildren[0] === 'string' && fc instanceof Text && !fc.nextSibling) {
498 if (fc.nodeValue != vchildren[0]) {
499 fc.nodeValue = vchildren[0];
500 }
501 } else if (vchildren && vchildren.length || fc) {
502 innerDiffNode(out, vchildren, context, mountAll);
503 }
504
505 if (originalAttributes && typeof originalAttributes.ref === 'function') {
506 (props.ref = originalAttributes.ref)(out);
507 }
508
509 isSvgMode = prevSvgMode;
510
511 return out;
512 }
513
514 /** Apply child and attribute changes between a VNode and a DOM Node to the DOM. */
515 function innerDiffNode(dom, vchildren, context, mountAll) {
516 var originalChildren = dom.childNodes,
517 children = [],
518 keyed = {},
519 keyedLen = 0,
520 min = 0,
521 len = originalChildren.length,
522 childrenLen = 0,
523 vlen = vchildren && vchildren.length,
524 j = undefined,
525 c = undefined,
526 vchild = undefined,
527 child = undefined;
528
529 if (len) {
530 for (var i = 0; i < len; i++) {
531 var _child = originalChildren[i],
532 props = _child[ATTR_KEY],
533 key = vlen ? (c = _child._component) ? c.__key : props ? props.key : null : null;
534 if (key != null) {
535 keyedLen++;
536 keyed[key] = _child;
537 } else if (hydrating || props) {
538 children[childrenLen++] = _child;
539 }
540 }
541 }
542
543 if (vlen) {
544 for (var i = 0; i < vlen; i++) {
545 vchild = vchildren[i];
546 child = null;
547
548 // if (isFunctionalComponent(vchild)) {
549 // vchild = buildFunctionalComponent(vchild);
550 // }
551
552 // attempt to find a node based on key matching
553 var key = vchild.key;
554 if (key != null) {
555 if (keyedLen && key in keyed) {
556 child = keyed[key];
557 keyed[key] = undefined;
558 keyedLen--;
559 }
560 }
561 // attempt to pluck a node of the same type from the existing children
562 else if (!child && min < childrenLen) {
563 for (j = min; j < childrenLen; j++) {
564 c = children[j];
565 if (c && isSameNodeType(c, vchild)) {
566 child = c;
567 children[j] = undefined;
568 if (j === childrenLen - 1) childrenLen--;
569 if (j === min) min++;
570 break;
571 }
572 }
573 if (!child && min < childrenLen && isFunction(vchild.nodeName) && mountAll) {
574 child = children[min];
575 children[min++] = undefined;
576 }
577 }
578
579 // morph the matched/found/created DOM child to match vchild (deep)
580 child = idiff(child, vchild, context, mountAll);
581
582 if (child && child !== dom && child !== originalChildren[i]) {
583 dom.insertBefore(child, originalChildren[i] || null);
584 }
585 }
586 }
587
588 if (keyedLen) {
589 for (var i in keyed) {
590 if (keyed[i]) recollectNodeTree(keyed[i]);
591 }
592 }
593
594 // remove orphaned children
595 if (min < childrenLen) {
596 removeOrphanedChildren(children);
597 }
598 }
599
600 /** Reclaim children that were unreferenced in the desired VTree */
601
602 function removeOrphanedChildren(children, unmountOnly) {
603 for (var i = children.length; i--;) {
604 if (children[i]) {
605 recollectNodeTree(children[i], unmountOnly);
606 }
607 }
608 }
609
610 /** Reclaim an entire tree of nodes, starting at the root. */
611
612 function recollectNodeTree(node, unmountOnly) {
613 // @TODO: Need to make a call on whether Preact should remove nodes not created by itself.
614 // Currently it *does* remove them. Discussion: https://github.com/developit/preact/issues/39
615 //if (!node[ATTR_KEY]) return;
616
617 var component = node._component;
618 if (component) {
619 unmountComponent(component, !unmountOnly);
620 } else {
621 if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
622
623 if (!unmountOnly) {
624 collectNode(node);
625 }
626
627 if (node.childNodes && node.childNodes.length) {
628 removeOrphanedChildren(node.childNodes, unmountOnly);
629 }
630 }
631 }
632
633 /** Apply differences in attributes from a VNode to the given DOM Node. */
634 function diffAttributes(dom, attrs, old) {
635 for (var _name in old) {
636 if (!(attrs && _name in attrs) && old[_name] != null) {
637 setAccessor(dom, _name, old[_name], old[_name] = undefined, isSvgMode);
638 }
639 }
640
641 // new & updated
642 if (attrs) {
643 for (var _name2 in attrs) {
644 if (_name2 !== 'children' && _name2 !== 'innerHTML' && (!(_name2 in old) || attrs[_name2] !== (_name2 === 'value' || _name2 === 'checked' ? dom[_name2] : old[_name2]))) {
645 setAccessor(dom, _name2, old[_name2], old[_name2] = attrs[_name2], isSvgMode);
646 }
647 }
648 }
649 }
650
651 /** Retains a pool of Components for re-use, keyed on component name.
652 * Note: since component names are not unique or even necessarily available, these are primarily a form of sharding.
653 * @private
654 */
655 var components = {};
656
657 function collectComponent(component) {
658 var name = component.constructor.name,
659 list = components[name];
660 if (list) list.push(component);else components[name] = [component];
661 }
662
663 function createComponent(Ctor, props, context) {
664 var inst = new Ctor(props, context),
665 list = components[Ctor.name];
666 Component.call(inst, props, context);
667 if (list) {
668 for (var i = list.length; i--;) {
669 if (list[i].constructor === Ctor) {
670 inst.nextBase = list[i].nextBase;
671 list.splice(i, 1);
672 break;
673 }
674 }
675 }
676 return inst;
677 }
678
679 /** Set a component's `props` (generally derived from JSX attributes).
680 * @param {Object} props
681 * @param {Object} [opts]
682 * @param {boolean} [opts.renderSync=false] If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
683 * @param {boolean} [opts.render=true] If `false`, no render will be triggered.
684 */
685
686 function setComponentProps(component, props, opts, context, mountAll) {
687 if (component._disable) return;
688 component._disable = true;
689
690 if (component.__ref = props.ref) delete props.ref;
691 if (component.__key = props.key) delete props.key;
692
693 if (!component.base || mountAll) {
694 if (component.componentWillMount) component.componentWillMount();
695 } else if (component.componentWillReceiveProps) {
696 component.componentWillReceiveProps(props, context);
697 }
698
699 if (context && context !== component.context) {
700 if (!component.prevContext) component.prevContext = component.context;
701 component.context = context;
702 }
703
704 if (!component.prevProps) component.prevProps = component.props;
705 component.props = props;
706
707 component._disable = false;
708
709 if (opts !== NO_RENDER) {
710 if (opts === SYNC_RENDER || options.syncComponentUpdates !== false || !component.base) {
711 renderComponent(component, SYNC_RENDER, mountAll);
712 } else {
713 enqueueRender(component);
714 }
715 }
716
717 if (component.__ref) component.__ref(component);
718 }
719
720 /** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
721 * @param {Component} component
722 * @param {Object} [opts]
723 * @param {boolean} [opts.build=false] If `true`, component will build and store a DOM node if not already associated with one.
724 * @private
725 */
726
727 function renderComponent(component, opts, mountAll, isChild) {
728 if (component._disable) return;
729
730 var skip = undefined,
731 rendered = undefined,
732 props = component.props,
733 state = component.state,
734 context = component.context,
735 previousProps = component.prevProps || props,
736 previousState = component.prevState || state,
737 previousContext = component.prevContext || context,
738 isUpdate = component.base,
739 nextBase = component.nextBase,
740 initialBase = isUpdate || nextBase,
741 initialChildComponent = component._component,
742 inst = undefined,
743 cbase = undefined;
744
745 // if updating
746 if (isUpdate) {
747 component.props = previousProps;
748 component.state = previousState;
749 component.context = previousContext;
750 if (opts !== FORCE_RENDER && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) {
751 skip = true;
752 } else if (component.componentWillUpdate) {
753 component.componentWillUpdate(props, state, context);
754 }
755 component.props = props;
756 component.state = state;
757 component.context = context;
758 }
759
760 component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
761 component._dirty = false;
762
763 if (!skip) {
764 if (component.render) rendered = component.render(props, state, context);
765
766 // context to pass to the child, can be updated via (grand-)parent component
767 if (component.getChildContext) {
768 context = extend(clone(context), component.getChildContext());
769 }
770
771 while (isFunctionalComponent(rendered)) {
772 rendered = buildFunctionalComponent(rendered, context);
773 }
774
775 var childComponent = rendered && rendered.nodeName,
776 toUnmount = undefined,
777 base = undefined;
778
779 if (isFunction(childComponent)) {
780 // set up high order component link
781
782 inst = initialChildComponent;
783 var childProps = getNodeProps(rendered);
784
785 if (inst && inst.constructor === childComponent) {
786 setComponentProps(inst, childProps, SYNC_RENDER, context);
787 } else {
788 toUnmount = inst;
789
790 inst = createComponent(childComponent, childProps, context);
791 inst.nextBase = inst.nextBase || nextBase;
792 inst._parentComponent = component;
793 component._component = inst;
794 setComponentProps(inst, childProps, NO_RENDER, context);
795 renderComponent(inst, SYNC_RENDER, mountAll, true);
796 }
797
798 base = inst.base;
799 } else {
800 cbase = initialBase;
801
802 // destroy high order component link
803 toUnmount = initialChildComponent;
804 if (toUnmount) {
805 cbase = component._component = null;
806 }
807
808 if (initialBase || opts === SYNC_RENDER) {
809 if (cbase) cbase._component = null;
810 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
811 }
812 }
813
814 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
815 var baseParent = initialBase.parentNode;
816 if (baseParent && base !== baseParent) {
817 baseParent.replaceChild(base, initialBase);
818
819 if (!toUnmount) {
820 initialBase._component = null;
821 recollectNodeTree(initialBase);
822 }
823 }
824 }
825
826 if (toUnmount) {
827 unmountComponent(toUnmount, base !== initialBase);
828 }
829
830 component.base = base;
831 if (base && !isChild) {
832 var componentRef = component,
833 t = component;
834 while (t = t._parentComponent) {
835 (componentRef = t).base = base;
836 }
837 base._component = componentRef;
838 base._componentConstructor = componentRef.constructor;
839 }
840 }
841
842 if (!isUpdate || mountAll) {
843 mounts.unshift(component);
844 } else if (!skip) {
845 if (component.componentDidUpdate) {
846 component.componentDidUpdate(previousProps, previousState, previousContext);
847 }
848 if (options.afterUpdate) options.afterUpdate(component);
849 }
850
851 var cb = component._renderCallbacks,
852 fn = undefined;
853 if (cb) while (fn = cb.pop()) fn.call(component);
854
855 if (!diffLevel && !isChild) flushMounts();
856 }
857
858 /** Apply the Component referenced by a VNode to the DOM.
859 * @param {Element} dom The DOM node to mutate
860 * @param {VNode} vnode A Component-referencing VNode
861 * @returns {Element} dom The created/mutated element
862 * @private
863 */
864
865 function buildComponentFromVNode(dom, vnode, context, mountAll) {
866 var c = dom && dom._component,
867 oldDom = dom,
868 isDirectOwner = c && dom._componentConstructor === vnode.nodeName,
869 isOwner = isDirectOwner,
870 props = getNodeProps(vnode);
871 while (c && !isOwner && (c = c._parentComponent)) {
872 isOwner = c.constructor === vnode.nodeName;
873 }
874
875 if (c && isOwner && (!mountAll || c._component)) {
876 setComponentProps(c, props, ASYNC_RENDER, context, mountAll);
877 dom = c.base;
878 } else {
879 if (c && !isDirectOwner) {
880 unmountComponent(c, true);
881 dom = oldDom = null;
882 }
883
884 c = createComponent(vnode.nodeName, props, context);
885 if (dom && !c.nextBase) {
886 c.nextBase = dom;
887 // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L241:
888 oldDom = null;
889 }
890 setComponentProps(c, props, SYNC_RENDER, context, mountAll);
891 dom = c.base;
892
893 if (oldDom && dom !== oldDom) {
894 oldDom._component = null;
895 recollectNodeTree(oldDom);
896 }
897 }
898
899 return dom;
900 }
901
902 /** Remove a component from the DOM and recycle it.
903 * @param {Element} dom A DOM node from which to unmount the given Component
904 * @param {Component} component The Component instance to unmount
905 * @private
906 */
907
908 function unmountComponent(component, remove) {
909 if (options.beforeUnmount) options.beforeUnmount(component);
910
911 // console.log(`${remove?'Removing':'Unmounting'} component: ${component.constructor.name}`);
912 var base = component.base;
913
914 component._disable = true;
915
916 if (component.componentWillUnmount) component.componentWillUnmount();
917
918 component.base = null;
919
920 // recursively tear down & recollect high-order component children:
921 var inner = component._component;
922 if (inner) {
923 unmountComponent(inner, remove);
924 } else if (base) {
925 if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);
926
927 component.nextBase = base;
928
929 if (remove) {
930 removeNode(base);
931 collectComponent(component);
932 }
933 removeOrphanedChildren(base.childNodes, !remove);
934 // removeOrphanedChildren(base.childNodes, true);
935 }
936
937 if (component.__ref) component.__ref(null);
938 if (component.componentDidUnmount) component.componentDidUnmount();
939 }
940
941 /** Base Component class, for he ES6 Class method of creating Components
942 * @public
943 *
944 * @example
945 * class MyFoo extends Component {
946 * render(props, state) {
947 * return <div />;
948 * }
949 * }
950 */
951
952 function Component(props, context) {
953 /** @private */
954 this._dirty = true;
955 // /** @public */
956 // this._disableRendering = false;
957 // /** @public */
958 // this.prevState = this.prevProps = this.prevContext = this.base = this.nextBase = this._parentComponent = this._component = this.__ref = this.__key = this._linkedStates = this._renderCallbacks = null;
959 /** @public */
960 this.context = context;
961 /** @type {object} */
962 this.props = props;
963 /** @type {object} */
964 if (!this.state) this.state = {};
965 }
966
967 extend(Component.prototype, {
968
969 /** Returns a `boolean` value indicating if the component should re-render when receiving the given `props` and `state`.
970 * @param {object} nextProps
971 * @param {object} nextState
972 * @param {object} nextContext
973 * @returns {Boolean} should the component re-render
974 * @name shouldComponentUpdate
975 * @function
976 */
977 // shouldComponentUpdate() {
978 // return true;
979 // },
980
981 /** Returns a function that sets a state property when called.
982 * Calling linkState() repeatedly with the same arguments returns a cached link function.
983 *
984 * Provides some built-in special cases:
985 * - Checkboxes and radio buttons link their boolean `checked` value
986 * - Inputs automatically link their `value` property
987 * - Event paths fall back to any associated Component if not found on an element
988 * - If linked value is a function, will invoke it and use the result
989 *
990 * @param {string} key The path to set - can be a dot-notated deep key
991 * @param {string} [eventPath] If set, attempts to find the new state value at a given dot-notated path within the object passed to the linkedState setter.
992 * @returns {function} linkStateSetter(e)
993 *
994 * @example Update a "text" state value when an input changes:
995 * <input onChange={ this.linkState('text') } />
996 *
997 * @example Set a deep state value on click
998 * <button onClick={ this.linkState('touch.coords', 'touches.0') }>Tap</button
999 */
1000 linkState: function linkState(key, eventPath) {
1001 var c = this._linkedStates || (this._linkedStates = {});
1002 return c[key + eventPath] || (c[key + eventPath] = createLinkedState(this, key, eventPath));
1003 },
1004
1005 /** Update component state by copying properties from `state` to `this.state`.
1006 * @param {object} state A hash of state properties to update with new values
1007 */
1008 setState: function setState(state, callback) {
1009 var s = this.state;
1010 if (!this.prevState) this.prevState = clone(s);
1011 extend(s, isFunction(state) ? state(s, this.props) : state);
1012 if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);
1013 enqueueRender(this);
1014 },
1015
1016 /** Immediately perform a synchronous re-render of the component.
1017 * @private
1018 */
1019 forceUpdate: function forceUpdate() {
1020 renderComponent(this, FORCE_RENDER);
1021 },
1022
1023 /** Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
1024 * Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
1025 * @param {object} props Props (eg: JSX attributes) received from parent element/component
1026 * @param {object} state The component's current state
1027 * @param {object} context Context object (if a parent component has provided context)
1028 * @returns VNode
1029 */
1030 render: function render() {}
1031
1032 });
1033
1034 /** Render JSX into a `parent` Element.
1035 * @param {VNode} vnode A (JSX) VNode to render
1036 * @param {Element} parent DOM element to render into
1037 * @param {Element} [merge] Attempt to re-use an existing DOM tree rooted at `merge`
1038 * @public
1039 *
1040 * @example
1041 * // render a div into <body>:
1042 * render(<div id="hello">hello!</div>, document.body);
1043 *
1044 * @example
1045 * // render a "Thing" component into #foo:
1046 * const Thing = ({ name }) => <span>{ name }</span>;
1047 * render(<Thing name="one" />, document.querySelector('#foo'));
1048 */
1049
1050 function render(vnode, parent, merge) {
1051 return diff(merge, vnode, {}, false, parent);
1052 }
1053
1054 exports.createElement = h;
1055 exports.h = h;
1056 exports.cloneElement = cloneElement;
1057 exports.Component = Component;
1058 exports.render = render;
1059 exports.rerender = rerender;
1060 exports.options = options;
1061
1062 Object.defineProperty(exports, '__esModule', { value: true });
1063
1064}));
1065//# sourceMappingURL=aliases.js.map