UNPKG

33.1 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 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 if (value) node.innerHTML = 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 function flushMounts() {
411 var c = undefined;
412 while (c = mounts.pop()) {
413 if (options.afterMount) options.afterMount(c);
414 if (c.componentDidMount) c.componentDidMount();
415 }
416 }
417
418 /** Apply differences in a given vnode (and it's deep children) to a real DOM Node.
419 * @param {Element} [dom=null] A DOM node to mutate into the shape of the `vnode`
420 * @param {VNode} vnode A VNode (with descendants forming a tree) representing the desired DOM structure
421 * @returns {Element} dom The created/mutated element
422 * @private
423 */
424
425 function diff(dom, vnode, context, mountAll, parent, componentRoot) {
426 if (! diffLevel++) isSvgMode = parent instanceof SVGElement;
427 var ret = idiff(dom, vnode, context, mountAll);
428 if (parent && ret.parentNode !== parent) parent.appendChild(ret);
429 if (! --diffLevel && !componentRoot) flushMounts();
430 return ret;
431 }
432
433 function idiff(dom, vnode, context, mountAll) {
434 var originalAttributes = vnode && vnode.attributes;
435
436 while (isFunctionalComponent(vnode)) {
437 vnode = buildFunctionalComponent(vnode, context);
438 }
439
440 if (vnode == null) vnode = '';
441
442 if (isString(vnode)) {
443 if (dom) {
444 if (dom instanceof Text && dom.parentNode) {
445 if (dom.nodeValue != vnode) {
446 dom.nodeValue = vnode;
447 }
448 return dom;
449 }
450 recollectNodeTree(dom);
451 }
452 return document.createTextNode(vnode);
453 }
454
455 if (isFunction(vnode.nodeName)) {
456 return buildComponentFromVNode(dom, vnode, context, mountAll);
457 }
458
459 var out = dom,
460 nodeName = vnode.nodeName,
461 prevSvgMode = isSvgMode,
462 vchildren = vnode.children;
463
464 if (!isString(nodeName)) {
465 nodeName = String(nodeName);
466 }
467
468 isSvgMode = nodeName === 'svg' ? true : nodeName === 'foreignObject' ? false : isSvgMode;
469
470 if (!dom) {
471 out = createNode(nodeName, isSvgMode);
472 } else if (!isNamedNode(dom, nodeName)) {
473 out = createNode(nodeName, isSvgMode);
474 // move children into the replacement node
475 while (dom.firstChild) out.appendChild(dom.firstChild);
476 // reclaim element nodes
477 recollectNodeTree(dom);
478 }
479
480 // fast-path for elements containing a single TextNode:
481 if (vchildren && vchildren.length === 1 && typeof vchildren[0] === 'string' && out.childNodes.length === 1 && out.firstChild instanceof Text) {
482 if (out.firstChild.nodeValue != vchildren[0]) {
483 out.firstChild.nodeValue = vchildren[0];
484 }
485 } else if (vchildren && vchildren.length || out.firstChild) {
486 innerDiffNode(out, vchildren, context, mountAll);
487 }
488
489 var props = out[ATTR_KEY];
490 if (!props) {
491 out[ATTR_KEY] = props = {};
492 for (var a = out.attributes, i = a.length; i--;) {
493 props[a[i].name] = a[i].value;
494 }
495 }
496
497 diffAttributes(out, vnode.attributes, props);
498
499 if (originalAttributes && typeof originalAttributes.ref === 'function') {
500 (props.ref = originalAttributes.ref)(out);
501 }
502
503 isSvgMode = prevSvgMode;
504
505 return out;
506 }
507
508 /** Apply child and attribute changes between a VNode and a DOM Node to the DOM. */
509 function innerDiffNode(dom, vchildren, context, mountAll) {
510 var originalChildren = dom.childNodes,
511 children = [],
512 keyed = {},
513 keyedLen = 0,
514 min = 0,
515 len = originalChildren.length,
516 childrenLen = 0,
517 vlen = vchildren && vchildren.length,
518 j = undefined,
519 c = undefined,
520 vchild = undefined,
521 child = undefined;
522
523 if (len) {
524 for (var i = 0; i < len; i++) {
525 var _child = originalChildren[i],
526 key = vlen ? (c = _child._component) ? c.__key : (c = _child[ATTR_KEY]) ? c.key : null : null;
527 if (key || key === 0) {
528 keyedLen++;
529 keyed[key] = _child;
530 } else {
531 children[childrenLen++] = _child;
532 }
533 }
534 }
535
536 if (vlen) {
537 for (var i = 0; i < vlen; i++) {
538 vchild = vchildren[i];
539 child = null;
540
541 // if (isFunctionalComponent(vchild)) {
542 // vchild = buildFunctionalComponent(vchild);
543 // }
544
545 // attempt to find a node based on key matching
546 var key = vchild.key;
547 if (key != null) {
548 if (keyedLen && key in keyed) {
549 child = keyed[key];
550 keyed[key] = undefined;
551 keyedLen--;
552 }
553 }
554 // attempt to pluck a node of the same type from the existing children
555 else if (!child && min < childrenLen) {
556 for (j = min; j < childrenLen; j++) {
557 c = children[j];
558 if (c && isSameNodeType(c, vchild)) {
559 child = c;
560 children[j] = undefined;
561 if (j === childrenLen - 1) childrenLen--;
562 if (j === min) min++;
563 break;
564 }
565 }
566 if (!child && min < childrenLen && isFunction(vchild.nodeName) && mountAll) {
567 child = children[min];
568 children[min++] = undefined;
569 }
570 }
571
572 // morph the matched/found/created DOM child to match vchild (deep)
573 child = idiff(child, vchild, context, mountAll);
574
575 if (child && child !== dom && child !== originalChildren[i]) {
576 dom.insertBefore(child, originalChildren[i] || null);
577 }
578 }
579 }
580
581 if (keyedLen) {
582 for (var i in keyed) {
583 if (keyed[i]) recollectNodeTree(keyed[i]);
584 }
585 }
586
587 // remove orphaned children
588 if (min < childrenLen) {
589 removeOrphanedChildren(children);
590 }
591 }
592
593 /** Reclaim children that were unreferenced in the desired VTree */
594
595 function removeOrphanedChildren(children, unmountOnly) {
596 for (var i = children.length; i--;) {
597 if (children[i]) {
598 recollectNodeTree(children[i], unmountOnly);
599 }
600 }
601 }
602
603 /** Reclaim an entire tree of nodes, starting at the root. */
604
605 function recollectNodeTree(node, unmountOnly) {
606 // @TODO: Need to make a call on whether Preact should remove nodes not created by itself.
607 // Currently it *does* remove them. Discussion: https://github.com/developit/preact/issues/39
608 //if (!node[ATTR_KEY]) return;
609
610 var component = node._component;
611 if (component) {
612 unmountComponent(component, !unmountOnly);
613 } else {
614 if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
615
616 if (!unmountOnly) {
617 collectNode(node);
618 }
619
620 if (node.childNodes && node.childNodes.length) {
621 removeOrphanedChildren(node.childNodes, unmountOnly);
622 }
623 }
624 }
625
626 /** Apply differences in attributes from a VNode to the given DOM Node. */
627 function diffAttributes(dom, attrs, old) {
628 for (var _name in old) {
629 if (!(attrs && _name in attrs) && old[_name] != null) {
630 setAccessor(dom, _name, old[_name], old[_name] = undefined, isSvgMode);
631 }
632 }
633
634 // new & updated
635 if (attrs) {
636 for (var _name2 in attrs) {
637 if (_name2 !== 'children' && _name2 !== 'innerHTML' && (!(_name2 in old) || attrs[_name2] !== (_name2 === 'value' || _name2 === 'checked' ? dom[_name2] : old[_name2]))) {
638 setAccessor(dom, _name2, old[_name2], old[_name2] = attrs[_name2], isSvgMode);
639 }
640 }
641 }
642 }
643
644 /** Retains a pool of Components for re-use, keyed on component name.
645 * Note: since component names are not unique or even necessarily available, these are primarily a form of sharding.
646 * @private
647 */
648 var components = {};
649
650 function collectComponent(component) {
651 var name = component.constructor.name,
652 list = components[name];
653 if (list) list.push(component);else components[name] = [component];
654 }
655
656 function createComponent(Ctor, props, context) {
657 var inst = new Ctor(props, context),
658 list = components[Ctor.name];
659 Component.call(inst, props, context);
660 if (list) {
661 for (var i = list.length; i--;) {
662 if (list[i].constructor === Ctor) {
663 inst.nextBase = list[i].nextBase;
664 list.splice(i, 1);
665 break;
666 }
667 }
668 }
669 return inst;
670 }
671
672 /** Set a component's `props` (generally derived from JSX attributes).
673 * @param {Object} props
674 * @param {Object} [opts]
675 * @param {boolean} [opts.renderSync=false] If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
676 * @param {boolean} [opts.render=true] If `false`, no render will be triggered.
677 */
678
679 function setComponentProps(component, props, opts, context, mountAll) {
680 if (component._disable) return;
681 component._disable = true;
682
683 if (component.__ref = props.ref) delete props.ref;
684 if (component.__key = props.key) delete props.key;
685
686 if (!component.base || mountAll) {
687 if (component.componentWillMount) component.componentWillMount();
688 } else if (component.componentWillReceiveProps) {
689 component.componentWillReceiveProps(props, context);
690 }
691
692 if (context && context !== component.context) {
693 if (!component.prevContext) component.prevContext = component.context;
694 component.context = context;
695 }
696
697 if (!component.prevProps) component.prevProps = component.props;
698 component.props = props;
699
700 component._disable = false;
701
702 if (opts !== NO_RENDER) {
703 if (opts === SYNC_RENDER || options.syncComponentUpdates !== false || !component.base) {
704 renderComponent(component, SYNC_RENDER, mountAll);
705 } else {
706 enqueueRender(component);
707 }
708 }
709
710 if (component.__ref) component.__ref(component);
711 }
712
713 /** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
714 * @param {Component} component
715 * @param {Object} [opts]
716 * @param {boolean} [opts.build=false] If `true`, component will build and store a DOM node if not already associated with one.
717 * @private
718 */
719
720 function renderComponent(component, opts, mountAll, isChild) {
721 if (component._disable) return;
722
723 var skip = undefined,
724 rendered = undefined,
725 props = component.props,
726 state = component.state,
727 context = component.context,
728 previousProps = component.prevProps || props,
729 previousState = component.prevState || state,
730 previousContext = component.prevContext || context,
731 isUpdate = component.base,
732 nextBase = component.nextBase,
733 initialBase = isUpdate || nextBase,
734 initialChildComponent = component._component,
735 inst = undefined,
736 cbase = undefined;
737
738 // if updating
739 if (isUpdate) {
740 component.props = previousProps;
741 component.state = previousState;
742 component.context = previousContext;
743 if (opts !== FORCE_RENDER && component.shouldComponentUpdate && component.shouldComponentUpdate(props, state, context) === false) {
744 skip = true;
745 } else if (component.componentWillUpdate) {
746 component.componentWillUpdate(props, state, context);
747 }
748 component.props = props;
749 component.state = state;
750 component.context = context;
751 }
752
753 component.prevProps = component.prevState = component.prevContext = component.nextBase = null;
754 component._dirty = false;
755
756 if (!skip) {
757 if (component.render) rendered = component.render(props, state, context);
758
759 // context to pass to the child, can be updated via (grand-)parent component
760 if (component.getChildContext) {
761 context = extend(clone(context), component.getChildContext());
762 }
763
764 while (isFunctionalComponent(rendered)) {
765 rendered = buildFunctionalComponent(rendered, context);
766 }
767
768 var childComponent = rendered && rendered.nodeName,
769 toUnmount = undefined,
770 base = undefined;
771
772 if (isFunction(childComponent)) {
773 // set up high order component link
774
775 inst = initialChildComponent;
776 var childProps = getNodeProps(rendered);
777
778 if (inst && inst.constructor === childComponent) {
779 setComponentProps(inst, childProps, SYNC_RENDER, context);
780 } else {
781 toUnmount = inst;
782
783 inst = createComponent(childComponent, childProps, context);
784 inst.nextBase = inst.nextBase || nextBase;
785 inst._parentComponent = component;
786 component._component = inst;
787 setComponentProps(inst, childProps, NO_RENDER, context);
788 renderComponent(inst, SYNC_RENDER, mountAll, true);
789 }
790
791 base = inst.base;
792 } else {
793 cbase = initialBase;
794
795 // destroy high order component link
796 toUnmount = initialChildComponent;
797 if (toUnmount) {
798 cbase = component._component = null;
799 }
800
801 if (initialBase || opts === SYNC_RENDER) {
802 if (cbase) cbase._component = null;
803 base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
804 }
805 }
806
807 if (initialBase && base !== initialBase && inst !== initialChildComponent) {
808 var baseParent = initialBase.parentNode;
809 if (baseParent && base !== baseParent) {
810 baseParent.replaceChild(base, initialBase);
811
812 if (!toUnmount) {
813 initialBase._component = null;
814 recollectNodeTree(initialBase);
815 }
816 }
817 }
818
819 if (toUnmount) {
820 unmountComponent(toUnmount, base !== initialBase);
821 }
822
823 component.base = base;
824 if (base && !isChild) {
825 var componentRef = component,
826 t = component;
827 while (t = t._parentComponent) {
828 (componentRef = t).base = base;
829 }
830 base._component = componentRef;
831 base._componentConstructor = componentRef.constructor;
832 }
833 }
834
835 if (!isUpdate || mountAll) {
836 mounts.unshift(component);
837 } else if (!skip) {
838 if (component.componentDidUpdate) {
839 component.componentDidUpdate(previousProps, previousState, previousContext);
840 }
841 if (options.afterUpdate) options.afterUpdate(component);
842 }
843
844 var cb = component._renderCallbacks,
845 fn = undefined;
846 if (cb) while (fn = cb.pop()) fn.call(component);
847
848 if (!diffLevel && !isChild) flushMounts();
849 }
850
851 /** Apply the Component referenced by a VNode to the DOM.
852 * @param {Element} dom The DOM node to mutate
853 * @param {VNode} vnode A Component-referencing VNode
854 * @returns {Element} dom The created/mutated element
855 * @private
856 */
857
858 function buildComponentFromVNode(dom, vnode, context, mountAll) {
859 var c = dom && dom._component,
860 oldDom = dom,
861 isDirectOwner = c && dom._componentConstructor === vnode.nodeName,
862 isOwner = isDirectOwner,
863 props = getNodeProps(vnode);
864 while (c && !isOwner && (c = c._parentComponent)) {
865 isOwner = c.constructor === vnode.nodeName;
866 }
867
868 if (c && isOwner && (!mountAll || c._component)) {
869 setComponentProps(c, props, ASYNC_RENDER, context, mountAll);
870 dom = c.base;
871 } else {
872 if (c && !isDirectOwner) {
873 unmountComponent(c, true);
874 dom = oldDom = null;
875 }
876
877 c = createComponent(vnode.nodeName, props, context);
878 if (dom && !c.nextBase) {
879 c.nextBase = dom;
880 // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L241:
881 oldDom = null;
882 }
883 setComponentProps(c, props, SYNC_RENDER, context, mountAll);
884 dom = c.base;
885
886 if (oldDom && dom !== oldDom) {
887 oldDom._component = null;
888 recollectNodeTree(oldDom);
889 }
890 }
891
892 return dom;
893 }
894
895 /** Remove a component from the DOM and recycle it.
896 * @param {Element} dom A DOM node from which to unmount the given Component
897 * @param {Component} component The Component instance to unmount
898 * @private
899 */
900
901 function unmountComponent(component, remove) {
902 if (options.beforeUnmount) options.beforeUnmount(component);
903
904 // console.log(`${remove?'Removing':'Unmounting'} component: ${component.constructor.name}`);
905 var base = component.base;
906
907 component._disable = true;
908
909 if (component.componentWillUnmount) component.componentWillUnmount();
910
911 component.base = null;
912
913 // recursively tear down & recollect high-order component children:
914 var inner = component._component;
915 if (inner) {
916 unmountComponent(inner, remove);
917 } else if (base) {
918 if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);
919
920 component.nextBase = base;
921
922 if (remove) {
923 removeNode(base);
924 collectComponent(component);
925 }
926 removeOrphanedChildren(base.childNodes, !remove);
927 }
928
929 if (component.__ref) component.__ref(null);
930 if (component.componentDidUnmount) component.componentDidUnmount();
931 }
932
933 /** Base Component class, for he ES6 Class method of creating Components
934 * @public
935 *
936 * @example
937 * class MyFoo extends Component {
938 * render(props, state) {
939 * return <div />;
940 * }
941 * }
942 */
943
944 function Component(props, context) {
945 /** @private */
946 this._dirty = true;
947 // /** @public */
948 // this._disableRendering = false;
949 // /** @public */
950 // this.prevState = this.prevProps = this.prevContext = this.base = this.nextBase = this._parentComponent = this._component = this.__ref = this.__key = this._linkedStates = this._renderCallbacks = null;
951 /** @public */
952 this.context = context;
953 /** @type {object} */
954 this.props = props;
955 /** @type {object} */
956 if (!this.state) this.state = {};
957 }
958
959 extend(Component.prototype, {
960
961 /** Returns a `boolean` value indicating if the component should re-render when receiving the given `props` and `state`.
962 * @param {object} nextProps
963 * @param {object} nextState
964 * @param {object} nextContext
965 * @returns {Boolean} should the component re-render
966 * @name shouldComponentUpdate
967 * @function
968 */
969 // shouldComponentUpdate() {
970 // return true;
971 // },
972
973 /** Returns a function that sets a state property when called.
974 * Calling linkState() repeatedly with the same arguments returns a cached link function.
975 *
976 * Provides some built-in special cases:
977 * - Checkboxes and radio buttons link their boolean `checked` value
978 * - Inputs automatically link their `value` property
979 * - Event paths fall back to any associated Component if not found on an element
980 * - If linked value is a function, will invoke it and use the result
981 *
982 * @param {string} key The path to set - can be a dot-notated deep key
983 * @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.
984 * @returns {function} linkStateSetter(e)
985 *
986 * @example Update a "text" state value when an input changes:
987 * <input onChange={ this.linkState('text') } />
988 *
989 * @example Set a deep state value on click
990 * <button onClick={ this.linkState('touch.coords', 'touches.0') }>Tap</button
991 */
992 linkState: function linkState(key, eventPath) {
993 var c = this._linkedStates || (this._linkedStates = {});
994 return c[key + eventPath] || (c[key + eventPath] = createLinkedState(this, key, eventPath));
995 },
996
997 /** Update component state by copying properties from `state` to `this.state`.
998 * @param {object} state A hash of state properties to update with new values
999 */
1000 setState: function setState(state, callback) {
1001 var s = this.state;
1002 if (!this.prevState) this.prevState = clone(s);
1003 extend(s, isFunction(state) ? state(s, this.props) : state);
1004 if (callback) (this._renderCallbacks = this._renderCallbacks || []).push(callback);
1005 enqueueRender(this);
1006 },
1007
1008 /** Immediately perform a synchronous re-render of the component.
1009 * @private
1010 */
1011 forceUpdate: function forceUpdate() {
1012 renderComponent(this, FORCE_RENDER);
1013 },
1014
1015 /** Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
1016 * Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
1017 * @param {object} props Props (eg: JSX attributes) received from parent element/component
1018 * @param {object} state The component's current state
1019 * @param {object} context Context object (if a parent component has provided context)
1020 * @returns VNode
1021 */
1022 render: function render() {}
1023
1024 });
1025
1026 /** Render JSX into a `parent` Element.
1027 * @param {VNode} vnode A (JSX) VNode to render
1028 * @param {Element} parent DOM element to render into
1029 * @param {Element} [merge] Attempt to re-use an existing DOM tree rooted at `merge`
1030 * @public
1031 *
1032 * @example
1033 * // render a div into <body>:
1034 * render(<div id="hello">hello!</div>, document.body);
1035 *
1036 * @example
1037 * // render a "Thing" component into #foo:
1038 * const Thing = ({ name }) => <span>{ name }</span>;
1039 * render(<Thing name="one" />, document.querySelector('#foo'));
1040 */
1041
1042 function render(vnode, parent, merge) {
1043 return diff(merge, vnode, {}, false, parent);
1044 }
1045
1046 exports.createElement = h;
1047 exports.h = h;
1048 exports.cloneElement = cloneElement;
1049 exports.Component = Component;
1050 exports.render = render;
1051 exports.rerender = rerender;
1052 exports.options = options;
1053
1054 Object.defineProperty(exports, '__esModule', { value: true });
1055
1056}));
1057//# sourceMappingURL=aliases.js.map