diff --git a/thirdparty/preact/.editorconfig b/thirdparty/preact/.editorconfig
new file mode 100644
index 000000000..9c378bd19
--- /dev/null
+++ b/thirdparty/preact/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[{package.json,.*rc,*.yml}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/thirdparty/preact/.gitignore b/thirdparty/preact/.gitignore
new file mode 100644
index 000000000..17acf3f20
--- /dev/null
+++ b/thirdparty/preact/.gitignore
@@ -0,0 +1,8 @@
+/node_modules
+/npm-debug.log
+.DS_Store
+/dist
+/_dev
+/coverage
+aliases.js
+aliases.js.map
diff --git a/thirdparty/preact/.travis.yml b/thirdparty/preact/.travis.yml
new file mode 100644
index 000000000..5953c1a84
--- /dev/null
+++ b/thirdparty/preact/.travis.yml
@@ -0,0 +1,30 @@
+sudo: false
+
+language: node_js
+
+node_js:
+ - "6"
+
+cache:
+ directories:
+ - node_modules
+
+install:
+ - npm install
+
+script:
+ - npm run build
+ - npm run test
+ - SAUCELABS=true COVERAGE=false FLAKEY=false PERFORMANCE=false npm run test:karma
+
+# Necessary to compile native modules for io.js v3 or Node.js v4
+env:
+ - CXX=g++-4.8
+
+# Necessary to compile native modules for io.js v3 or Node.js v4
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - g++-4.8
diff --git a/thirdparty/preact/LICENSE b/thirdparty/preact/LICENSE
new file mode 100644
index 000000000..445ef15a4
--- /dev/null
+++ b/thirdparty/preact/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Jason Miller
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/thirdparty/preact/README.md b/thirdparty/preact/README.md
new file mode 100644
index 000000000..582c55216
--- /dev/null
+++ b/thirdparty/preact/README.md
@@ -0,0 +1,345 @@
+
+
+
+
+**Preact is a fast, `3kb` alternative to React, with the same ES2015 API.**
+
+Preact retains a large amount of compatibility with React, but only the modern ([ES6 Classes] and [stateless functional components](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components)) interfaces.
+As one would expect coming from React, Components are simple building blocks for composing a User Interface.
+
+### :information_desk_person: Full documentation is available at the [Preact Website ➞](https://preactjs.com)
+
+[](http://npm.im/preact)
+[](https://travis-ci.org/developit/preact)
+[](https://gitter.im/developit/preact)
+
+[](https://saucelabs.com/u/preact)
+
+
+---
+
+
+## Demos
+
+- [**ESBench**](http://esbench.com) is built using Preact.
+- [**Nectarine.rocks**](http://nectarine.rocks) _([Github Project](https://github.com/developit/nectarine))_ :peach:
+- [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([Github Project](https://github.com/developit/documentation-viewer))_
+- [**TodoMVC**](http://developit.github.io/preact-todomvc/) _([Github Project](https://github.com/developit/preact-todomvc))_
+- [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([Github Project](https://github.com/developit/hn_minimal))_
+- [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([Github Project](https://github.com/developit/preact-boilerplate))_ :zap:
+- [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
+- [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen)
+- [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen)
+- [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen)
+- [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle)
+- [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen)
+- [**Stock Ticker**](http://codepen.io/developit/pen/wMYoBb?editors=0010) (@ CodePen)
+- [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle)
+- [**Preact Coffeescript**](https://github.com/crisward/preact-coffee)
+- [**GuriVR**](https://gurivr.com) _([Github Project](https://github.com/opennewslabs/guri-vr))_
+- [**V2EX Preact**](https://github.com/yanni4night/v2ex-preact)
+
+## Libraries & Add-ons
+
+- :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components.
+- :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering.
+- :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact. *([full example](http://git.io/preact-compat-example))*
+- :rocket: [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com).
+- :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM, right in Node)_
+- :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components
+- :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component
+- :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact.
+- :satellite: [**preact-portal**](https://git.io/preact-portal): Render Preact components into (a) SPACE :milky_way:
+- :construction: [**preact-classless-component**](https://github.com/ld0rman/preact-classless-component): A utility method to create components without the `class` keyword
+- :hammer: [**preact-hyperscript**](https://github.com/queckezz/preact-hyperscript): Hyperscript-like syntax for creating elements
+
+
+## Getting Started
+
+> :information_desk_person: You [don't _have_ to use ES2015 to use Preact](https://github.com/developit/preact-without-babel)... but you should.
+
+The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc. If you don't, start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010).
+
+
+### Import what you need
+
+The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals:
+
+##### Named:
+
+```js
+import { h, render, Component } from 'preact';
+
+// Tell Babel to transform JSX into h() calls:
+/** @jsx h */
+```
+
+##### Default:
+
+```js
+import preact from 'preact';
+
+// Tell Babel to transform JSX into preact.h() calls:
+/** @jsx preact.h */
+```
+
+> Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library.
+>
+> Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
+>
+> **For Babel 5 and prior:**
+>
+> ```json
+> { "jsxPragma": "h" }
+> ```
+>
+> **For Babel 6:**
+>
+> ```json
+> {
+> "plugins": [
+> ["transform-react-jsx", { "pragma":"h" }]
+> ]
+> }
+> ```
+
+
+### Rendering JSX
+
+Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM.
+
+To render some JSX, just import those two functions and use them like so:
+
+```js
+import { h, render } from 'preact';
+
+render((
+
+ Hello, world!
+
+
+), document.body);
+```
+
+This should seem pretty straightforward if you've used [hyperscript] or one of its many friends.
+
+Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2:
+
+
+### Components
+
+Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface. Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`. Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update.
+
+Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX.
+
+Let's take a look at a very simple `Clock` component, which shows the current time.
+
+```js
+import { h, render, Component } from 'preact';
+
+class Clock extends Component {
+ render() {
+ let time = new Date().toLocaleTimeString();
+ return { time };
+ }
+}
+
+// render an instance of Clock into :
+render(, document.body);
+```
+
+
+That's great. Running this produces the following HTML DOM structure:
+
+```html
+10:28:57 PM
+```
+
+In order to have the clock's time update every second, we need to know when `` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component:
+
+| Lifecycle method | When it gets called |
+|-----------------------------|--------------------------------------------------|
+| `componentWillMount` | before the component gets mounted to the DOM |
+| `componentDidMount` | after the component gets mounted to the DOM |
+| `componentWillUnmount` | prior to removal from the DOM |
+| `componentDidUnmount` | after removal from the DOM |
+| `componentWillReceiveProps` | before new props get accepted |
+| `shouldComponentUpdate` | before `render()`. Return `false` to skip render |
+| `componentWillUpdate` | before `render()` |
+| `componentDidUpdate` | after `render()` |
+
+
+
+So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component.
+
+```js
+import { h, render, Component } from 'preact';
+
+class Clock extends Component {
+ constructor() {
+ super();
+ // set initial time:
+ this.state.time = Date.now();
+ }
+
+ componentDidMount() {
+ // update time every second
+ this.timer = setInterval(() => {
+ this.setState({ time: Date.now() });
+ }, 1000);
+ }
+
+ componentWillUnmount() {
+ // stop when not renderable
+ clearInterval(this.timer);
+ }
+
+ render(props, state) {
+ let time = new Date(state.time).toLocaleTimeString();
+ return { time };
+ }
+}
+
+// render an instance of Clock into :
+render(, document.body);
+```
+
+Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)!
+
+
+### Props & State
+
+The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes. You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`.
+
+
+---
+
+
+## Linked State
+
+One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events. Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary.
+
+One solution to this is to bind component methods declaratively.
+Here is an example using [decko](http://git.io/decko):
+
+```js
+class Foo extends Component {
+ @bind
+ updateText(e) {
+ this.setState({ text: e.target.value });
+ }
+ render({ }, { text }) {
+ return ;
+ }
+}
+```
+
+While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI.
+
+Fortunately there is a solution, in the form of `linkState()`. Calling `component.linkState('text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to linkState() with the same state property are cached, so there is no performance penalty. Here is the previous example rewritten using _Linked State_:
+
+```js
+class Foo extends Component {
+ render({ }, { text }) {
+ return ;
+ }
+}
+```
+
+Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value.
+
+
+## Examples
+
+Here is a somewhat verbose Preact `` component:
+
+```js
+class Link extends Component {
+ render(props, state) {
+ return { props.children };
+ }
+}
+```
+
+Since this is ES6/ES2015, we can further simplify:
+
+```js
+class Link extends Component {
+ render({ href, children }) {
+ return ;
+ }
+}
+
+// or, for wide-open props support:
+class Link extends Component {
+ render(props) {
+ return ;
+ }
+}
+
+// or, as a stateless functional component:
+const Link = ({ children, ...props }) => (
+ { children }
+);
+```
+
+
+## Extensions
+
+It is likely that some projects based on Preact would wish to extend Component with great new functionality.
+
+Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`. Just use ES2015 inheritance:
+
+```js
+class BoundComponent extends Component {
+ constructor(props) {
+ super(props);
+ this.bind();
+ }
+ bind() {
+ this.binds = {};
+ for (let i in this) {
+ this.binds[i] = this[i].bind(this);
+ }
+ }
+}
+
+// example usage
+class Link extends BoundComponent {
+ click() {
+ open(this.href);
+ }
+ render() {
+ let { click } = this.binds;
+ return { children };
+ }
+}
+```
+
+
+The possibilities are pretty endless here. You could even add support for rudimentary mixins:
+
+```js
+class MixedComponent extends Component {
+ constructor() {
+ super();
+ (this.mixins || []).forEach( m => Object.assign(this, m) );
+ }
+}
+```
+
+
+## License
+
+MIT
+
+
+
+[](https://preactjs.com)
+
+
+
+[ES6 Classes]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
+[hyperscript]: https://github.com/dominictarr/hyperscript
+[preact-boilerplate]: https://github.com/developit/preact-boilerplate
+[lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html
diff --git a/thirdparty/preact/config/codemod-const.js b/thirdparty/preact/config/codemod-const.js
new file mode 100644
index 000000000..4bffd9add
--- /dev/null
+++ b/thirdparty/preact/config/codemod-const.js
@@ -0,0 +1,39 @@
+/* eslint no-console:0 */
+
+/** Find constants (identified by ALL_CAPS_DECLARATIONS), and inline them globally.
+ * This is safe because Preact *only* uses global constants.
+ */
+export default (file, api) => {
+ let j = api.jscodeshift,
+ code = j(file.source),
+ constants = {},
+ found = 0;
+
+ code.find(j.VariableDeclaration)
+ .filter( decl => {
+ for (let i=decl.value.declarations.length; i--; ) {
+ let node = decl.value.declarations[i],
+ name = node.id && node.id.name,
+ init = node.init;
+ if (name && init && name.match(/^[A-Z0-9_$]+$/g)) {
+ if (init.type==='Literal') {
+ console.log(`Inlining constant: ${name}=${init.raw}`);
+ found++;
+ constants[name] = init;
+ // remove declaration
+ decl.value.declarations.splice(i, 1);
+ // if it's the last, we'll remove the whole statement
+ return !decl.value.declarations.length;
+ }
+ }
+ }
+ return false;
+ })
+ .remove();
+
+ code.find(j.Identifier)
+ .filter( path => path.value.name && constants.hasOwnProperty(path.value.name) )
+ .replaceWith( path => (found++, constants[path.value.name]) );
+
+ return found ? code.toSource({ quote: 'single' }) : null;
+};
diff --git a/thirdparty/preact/config/codemod-strip-tdz.js b/thirdparty/preact/config/codemod-strip-tdz.js
new file mode 100644
index 000000000..a1f07fafe
--- /dev/null
+++ b/thirdparty/preact/config/codemod-strip-tdz.js
@@ -0,0 +1,56 @@
+/* eslint no-console:0 */
+
+
+// parent node types that we don't want to remove pointless initializations from (because it breaks hoisting)
+const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement'
+
+/** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */
+export default (file, api) => {
+ let { jscodeshift } = api,
+ found = 0;
+
+ let code = jscodeshift(file.source)
+ .find(jscodeshift.VariableDeclaration)
+ .forEach(handleDeclaration);
+
+ function handleDeclaration(decl) {
+ let p = decl,
+ remove = true;
+
+ while ((p = p.parentPath)) {
+ if (~BLOCKED.indexOf(p.value.type)) {
+ remove = false;
+ break;
+ }
+ }
+
+ decl.value.declarations.filter(isPointless).forEach( node => {
+ if (remove===false) {
+ console.log(`> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}`);
+ }
+ else {
+ removeNodeInitialization(node);
+ }
+ });
+ }
+
+ function removeNodeInitialization(node) {
+ node.init = null;
+ found++;
+ }
+
+ function isPointless(node) {
+ let { init } = node;
+ if (init) {
+ if (init.type==='UnaryExpression' && init.operator==='void' && init.argument.value==0) {
+ return true;
+ }
+ if (init.type==='Identifier' && init.name==='undefined') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return found ? code.toSource({ quote: 'single' }) : null;
+};
diff --git a/thirdparty/preact/config/eslint-config.js b/thirdparty/preact/config/eslint-config.js
new file mode 100644
index 000000000..95ac48965
--- /dev/null
+++ b/thirdparty/preact/config/eslint-config.js
@@ -0,0 +1,66 @@
+module.exports = {
+ parser: 'babel-eslint',
+ extends: 'eslint:recommended',
+ plugins: [
+ 'react'
+ ],
+ env: {
+ browser: true,
+ mocha: true,
+ node: true,
+ es6: true
+ },
+ parserOptions: {
+ ecmaFeatures: {
+ modules: true,
+ jsx: true
+ }
+ },
+ globals: {
+ sinon: true,
+ expect: true
+ },
+ rules: {
+ 'react/jsx-uses-react': 2,
+ 'react/jsx-uses-vars': 2,
+ 'no-unused-vars': [1, { varsIgnorePattern: '^h$' }],
+ 'no-cond-assign': 1,
+ 'no-empty': 0,
+ 'no-console': 1,
+ semi: 2,
+ camelcase: 0,
+ 'comma-style': 2,
+ 'comma-dangle': [2, 'never'],
+ indent: [2, 'tab', {SwitchCase: 1}],
+ 'no-mixed-spaces-and-tabs': [2, 'smart-tabs'],
+ 'no-trailing-spaces': [2, { skipBlankLines: true }],
+ 'max-nested-callbacks': [2, 3],
+ 'no-eval': 2,
+ 'no-implied-eval': 2,
+ 'no-new-func': 2,
+ 'guard-for-in': 0,
+ eqeqeq: 0,
+ 'no-else-return': 2,
+ 'no-redeclare': 2,
+ 'no-dupe-keys': 2,
+ radix: 2,
+ strict: [2, 'never'],
+ 'no-shadow': 0,
+ 'callback-return': [1, ['callback', 'cb', 'next', 'done']],
+ 'no-delete-var': 2,
+ 'no-undef-init': 2,
+ 'no-shadow-restricted-names': 2,
+ 'handle-callback-err': 0,
+ 'no-lonely-if': 2,
+ 'keyword-spacing': 2,
+ 'constructor-super': 2,
+ 'no-this-before-super': 2,
+ 'no-dupe-class-members': 2,
+ 'no-const-assign': 2,
+ 'prefer-spread': 2,
+ 'no-useless-concat': 2,
+ 'no-var': 2,
+ 'object-shorthand': 2,
+ 'prefer-arrow-callback': 2
+ }
+};
diff --git a/thirdparty/preact/config/rollup.config.aliases.js b/thirdparty/preact/config/rollup.config.aliases.js
new file mode 100644
index 000000000..e5c549fc3
--- /dev/null
+++ b/thirdparty/preact/config/rollup.config.aliases.js
@@ -0,0 +1,12 @@
+import memory from 'rollup-plugin-memory';
+import rollupConfig from './rollup.config';
+
+export default Object.assign({}, rollupConfig, {
+ plugins: [
+ memory({
+ path: 'src/preact',
+ contents: `import { h } from './preact';export * from './preact';export { h as createElement };`
+ }),
+ ...rollupConfig.plugins.slice(1)
+ ]
+});
diff --git a/thirdparty/preact/config/rollup.config.js b/thirdparty/preact/config/rollup.config.js
new file mode 100644
index 000000000..57f71e1e7
--- /dev/null
+++ b/thirdparty/preact/config/rollup.config.js
@@ -0,0 +1,23 @@
+import nodeResolve from 'rollup-plugin-node-resolve';
+import babel from 'rollup-plugin-babel';
+import memory from 'rollup-plugin-memory';
+
+export default {
+ exports: 'named',
+ useStrict: false,
+ plugins: [
+ memory({
+ path: 'src/preact',
+ contents: "export * from './preact';"
+ }),
+ nodeResolve({
+ main: true
+ }),
+ babel({
+ sourceMap: true,
+ loose: 'all',
+ blacklist: ['es6.tailCall'],
+ exclude: 'node_modules/**'
+ })
+ ]
+};
diff --git a/thirdparty/preact/package.json b/thirdparty/preact/package.json
new file mode 100644
index 000000000..293597fae
--- /dev/null
+++ b/thirdparty/preact/package.json
@@ -0,0 +1,103 @@
+{
+ "name": "preact",
+ "amdName": "preact",
+ "version": "6.2.1",
+ "description": "Tiny & fast Component-based virtual DOM framework.",
+ "main": "dist/preact.js",
+ "jsnext:main": "src/preact.js",
+ "aliases:main": "aliases.js",
+ "dev:main": "dist/preact.dev.js",
+ "minified:main": "dist/preact.min.js",
+ "scripts": {
+ "clean": "rimraf dist/ $npm_package_aliases_main ${npm_package_aliases_main}.map",
+ "copy-flow-definition": "cp src/preact.js.flow dist/preact.js.flow",
+ "copy-typescript-definition": "cp src/preact.d.ts dist/preact.d.ts",
+ "build": "npm-run-all --silent clean transpile copy-flow-definition copy-typescript-definition strip optimize minify size",
+ "transpile:main": "rollup -c config/rollup.config.js -m ${npm_package_dev_main}.map -f umd -n $npm_package_amdName $npm_package_jsnext_main -o $npm_package_dev_main",
+ "transpile:aliases": "rollup -c config/rollup.config.aliases.js -m ${npm_package_aliases_main}.map -f umd -n $npm_package_amdName $npm_package_jsnext_main -o $npm_package_aliases_main",
+ "transpile": "npm-run-all transpile:main transpile:aliases",
+ "optimize": "uglifyjs $npm_package_dev_main -c conditionals=false,sequences=false,loops=false,join_vars=false,collapse_vars=false --pure-funcs=Object.defineProperty -b width=120,quote_style=3 -o $npm_package_main -p relative --in-source-map ${npm_package_dev_main}.map --source-map ${npm_package_main}.map",
+ "minify": "uglifyjs $npm_package_main -c collapse_vars,evaluate,screw_ie8,unsafe,loops=false,keep_fargs=false,pure_getters,unused,dead_code -m -o $npm_package_minified_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_minified_main}.map",
+ "strip": "jscodeshift --run-in-band -s -t config/codemod-strip-tdz.js dist/preact.dev.js && jscodeshift --run-in-band -s -t config/codemod-const.js dist/preact.dev.js",
+ "size": "size=$(gzip-size $npm_package_minified_main) && echo \"gzip size: $size / $(pretty-bytes $size)\"",
+ "test": "npm-run-all lint --parallel test:mocha test:karma",
+ "test:mocha": "mocha --recursive --compilers js:babel/register test/shared test/node",
+ "test:karma": "karma start test/karma.conf.js --single-run",
+ "test:mocha:watch": "npm run test:mocha -- --watch",
+ "test:karma:watch": "npm run test:karma -- no-single-run",
+ "lint": "eslint src test",
+ "prepublish": "npm run build",
+ "release": "npm run build && npm test && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
+ },
+ "eslintConfig": {
+ "extends": "./config/eslint-config.js"
+ },
+ "typings": "./src/preact.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/developit/preact.git"
+ },
+ "files": [
+ "src",
+ "dist",
+ "aliases.js",
+ "aliases.js.map",
+ "typings.json"
+ ],
+ "author": "Jason Miller ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/developit/preact/issues"
+ },
+ "homepage": "https://github.com/developit/preact",
+ "devDependencies": {
+ "babel": "^5.8.23",
+ "babel-core": "^5.8.24",
+ "babel-eslint": "^6.1.0",
+ "babel-loader": "^5.3.2",
+ "babel-runtime": "^5.8.24",
+ "chai": "^3.4.1",
+ "diff": "^3.0.0",
+ "eslint": "^3.0.0",
+ "eslint-plugin-react": "^6.0.0",
+ "gzip-size-cli": "^1.0.0",
+ "isparta-loader": "^2.0.0",
+ "jscodeshift": "^0.3.25",
+ "karma": "^1.1.0",
+ "karma-babel-preprocessor": "^5.2.2",
+ "karma-chai": "^0.1.0",
+ "karma-chai-sinon": "^0.1.5",
+ "karma-coverage": "^1.0.0",
+ "karma-mocha": "^1.1.1",
+ "karma-mocha-reporter": "^2.0.4",
+ "karma-phantomjs-launcher": "^1.0.1",
+ "karma-sauce-launcher": "^1.0.0",
+ "karma-source-map-support": "^1.1.0",
+ "karma-sourcemap-loader": "^0.3.6",
+ "karma-webpack": "^1.7.0",
+ "mocha": "^3.0.1",
+ "npm-run-all": "^3.0.0",
+ "phantomjs-prebuilt": "^2.1.7",
+ "pretty-bytes-cli": "^2.0.0",
+ "rimraf": "^2.5.3",
+ "rollup": "^0.34.1",
+ "rollup-plugin-babel": "^1.0.0",
+ "rollup-plugin-memory": "^2.0.0",
+ "rollup-plugin-node-resolve": "^2.0.0",
+ "sinon": "^1.17.4",
+ "sinon-chai": "^2.8.0",
+ "uglify-js": "^2.7.0",
+ "webpack": "^1.13.1"
+ },
+ "greenkeeper": {
+ "ignore": [
+ "rollup-plugin-babel",
+ "babel",
+ "babel-core",
+ "babel-eslint",
+ "babel-loader",
+ "babel-runtime",
+ "jscodeshift"
+ ]
+ }
+}
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 ;
+ * }
+ * }
+ */
+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:
+ *
+ *
+ * @example Set a deep state value on click
+ * foo, 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|string;
+ attributes:{[name:string]:any};
+ children:VNode[];
+ key:string;
+ }
+
+ interface ComponentLifecycle {
+ componentWillMount?():void;
+
+ componentDidMount?():void;
+
+ componentWillUnmount?():void;
+
+ componentDidUnmount?():void;
+
+ componentWillReceiveProps?(props:PropsType):void;
+
+ shouldComponentUpdate?(props:PropsType):boolean;
+
+ componentWillUpdate?():void;
+
+ componentDidUpdate?():void;
+ }
+
+ interface ComponentConstructor {
+ new (props?:PropsType):Component;
+ }
+
+ abstract class Component implements ComponentLifecycle {
+ 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(node:ComponentConstructor, 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 {
+
+ }
+
+ 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 {
+ (event:E):void;
+ }
+
+ type ClipboardEventHandler = EventHandler;
+ type CompositionEventHandler = EventHandler;
+ type DragEventHandler = EventHandler;
+ type FocusEventHandler = EventHandler;
+ type KeyboardEventHandler = EventHandler;
+ type MouseEventHandler = EventHandler;
+ type TouchEventHandler = EventHandler;
+ type UIEventHandler = EventHandler;
+ type WheelEventHandler = EventHandler;
+ type AnimationEventHandler = EventHandler;
+ type TransitionEventHandler = EventHandler;
+
+ type GenericEventHandler = EventHandler;
+
+ 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 :
+ * render(
hello!
, document.body);
+ *
+ * @example
+ * // render a "Thing" component into #foo:
+ * const Thing = ({ name }) => { name };
+ * render(, 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 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|undefined} */
+ this.attributes = attributes;
+
+ /** @type {array|undefined} */
+ this.children = children;
+
+ /** Reference to the given key. */
+ this.key = attributes && attributes.key;
+}
diff --git a/thirdparty/preact/test/browser/components.js b/thirdparty/preact/test/browser/components.js
new file mode 100644
index 000000000..b4649a719
--- /dev/null
+++ b/thirdparty/preact/test/browser/components.js
@@ -0,0 +1,713 @@
+import { h, render, rerender, Component } from '../../src/preact';
+/** @jsx h */
+
+let spyAll = obj => Object.keys(obj).forEach( key => sinon.spy(obj,key) );
+
+function getAttributes(node) {
+ let attrs = {};
+ if (node.attributes) {
+ for (let i=node.attributes.length; i--; ) {
+ attrs[node.attributes[i].name] = node.attributes[i].value;
+ }
+ }
+ return attrs;
+}
+
+// hacky normalization of attribute order across browsers.
+function sortAttributes(html) {
+ return html.replace(/<([a-z0-9-]+)((?:\s[a-z0-9:_.-]+=".*?")+)((?:\s*\/)?>)/gi, (s, pre, attrs, after) => {
+ let list = attrs.match(/\s[a-z0-9:_.-]+=".*?"/gi).sort( (a, b) => a>b ? 1 : -1 );
+ if (~after.indexOf('/')) after = '>'+pre+'>';
+ return '<' + pre + list.join('') + after;
+ });
+}
+
+const Empty = () => null;
+
+describe('Components', () => {
+ let scratch;
+
+ before( () => {
+ scratch = document.createElement('div');
+ (document.body || document.documentElement).appendChild(scratch);
+ });
+
+ beforeEach( () => {
+ let c = scratch.firstElementChild;
+ if (c) render(, scratch, c);
+ scratch.innerHTML = '';
+ });
+
+ after( () => {
+ scratch.parentNode.removeChild(scratch);
+ scratch = null;
+ });
+
+ it('should render components', () => {
+ class C1 extends Component {
+ render() {
+ return
);
+ test();
+
+ expect(scratch.innerHTML).to.equal('span in a component');
+ });
+
+
+ // Test for Issue #176
+ it('should remove children when root changes to text node', () => {
+ let comp;
+
+ class Comp extends Component {
+ render(_, { alt }) {
+ return alt ? 'asdf' :