aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/preact/src
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/preact/src')
-rw-r--r--thirdparty/preact/src/clone-element.js10
-rw-r--r--thirdparty/preact/src/component.js102
-rw-r--r--thirdparty/preact/src/constants.js20
-rw-r--r--thirdparty/preact/src/dom/index.js100
-rw-r--r--thirdparty/preact/src/dom/recycler.js25
-rw-r--r--thirdparty/preact/src/h.js51
-rw-r--r--thirdparty/preact/src/linked-state.js28
-rw-r--r--thirdparty/preact/src/options.js18
-rw-r--r--thirdparty/preact/src/preact.d.ts554
-rw-r--r--thirdparty/preact/src/preact.js24
-rw-r--r--thirdparty/preact/src/preact.js.flow9
-rw-r--r--thirdparty/preact/src/render-queue.js23
-rw-r--r--thirdparty/preact/src/render.js20
-rw-r--r--thirdparty/preact/src/util.js68
-rw-r--r--thirdparty/preact/src/vdom/component-recycler.js32
-rw-r--r--thirdparty/preact/src/vdom/component.js270
-rw-r--r--thirdparty/preact/src/vdom/diff.js247
-rw-r--r--thirdparty/preact/src/vdom/functional-component.js25
-rw-r--r--thirdparty/preact/src/vdom/index.js50
-rw-r--r--thirdparty/preact/src/vnode.js14
20 files changed, 1690 insertions, 0 deletions
diff --git a/thirdparty/preact/src/clone-element.js b/thirdparty/preact/src/clone-element.js
new file mode 100644
index 000000000..fc7103056
--- /dev/null
+++ b/thirdparty/preact/src/clone-element.js
@@ -0,0 +1,10 @@
+import { clone, extend } from './util';
+import { h } from './h';
+
+export function cloneElement(vnode, props) {
+ return h(
+ vnode.nodeName,
+ extend(clone(vnode.attributes), props),
+ arguments.length>2 ? [].slice.call(arguments, 2) : vnode.children
+ );
+}
diff --git a/thirdparty/preact/src/component.js b/thirdparty/preact/src/component.js
new file mode 100644
index 000000000..aefaebe98
--- /dev/null
+++ b/thirdparty/preact/src/component.js
@@ -0,0 +1,102 @@
+import { FORCE_RENDER } from './constants';
+import { extend, clone, isFunction } from './util';
+import { createLinkedState } from './linked-state';
+import { renderComponent } from './vdom/component';
+import { enqueueRender } from './render-queue';
+
+/** Base Component class, for he ES6 Class method of creating Components
+ * @public
+ *
+ * @example
+ * class MyFoo extends Component {
+ * render(props, state) {
+ * return <div />;
+ * }
+ * }
+ */
+export function Component(props, context) {
+ /** @private */
+ this._dirty = true;
+ // /** @public */
+ // this._disableRendering = false;
+ // /** @public */
+ // this.prevState = this.prevProps = this.prevContext = this.base = this.nextBase = this._parentComponent = this._component = this.__ref = this.__key = this._linkedStates = this._renderCallbacks = null;
+ /** @public */
+ this.context = context;
+ /** @type {object} */
+ this.props = props;
+ /** @type {object} */
+ if (!this.state) this.state = {};
+}
+
+
+extend(Component.prototype, {
+
+ /** Returns a `boolean` value 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
+ */
+ // shouldComponentUpdate() {
+ // return true;
+ // },
+
+
+ /** Returns a function that sets a state property when called.
+ * Calling linkState() repeatedly with the same arguments returns a cached link function.
+ *
+ * Provides some built-in special cases:
+ * - Checkboxes and radio buttons link their boolean `checked` value
+ * - Inputs automatically link their `value` property
+ * - Event paths fall back to any associated Component if not found on an element
+ * - If linked value is a function, will invoke it and use the result
+ *
+ * @param {string} key The path to set - can be a dot-notated deep key
+ * @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.
+ * @returns {function} linkStateSetter(e)
+ *
+ * @example Update a "text" state value when an input changes:
+ * <input onChange={ this.linkState('text') } />
+ *
+ * @example Set a deep state value on click
+ * <button onClick={ this.linkState('touch.coords', 'touches.0') }>Tap</button
+ */
+ linkState(key, eventPath) {
+ let c = this._linkedStates || (this._linkedStates = {});
+ return c[key+eventPath] || (c[key+eventPath] = createLinkedState(this, key, eventPath));
+ },
+
+
+ /** Update component state by copying properties from `state` to `this.state`.
+ * @param {object} state A hash of state properties to update with new values
+ */
+ setState(state, callback) {
+ let s = this.state;
+ if (!this.prevState) this.prevState = clone(s);
+ extend(s, isFunction(state) ? state(s, this.props) : state);
+ if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
+ enqueueRender(this);
+ },
+
+
+ /** Immediately perform a synchronous re-render of the component.
+ * @private
+ */
+ forceUpdate() {
+ renderComponent(this, FORCE_RENDER);
+ },
+
+
+ /** 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() {}
+
+});
diff --git a/thirdparty/preact/src/constants.js b/thirdparty/preact/src/constants.js
new file mode 100644
index 000000000..138cd0983
--- /dev/null
+++ b/thirdparty/preact/src/constants.js
@@ -0,0 +1,20 @@
+// render modes
+
+export const NO_RENDER = 0;
+export const SYNC_RENDER = 1;
+export const FORCE_RENDER = 2;
+export const ASYNC_RENDER = 3;
+
+export const EMPTY = {};
+
+export const ATTR_KEY = typeof Symbol!=='undefined' ? Symbol.for('preactattr') : '__preactattr_';
+
+// DOM properties that should NOT have "px" added when numeric
+export const NON_DIMENSION_PROPS = {
+ boxFlex:1, boxFlexGroup:1, columnCount:1, fillOpacity:1, flex:1, flexGrow:1,
+ flexPositive:1, flexShrink:1, flexNegative:1, fontWeight:1, lineClamp:1, lineHeight:1,
+ opacity:1, order:1, orphans:1, strokeOpacity:1, widows:1, zIndex:1, zoom:1
+};
+
+// DOM event types that do not bubble and should be attached via useCapture
+export const NON_BUBBLING_EVENTS = { blur:1, error:1, focus:1, load:1, resize:1, scroll:1 };
diff --git a/thirdparty/preact/src/dom/index.js b/thirdparty/preact/src/dom/index.js
new file mode 100644
index 000000000..248a3cdc5
--- /dev/null
+++ b/thirdparty/preact/src/dom/index.js
@@ -0,0 +1,100 @@
+import { ATTR_KEY, NON_DIMENSION_PROPS, NON_BUBBLING_EVENTS } from '../constants';
+import options from '../options';
+import { toLowerCase, isString, isFunction, hashToClassName } from '../util';
+
+
+
+
+/** Removes a given DOM Node from its parent. */
+export function removeNode(node) {
+ let p = node.parentNode;
+ if (p) p.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} value An attribute value, such as a function to be used as an event handler
+ * @param {any} previousValue The last value that was set for this name/node pair
+ * @private
+ */
+export function setAccessor(node, name, value, old, isSvg) {
+ node[ATTR_KEY][name] = value;
+
+ if (name==='className') name = 'class';
+
+ if (name==='class' && value && typeof value==='object') {
+ value = hashToClassName(value);
+ }
+
+ if (name==='key' || name==='children' || name==='innerHTML') {
+ // skip these
+ }
+ else if (name==='class' && !isSvg) {
+ node.className = value || '';
+ }
+ else if (name==='style') {
+ if (!value || isString(value) || isString(old)) {
+ node.style.cssText = value || '';
+ }
+ if (value && typeof value==='object') {
+ if (!isString(old)) {
+ for (let i in old) if (!(i in value)) node.style[i] = '';
+ }
+ for (let i in value) {
+ node.style[i] = typeof value[i]==='number' && !NON_DIMENSION_PROPS[i] ? (value[i]+'px') : value[i];
+ }
+ }
+ }
+ else if (name==='dangerouslySetInnerHTML') {
+ if (value) node.innerHTML = value.__html;
+ }
+ else if (name[0]=='o' && name[1]=='n') {
+ let l = node._listeners || (node._listeners = {});
+ name = toLowerCase(name.substring(2));
+ // @TODO: this might be worth it later, un-breaks focus/blur bubbling in IE9:
+ // if (node.attachEvent) name = name=='focus'?'focusin':name=='blur'?'focusout':name;
+ if (value) {
+ if (!l[name]) node.addEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
+ }
+ else if (l[name]) {
+ node.removeEventListener(name, eventProxy, !!NON_BUBBLING_EVENTS[name]);
+ }
+ l[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 {
+ let ns = isSvg && name.match(/^xlink\:?(.+)/);
+ if (value==null || value===false) {
+ if (ns) node.removeAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]));
+ else node.removeAttribute(name);
+ }
+ else if (typeof value!=='object' && !isFunction(value)) {
+ if (ns) node.setAttributeNS('http://www.w3.org/1999/xlink', toLowerCase(ns[1]), 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);
+}
diff --git a/thirdparty/preact/src/dom/recycler.js b/thirdparty/preact/src/dom/recycler.js
new file mode 100644
index 000000000..22085a916
--- /dev/null
+++ b/thirdparty/preact/src/dom/recycler.js
@@ -0,0 +1,25 @@
+import { toLowerCase } from '../util';
+import { removeNode } from './index';
+
+/** DOM node pool, keyed on nodeName. */
+
+const nodes = {};
+
+export function collectNode(node) {
+ removeNode(node);
+
+ if (node instanceof Element) {
+ node._component = node._componentConstructor = null;
+
+ let name = node.normalizedNodeName || toLowerCase(node.nodeName);
+ (nodes[name] || (nodes[name] = [])).push(node);
+ }
+}
+
+
+export function createNode(nodeName, isSvg) {
+ let name = toLowerCase(nodeName),
+ node = nodes[name] && nodes[name].pop() || (isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName));
+ node.normalizedNodeName = name;
+ return node;
+}
diff --git a/thirdparty/preact/src/h.js b/thirdparty/preact/src/h.js
new file mode 100644
index 000000000..e57ce4bde
--- /dev/null
+++ b/thirdparty/preact/src/h.js
@@ -0,0 +1,51 @@
+import { VNode } from './vnode';
+import options from './options';
+
+
+let stack = [];
+
+
+
+/** JSX/hyperscript reviver
+* Benchmarks: https://esbench.com/bench/57ee8f8e330ab09900a1a1a0
+ * @see http://jasonformat.com/wtf-is-jsx
+ * @public
+ * @example
+ * /** @jsx h *\/
+ * import { render, h } from 'preact';
+ * render(<span>foo</span>, document.body);
+ */
+export function h(nodeName, attributes) {
+ let children, lastSimple, child, simple, i;
+ for (i=arguments.length; i-- > 2; ) {
+ stack.push(arguments[i]);
+ }
+ if (attributes && attributes.children) {
+ if (!stack.length) stack.push(attributes.children);
+ delete attributes.children;
+ }
+ while (stack.length) {
+ if ((child = stack.pop()) instanceof Array) {
+ for (i=child.length; i--; ) stack.push(child[i]);
+ }
+ else if (child!=null && child!==false) {
+ if (typeof child=='number' || child===true) child = String(child);
+ simple = typeof child=='string';
+ if (simple && lastSimple) {
+ children[children.length-1] += child;
+ }
+ else {
+ if (children) children.push(child);
+ else children = [child];
+ lastSimple = simple;
+ }
+ }
+ }
+
+ let p = new VNode(nodeName, attributes || undefined, children);
+
+ // if a "vnode hook" is defined, pass every created VNode to it
+ if (options.vnode) options.vnode(p);
+
+ return p;
+}
diff --git a/thirdparty/preact/src/linked-state.js b/thirdparty/preact/src/linked-state.js
new file mode 100644
index 000000000..ed72bd8bc
--- /dev/null
+++ b/thirdparty/preact/src/linked-state.js
@@ -0,0 +1,28 @@
+import { isString, delve } from './util';
+
+/** Create an Event handler function that sets a given state property.
+ * @param {Component} component The component whose state should be updated
+ * @param {string} key A dot-notated key path to update in the component's state
+ * @param {string} eventPath A dot-notated key path to the value that should be retrieved from the Event or component
+ * @returns {function} linkedStateHandler
+ * @private
+ */
+export function createLinkedState(component, key, eventPath) {
+ let path = key.split('.'),
+ p0 = path[0];
+ return function(e) {
+ let t = e && e.currentTarget || this,
+ s = component.state,
+ obj = s,
+ v = isString(eventPath) ? delve(e, eventPath) : t.nodeName ? ((t.nodeName+t.type).match(/^input(che|rad)/i) ? t.checked : t.value) : e,
+ i;
+ if (path.length>1) {
+ for (i=0; i<path.length-1; i++) {
+ obj = obj[path[i]] || (obj[path[i]] = {});
+ }
+ obj[path[i]] = v;
+ v = s[p0];
+ }
+ component.setState({ [p0]: v });
+ };
+}
diff --git a/thirdparty/preact/src/options.js b/thirdparty/preact/src/options.js
new file mode 100644
index 000000000..35b7418fc
--- /dev/null
+++ b/thirdparty/preact/src/options.js
@@ -0,0 +1,18 @@
+/** Global options
+ * @public
+ * @namespace options {Object}
+ */
+export default {
+
+ /** 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) { }
+};
diff --git a/thirdparty/preact/src/preact.d.ts b/thirdparty/preact/src/preact.d.ts
new file mode 100644
index 000000000..2dd8299a9
--- /dev/null
+++ b/thirdparty/preact/src/preact.d.ts
@@ -0,0 +1,554 @@
+declare namespace preact {
+ interface ComponentProps {
+ children?:JSX.Element[];
+ key?:string;
+ }
+
+ interface PreactHTMLAttributes {
+ key?:string;
+ }
+
+ interface VNode {
+ nodeName:ComponentConstructor<any, any>|string;
+ attributes:{[name:string]:any};
+ children:VNode[];
+ key:string;
+ }
+
+ interface ComponentLifecycle<PropsType, StateType> {
+ componentWillMount?():void;
+
+ componentDidMount?():void;
+
+ componentWillUnmount?():void;
+
+ componentDidUnmount?():void;
+
+ componentWillReceiveProps?(props:PropsType):void;
+
+ shouldComponentUpdate?(props:PropsType):boolean;
+
+ componentWillUpdate?():void;
+
+ componentDidUpdate?():void;
+ }
+
+ interface ComponentConstructor<PropsType, StateType> {
+ new (props?:PropsType):Component<PropsType, StateType>;
+ }
+
+ abstract class Component<PropsType, StateType> implements ComponentLifecycle<PropsType, StateType> {
+ constructor(props?:PropsType);
+
+ state:StateType;
+ props:PropsType & ComponentProps;
+ base:HTMLElement;
+
+ linkState:(name:string) => void;
+
+ setState(state:StateType, opts?:any):void;
+
+ abstract render(props:PropsType & ComponentProps, state:any):JSX.Element;
+ }
+
+ function h<PropsType>(node:ComponentConstructor<PropsType, any>, params:PropsType, ...children:(JSX.Element|string)[]):JSX.Element;
+ function h(node:string, params:JSX.HTMLAttributes&JSX.SVGAttributes, ...children:(JSX.Element|string)[]):JSX.Element;
+
+ function render(node:JSX.Element, parent:Element, merge?:boolean):Element;
+
+ function rerender():void;
+
+ function cloneElement(element:JSX.Element, props:any):JSX.Element;
+
+ var options:{
+ syncComponentUpdates?:boolean;
+ debounceRendering?:(render:() => void) => void;
+ vnode?:(vnode:VNode) => void;
+ event?:(event:Event) => Event;
+ };
+}
+
+declare module "preact" {
+ export = preact;
+}
+
+declare namespace JSX {
+ interface Element extends preact.VNode {
+
+ }
+
+ interface ElementClass extends preact.Component<any, any> {
+
+ }
+
+ interface ElementAttributesProperty {
+ props:any;
+ }
+
+ interface SVGAttributes {
+ clipPath?:string;
+ cx?:number | string;
+ cy?:number | string;
+ d?:string;
+ dx?:number | string;
+ dy?:number | string;
+ fill?:string;
+ fillOpacity?:number | string;
+ fontFamily?:string;
+ fontSize?:number | string;
+ fx?:number | string;
+ fy?:number | string;
+ gradientTransform?:string;
+ gradientUnits?:string;
+ markerEnd?:string;
+ markerMid?:string;
+ markerStart?:string;
+ offset?:number | string;
+ opacity?:number | string;
+ patternContentUnits?:string;
+ patternUnits?:string;
+ points?:string;
+ preserveAspectRatio?:string;
+ r?:number | string;
+ rx?:number | string;
+ ry?:number | string;
+ spreadMethod?:string;
+ stopColor?:string;
+ stopOpacity?:number | string;
+ stroke?:string;
+ strokeDasharray?:string;
+ strokeLinecap?:string;
+ strokeMiterlimit?:string;
+ strokeOpacity?:number | string;
+ strokeWidth?:number | string;
+ textAnchor?:string;
+ transform?:string;
+ version?:string;
+ viewBox?:string;
+ x1?:number | string;
+ x2?:number | string;
+ x?:number | string;
+ xlinkActuate?:string;
+ xlinkArcrole?:string;
+ xlinkHref?:string;
+ xlinkRole?:string;
+ xlinkShow?:string;
+ xlinkTitle?:string;
+ xlinkType?:string;
+ xmlBase?:string;
+ xmlLang?:string;
+ xmlSpace?:string;
+ y1?:number | string;
+ y2?:number | string;
+ y?:number | string;
+ }
+
+ interface PathAttributes {
+ d:string;
+ }
+
+ interface EventHandler<E extends Event> {
+ (event:E):void;
+ }
+
+ type ClipboardEventHandler = EventHandler<ClipboardEvent>;
+ type CompositionEventHandler = EventHandler<CompositionEvent>;
+ type DragEventHandler = EventHandler<DragEvent>;
+ type FocusEventHandler = EventHandler<FocusEvent>;
+ type KeyboardEventHandler = EventHandler<KeyboardEvent>;
+ type MouseEventHandler = EventHandler<MouseEvent>;
+ type TouchEventHandler = EventHandler<TouchEvent>;
+ type UIEventHandler = EventHandler<UIEvent>;
+ type WheelEventHandler = EventHandler<WheelEvent>;
+ type AnimationEventHandler = EventHandler<AnimationEvent>;
+ type TransitionEventHandler = EventHandler<TransitionEvent>;
+
+ type GenericEventHandler = EventHandler<Event>;
+
+ interface DOMAttributed {
+ // Clipboard Events
+ onCopy?:ClipboardEventHandler;
+ onCut?:ClipboardEventHandler;
+ onPaste?:ClipboardEventHandler;
+
+ // Composition Events
+ onCompositionEnd?:CompositionEventHandler;
+ onCompositionStart?:CompositionEventHandler;
+ onCompositionUpdate?:CompositionEventHandler;
+
+ // Focus Events
+ onFocus?:FocusEventHandler;
+ onBlur?:FocusEventHandler;
+
+ // Form Events
+ onChange?:GenericEventHandler;
+ onInput?:GenericEventHandler;
+ onSubmit?:GenericEventHandler;
+
+ // Keyboard Events
+ onKeyDown?:KeyboardEventHandler;
+ onKeyPress?:KeyboardEventHandler;
+ onKeyUp?:KeyboardEventHandler;
+
+ // Media Events
+ onAbort?:GenericEventHandler;
+ onCanPlay?:GenericEventHandler;
+ onCanPlayThrough?:GenericEventHandler;
+ onDurationChange?:GenericEventHandler;
+ onEmptied?:GenericEventHandler;
+ onEncrypted?:GenericEventHandler;
+ onEnded?:GenericEventHandler;
+ onLoadedData?:GenericEventHandler;
+ onLoadedMetadata?:GenericEventHandler;
+ onLoadStart?:GenericEventHandler;
+ onPause?:GenericEventHandler;
+ onPlay?:GenericEventHandler;
+ onPlaying?:GenericEventHandler;
+ onProgress?:GenericEventHandler;
+ onRateChange?:GenericEventHandler;
+ onSeeked?:GenericEventHandler;
+ onSeeking?:GenericEventHandler;
+ onStalled?:GenericEventHandler;
+ onSuspend?:GenericEventHandler;
+ onTimeUpdate?:GenericEventHandler;
+ onVolumeChange?:GenericEventHandler;
+ onWaiting?:GenericEventHandler;
+
+ // MouseEvents
+ onClick?:MouseEventHandler;
+ onContextMenu?:MouseEventHandler;
+ onDoubleClick?:MouseEventHandler;
+ onDrag?:DragEventHandler;
+ onDragEnd?:DragEventHandler;
+ onDragEnter?:DragEventHandler;
+ onDragExit?:DragEventHandler;
+ onDragLeave?:DragEventHandler;
+ onDragOver?:DragEventHandler;
+ onDragStart?:DragEventHandler;
+ onDrop?:DragEventHandler;
+ onMouseDown?:MouseEventHandler;
+ onMouseEnter?:MouseEventHandler;
+ onMouseLeave?:MouseEventHandler;
+ onMouseMove?:MouseEventHandler;
+ onMouseOut?:MouseEventHandler;
+ onMouseOver?:MouseEventHandler;
+ onMouseUp?:MouseEventHandler;
+
+ // Selection Events
+ onSelect?:GenericEventHandler;
+
+ // Touch Events
+ onTouchCancel?:TouchEventHandler;
+ onTouchEnd?:TouchEventHandler;
+ onTouchMove?:TouchEventHandler;
+ onTouchStart?:TouchEventHandler;
+
+ // UI Events
+ onScroll?:UIEventHandler;
+
+ // Wheel Events
+ onWheel?:WheelEventHandler;
+
+ // Animation Events
+ onAnimationStart?:AnimationEventHandler;
+ onAnimationEnd?:AnimationEventHandler;
+ onAnimationIteration?:AnimationEventHandler;
+
+ // Transition Events
+ onTransitionEnd?:TransitionEventHandler;
+ }
+
+ interface HTMLAttributes extends preact.PreactHTMLAttributes, DOMAttributed {
+ // Standard HTML Attributes
+ accept?:string;
+ acceptCharset?:string;
+ accessKey?:string;
+ action?:string;
+ allowFullScreen?:boolean;
+ allowTransparency?:boolean;
+ alt?:string;
+ async?:boolean;
+ autocomplete?:string;
+ autofocus?:boolean;
+ autoPlay?:boolean;
+ capture?:boolean;
+ cellPadding?:number | string;
+ cellSpacing?:number | string;
+ charSet?:string;
+ challenge?:string;
+ checked?:boolean;
+ class?:string;
+ className?:string;
+ cols?:number;
+ colSpan?:number;
+ content?:string;
+ contentEditable?:boolean;
+ contextMenu?:string;
+ controls?:boolean;
+ coords?:string;
+ crossOrigin?:string;
+ data?:string;
+ dateTime?:string;
+ default?:boolean;
+ defer?:boolean;
+ dir?:string;
+ disabled?:boolean;
+ download?:any;
+ draggable?:boolean;
+ encType?:string;
+ form?:string;
+ formAction?:string;
+ formEncType?:string;
+ formMethod?:string;
+ formNoValidate?:boolean;
+ formTarget?:string;
+ frameBorder?:number | string;
+ headers?:string;
+ height?:number | string;
+ hidden?:boolean;
+ high?:number;
+ href?:string;
+ hrefLang?:string;
+ for?:string;
+ httpEquiv?:string;
+ icon?:string;
+ id?:string;
+ inputMode?:string;
+ integrity?:string;
+ is?:string;
+ keyParams?:string;
+ keyType?:string;
+ kind?:string;
+ label?:string;
+ lang?:string;
+ list?:string;
+ loop?:boolean;
+ low?:number;
+ manifest?:string;
+ marginHeight?:number;
+ marginWidth?:number;
+ max?:number | string;
+ maxLength?:number;
+ media?:string;
+ mediaGroup?:string;
+ method?:string;
+ min?:number | string;
+ minLength?:number;
+ multiple?:boolean;
+ muted?:boolean;
+ name?:string;
+ noValidate?:boolean;
+ open?:boolean;
+ optimum?:number;
+ pattern?:string;
+ placeholder?:string;
+ poster?:string;
+ preload?:string;
+ radioGroup?:string;
+ readOnly?:boolean;
+ rel?:string;
+ required?:boolean;
+ role?:string;
+ rows?:number;
+ rowSpan?:number;
+ sandbox?:string;
+ scope?:string;
+ scoped?:boolean;
+ scrolling?:string;
+ seamless?:boolean;
+ selected?:boolean;
+ shape?:string;
+ size?:number;
+ sizes?:string;
+ span?:number;
+ spellCheck?:boolean;
+ src?:string;
+ srcset?:string;
+ srcDoc?:string;
+ srcLang?:string;
+ srcSet?:string;
+ start?:number;
+ step?:number | string;
+ style?:any;
+ summary?:string;
+ tabIndex?:number;
+ target?:string;
+ title?:string;
+ type?:string;
+ useMap?:string;
+ value?:string | string[];
+ width?:number | string;
+ wmode?:string;
+ wrap?:string;
+
+ // RDFa Attributes
+ about?:string;
+ datatype?:string;
+ inlist?:any;
+ prefix?:string;
+ property?:string;
+ resource?:string;
+ typeof?:string;
+ vocab?:string;
+ }
+
+ interface IntrinsicElements {
+ // HTML
+ a:HTMLAttributes;
+ abbr:HTMLAttributes;
+ address:HTMLAttributes;
+ area:HTMLAttributes;
+ article:HTMLAttributes;
+ aside:HTMLAttributes;
+ audio:HTMLAttributes;
+ b:HTMLAttributes;
+ base:HTMLAttributes;
+ bdi:HTMLAttributes;
+ bdo:HTMLAttributes;
+ big:HTMLAttributes;
+ blockquote:HTMLAttributes;
+ body:HTMLAttributes;
+ br:HTMLAttributes;
+ button:HTMLAttributes;
+ canvas:HTMLAttributes;
+ caption:HTMLAttributes;
+ cite:HTMLAttributes;
+ code:HTMLAttributes;
+ col:HTMLAttributes;
+ colgroup:HTMLAttributes;
+ data:HTMLAttributes;
+ datalist:HTMLAttributes;
+ dd:HTMLAttributes;
+ del:HTMLAttributes;
+ details:HTMLAttributes;
+ dfn:HTMLAttributes;
+ dialog:HTMLAttributes;
+ div:HTMLAttributes;
+ dl:HTMLAttributes;
+ dt:HTMLAttributes;
+ em:HTMLAttributes;
+ embed:HTMLAttributes;
+ fieldset:HTMLAttributes;
+ figcaption:HTMLAttributes;
+ figure:HTMLAttributes;
+ footer:HTMLAttributes;
+ form:HTMLAttributes;
+ h1:HTMLAttributes;
+ h2:HTMLAttributes;
+ h3:HTMLAttributes;
+ h4:HTMLAttributes;
+ h5:HTMLAttributes;
+ h6:HTMLAttributes;
+ head:HTMLAttributes;
+ header:HTMLAttributes;
+ hr:HTMLAttributes;
+ html:HTMLAttributes;
+ i:HTMLAttributes;
+ iframe:HTMLAttributes;
+ img:HTMLAttributes;
+ input:HTMLAttributes;
+ ins:HTMLAttributes;
+ kbd:HTMLAttributes;
+ keygen:HTMLAttributes;
+ label:HTMLAttributes;
+ legend:HTMLAttributes;
+ li:HTMLAttributes;
+ link:HTMLAttributes;
+ main:HTMLAttributes;
+ map:HTMLAttributes;
+ mark:HTMLAttributes;
+ menu:HTMLAttributes;
+ menuitem:HTMLAttributes;
+ meta:HTMLAttributes;
+ meter:HTMLAttributes;
+ nav:HTMLAttributes;
+ noscript:HTMLAttributes;
+ object:HTMLAttributes;
+ ol:HTMLAttributes;
+ optgroup:HTMLAttributes;
+ option:HTMLAttributes;
+ output:HTMLAttributes;
+ p:HTMLAttributes;
+ param:HTMLAttributes;
+ picture:HTMLAttributes;
+ pre:HTMLAttributes;
+ progress:HTMLAttributes;
+ q:HTMLAttributes;
+ rp:HTMLAttributes;
+ rt:HTMLAttributes;
+ ruby:HTMLAttributes;
+ s:HTMLAttributes;
+ samp:HTMLAttributes;
+ script:HTMLAttributes;
+ section:HTMLAttributes;
+ select:HTMLAttributes;
+ small:HTMLAttributes;
+ source:HTMLAttributes;
+ span:HTMLAttributes;
+ strong:HTMLAttributes;
+ style:HTMLAttributes;
+ sub:HTMLAttributes;
+ summary:HTMLAttributes;
+ sup:HTMLAttributes;
+ table:HTMLAttributes;
+ tbody:HTMLAttributes;
+ td:HTMLAttributes;
+ textarea:HTMLAttributes;
+ tfoot:HTMLAttributes;
+ th:HTMLAttributes;
+ thead:HTMLAttributes;
+ time:HTMLAttributes;
+ title:HTMLAttributes;
+ tr:HTMLAttributes;
+ track:HTMLAttributes;
+ u:HTMLAttributes;
+ ul:HTMLAttributes;
+ "var":HTMLAttributes;
+ video:HTMLAttributes;
+ wbr:HTMLAttributes;
+
+ //SVG
+ svg:SVGAttributes;
+
+ circle:SVGAttributes;
+ clipPath:SVGAttributes;
+ defs:SVGAttributes;
+ ellipse:SVGAttributes;
+ feBlend:SVGAttributes;
+ feColorMatrix:SVGAttributes;
+ feComponentTransfer:SVGAttributes;
+ feComposite:SVGAttributes;
+ feConvolveMatrix:SVGAttributes;
+ feDiffuseLighting:SVGAttributes;
+ feDisplacementMap:SVGAttributes;
+ feFlood:SVGAttributes;
+ feGaussianBlur:SVGAttributes;
+ feImage:SVGAttributes;
+ feMerge:SVGAttributes;
+ feMergeNode:SVGAttributes;
+ feMorphology:SVGAttributes;
+ feOffset:SVGAttributes;
+ feSpecularLighting:SVGAttributes;
+ feTile:SVGAttributes;
+ feTurbulence:SVGAttributes;
+ filter:SVGAttributes;
+ foreignObject:SVGAttributes;
+ g:SVGAttributes;
+ image:SVGAttributes;
+ line:SVGAttributes;
+ linearGradient:SVGAttributes;
+ marker:SVGAttributes;
+ mask:SVGAttributes;
+ path:SVGAttributes;
+ pattern:SVGAttributes;
+ polygon:SVGAttributes;
+ polyline:SVGAttributes;
+ radialGradient:SVGAttributes;
+ rect:SVGAttributes;
+ stop:SVGAttributes;
+ symbol:SVGAttributes;
+ text:SVGAttributes;
+ tspan:SVGAttributes;
+ use:SVGAttributes;
+ }
+}
diff --git a/thirdparty/preact/src/preact.js b/thirdparty/preact/src/preact.js
new file mode 100644
index 000000000..1fa169c04
--- /dev/null
+++ b/thirdparty/preact/src/preact.js
@@ -0,0 +1,24 @@
+import { h } from './h';
+import { cloneElement } from './clone-element';
+import { Component } from './component';
+import { render } from './render';
+import { rerender } from './render-queue';
+import options from './options';
+
+export default {
+ h,
+ cloneElement,
+ Component,
+ render,
+ rerender,
+ options
+};
+
+export {
+ h,
+ cloneElement,
+ Component,
+ render,
+ rerender,
+ options
+};
diff --git a/thirdparty/preact/src/preact.js.flow b/thirdparty/preact/src/preact.js.flow
new file mode 100644
index 000000000..37745faf6
--- /dev/null
+++ b/thirdparty/preact/src/preact.js.flow
@@ -0,0 +1,9 @@
+/* @flow */
+
+import { createElement as h, cloneElement, Component, render } from 'react';
+
+export { h, cloneElement, Component, render };
+export default { h, cloneElement, Component, render };
+
+declare export function rerender(): void;
+declare export var options: Object;
diff --git a/thirdparty/preact/src/render-queue.js b/thirdparty/preact/src/render-queue.js
new file mode 100644
index 000000000..ff603611b
--- /dev/null
+++ b/thirdparty/preact/src/render-queue.js
@@ -0,0 +1,23 @@
+import options from './options';
+import { defer } from './util';
+import { renderComponent } from './vdom/component';
+
+/** Managed queue of dirty components to be re-rendered */
+
+// items/itemsOffline swap on each rerender() call (just a simple pool technique)
+let items = [];
+
+export function enqueueRender(component) {
+ if (!component._dirty && (component._dirty = true) && items.push(component)==1) {
+ (options.debounceRendering || defer)(rerender);
+ }
+}
+
+
+export function rerender() {
+ let p, list = items;
+ items = [];
+ while ( (p = list.pop()) ) {
+ if (p._dirty) renderComponent(p);
+ }
+}
diff --git a/thirdparty/preact/src/render.js b/thirdparty/preact/src/render.js
new file mode 100644
index 000000000..e0e8526ec
--- /dev/null
+++ b/thirdparty/preact/src/render.js
@@ -0,0 +1,20 @@
+import { diff } from './vdom/diff';
+
+/** 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'));
+ */
+export function render(vnode, parent, merge) {
+ return diff(merge, vnode, {}, false, parent);
+}
diff --git a/thirdparty/preact/src/util.js b/thirdparty/preact/src/util.js
new file mode 100644
index 000000000..d2e63b090
--- /dev/null
+++ b/thirdparty/preact/src/util.js
@@ -0,0 +1,68 @@
+/** Copy own-properties from `props` onto `obj`.
+ * @returns obj
+ * @private
+ */
+export function extend(obj, props) {
+ if (props) {
+ for (let i in props) obj[i] = props[i];
+ }
+ return obj;
+}
+
+
+/** Fast clone. Note: does not filter out non-own properties.
+ * @see https://esbench.com/bench/56baa34f45df6895002e03b6
+ */
+export function clone(obj) {
+ return extend({}, obj);
+}
+
+
+/** Get a deep property value from the given object, expressed in dot-notation.
+ * @private
+ */
+export function delve(obj, key) {
+ for (let p=key.split('.'), i=0; i<p.length && obj; i++) {
+ obj = obj[p[i]];
+ }
+ return obj;
+}
+
+
+/** @private is the given object a Function? */
+export function isFunction(obj) {
+ return 'function'===typeof obj;
+}
+
+
+/** @private is the given object a String? */
+export function isString(obj) {
+ return 'string'===typeof obj;
+}
+
+
+/** Convert a hashmap of CSS classes to a space-delimited className string
+ * @private
+ */
+export function hashToClassName(c) {
+ let str = '';
+ for (let prop in c) {
+ if (c[prop]) {
+ if (str) str += ' ';
+ str += prop;
+ }
+ }
+ return str;
+}
+
+
+/** Just a memoized String#toLowerCase */
+let lcCache = {};
+export const toLowerCase = s => lcCache[s] || (lcCache[s] = s.toLowerCase());
+
+
+/** Call a function asynchronously, as soon as possible.
+ * @param {Function} callback
+ */
+let resolved = typeof Promise!=='undefined' && Promise.resolve();
+export const defer = resolved ? (f => { resolved.then(f); }) : setTimeout;
diff --git a/thirdparty/preact/src/vdom/component-recycler.js b/thirdparty/preact/src/vdom/component-recycler.js
new file mode 100644
index 000000000..a70f0ece0
--- /dev/null
+++ b/thirdparty/preact/src/vdom/component-recycler.js
@@ -0,0 +1,32 @@
+import { Component } from '../component';
+
+/** 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
+ */
+const components = {};
+
+
+export function collectComponent(component) {
+ let name = component.constructor.name,
+ list = components[name];
+ if (list) list.push(component);
+ else components[name] = [component];
+}
+
+
+export function createComponent(Ctor, props, context) {
+ let inst = new Ctor(props, context),
+ list = components[Ctor.name];
+ Component.call(inst, props, context);
+ if (list) {
+ for (let i=list.length; i--; ) {
+ if (list[i].constructor===Ctor) {
+ inst.nextBase = list[i].nextBase;
+ list.splice(i, 1);
+ break;
+ }
+ }
+ }
+ return inst;
+}
diff --git a/thirdparty/preact/src/vdom/component.js b/thirdparty/preact/src/vdom/component.js
new file mode 100644
index 000000000..bb2e4fa5d
--- /dev/null
+++ b/thirdparty/preact/src/vdom/component.js
@@ -0,0 +1,270 @@
+import { SYNC_RENDER, NO_RENDER, FORCE_RENDER, ASYNC_RENDER, ATTR_KEY } from '../constants';
+import options from '../options';
+import { isFunction, clone, extend } from '../util';
+import { enqueueRender } from '../render-queue';
+import { getNodeProps } from './index';
+import { diff, mounts, diffLevel, flushMounts, removeOrphanedChildren, recollectNodeTree } from './diff';
+import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';
+import { createComponent, collectComponent } from './component-recycler';
+import { removeNode } from '../dom/index';
+
+
+
+/** 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.
+ */
+export 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!==NO_RENDER) {
+ if (opts===SYNC_RENDER || options.syncComponentUpdates!==false || !component.base) {
+ renderComponent(component, SYNC_RENDER, 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
+ */
+export function renderComponent(component, opts, mountAll, isChild) {
+ if (component._disable) return;
+
+ let skip, rendered,
+ 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,
+ inst, cbase;
+
+ // if updating
+ if (isUpdate) {
+ component.props = previousProps;
+ component.state = previousState;
+ component.context = previousContext;
+ if (opts!==FORCE_RENDER
+ && 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) {
+ if (component.render) rendered = component.render(props, state, context);
+
+ // context to pass to the child, can be updated via (grand-)parent component
+ if (component.getChildContext) {
+ context = extend(clone(context), component.getChildContext());
+ }
+
+ while (isFunctionalComponent(rendered)) {
+ rendered = buildFunctionalComponent(rendered, context);
+ }
+
+ let childComponent = rendered && rendered.nodeName,
+ toUnmount, base;
+
+ if (isFunction(childComponent)) {
+ // set up high order component link
+
+
+ inst = initialChildComponent;
+ let childProps = getNodeProps(rendered);
+
+ if (inst && inst.constructor===childComponent) {
+ setComponentProps(inst, childProps, SYNC_RENDER, context);
+ }
+ else {
+ toUnmount = inst;
+
+ inst = createComponent(childComponent, childProps, context);
+ inst.nextBase = inst.nextBase || nextBase;
+ inst._parentComponent = component;
+ component._component = inst;
+ setComponentProps(inst, childProps, NO_RENDER, context);
+ renderComponent(inst, SYNC_RENDER, mountAll, true);
+ }
+
+ base = inst.base;
+ }
+ else {
+ cbase = initialBase;
+
+ // destroy high order component link
+ toUnmount = initialChildComponent;
+ if (toUnmount) {
+ cbase = component._component = null;
+ }
+
+ if (initialBase || opts===SYNC_RENDER) {
+ if (cbase) cbase._component = null;
+ base = diff(cbase, rendered, context, mountAll || !isUpdate, initialBase && initialBase.parentNode, true);
+ }
+ }
+
+ if (initialBase && base!==initialBase && inst!==initialChildComponent) {
+ let baseParent = initialBase.parentNode;
+ if (baseParent && base!==baseParent) {
+ baseParent.replaceChild(base, initialBase);
+ }
+
+ if (!cbase && !toUnmount && component._parentComponent) {
+ initialBase._component = null;
+ recollectNodeTree(initialBase);
+ }
+ }
+
+ if (toUnmount) {
+ unmountComponent(toUnmount, base!==initialBase);
+ }
+
+ component.base = base;
+ if (base && !isChild) {
+ let componentRef = component,
+ t = component;
+ while ((t=t._parentComponent)) { componentRef = t; }
+ base._component = componentRef;
+ base._componentConstructor = componentRef.constructor;
+ }
+ }
+
+ if (!isUpdate || mountAll) {
+ mounts.unshift(component);
+ }
+ else if (!skip && component.componentDidUpdate) {
+ component.componentDidUpdate(previousProps, previousState, previousContext);
+ }
+
+ let cb = component._renderCallbacks, fn;
+ if (cb) while ( (fn = cb.pop()) ) fn.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
+ */
+export function buildComponentFromVNode(dom, vnode, context, mountAll) {
+ let c = dom && dom._component,
+ 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, ASYNC_RENDER, context, mountAll);
+ dom = c.base;
+ }
+ else {
+ if (c && !isDirectOwner) {
+ unmountComponent(c, true);
+ dom = oldDom = null;
+ }
+
+ c = createComponent(vnode.nodeName, props, context);
+ if (dom && !c.nextBase) c.nextBase = dom;
+ setComponentProps(c, props, SYNC_RENDER, context, mountAll);
+ dom = c.base;
+
+ if (oldDom && dom!==oldDom) {
+ oldDom._component = null;
+ recollectNodeTree(oldDom);
+ }
+ }
+
+ return dom;
+}
+
+
+
+/** Remove a component from the DOM and recycle it.
+ * @param {Element} dom A DOM node from which to unmount the given Component
+ * @param {Component} component The Component instance to unmount
+ * @private
+ */
+export function unmountComponent(component, remove) {
+ // console.log(`${remove?'Removing':'Unmounting'} component: ${component.constructor.name}`);
+ let base = component.base;
+
+ component._disable = true;
+
+ if (component.componentWillUnmount) component.componentWillUnmount();
+
+ component.base = null;
+
+ // recursively tear down & recollect high-order component children:
+ let inner = component._component;
+ if (inner) {
+ unmountComponent(inner, remove);
+ }
+ else if (base) {
+ if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);
+
+ component.nextBase = base;
+
+ if (remove) {
+ removeNode(base);
+ collectComponent(component);
+ }
+ removeOrphanedChildren(base.childNodes, !remove);
+ }
+
+ if (component.__ref) component.__ref(null);
+ if (component.componentDidUnmount) component.componentDidUnmount();
+}
diff --git a/thirdparty/preact/src/vdom/diff.js b/thirdparty/preact/src/vdom/diff.js
new file mode 100644
index 000000000..691434e98
--- /dev/null
+++ b/thirdparty/preact/src/vdom/diff.js
@@ -0,0 +1,247 @@
+import { ATTR_KEY } from '../constants';
+import { isString, isFunction } from '../util';
+import { isSameNodeType, isNamedNode } from './index';
+import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';
+import { buildComponentFromVNode } from './component';
+import { setAccessor } from '../dom/index';
+import { createNode, collectNode } from '../dom/recycler';
+import { unmountComponent } from './component';
+
+
+/** Diff recursion count, used to track the end of the diff cycle. */
+export const mounts = [];
+
+/** Diff recursion count, used to track the end of the diff cycle. */
+export let diffLevel = 0;
+
+let isSvgMode = false;
+
+
+export function flushMounts() {
+ let c;
+ while ((c=mounts.pop())) {
+ 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
+ */
+export function diff(dom, vnode, context, mountAll, parent, componentRoot) {
+ if (!diffLevel++) isSvgMode = parent instanceof SVGElement;
+ let ret = idiff(dom, vnode, context, mountAll);
+ if (parent && ret.parentNode!==parent) parent.appendChild(ret);
+ if (!--diffLevel && !componentRoot) flushMounts();
+ return ret;
+}
+
+
+function idiff(dom, vnode, context, mountAll) {
+ let originalAttributes = vnode && vnode.attributes;
+
+ while (isFunctionalComponent(vnode)) {
+ vnode = buildFunctionalComponent(vnode, context);
+ }
+
+ if (vnode==null) vnode = '';
+
+ if (isString(vnode)) {
+ if (dom) {
+ if (dom instanceof Text && dom.parentNode) {
+ dom.nodeValue = vnode;
+ return dom;
+ }
+ recollectNodeTree(dom);
+ }
+ return document.createTextNode(vnode);
+ }
+
+ if (isFunction(vnode.nodeName)) {
+ return buildComponentFromVNode(dom, vnode, context, mountAll);
+ }
+
+ let out = dom,
+ nodeName = vnode.nodeName,
+ prevSvgMode = isSvgMode;
+
+ if (!isString(nodeName)) {
+ nodeName = String(nodeName);
+ }
+
+ isSvgMode = nodeName==='svg' ? true : nodeName==='foreignObject' ? false : isSvgMode;
+
+ if (!dom) {
+ out = createNode(nodeName, isSvgMode);
+ }
+ else if (!isNamedNode(dom, nodeName)) {
+ out = createNode(nodeName, isSvgMode);
+ // move children into the replacement node
+ while (dom.firstChild) out.appendChild(dom.firstChild);
+ // reclaim element nodes
+ recollectNodeTree(dom);
+ }
+
+ // fast-path for elements containing a single TextNode:
+ if (vnode.children && vnode.children.length===1 && typeof vnode.children[0]==='string' && out.childNodes.length===1 && out.firstChild instanceof Text) {
+ out.firstChild.nodeValue = vnode.children[0];
+ }
+ else if (vnode.children || out.firstChild) {
+ innerDiffNode(out, vnode.children, context, mountAll);
+ }
+
+ let props = out[ATTR_KEY];
+ if (!props) {
+ out[ATTR_KEY] = props = {};
+ for (let a=out.attributes, i=a.length; i--; ) props[a[i].name] = a[i].value;
+ }
+
+ diffAttributes(out, vnode.attributes, props);
+
+ if (originalAttributes && typeof originalAttributes.ref==='function') {
+ (props.ref = originalAttributes.ref)(out);
+ }
+
+ isSvgMode = prevSvgMode;
+
+ return out;
+}
+
+
+/** Apply child and attribute changes between a VNode and a DOM Node to the DOM. */
+function innerDiffNode(dom, vchildren, context, mountAll) {
+ let originalChildren = dom.childNodes,
+ children = [],
+ keyed = {},
+ keyedLen = 0,
+ min = 0,
+ len = originalChildren.length,
+ childrenLen = 0,
+ vlen = vchildren && vchildren.length,
+ j, c, vchild, child;
+
+ if (len) {
+ for (let i=0; i<len; i++) {
+ let child = originalChildren[i],
+ key = vlen ? ((c = child._component) ? c.__key : (c = child[ATTR_KEY]) ? c.key : null) : null;
+ if (key || key===0) {
+ keyedLen++;
+ keyed[key] = child;
+ }
+ else {
+ children[childrenLen++] = child;
+ }
+ }
+ }
+
+ if (vlen) {
+ for (let i=0; i<vlen; i++) {
+ vchild = vchildren[i];
+ child = null;
+
+ // if (isFunctionalComponent(vchild)) {
+ // vchild = buildFunctionalComponent(vchild);
+ // }
+
+ // attempt to find a node based on key matching
+ let key = vchild.key;
+ if (key!=null) {
+ if (keyedLen && key in keyed) {
+ 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++) {
+ c = children[j];
+ if (c && isSameNodeType(c, vchild)) {
+ child = c;
+ children[j] = undefined;
+ if (j===childrenLen-1) childrenLen--;
+ if (j===min) min++;
+ break;
+ }
+ }
+ if (!child && min<childrenLen && isFunction(vchild.nodeName) && mountAll) {
+ child = children[min];
+ children[min++] = undefined;
+ }
+ }
+
+ // morph the matched/found/created DOM child to match vchild (deep)
+ child = idiff(child, vchild, context, mountAll);
+
+ if (child && child!==dom && child!==originalChildren[i]) {
+ dom.insertBefore(child, originalChildren[i] || null);
+ }
+ }
+ }
+
+
+ if (keyedLen) {
+ for (let i in keyed) if (keyed[i]) recollectNodeTree(keyed[i]);
+ }
+
+ // remove orphaned children
+ if (min<childrenLen) {
+ removeOrphanedChildren(children);
+ }
+}
+
+
+/** Reclaim children that were unreferenced in the desired VTree */
+export function removeOrphanedChildren(children, unmountOnly) {
+ for (let i=children.length; i--; ) {
+ if (children[i]) {
+ recollectNodeTree(children[i], unmountOnly);
+ }
+ }
+}
+
+
+/** Reclaim an entire tree of nodes, starting at the root. */
+export function recollectNodeTree(node, unmountOnly) {
+ // @TODO: Need to make a call on whether Preact should remove nodes not created by itself.
+ // Currently it *does* remove them. Discussion: https://github.com/developit/preact/issues/39
+ //if (!node[ATTR_KEY]) return;
+
+ let component = node._component;
+ if (component) {
+ unmountComponent(component, !unmountOnly);
+ }
+ else {
+ if (node[ATTR_KEY] && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);
+
+ if (!unmountOnly) {
+ collectNode(node);
+ }
+
+ if (node.childNodes && node.childNodes.length) {
+ removeOrphanedChildren(node.childNodes, unmountOnly);
+ }
+ }
+}
+
+
+/** Apply differences in attributes from a VNode to the given DOM Node. */
+function diffAttributes(dom, attrs, old) {
+ for (let name in old) {
+ if (!(attrs && name in attrs) && old[name]!=null) {
+ setAccessor(dom, name, null, old[name], isSvgMode);
+ }
+ }
+
+ // new & updated
+ if (attrs) {
+ for (let name in attrs) {
+ if (!(name in old) || attrs[name]!==(name==='value' || name==='checked' ? dom[name] : old[name])) {
+ setAccessor(dom, name, attrs[name], old[name], isSvgMode);
+ }
+ }
+ }
+}
diff --git a/thirdparty/preact/src/vdom/functional-component.js b/thirdparty/preact/src/vdom/functional-component.js
new file mode 100644
index 000000000..04bc5a464
--- /dev/null
+++ b/thirdparty/preact/src/vdom/functional-component.js
@@ -0,0 +1,25 @@
+import { EMPTY } from '../constants';
+import { getNodeProps } from './index';
+import { isFunction } from '../util';
+
+
+/** Check if a VNode is a reference to a stateless functional component.
+ * A function component is represented as a VNode whose `nodeName` property is a reference to a function.
+ * If that function is not a Component (ie, has no `.render()` method on a prototype), it is considered a stateless functional component.
+ * @param {VNode} vnode A VNode
+ * @private
+ */
+export function isFunctionalComponent(vnode) {
+ let nodeName = vnode && vnode.nodeName;
+ return nodeName && isFunction(nodeName) && !(nodeName.prototype && nodeName.prototype.render);
+}
+
+
+
+/** Construct a resultant VNode from a VNode referencing a stateless functional component.
+ * @param {VNode} vnode A VNode with a `nodeName` property that is a reference to a function.
+ * @private
+ */
+export function buildFunctionalComponent(vnode, context) {
+ return vnode.nodeName(getNodeProps(vnode), context || EMPTY);
+}
diff --git a/thirdparty/preact/src/vdom/index.js b/thirdparty/preact/src/vdom/index.js
new file mode 100644
index 000000000..50d4ca2b9
--- /dev/null
+++ b/thirdparty/preact/src/vdom/index.js
@@ -0,0 +1,50 @@
+import { clone, isString, isFunction, toLowerCase } from '../util';
+import { isFunctionalComponent } from './functional-component';
+
+
+/** Check if two nodes are equivalent.
+ * @param {Element} node
+ * @param {VNode} vnode
+ * @private
+ */
+export function isSameNodeType(node, vnode) {
+ if (isString(vnode)) {
+ return node instanceof Text;
+ }
+ if (isString(vnode.nodeName)) {
+ return isNamedNode(node, vnode.nodeName);
+ }
+ if (isFunction(vnode.nodeName)) {
+ return node._componentConstructor===vnode.nodeName || isFunctionalComponent(vnode);
+ }
+}
+
+
+export function isNamedNode(node, nodeName) {
+ return node.normalizedNodeName===nodeName || toLowerCase(node.nodeName)===toLowerCase(nodeName);
+}
+
+
+/**
+ * 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
+ */
+export function getNodeProps(vnode) {
+ let defaultProps = vnode.nodeName.defaultProps,
+ props = clone(vnode.attributes);
+
+ if (defaultProps) {
+ for (let i in defaultProps) {
+ if (props[i]===undefined) {
+ props[i] = defaultProps[i];
+ }
+ }
+ }
+
+ if (vnode.children) props.children = vnode.children;
+
+ return props;
+}
diff --git a/thirdparty/preact/src/vnode.js b/thirdparty/preact/src/vnode.js
new file mode 100644
index 000000000..1c3f10e3d
--- /dev/null
+++ b/thirdparty/preact/src/vnode.js
@@ -0,0 +1,14 @@
+/** Virtual DOM Node */
+export function VNode(nodeName, attributes, children) {
+ /** @type {string|function} */
+ this.nodeName = nodeName;
+
+ /** @type {object<string>|undefined} */
+ this.attributes = attributes;
+
+ /** @type {array<VNode>|undefined} */
+ this.children = children;
+
+ /** Reference to the given key. */
+ this.key = attributes && attributes.key;
+}