Full Code of Matt-Esch/virtual-dom for AI

master 947ecf92b67d cached
81 files
249.7 KB
65.7k tokens
198 symbols
1 requests
Download .txt
Showing preview only (268K chars total). Download the full file or copy to clipboard to get everything.
Repository: Matt-Esch/virtual-dom
Branch: master
Commit: 947ecf92b67d
Files: 81
Total size: 249.7 KB

Directory structure:
gitextract_pkovor08/

├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .zuul.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── create-element.js
├── diff.js
├── dist/
│   └── virtual-dom.js
├── docs/
│   ├── README.md
│   ├── css-animations.md
│   ├── faq.md
│   ├── hooks.md
│   ├── thunk.md
│   ├── vnode.md
│   ├── vtext.md
│   └── widget.md
├── docs.jsig
├── h.js
├── index.js
├── package.json
├── patch.js
├── test/
│   ├── attributes.js
│   ├── hook.js
│   ├── index.js
│   ├── keys.js
│   ├── lib/
│   │   ├── assert-childNodes-from-array.js
│   │   ├── assert-equal-dom.js
│   │   ├── nodes-from-array.js
│   │   └── patch-count.js
│   ├── main.js
│   ├── nested-properties.js
│   ├── non-string.js
│   ├── sort.js
│   ├── style.js
│   ├── thunk.js
│   └── undefined-properties.js
├── vdom/
│   ├── README.md
│   ├── apply-properties.js
│   ├── create-element.js
│   ├── dom-index.js
│   ├── patch-op.js
│   ├── patch.js
│   ├── test/
│   │   ├── dom-index.js
│   │   ├── index.js
│   │   ├── patch-index.js
│   │   └── patch-op-index.js
│   └── update-widget.js
├── virtual-hyperscript/
│   ├── README.md
│   ├── hooks/
│   │   ├── attribute-hook.js
│   │   ├── ev-hook.js
│   │   ├── focus-hook.js
│   │   └── soft-set-hook.js
│   ├── index.js
│   ├── parse-tag.js
│   ├── svg-attribute-namespace.js
│   ├── svg.js
│   └── test/
│       ├── attribute-hook.js
│       ├── ev-hook.js
│       ├── h.js
│       ├── index.js
│       └── svg.js
├── vnode/
│   ├── handle-thunk.js
│   ├── is-thunk.js
│   ├── is-vhook.js
│   ├── is-vnode.js
│   ├── is-vtext.js
│   ├── is-widget.js
│   ├── test/
│   │   ├── handle-thunk.js
│   │   └── index.js
│   ├── version.js
│   ├── vnode.js
│   ├── vpatch.js
│   └── vtext.js
└── vtree/
    ├── README.md
    ├── diff-props.js
    ├── diff.js
    └── test/
        ├── diff-props.js
        └── index.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .gitignore
================================================
.idea/
.DS_Store
.monitor
.*.swp
.nodemonignore
releases
*.log
*.err
fleet.json
public/browserify
bin/*.json
.bin
build
compile
.lock-wscript
coverage
node_modules


================================================
FILE: .jshintrc
================================================
{
    "asi": true,
    "bitwise": false,
    "camelcase": true,
    "curly": false,
    "eqeqeq": true,
    "eqnull": true,
    "forin": false,
    "immed": true,
    "indent": 4,
    "latedef": "nofunc",
    "newcap": false,
    "noarg": true,
    "nonew": true,
    "plusplus": false,
    "proto": true,
    "quotmark": false,
    "regexp": false,
    "undef": true,
    "unused": true,
    "strict": false,
    "trailing": true,
    "noempty": true,
    "maxdepth": 5,
    "maxparams": 5,
    "globals": {
        "console": true,
        "Buffer": true,
        "setTimeout": true,
        "clearTimeout": true,
        "setInterval": true,
        "clearInterval": true,
        "require": false,
        "module": false,
        "exports": true,
        "global": false,
        "process": true,
        "__dirname": false,
        "__filename": false
    }
}


================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- '0.10'
- '0.12'
before_install:
- npm install npm -g
before_script:
- npm install
- npm install istanbul coveralls
script: npm run travis-test
env:
  global:
  - secure: JTqjyeAoQmKLVH9SzSeIWy02DcY9WAkfIU/w8YotxIsELWbPyZ6seYBEhHiB+JYJ9UeuUgyYhZ144SPm6K8o5w+merMD7zMglr95a/8CJnjMBdPK2/0Rjckz9OcANao5k+Qm+QBBu3keo6dRtKX25g6WrKwOYu8OLz5cradzhLs=
  - secure: OBCtZZfM5LZyT+fT84Kb7N0PP1JdTQQ3SymZD5Ac+JYK30gCG6UO/o713QjlI5yVG7qwdFlPQOYF2VVf7yw6dUvf6pKj51427b9nOir8Hb9XjWlpo/770dKxlvrNxtQ3kkygAJTU8Fe7Jbk30zJef/Be17C07viiCZs4sHsiZC8=


================================================
FILE: .zuul.yml
================================================
ui: tape
concurrency: 1
browsers:
  - name: chrome
    version: [26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, latest]
  - name: ie
    version: 6..latest
  - name: firefox
    version: 3..latest
  - name: iphone
    version: 4.3..latest
  - name: ipad
    version: 4.3..latest
  - name: android
    version: 4.0..latest
  - name: opera
    version: 11..latest
  - name: safari
    version: 5..latest


================================================
FILE: CHANGELOG.md
================================================
# Release Notes

## v2.0.0

  Provides fundamental fixes and tests for reordering of keyed nodes.

  Everyone is encouraged to upgrade to v2. The main caveat of this upgrade
  is that it changes the patch format to encode all of the move data,
  instead of previously providing the mapping and expecting patch to work
  out which moves are required. While this might limit options for
  reordering, on the grounds of performance and debugging it made more
  sense.

  This is considered a breaking change for that reason. However, for those
  of you who only consume `create`, `diff`, `patch` and `h`, this will
  not affect your usage, and upgrading is recommended due to the bugs this
  new version fixes.

## v1.3.0

  - Add optimization to AttributHook to prevent resetting attributes where
    new hook instances are used but their values remain the same.

  - Extend the interface of unhook to take the next value from diff.

  - Fix bug where hook is called on unhook-only hooks.

  - Code refactor: diffProps broken out into it's own file

## v1.2.0

  - Correctly sets SVG attributes that are namespaced using a (fixed)
    attribute hook.

  - Add CSS animation notes from github issue (css-animations.md)

  - A hook with an `unhook` method and no `hook` method is now considered a
    valid hook.

  - Fixes issue where unhook was not called when a hook property is replaced
    with a new value

  - Fixes dist script update

  - Update README to note that an instance of `dom-delegator` is required to
    use the `ev-*` properties.

## v1.1.0 - Element reordering

  - Updates the way in which elements are reordered to increase performance
    in common use cases.

  - Adds additional SVG display attributes.

# v1.0.0 - Sensible versioning begins

# v0.0.24 - Fix destroy ordering

  - Fixes a bug where widgets cannot be replaced by vnodes due to a bug in the
    order of destroy patches.

# v0.0.23 - Release notes begin


================================================
FILE: LICENSE
================================================
Copyright (c) 2014 Matt-Esch.

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.


================================================
FILE: README.md
================================================
# virtual-dom

A JavaScript [DOM model](#dom-model) supporting [element creation](#element-creation), [diff computation](#diff-computation) and [patch operations](#patch-operations) for efficient re-rendering

[![build status][1]][2]
[![NPM version][3]][4]
[![Coverage Status][5]][6]
[![Davis Dependency status][7]][8]
[![experimental](http://hughsk.github.io/stability-badges/dist/experimental.svg)](http://github.com/hughsk/stability-badges)
[![Join the chat at https://gitter.im/Matt-Esch/virtual-dom](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Matt-Esch/virtual-dom?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

[![Sauce Test Status](https://saucelabs.com/browser-matrix/mattesch.svg)](https://saucelabs.com/u/mattesch)

## Motivation

Manual DOM manipulation is messy and keeping track of the previous DOM state is hard. A solution to this problem is to write your code as if you were recreating the entire DOM whenever state changes. Of course, if you actually recreated the entire DOM every time your application state changed, your app would be very slow and your input fields would lose focus.

`virtual-dom` is a collection of modules designed to provide a declarative way of representing the DOM for your app. So instead of updating the DOM when your application state changes, you simply create a virtual tree or `VTree`, which looks like the DOM state that you want. `virtual-dom` will then figure out how to make the DOM look like this efficiently without recreating all of the DOM nodes.

`virtual-dom` allows you to update a view whenever state changes by creating a full `VTree` of the view and then patching the DOM efficiently to look exactly as you described it. This results in keeping manual DOM manipulation and previous state tracking out of your application code, promoting clean and maintainable rendering logic for web applications.

## Example

```javascript
var h = require('virtual-dom/h');
var diff = require('virtual-dom/diff');
var patch = require('virtual-dom/patch');
var createElement = require('virtual-dom/create-element');

// 1: Create a function that declares what the DOM should look like
function render(count)  {
    return h('div', {
        style: {
            textAlign: 'center',
            lineHeight: (100 + count) + 'px',
            border: '1px solid red',
            width: (100 + count) + 'px',
            height: (100 + count) + 'px'
        }
    }, [String(count)]);
}

// 2: Initialise the document
var count = 0;      // We need some app data. Here we just store a count.

var tree = render(count);               // We need an initial tree
var rootNode = createElement(tree);     // Create an initial root DOM node ...
document.body.appendChild(rootNode);    // ... and it should be in the document

// 3: Wire up the update logic
setInterval(function () {
      count++;

      var newTree = render(count);
      var patches = diff(tree, newTree);
      rootNode = patch(rootNode, patches);
      tree = newTree;
}, 1000);
```
[View on RequireBin](http://requirebin.com/?gist=5492847b9a9025e64bab)

## Documentation

You can find the documentation for the seperate components
  in their READMEs

 - For `create-element.js` see the [vdom README](vdom/README.md)
 - For `diff.js` see the [vtree README](vtree/README.md)
 - For `h.js` see the [virtual-hyperscript README](virtual-hyperscript/README.md)
 - For `patch.js` see the [vdom README](vdom/README.md)

For information about the type signatures of these modules feel
  free to read the [javascript signature definition](docs.jsig)

## DOM model

`virtual-dom` exposes a set of objects designed for representing DOM nodes. A "Document Object Model Model" might seem like a strange term, but it is exactly that. It's a native JavaScript tree structure that represents a native DOM node tree. We call this a **VTree**

We can create a VTree using the objects directly in a verbose manner, or we can use the more terse virtual-hyperscript.

### Example - creating a VTree using the objects directly

```javascript
var VNode = require('virtual-dom/vnode/vnode');
var VText = require('virtual-dom/vnode/vtext');

function render(data) {
    return new VNode('div', {
        className: "greeting"
    }, [
        new VText("Hello " + String(data.name))
    ]);
}

module.exports = render;
```

### Example - creating a VTree using virtual-hyperscript

```javascript
var h = require('virtual-dom/h');

function render(data) {
    return h('.greeting', ['Hello ' + data.name]);
}

module.exports = render;
```

The DOM model is designed to be efficient to create and read from. The reason why we don't just create a real DOM tree is that creating DOM nodes and reading the node properties is an expensive operation which is what we are trying to avoid. Reading some DOM node properties even causes side effects, so recreating the entire DOM structure with real DOM nodes simply isn't suitable for high performance rendering and it is not easy to reason about either.

A `VTree` is designed to be equivalent to an immutable data structure. While it's not actually immutable, you can reuse the nodes in multiple places and the functions we have exposed that take VTrees as arguments never mutate the trees. We could freeze the objects in the model but don't for efficiency. (The benefits of an immutable-equivalent data structure will be documented in vtree or blog post at some point)



## Element creation

```haskell
createElement(tree:VTree) -> DOMNode
```

Given that we have created a `VTree`, we need some way to translate this into a real DOM tree of some sort. This is provided by `create-element.js`. When rendering for the first time we would pass a complete `VTree` to create-element function to create the equivalent DOM node.

## Diff computation

```haskell
diff(previous:VTree, current:VTree) -> PatchObject
```

The primary motivation behind virtual-dom is to allow us to write code independent of previous state. So when our application state changes we will generate a new `VTree`. The `diff` function creates a set of DOM patches that, based on the difference between the previous `VTree` and the current `VTree`, will update the previous DOM tree to match the new `VTree`.

## Patch operations

```haskell
patch(rootNode:DOMNode, patches:PatchObject) -> DOMNode newRootNode
```

Once we have computed the set of patches required to apply to the DOM, we need a function that can apply those patches. This is provided by the `patch` function. Given a DOM root node and a set of DOM patches, the `patch` function will update the DOM. After applying the patches to the DOM, the DOM should look like the new `VTree`.


## Original motivation

virtual-dom is heavily inspired by the inner workings of React by facebook. This project originated as a gist of ideas, which [we have linked to provide some background context](https://gist.github.com/Raynos/8414846).

## Tools

* [html2hscript](https://github.com/twilson63/html2hscript) - Parse HTML into hyperscript
* [html2hscript.herokuapp.com](http://html2hscript.herokuapp.com/) - Online Tool that converts html snippets to hyperscript
* [html2hyperscript](https://github.com/unframework/html2hyperscript) - Original commandline utility to convert legacy HTML markup into hyperscript


[1]: https://secure.travis-ci.org/Matt-Esch/virtual-dom.svg
[2]: https://travis-ci.org/Matt-Esch/virtual-dom
[3]: https://badge.fury.io/js/virtual-dom.svg
[4]: https://badge.fury.io/js/virtual-dom
[5]: http://img.shields.io/coveralls/Matt-Esch/virtual-dom.svg
[6]: https://coveralls.io/r/Matt-Esch/virtual-dom
[7]: https://david-dm.org/Matt-Esch/virtual-dom.svg
[8]: https://david-dm.org/Matt-Esch/virtual-dom


================================================
FILE: create-element.js
================================================
var createElement = require("./vdom/create-element.js")

module.exports = createElement


================================================
FILE: diff.js
================================================
var diff = require("./vtree/diff.js")

module.exports = diff


================================================
FILE: dist/virtual-dom.js
================================================
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.virtualDom=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var createElement = require("./vdom/create-element.js")

module.exports = createElement

},{"./vdom/create-element.js":15}],2:[function(require,module,exports){
var diff = require("./vtree/diff.js")

module.exports = diff

},{"./vtree/diff.js":35}],3:[function(require,module,exports){
var h = require("./virtual-hyperscript/index.js")

module.exports = h

},{"./virtual-hyperscript/index.js":22}],4:[function(require,module,exports){
var diff = require("./diff.js")
var patch = require("./patch.js")
var h = require("./h.js")
var create = require("./create-element.js")
var VNode = require('./vnode/vnode.js')
var VText = require('./vnode/vtext.js')

module.exports = {
    diff: diff,
    patch: patch,
    h: h,
    create: create,
    VNode: VNode,
    VText: VText
}

},{"./create-element.js":1,"./diff.js":2,"./h.js":3,"./patch.js":13,"./vnode/vnode.js":31,"./vnode/vtext.js":33}],5:[function(require,module,exports){
/*!
 * Cross-Browser Split 1.1.1
 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
 * Available under the MIT License
 * ECMAScript compliant, uniform cross-browser split method
 */

/**
 * Splits a string into an array of strings using a regex or string separator. Matches of the
 * separator are not included in the result array. However, if `separator` is a regex that contains
 * capturing groups, backreferences are spliced into the result each time `separator` is matched.
 * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably
 * cross-browser.
 * @param {String} str String to split.
 * @param {RegExp|String} separator Regex or string to use for separating the string.
 * @param {Number} [limit] Maximum number of items to include in the result array.
 * @returns {Array} Array of substrings.
 * @example
 *
 * // Basic use
 * split('a b c d', ' ');
 * // -> ['a', 'b', 'c', 'd']
 *
 * // With limit
 * split('a b c d', ' ', 2);
 * // -> ['a', 'b']
 *
 * // Backreferences in result array
 * split('..word1 word2..', /([a-z]+)(\d+)/i);
 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
 */
module.exports = (function split(undef) {

  var nativeSplit = String.prototype.split,
    compliantExecNpcg = /()??/.exec("")[1] === undef,
    // NPCG: nonparticipating capturing group
    self;

  self = function(str, separator, limit) {
    // If `separator` is not a regex, use `nativeSplit`
    if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
      return nativeSplit.call(str, separator, limit);
    }
    var output = [],
      flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6
      (separator.sticky ? "y" : ""),
      // Firefox 3+
      lastLastIndex = 0,
      // Make `global` and avoid `lastIndex` issues by working with a copy
      separator = new RegExp(separator.source, flags + "g"),
      separator2, match, lastIndex, lastLength;
    str += ""; // Type-convert
    if (!compliantExecNpcg) {
      // Doesn't need flags gy, but they don't hurt
      separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
    }
    /* Values for `limit`, per the spec:
     * If undefined: 4294967295 // Math.pow(2, 32) - 1
     * If 0, Infinity, or NaN: 0
     * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
     * If negative number: 4294967296 - Math.floor(Math.abs(limit))
     * If other: Type-convert, then use the above rules
     */
    limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1
    limit >>> 0; // ToUint32(limit)
    while (match = separator.exec(str)) {
      // `separator.lastIndex` is not reliable cross-browser
      lastIndex = match.index + match[0].length;
      if (lastIndex > lastLastIndex) {
        output.push(str.slice(lastLastIndex, match.index));
        // Fix browsers whose `exec` methods don't consistently return `undefined` for
        // nonparticipating capturing groups
        if (!compliantExecNpcg && match.length > 1) {
          match[0].replace(separator2, function() {
            for (var i = 1; i < arguments.length - 2; i++) {
              if (arguments[i] === undef) {
                match[i] = undef;
              }
            }
          });
        }
        if (match.length > 1 && match.index < str.length) {
          Array.prototype.push.apply(output, match.slice(1));
        }
        lastLength = match[0].length;
        lastLastIndex = lastIndex;
        if (output.length >= limit) {
          break;
        }
      }
      if (separator.lastIndex === match.index) {
        separator.lastIndex++; // Avoid an infinite loop
      }
    }
    if (lastLastIndex === str.length) {
      if (lastLength || !separator.test("")) {
        output.push("");
      }
    } else {
      output.push(str.slice(lastLastIndex));
    }
    return output.length > limit ? output.slice(0, limit) : output;
  };

  return self;
})();

},{}],6:[function(require,module,exports){

},{}],7:[function(require,module,exports){
'use strict';

var OneVersionConstraint = require('individual/one-version');

var MY_VERSION = '7';
OneVersionConstraint('ev-store', MY_VERSION);

var hashKey = '__EV_STORE_KEY@' + MY_VERSION;

module.exports = EvStore;

function EvStore(elem) {
    var hash = elem[hashKey];

    if (!hash) {
        hash = elem[hashKey] = {};
    }

    return hash;
}

},{"individual/one-version":9}],8:[function(require,module,exports){
(function (global){
'use strict';

/*global window, global*/

var root = typeof window !== 'undefined' ?
    window : typeof global !== 'undefined' ?
    global : {};

module.exports = Individual;

function Individual(key, value) {
    if (key in root) {
        return root[key];
    }

    root[key] = value;

    return value;
}

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],9:[function(require,module,exports){
'use strict';

var Individual = require('./index.js');

module.exports = OneVersion;

function OneVersion(moduleName, version, defaultValue) {
    var key = '__INDIVIDUAL_ONE_VERSION_' + moduleName;
    var enforceKey = key + '_ENFORCE_SINGLETON';

    var versionValue = Individual(enforceKey, version);

    if (versionValue !== version) {
        throw new Error('Can only have one copy of ' +
            moduleName + '.\n' +
            'You already have version ' + versionValue +
            ' installed.\n' +
            'This means you cannot install version ' + version);
    }

    return Individual(key, defaultValue);
}

},{"./index.js":8}],10:[function(require,module,exports){
(function (global){
var topLevel = typeof global !== 'undefined' ? global :
    typeof window !== 'undefined' ? window : {}
var minDoc = require('min-document');

if (typeof document !== 'undefined') {
    module.exports = document;
} else {
    var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];

    if (!doccy) {
        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
    }

    module.exports = doccy;
}

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"min-document":6}],11:[function(require,module,exports){
"use strict";

module.exports = function isObject(x) {
	return typeof x === "object" && x !== null;
};

},{}],12:[function(require,module,exports){
var nativeIsArray = Array.isArray
var toString = Object.prototype.toString

module.exports = nativeIsArray || isArray

function isArray(obj) {
    return toString.call(obj) === "[object Array]"
}

},{}],13:[function(require,module,exports){
var patch = require("./vdom/patch.js")

module.exports = patch

},{"./vdom/patch.js":18}],14:[function(require,module,exports){
var isObject = require("is-object")
var isHook = require("../vnode/is-vhook.js")

module.exports = applyProperties

function applyProperties(node, props, previous) {
    for (var propName in props) {
        var propValue = props[propName]

        if (propValue === undefined) {
            removeProperty(node, propName, propValue, previous);
        } else if (isHook(propValue)) {
            removeProperty(node, propName, propValue, previous)
            if (propValue.hook) {
                propValue.hook(node,
                    propName,
                    previous ? previous[propName] : undefined)
            }
        } else {
            if (isObject(propValue)) {
                patchObject(node, props, previous, propName, propValue);
            } else {
                node[propName] = propValue
            }
        }
    }
}

function removeProperty(node, propName, propValue, previous) {
    if (previous) {
        var previousValue = previous[propName]

        if (!isHook(previousValue)) {
            if (propName === "attributes") {
                for (var attrName in previousValue) {
                    node.removeAttribute(attrName)
                }
            } else if (propName === "style") {
                for (var i in previousValue) {
                    node.style[i] = ""
                }
            } else if (typeof previousValue === "string") {
                node[propName] = ""
            } else {
                node[propName] = null
            }
        } else if (previousValue.unhook) {
            previousValue.unhook(node, propName, propValue)
        }
    }
}

function patchObject(node, props, previous, propName, propValue) {
    var previousValue = previous ? previous[propName] : undefined

    // Set attributes
    if (propName === "attributes") {
        for (var attrName in propValue) {
            var attrValue = propValue[attrName]

            if (attrValue === undefined) {
                node.removeAttribute(attrName)
            } else {
                node.setAttribute(attrName, attrValue)
            }
        }

        return
    }

    if(previousValue && isObject(previousValue) &&
        getPrototype(previousValue) !== getPrototype(propValue)) {
        node[propName] = propValue
        return
    }

    if (!isObject(node[propName])) {
        node[propName] = {}
    }

    var replacer = propName === "style" ? "" : undefined

    for (var k in propValue) {
        var value = propValue[k]
        node[propName][k] = (value === undefined) ? replacer : value
    }
}

function getPrototype(value) {
    if (Object.getPrototypeOf) {
        return Object.getPrototypeOf(value)
    } else if (value.__proto__) {
        return value.__proto__
    } else if (value.constructor) {
        return value.constructor.prototype
    }
}

},{"../vnode/is-vhook.js":26,"is-object":11}],15:[function(require,module,exports){
var document = require("global/document")

var applyProperties = require("./apply-properties")

var isVNode = require("../vnode/is-vnode.js")
var isVText = require("../vnode/is-vtext.js")
var isWidget = require("../vnode/is-widget.js")
var handleThunk = require("../vnode/handle-thunk.js")

module.exports = createElement

function createElement(vnode, opts) {
    var doc = opts ? opts.document || document : document
    var warn = opts ? opts.warn : null

    vnode = handleThunk(vnode).a

    if (isWidget(vnode)) {
        return vnode.init()
    } else if (isVText(vnode)) {
        return doc.createTextNode(vnode.text)
    } else if (!isVNode(vnode)) {
        if (warn) {
            warn("Item is not a valid virtual dom node", vnode)
        }
        return null
    }

    var node = (vnode.namespace === null) ?
        doc.createElement(vnode.tagName) :
        doc.createElementNS(vnode.namespace, vnode.tagName)

    var props = vnode.properties
    applyProperties(node, props)

    var children = vnode.children

    for (var i = 0; i < children.length; i++) {
        var childNode = createElement(children[i], opts)
        if (childNode) {
            node.appendChild(childNode)
        }
    }

    return node
}

},{"../vnode/handle-thunk.js":24,"../vnode/is-vnode.js":27,"../vnode/is-vtext.js":28,"../vnode/is-widget.js":29,"./apply-properties":14,"global/document":10}],16:[function(require,module,exports){
// Maps a virtual DOM tree onto a real DOM tree in an efficient manner.
// We don't want to read all of the DOM nodes in the tree so we use
// the in-order tree indexing to eliminate recursion down certain branches.
// We only recurse into a DOM node if we know that it contains a child of
// interest.

var noChild = {}

module.exports = domIndex

function domIndex(rootNode, tree, indices, nodes) {
    if (!indices || indices.length === 0) {
        return {}
    } else {
        indices.sort(ascending)
        return recurse(rootNode, tree, indices, nodes, 0)
    }
}

function recurse(rootNode, tree, indices, nodes, rootIndex) {
    nodes = nodes || {}


    if (rootNode) {
        if (indexInRange(indices, rootIndex, rootIndex)) {
            nodes[rootIndex] = rootNode
        }

        var vChildren = tree.children

        if (vChildren) {

            var childNodes = rootNode.childNodes

            for (var i = 0; i < tree.children.length; i++) {
                rootIndex += 1

                var vChild = vChildren[i] || noChild
                var nextIndex = rootIndex + (vChild.count || 0)

                // skip recursion down the tree if there are no nodes down here
                if (indexInRange(indices, rootIndex, nextIndex)) {
                    recurse(childNodes[i], vChild, indices, nodes, rootIndex)
                }

                rootIndex = nextIndex
            }
        }
    }

    return nodes
}

// Binary search for an index in the interval [left, right]
function indexInRange(indices, left, right) {
    if (indices.length === 0) {
        return false
    }

    var minIndex = 0
    var maxIndex = indices.length - 1
    var currentIndex
    var currentItem

    while (minIndex <= maxIndex) {
        currentIndex = ((maxIndex + minIndex) / 2) >> 0
        currentItem = indices[currentIndex]

        if (minIndex === maxIndex) {
            return currentItem >= left && currentItem <= right
        } else if (currentItem < left) {
            minIndex = currentIndex + 1
        } else  if (currentItem > right) {
            maxIndex = currentIndex - 1
        } else {
            return true
        }
    }

    return false;
}

function ascending(a, b) {
    return a > b ? 1 : -1
}

},{}],17:[function(require,module,exports){
var applyProperties = require("./apply-properties")

var isWidget = require("../vnode/is-widget.js")
var VPatch = require("../vnode/vpatch.js")

var updateWidget = require("./update-widget")

module.exports = applyPatch

function applyPatch(vpatch, domNode, renderOptions) {
    var type = vpatch.type
    var vNode = vpatch.vNode
    var patch = vpatch.patch

    switch (type) {
        case VPatch.REMOVE:
            return removeNode(domNode, vNode)
        case VPatch.INSERT:
            return insertNode(domNode, patch, renderOptions)
        case VPatch.VTEXT:
            return stringPatch(domNode, vNode, patch, renderOptions)
        case VPatch.WIDGET:
            return widgetPatch(domNode, vNode, patch, renderOptions)
        case VPatch.VNODE:
            return vNodePatch(domNode, vNode, patch, renderOptions)
        case VPatch.ORDER:
            reorderChildren(domNode, patch)
            return domNode
        case VPatch.PROPS:
            applyProperties(domNode, patch, vNode.properties)
            return domNode
        case VPatch.THUNK:
            return replaceRoot(domNode,
                renderOptions.patch(domNode, patch, renderOptions))
        default:
            return domNode
    }
}

function removeNode(domNode, vNode) {
    var parentNode = domNode.parentNode

    if (parentNode) {
        parentNode.removeChild(domNode)
    }

    destroyWidget(domNode, vNode);

    return null
}

function insertNode(parentNode, vNode, renderOptions) {
    var newNode = renderOptions.render(vNode, renderOptions)

    if (parentNode) {
        parentNode.appendChild(newNode)
    }

    return parentNode
}

function stringPatch(domNode, leftVNode, vText, renderOptions) {
    var newNode

    if (domNode.nodeType === 3) {
        domNode.replaceData(0, domNode.length, vText.text)
        newNode = domNode
    } else {
        var parentNode = domNode.parentNode
        newNode = renderOptions.render(vText, renderOptions)

        if (parentNode && newNode !== domNode) {
            parentNode.replaceChild(newNode, domNode)
        }
    }

    return newNode
}

function widgetPatch(domNode, leftVNode, widget, renderOptions) {
    var updating = updateWidget(leftVNode, widget)
    var newNode

    if (updating) {
        newNode = widget.update(leftVNode, domNode) || domNode
    } else {
        newNode = renderOptions.render(widget, renderOptions)
    }

    var parentNode = domNode.parentNode

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    if (!updating) {
        destroyWidget(domNode, leftVNode)
    }

    return newNode
}

function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
    var parentNode = domNode.parentNode
    var newNode = renderOptions.render(vNode, renderOptions)

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    return newNode
}

function destroyWidget(domNode, w) {
    if (typeof w.destroy === "function" && isWidget(w)) {
        w.destroy(domNode)
    }
}

function reorderChildren(domNode, moves) {
    var childNodes = domNode.childNodes
    var keyMap = {}
    var node
    var remove
    var insert

    for (var i = 0; i < moves.removes.length; i++) {
        remove = moves.removes[i]
        node = childNodes[remove.from]
        if (remove.key) {
            keyMap[remove.key] = node
        }
        domNode.removeChild(node)
    }

    var length = childNodes.length
    for (var j = 0; j < moves.inserts.length; j++) {
        insert = moves.inserts[j]
        node = keyMap[insert.key]
        // this is the weirdest bug i've ever seen in webkit
        domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to])
    }
}

function replaceRoot(oldRoot, newRoot) {
    if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) {
        oldRoot.parentNode.replaceChild(newRoot, oldRoot)
    }

    return newRoot;
}

},{"../vnode/is-widget.js":29,"../vnode/vpatch.js":32,"./apply-properties":14,"./update-widget":19}],18:[function(require,module,exports){
var document = require("global/document")
var isArray = require("x-is-array")

var render = require("./create-element")
var domIndex = require("./dom-index")
var patchOp = require("./patch-op")
module.exports = patch

function patch(rootNode, patches, renderOptions) {
    renderOptions = renderOptions || {}
    renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch
        ? renderOptions.patch
        : patchRecursive
    renderOptions.render = renderOptions.render || render

    return renderOptions.patch(rootNode, patches, renderOptions)
}

function patchRecursive(rootNode, patches, renderOptions) {
    var indices = patchIndices(patches)

    if (indices.length === 0) {
        return rootNode
    }

    var index = domIndex(rootNode, patches.a, indices)
    var ownerDocument = rootNode.ownerDocument

    if (!renderOptions.document && ownerDocument !== document) {
        renderOptions.document = ownerDocument
    }

    for (var i = 0; i < indices.length; i++) {
        var nodeIndex = indices[i]
        rootNode = applyPatch(rootNode,
            index[nodeIndex],
            patches[nodeIndex],
            renderOptions)
    }

    return rootNode
}

function applyPatch(rootNode, domNode, patchList, renderOptions) {
    if (!domNode) {
        return rootNode
    }

    var newNode

    if (isArray(patchList)) {
        for (var i = 0; i < patchList.length; i++) {
            newNode = patchOp(patchList[i], domNode, renderOptions)

            if (domNode === rootNode) {
                rootNode = newNode
            }
        }
    } else {
        newNode = patchOp(patchList, domNode, renderOptions)

        if (domNode === rootNode) {
            rootNode = newNode
        }
    }

    return rootNode
}

function patchIndices(patches) {
    var indices = []

    for (var key in patches) {
        if (key !== "a") {
            indices.push(Number(key))
        }
    }

    return indices
}

},{"./create-element":15,"./dom-index":16,"./patch-op":17,"global/document":10,"x-is-array":12}],19:[function(require,module,exports){
var isWidget = require("../vnode/is-widget.js")

module.exports = updateWidget

function updateWidget(a, b) {
    if (isWidget(a) && isWidget(b)) {
        if ("name" in a && "name" in b) {
            return a.id === b.id
        } else {
            return a.init === b.init
        }
    }

    return false
}

},{"../vnode/is-widget.js":29}],20:[function(require,module,exports){
'use strict';

var EvStore = require('ev-store');

module.exports = EvHook;

function EvHook(value) {
    if (!(this instanceof EvHook)) {
        return new EvHook(value);
    }

    this.value = value;
}

EvHook.prototype.hook = function (node, propertyName) {
    var es = EvStore(node);
    var propName = propertyName.substr(3);

    es[propName] = this.value;
};

EvHook.prototype.unhook = function(node, propertyName) {
    var es = EvStore(node);
    var propName = propertyName.substr(3);

    es[propName] = undefined;
};

},{"ev-store":7}],21:[function(require,module,exports){
'use strict';

module.exports = SoftSetHook;

function SoftSetHook(value) {
    if (!(this instanceof SoftSetHook)) {
        return new SoftSetHook(value);
    }

    this.value = value;
}

SoftSetHook.prototype.hook = function (node, propertyName) {
    if (node[propertyName] !== this.value) {
        node[propertyName] = this.value;
    }
};

},{}],22:[function(require,module,exports){
'use strict';

var isArray = require('x-is-array');

var VNode = require('../vnode/vnode.js');
var VText = require('../vnode/vtext.js');
var isVNode = require('../vnode/is-vnode');
var isVText = require('../vnode/is-vtext');
var isWidget = require('../vnode/is-widget');
var isHook = require('../vnode/is-vhook');
var isVThunk = require('../vnode/is-thunk');

var parseTag = require('./parse-tag.js');
var softSetHook = require('./hooks/soft-set-hook.js');
var evHook = require('./hooks/ev-hook.js');

module.exports = h;

function h(tagName, properties, children) {
    var childNodes = [];
    var tag, props, key, namespace;

    if (!children && isChildren(properties)) {
        children = properties;
        props = {};
    }

    props = props || properties || {};
    tag = parseTag(tagName, props);

    // support keys
    if (props.hasOwnProperty('key')) {
        key = props.key;
        props.key = undefined;
    }

    // support namespace
    if (props.hasOwnProperty('namespace')) {
        namespace = props.namespace;
        props.namespace = undefined;
    }

    // fix cursor bug
    if (tag === 'INPUT' &&
        !namespace &&
        props.hasOwnProperty('value') &&
        props.value !== undefined &&
        !isHook(props.value)
    ) {
        props.value = softSetHook(props.value);
    }

    transformProperties(props);

    if (children !== undefined && children !== null) {
        addChild(children, childNodes, tag, props);
    }


    return new VNode(tag, props, childNodes, key, namespace);
}

function addChild(c, childNodes, tag, props) {
    if (typeof c === 'string') {
        childNodes.push(new VText(c));
    } else if (typeof c === 'number') {
        childNodes.push(new VText(String(c)));
    } else if (isChild(c)) {
        childNodes.push(c);
    } else if (isArray(c)) {
        for (var i = 0; i < c.length; i++) {
            addChild(c[i], childNodes, tag, props);
        }
    } else if (c === null || c === undefined) {
        return;
    } else {
        throw UnexpectedVirtualElement({
            foreignObject: c,
            parentVnode: {
                tagName: tag,
                properties: props
            }
        });
    }
}

function transformProperties(props) {
    for (var propName in props) {
        if (props.hasOwnProperty(propName)) {
            var value = props[propName];

            if (isHook(value)) {
                continue;
            }

            if (propName.substr(0, 3) === 'ev-') {
                // add ev-foo support
                props[propName] = evHook(value);
            }
        }
    }
}

function isChild(x) {
    return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x);
}

function isChildren(x) {
    return typeof x === 'string' || isArray(x) || isChild(x);
}

function UnexpectedVirtualElement(data) {
    var err = new Error();

    err.type = 'virtual-hyperscript.unexpected.virtual-element';
    err.message = 'Unexpected virtual child passed to h().\n' +
        'Expected a VNode / Vthunk / VWidget / string but:\n' +
        'got:\n' +
        errorString(data.foreignObject) +
        '.\n' +
        'The parent vnode is:\n' +
        errorString(data.parentVnode)
        '\n' +
        'Suggested fix: change your `h(..., [ ... ])` callsite.';
    err.foreignObject = data.foreignObject;
    err.parentVnode = data.parentVnode;

    return err;
}

function errorString(obj) {
    try {
        return JSON.stringify(obj, null, '    ');
    } catch (e) {
        return String(obj);
    }
}

},{"../vnode/is-thunk":25,"../vnode/is-vhook":26,"../vnode/is-vnode":27,"../vnode/is-vtext":28,"../vnode/is-widget":29,"../vnode/vnode.js":31,"../vnode/vtext.js":33,"./hooks/ev-hook.js":20,"./hooks/soft-set-hook.js":21,"./parse-tag.js":23,"x-is-array":12}],23:[function(require,module,exports){
'use strict';

var split = require('browser-split');

var classIdSplit = /([\.#]?[a-zA-Z0-9\u007F-\uFFFF_:-]+)/;
var notClassId = /^\.|#/;

module.exports = parseTag;

function parseTag(tag, props) {
    if (!tag) {
        return 'DIV';
    }

    var noId = !(props.hasOwnProperty('id'));

    var tagParts = split(tag, classIdSplit);
    var tagName = null;

    if (notClassId.test(tagParts[1])) {
        tagName = 'DIV';
    }

    var classes, part, type, i;

    for (i = 0; i < tagParts.length; i++) {
        part = tagParts[i];

        if (!part) {
            continue;
        }

        type = part.charAt(0);

        if (!tagName) {
            tagName = part;
        } else if (type === '.') {
            classes = classes || [];
            classes.push(part.substring(1, part.length));
        } else if (type === '#' && noId) {
            props.id = part.substring(1, part.length);
        }
    }

    if (classes) {
        if (props.className) {
            classes.push(props.className);
        }

        props.className = classes.join(' ');
    }

    return props.namespace ? tagName : tagName.toUpperCase();
}

},{"browser-split":5}],24:[function(require,module,exports){
var isVNode = require("./is-vnode")
var isVText = require("./is-vtext")
var isWidget = require("./is-widget")
var isThunk = require("./is-thunk")

module.exports = handleThunk

function handleThunk(a, b) {
    var renderedA = a
    var renderedB = b

    if (isThunk(b)) {
        renderedB = renderThunk(b, a)
    }

    if (isThunk(a)) {
        renderedA = renderThunk(a, null)
    }

    return {
        a: renderedA,
        b: renderedB
    }
}

function renderThunk(thunk, previous) {
    var renderedThunk = thunk.vnode

    if (!renderedThunk) {
        renderedThunk = thunk.vnode = thunk.render(previous)
    }

    if (!(isVNode(renderedThunk) ||
            isVText(renderedThunk) ||
            isWidget(renderedThunk))) {
        throw new Error("thunk did not return a valid node");
    }

    return renderedThunk
}

},{"./is-thunk":25,"./is-vnode":27,"./is-vtext":28,"./is-widget":29}],25:[function(require,module,exports){
module.exports = isThunk

function isThunk(t) {
    return t && t.type === "Thunk"
}

},{}],26:[function(require,module,exports){
module.exports = isHook

function isHook(hook) {
    return hook &&
      (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") ||
       typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook"))
}

},{}],27:[function(require,module,exports){
var version = require("./version")

module.exports = isVirtualNode

function isVirtualNode(x) {
    return x && x.type === "VirtualNode" && x.version === version
}

},{"./version":30}],28:[function(require,module,exports){
var version = require("./version")

module.exports = isVirtualText

function isVirtualText(x) {
    return x && x.type === "VirtualText" && x.version === version
}

},{"./version":30}],29:[function(require,module,exports){
module.exports = isWidget

function isWidget(w) {
    return w && w.type === "Widget"
}

},{}],30:[function(require,module,exports){
module.exports = "2"

},{}],31:[function(require,module,exports){
var version = require("./version")
var isVNode = require("./is-vnode")
var isWidget = require("./is-widget")
var isThunk = require("./is-thunk")
var isVHook = require("./is-vhook")

module.exports = VirtualNode

var noProperties = {}
var noChildren = []

function VirtualNode(tagName, properties, children, key, namespace) {
    this.tagName = tagName
    this.properties = properties || noProperties
    this.children = children || noChildren
    this.key = key != null ? String(key) : undefined
    this.namespace = (typeof namespace === "string") ? namespace : null

    var count = (children && children.length) || 0
    var descendants = 0
    var hasWidgets = false
    var hasThunks = false
    var descendantHooks = false
    var hooks

    for (var propName in properties) {
        if (properties.hasOwnProperty(propName)) {
            var property = properties[propName]
            if (isVHook(property) && property.unhook) {
                if (!hooks) {
                    hooks = {}
                }

                hooks[propName] = property
            }
        }
    }

    for (var i = 0; i < count; i++) {
        var child = children[i]
        if (isVNode(child)) {
            descendants += child.count || 0

            if (!hasWidgets && child.hasWidgets) {
                hasWidgets = true
            }

            if (!hasThunks && child.hasThunks) {
                hasThunks = true
            }

            if (!descendantHooks && (child.hooks || child.descendantHooks)) {
                descendantHooks = true
            }
        } else if (!hasWidgets && isWidget(child)) {
            if (typeof child.destroy === "function") {
                hasWidgets = true
            }
        } else if (!hasThunks && isThunk(child)) {
            hasThunks = true;
        }
    }

    this.count = count + descendants
    this.hasWidgets = hasWidgets
    this.hasThunks = hasThunks
    this.hooks = hooks
    this.descendantHooks = descendantHooks
}

VirtualNode.prototype.version = version
VirtualNode.prototype.type = "VirtualNode"

},{"./is-thunk":25,"./is-vhook":26,"./is-vnode":27,"./is-widget":29,"./version":30}],32:[function(require,module,exports){
var version = require("./version")

VirtualPatch.NONE = 0
VirtualPatch.VTEXT = 1
VirtualPatch.VNODE = 2
VirtualPatch.WIDGET = 3
VirtualPatch.PROPS = 4
VirtualPatch.ORDER = 5
VirtualPatch.INSERT = 6
VirtualPatch.REMOVE = 7
VirtualPatch.THUNK = 8

module.exports = VirtualPatch

function VirtualPatch(type, vNode, patch) {
    this.type = Number(type)
    this.vNode = vNode
    this.patch = patch
}

VirtualPatch.prototype.version = version
VirtualPatch.prototype.type = "VirtualPatch"

},{"./version":30}],33:[function(require,module,exports){
var version = require("./version")

module.exports = VirtualText

function VirtualText(text) {
    this.text = String(text)
}

VirtualText.prototype.version = version
VirtualText.prototype.type = "VirtualText"

},{"./version":30}],34:[function(require,module,exports){
var isObject = require("is-object")
var isHook = require("../vnode/is-vhook")

module.exports = diffProps

function diffProps(a, b) {
    var diff

    for (var aKey in a) {
        if (!(aKey in b)) {
            diff = diff || {}
            diff[aKey] = undefined
        }

        var aValue = a[aKey]
        var bValue = b[aKey]

        if (aValue === bValue) {
            continue
        } else if (isObject(aValue) && isObject(bValue)) {
            if (getPrototype(bValue) !== getPrototype(aValue)) {
                diff = diff || {}
                diff[aKey] = bValue
            } else if (isHook(bValue)) {
                 diff = diff || {}
                 diff[aKey] = bValue
            } else {
                var objectDiff = diffProps(aValue, bValue)
                if (objectDiff) {
                    diff = diff || {}
                    diff[aKey] = objectDiff
                }
            }
        } else {
            diff = diff || {}
            diff[aKey] = bValue
        }
    }

    for (var bKey in b) {
        if (!(bKey in a)) {
            diff = diff || {}
            diff[bKey] = b[bKey]
        }
    }

    return diff
}

function getPrototype(value) {
  if (Object.getPrototypeOf) {
    return Object.getPrototypeOf(value)
  } else if (value.__proto__) {
    return value.__proto__
  } else if (value.constructor) {
    return value.constructor.prototype
  }
}

},{"../vnode/is-vhook":26,"is-object":11}],35:[function(require,module,exports){
var isArray = require("x-is-array")

var VPatch = require("../vnode/vpatch")
var isVNode = require("../vnode/is-vnode")
var isVText = require("../vnode/is-vtext")
var isWidget = require("../vnode/is-widget")
var isThunk = require("../vnode/is-thunk")
var handleThunk = require("../vnode/handle-thunk")

var diffProps = require("./diff-props")

module.exports = diff

function diff(a, b) {
    var patch = { a: a }
    walk(a, b, patch, 0)
    return patch
}

function walk(a, b, patch, index) {
    if (a === b) {
        return
    }

    var apply = patch[index]
    var applyClear = false

    if (isThunk(a) || isThunk(b)) {
        thunks(a, b, patch, index)
    } else if (b == null) {

        // If a is a widget we will add a remove patch for it
        // Otherwise any child widgets/hooks must be destroyed.
        // This prevents adding two remove patches for a widget.
        if (!isWidget(a)) {
            clearState(a, patch, index)
            apply = patch[index]
        }

        apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b))
    } else if (isVNode(b)) {
        if (isVNode(a)) {
            if (a.tagName === b.tagName &&
                a.namespace === b.namespace &&
                a.key === b.key) {
                var propsPatch = diffProps(a.properties, b.properties)
                if (propsPatch) {
                    apply = appendPatch(apply,
                        new VPatch(VPatch.PROPS, a, propsPatch))
                }
                apply = diffChildren(a, b, patch, apply, index)
            } else {
                apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
                applyClear = true
            }
        } else {
            apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
            applyClear = true
        }
    } else if (isVText(b)) {
        if (!isVText(a)) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
            applyClear = true
        } else if (a.text !== b.text) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
        }
    } else if (isWidget(b)) {
        if (!isWidget(a)) {
            applyClear = true
        }

        apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b))
    }

    if (apply) {
        patch[index] = apply
    }

    if (applyClear) {
        clearState(a, patch, index)
    }
}

function diffChildren(a, b, patch, apply, index) {
    var aChildren = a.children
    var orderedSet = reorder(aChildren, b.children)
    var bChildren = orderedSet.children

    var aLen = aChildren.length
    var bLen = bChildren.length
    var len = aLen > bLen ? aLen : bLen

    for (var i = 0; i < len; i++) {
        var leftNode = aChildren[i]
        var rightNode = bChildren[i]
        index += 1

        if (!leftNode) {
            if (rightNode) {
                // Excess nodes in b need to be added
                apply = appendPatch(apply,
                    new VPatch(VPatch.INSERT, null, rightNode))
            }
        } else {
            walk(leftNode, rightNode, patch, index)
        }

        if (isVNode(leftNode) && leftNode.count) {
            index += leftNode.count
        }
    }

    if (orderedSet.moves) {
        // Reorder nodes last
        apply = appendPatch(apply, new VPatch(
            VPatch.ORDER,
            a,
            orderedSet.moves
        ))
    }

    return apply
}

function clearState(vNode, patch, index) {
    // TODO: Make this a single walk, not two
    unhook(vNode, patch, index)
    destroyWidgets(vNode, patch, index)
}

// Patch records for all destroyed widgets must be added because we need
// a DOM node reference for the destroy function
function destroyWidgets(vNode, patch, index) {
    if (isWidget(vNode)) {
        if (typeof vNode.destroy === "function") {
            patch[index] = appendPatch(
                patch[index],
                new VPatch(VPatch.REMOVE, vNode, null)
            )
        }
    } else if (isVNode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) {
        var children = vNode.children
        var len = children.length
        for (var i = 0; i < len; i++) {
            var child = children[i]
            index += 1

            destroyWidgets(child, patch, index)

            if (isVNode(child) && child.count) {
                index += child.count
            }
        }
    } else if (isThunk(vNode)) {
        thunks(vNode, null, patch, index)
    }
}

// Create a sub-patch for thunks
function thunks(a, b, patch, index) {
    var nodes = handleThunk(a, b)
    var thunkPatch = diff(nodes.a, nodes.b)
    if (hasPatches(thunkPatch)) {
        patch[index] = new VPatch(VPatch.THUNK, null, thunkPatch)
    }
}

function hasPatches(patch) {
    for (var index in patch) {
        if (index !== "a") {
            return true
        }
    }

    return false
}

// Execute hooks when two nodes are identical
function unhook(vNode, patch, index) {
    if (isVNode(vNode)) {
        if (vNode.hooks) {
            patch[index] = appendPatch(
                patch[index],
                new VPatch(
                    VPatch.PROPS,
                    vNode,
                    undefinedKeys(vNode.hooks)
                )
            )
        }

        if (vNode.descendantHooks || vNode.hasThunks) {
            var children = vNode.children
            var len = children.length
            for (var i = 0; i < len; i++) {
                var child = children[i]
                index += 1

                unhook(child, patch, index)

                if (isVNode(child) && child.count) {
                    index += child.count
                }
            }
        }
    } else if (isThunk(vNode)) {
        thunks(vNode, null, patch, index)
    }
}

function undefinedKeys(obj) {
    var result = {}

    for (var key in obj) {
        result[key] = undefined
    }

    return result
}

// List diff, naive left to right reordering
function reorder(aChildren, bChildren) {
    // O(M) time, O(M) memory
    var bChildIndex = keyIndex(bChildren)
    var bKeys = bChildIndex.keys
    var bFree = bChildIndex.free

    if (bFree.length === bChildren.length) {
        return {
            children: bChildren,
            moves: null
        }
    }

    // O(N) time, O(N) memory
    var aChildIndex = keyIndex(aChildren)
    var aKeys = aChildIndex.keys
    var aFree = aChildIndex.free

    if (aFree.length === aChildren.length) {
        return {
            children: bChildren,
            moves: null
        }
    }

    // O(MAX(N, M)) memory
    var newChildren = []

    var freeIndex = 0
    var freeCount = bFree.length
    var deletedItems = 0

    // Iterate through a and match a node in b
    // O(N) time,
    for (var i = 0 ; i < aChildren.length; i++) {
        var aItem = aChildren[i]
        var itemIndex

        if (aItem.key) {
            if (bKeys.hasOwnProperty(aItem.key)) {
                // Match up the old keys
                itemIndex = bKeys[aItem.key]
                newChildren.push(bChildren[itemIndex])

            } else {
                // Remove old keyed items
                itemIndex = i - deletedItems++
                newChildren.push(null)
            }
        } else {
            // Match the item in a with the next free item in b
            if (freeIndex < freeCount) {
                itemIndex = bFree[freeIndex++]
                newChildren.push(bChildren[itemIndex])
            } else {
                // There are no free items in b to match with
                // the free items in a, so the extra free nodes
                // are deleted.
                itemIndex = i - deletedItems++
                newChildren.push(null)
            }
        }
    }

    var lastFreeIndex = freeIndex >= bFree.length ?
        bChildren.length :
        bFree[freeIndex]

    // Iterate through b and append any new keys
    // O(M) time
    for (var j = 0; j < bChildren.length; j++) {
        var newItem = bChildren[j]

        if (newItem.key) {
            if (!aKeys.hasOwnProperty(newItem.key)) {
                // Add any new keyed items
                // We are adding new items to the end and then sorting them
                // in place. In future we should insert new items in place.
                newChildren.push(newItem)
            }
        } else if (j >= lastFreeIndex) {
            // Add any leftover non-keyed items
            newChildren.push(newItem)
        }
    }

    var simulate = newChildren.slice()
    var simulateIndex = 0
    var removes = []
    var inserts = []
    var simulateItem

    for (var k = 0; k < bChildren.length;) {
        var wantedItem = bChildren[k]
        simulateItem = simulate[simulateIndex]

        // remove items
        while (simulateItem === null && simulate.length) {
            removes.push(remove(simulate, simulateIndex, null))
            simulateItem = simulate[simulateIndex]
        }

        if (!simulateItem || simulateItem.key !== wantedItem.key) {
            // if we need a key in this position...
            if (wantedItem.key) {
                if (simulateItem && simulateItem.key) {
                    // if an insert doesn't put this key in place, it needs to move
                    if (bKeys[simulateItem.key] !== k + 1) {
                        removes.push(remove(simulate, simulateIndex, simulateItem.key))
                        simulateItem = simulate[simulateIndex]
                        // if the remove didn't put the wanted item in place, we need to insert it
                        if (!simulateItem || simulateItem.key !== wantedItem.key) {
                            inserts.push({key: wantedItem.key, to: k})
                        }
                        // items are matching, so skip ahead
                        else {
                            simulateIndex++
                        }
                    }
                    else {
                        inserts.push({key: wantedItem.key, to: k})
                    }
                }
                else {
                    inserts.push({key: wantedItem.key, to: k})
                }
                k++
            }
            // a key in simulate has no matching wanted key, remove it
            else if (simulateItem && simulateItem.key) {
                removes.push(remove(simulate, simulateIndex, simulateItem.key))
            }
        }
        else {
            simulateIndex++
            k++
        }
    }

    // remove all the remaining nodes from simulate
    while(simulateIndex < simulate.length) {
        simulateItem = simulate[simulateIndex]
        removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))
    }

    // If the only moves we have are deletes then we can just
    // let the delete patch remove these items.
    if (removes.length === deletedItems && !inserts.length) {
        return {
            children: newChildren,
            moves: null
        }
    }

    return {
        children: newChildren,
        moves: {
            removes: removes,
            inserts: inserts
        }
    }
}

function remove(arr, index, key) {
    arr.splice(index, 1)

    return {
        from: index,
        key: key
    }
}

function keyIndex(children) {
    var keys = {}
    var free = []
    var length = children.length

    for (var i = 0; i < length; i++) {
        var child = children[i]

        if (child.key) {
            keys[child.key] = i
        } else {
            free.push(i)
        }
    }

    return {
        keys: keys,     // A hash of key name to index
        free: free      // An array of unkeyed item indices
    }
}

function appendPatch(apply, patch) {
    if (apply) {
        if (isArray(apply)) {
            apply.push(patch)
        } else {
            apply = [apply, patch]
        }

        return apply
    } else {
        return patch
    }
}

},{"../vnode/handle-thunk":24,"../vnode/is-thunk":25,"../vnode/is-vnode":27,"../vnode/is-vtext":28,"../vnode/is-widget":29,"../vnode/vpatch":32,"./diff-props":34,"x-is-array":12}]},{},[4])(4)
});

================================================
FILE: docs/README.md
================================================
# virtual-dom documentation
This documentation is aimed at people who would like to work with virtual-dom directly, or gain a deeper understanding of how their virtual-dom based framework works. If you would rather be working at a higher level, you may find the [mercury framework](https://github.com/Raynos/mercury) a better place to start.

## Overview

virtual-dom consists of four main parts:

[vtree](https://github.com/Matt-Esch/virtual-dom/tree/master/vtree) - responsible for diffing two virtual representations DOM nodes  
[vdom](https://github.com/Matt-Esch/virtual-dom/tree/master/vdom) - responsible for taking the [patch](https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/patch.js) genereated by [vtree/diff](https://github.com/Matt-Esch/virtual-dom/blob/master/vtree/diff.js) and using it to modify the rendered DOM  
[vnode](https://github.com/Matt-Esch/virtual-dom/tree/master/vnode) - virtual representation of dom elements  
[virtual-hyperscript](https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript) - an interface for generating VNodes from simple data structures

Newcomers should start by reading the VNode and VText documentation, as virtual nodes are central to the operation of virtual-dom. Hooks, Thunks, and Widgets are more advanced features, and you will find both documentation of their interfaces and several examples on their respective pages.

## Contents

[VNode](vnode.md) - A representation of a DOM element

[VText](vtext.md) - A representation of a text node

[Hooks](hooks.md) - The mechanism for executing functions after a new node has been created

[Thunk](thunk.md) - The mechanism for taking control of diffing a specific DOM sub-tree

[Widget](widget.md) - The mechanism for taking control of node patching: DOM Element creation, updating, and removal.

[CSS animations](css-animations.md)


================================================
FILE: docs/css-animations.md
================================================
# CSS animations
Based on a discusison in [question](https://github.com/Matt-Esch/virtual-dom/issues/104#issuecomment-68611995).

You should be activating the CSS transitions using hooks and nextTick. Here is a basic example of inserting an element through transition:
```javascript
Item.render = function(state) {
  return h('li', {
    'class' : new ItemInsertHook('item'),
  }, [
    h('div.text', state.text), 
    h('button', {
      'ev-click' : mercury.event(...)
    }, 'Remove or something...'),
  ]);
}

function ItemInsertHook(value) {
  this.value = value;
}

ItemInsertHook.prototype.hook = function(elem, propName) {
  
  // Here we want to see if this is a newly created dom element
  // or an element that was inserted before and is revisited.
  // The way to check for that in this case is see if the element is 
  // attached to the dom.
  // Newly created element will not be attached to the dom when hook is executed.
  
  if (!document.body.contains(elem)) {
    elem.setAttribute(propName, this.value + ' inserting');

    nextTick(function () {
      elem.setAttribute(propName, this.value + '');
    }.bind(this))
  }
}

//Elswhere at the top level of application:
function renderItemsList(state) {
  return h('ul#item-list', [
    state.items.map(function(item) {return Item.render(item);})
  ]);
}
```

And css:
```css
li.item.inserting { opacity : 0.01; }
li.item { transition: opacity 0.2s ease-in-out; }
li.item { opacity : 0.99; }
```

See full example on requirebin: http://requirebin.com/?gist=250e6e59aa40d5ff0fcc

In a more complex case it may be necessary to encode animation state in the model. You should know exactly which nodes you wish to animate based on your data, and you should use that data to add a transition hook based on next tick.

You don't have to do animations with JS, prefer CSS transitions, but you do need to model your expectations properly in your data model and apply transitions to the nodes using that data. Generic transitions that rely on the way in which the DOM is mutated isn't going to work consistently.

For example, if you want an inserted transition, you might add a wasInserted boolean flag to your model.

On rendering that item, if wasInserted is true, you add an animation hook which, on next tick, adds a css class like .inserted. You code your CSS transitions against this class.

On next tick your hook will add the class and the transition will happen. Further to that you will want to clear the wasInserted flag, probably also on next tick.

There are tons of these flag situations which should not trigger re-render. I think mercury needs to add something like this as a primitive type but that's outside the scope of virtual-dom.

But as you can see, the animation works because you have recorded the state and necessity for the transition, and did not rely simply on the insertion of the node to trigger that animation.


================================================
FILE: docs/faq.md
================================================
# FAQ

These are frequently asked questions. If you have any questions
not on this list, then **please** [open an issue and ask][new-issue].

[new-issue]: https://github.com/Matt-Esch/virtual-dom/issues/new

## How do I do custom rendering

If you want to embed a custom piece of rendering machinery in
  the virtual DOM you can use widgets.

A widget is a object with an `init()` and `update()` method and a `type` attribute with the "Widget" value.

```js
function GoogleMapWidget(initialPosition) {
    this.type = 'Widget'
    this.position = initialPosition
}

GoogleMapWidget.prototype.init = function () {
    var elem = document.createElement('div')
    this.map = GoogleMap(elem)
    this.map.setPosition(this.position)
    return elem
}

GoogleMapWidget.prototype.update = function (prev, elem) {
    this.map = this.map || prev.map
    this.map.setPosition(this.position)
}

h('div', [
    new GoogleMapWidget({ x: 0, y: 0 })
])
```

The rules for a widget is that the first time it's seen we call
  `init()`, we expect `init()` to return a DOM element.

The DOM element you return is yours to keep & mutate, virtual
  DOM will not touch it or its children. However you should never
  touch `elem.parentNode` as that does not belong to the widget

The second method is `update()` if we see a widget and we have
  the same widget in the previous tree we call `update(prev, elem)`
  instead. `update()` is a good place to copy over any stateful
  things from the `prev` widget instance and then to update the
  state with the current properties by accessing them with `this`

For another example of a widget see the
    [canvas demo](examples/canvas.js)

## How do I update custom properties

If you want to update a custom property on a DOM element, like
  calling `setAttribute()` or calling `focus()` then you can
  use a hook

```js
function AttributeHook(value) {
    this.value = value
}

AttributeHook.prototype.hook = function (elem, prop) {
    elem.setAttribute(prop, this.value)
}

h('div', {
    class: new AttributeHook('some-class-name')
})
```

For another example of a hook see
  [TodoMVC focus hook](https://github.com/Raynos/mercury/blob/master/examples/lib/focus-hook.js)

## How do I get life cycle hooks for VNodes

`VNode` only exposes one life cycle mechanism. which is the hook
  mechanism.

### Hooking into VNode creation

If you want to do some custom DOM logic immediately once a VNode
  is created you can add a hook, I normally add them to
  `ev-foo` properties.

```js
function MyHook(args) {
  this.args = args
}

MyHook.prototype.hook = function (elem, propName) {
  /* do DOM stuff */
}

h('div', {
    'ev-myHook': new MyHook(args)
})
```

### Hooking into VNode after it's in the DOM

If you want to a hook to fire after the DOM element has been
  appended into the DOM you will have to delay the hook manually

```js
function MyHook(args) {
  this.args = args
}

MyHook.prototype.hook = function (elem, propName) {
  setImmediate(function () {
    // DOM element will be in the real DOM by now
    // do DOM stuff
  })
}

h('div', {
    'ev-myHook': new MyHook(args)
})
```

We only have one type of hook as maintaining both life cycles
  separately is very complex when it can simply be done at
  the user level with a `setImmediate`

We have the hook fire immediately by default because sometimes
  you need to run DOM logic BEFORE the element is in the DOM.

Firing the hook when the element is in the DOM makes it
  impossible to fire it when it's not in the DOM.



================================================
FILE: docs/hooks.md
================================================
# Hooks
Hooks are functions that execute after turning a VNode into an Element. They are set by passing a VNode any property key with an object that has a function called hook that has not been directly assigned. The simplest way to ensure that a function isn't directly assigned is for it to be a prototype on an object.

Will Work
```javascript
var Hook = function(){}
Hook.prototype.hook = function(node, propertyName, previousValue) { 
  console.log("Hello, World")
}
createElement(h('div', { "my-hook": new Hook() }))
```

Won't Work
```javascript
var hook = { hook: function(node, propertyName, previousvalue) { console.log("Hello, World") } }
createElement(h('div', { "my-hook": hook }))
```

## Arguments
Your hook function will be given the following arguments

**node**  
The Element generated from the VNode.

```javascript
var Hook = function(){}
Hook.prototype.hook = function(node, propertyName, previousValue) {
  console.log("type: ", node.constructor.name)
}
createElement(h('div', { "my-hook": new Hook() }))
// logs "type: HTMLDivElement"
```

**propertyName**  
String, key of the property this hook was assigned from.

```javascript
var Hook = function(){}
Hook.prototype.hook = function(node, propertyName, previousValue) { 
  console.log("name: " + propertyName)
}
createElement(h('div', { "my-hook": new Hook() }))
// logs "name: my-hook"
```

**previousValue** *(optional)*  
If this node is having just its properties changed during a patch, it will receive the value that was previously assigned to the key. Otherwise, this argument will be undefined.

## Other Examples
[virtual-hyperscript](https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript) uses hooks for several things, including setting up events and returning focus to input elements after a render. You can view these hooks in the [virtual-hyperscript/hooks](https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript/hooks) folder.


================================================
FILE: docs/thunk.md
================================================
# Thunk
Thunks allow the user to take control of the diff'ing process for a specific dom tree, usually to avoid doing calculations you know are unneeded, such as diff'ing a tree you know hasn't changed.

## Thunk Interface
A Thunk needs to be an object with two keys

**type**   
must be the string "Thunk"

**render**   
Function that returns a VNode, Widget, or VText.

```javascript
// Boilerplate Thunk
var Thunk = function (){}
Thunk.prototype.type = "Thunk"
Thunk.prototype.render = function(previous){}
```

## Render Method Arguments
When diff is run, the render method is passed a single argument.

**previous**  
The previous VNode, Thunk, Widget, or VText that the Thunk is being diffed against

## The Special "vnode" Property
When `render` is called by `diff`, it will create a cache of whatever it returns in the key `vnode`. When implementing the Thunk interface, don't define a `vnode` key, as it will be overwritten! You should use the `vnode` property when render is provided with the `previous` argument, and you'd like to return the cached copy to prevent a Thunk from re-rendering. We give an example of this in the ConstantlyThunk implementation below.

## Simple Example
Here we implement a simple Thunk called ConstantlyThunk. Any instance of ConstantlyThunk that gets diffed with another instance of ConstantlyThunk will return the value of the previous ConstantlyThunk, preventing a patch from being generated.

```javascript
// Only the first instance of this Thunk will be shown. 
// Once its been rendered, any other instances of ConstantlyThunk that
// diff with it will return a reference to the cached value that is automatically
// assigned to the "vnode" property
var ConstantlyThunk = function(greeting){
  this.greeting = greeting
}
ConstantlyThunk.prototype.type = "Thunk"
ConstantlyThunk.prototype.render = function(previous) {
  if (previous && previous.vnode) {
    return previous.vnode
  } else {
    return h('div', ["Constantly "+ this.greeting])
  }
}

Thunk1 = new ConstantlyThunk("Thunk!")
Thunk2 = new ConstantlyThunk("I won't be rendered!")
thunkElem = createElement(Thunk1)
document.body.appendChild(thunkElem)
// No new patches are generated
patches = diff(Thunk1, Thunk2)
// Nothing will happen
patch(thunkElem, patches)

```

## Full Example
Here we implement GenericThunk, a simplified version of Raynos' [immutable-thunk](https://github.com/Raynos/vdom-thunk/blob/master/immutable-thunk.js)

It takes a rendering function, a comparison function, and a state. When it's being diffed vs. another instance of GenericThunk, it will use the comparison function to look at the new state and old state, and decide if it's ok to update.

```javascript
var diff = require("virtual-dom").diff
var patch = require("virtual-dom").patch
var h = require("virtual-dom").h
var createElement = require("virtual-dom").create
// Our GenericThunk will take 3 arguments
// renderFn is the function that will generate the VNode
// cmpFn is the function that will be used to compare state to see if an update is necessary.
// returns true if the update should re-render, and false if it should use the previous render
// state is a value that holds the information cmpFn will use to decide whether we should
// update the Thunk or not
var GenericThunk = function(renderFn, cmpFn, state) {
  this.renderFn = renderFn
  this.cmpFn = cmpFn
  this.state = state
}

GenericThunk.prototype.type = "Thunk"

GenericThunk.prototype.render = function(previous) {
  // The first time the Thunk renders, there will be no previous state
  var previousState = previous ? previous.state : null
  // We run the comparison function to see if the state has changed enough
  // for us to re-render. If it returns truthy, then we call the render
  // function to give us a new VNode
  if ((!previousState || !this.state) || this.cmpFn(previousState, this.state)) {
    return this.renderFn(previous, this)
  } else {
    // vnode will be set automatically when a thunk has been created
    // it contains the VNode, VText, Thunk, or Widget generated by
    // our render function.
    return previous.vnode
  }
}

// The function we'll pass to GenericThunk to see if the color has changed
// We return a true value if the colors are different
var titleCompare = function(previousState, currentState) {
  return previousState.color !== currentState.color
}
// The function that builds our title when we detect that
// the color has changed
var titleRender = function(previousThunk, currentThunk) {
  var currentColor = currentThunk.state.color
  return h("h1", { style: {color: currentColor}}, ["Hello, I'm a title colored " + currentColor])
}

var GreenColoredThunk = new GenericThunk(titleRender, titleCompare, { color: "green"})
var BlueColoredThunk = new GenericThunk(titleRender, titleCompare, { color: "blue"})

var currentNode = GreenColoredThunk
var rootNode = createElement(currentNode)

// A simple function to diff your thunks, and patch the dom
var update = function(nextNode) {
  var patches = diff(currentNode, nextNode)
  rootNode = patch(rootNode, patches)
  currentNode = nextNode
}

document.body.appendChild(rootNode)
// We schedule a couple updates
// Our first update will see that our color hasn't changed, and will stop comparing at that point,
// instead returning a reference to GreenColoredThunk.vnode
setTimeout(function() {
    update(new GenericThunk(titleRender, titleCompare, { color: "green" }))
  },
  1000)

// In our second update, BlueColoredThunk will see that state.color has changed,
// and will return a new VNode, generating a patch
setTimeout(function() {
    update(BlueColoredThunk)
  },
  2000)

```

## Other Resources
Raynos has created a library for making Thunks at [vdom-thunk](https://github.com/Raynos/vdom-thunk).


================================================
FILE: docs/vnode.md
================================================
# VNode

A VNode is a representation of a dom node. Most end users will probably have their VNodes generated through [virtual-hyperscript](https://github.com/Matt-Esch/virtual-dom/tree/master/virtual-hyperscript), but understanding the VNode interface can be useful.

In virtual-dom, VNodes are turned from virtual nodes into real nodes with the createElement function. You can read the code at [vdom/create-element](https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/create-element.js)

## Arguments

**tagName**  
A string, e.g. 'div', 'span', 'article'

**properties** *(optional)*  
An object that maps property names to their values, e.g. `{ id: "foo", className: "bar" }`

**children** *(optional)*  
An array of any combination of VNodes, VText, Widgets, or Thunks

**key** *(optional)*  
An identifying string used to differentiate this node from others. Used internally by vtree/diff to do node reordering.

**namespace** *(optional*)  
A string specifying the namespace uri to associate with the element. VNodes created with a namespace will use [Document#createElementNS](https://developer.mozilla.org/en-US/docs/Web/API/document.createElementNS)

### A full example
```javascript
var VNode = require("virtual-dom").VNode
var createElement = require("virtual-dom").create

var Hook = function(){}
Hook.prototype.hook = function(elem, key, previousValue) {
  console.log("Hello from " + elem.id + "!\nMy key was: " + key)
}

var tagName = "div"
var style = "width: 100px; height: 100px; background-color: #FF0000;"
var attributes = {"class": "red box", style: style }
var key = "my-unique-red-box"
var namespace = "http://www.w3.org/1999/xhtml"
var properties = {
  attributes: attributes,
  id: "my-red-box",
  "a hook can have any property key": new Hook()
}
var childNode = new VNode("div", {id: "red-box-child"})

RedBoxNode = new VNode(tagName, properties, [childNode], key, namespace)
RedBoxElem = createElement(RedBoxNode)
document.body.appendChild(RedBoxElem)

// Will result in html that looks like
// <div class="red box" style="width: 100px; height: 100px; background-color: #FF0000;" id="my-red-box">
//   <div id="red-box-child"></div>
// </div>
```


### properties
Keys in properties have several special cases. 
#### properties.attributes
Everything in the `properties.attributes` object will be set on the rendered dom node using `Element#setAttribute(key, value)`, and removed using `Element#removeAttribute`. Refer to [MDN HTML Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes) for available attributes. 

#### Hook Objects
Any key whose value is an object with an inherited key called "hook" is considered a hook. Hooks are used to run functions at render time. Refer to the [hook documentation](hooks.md) for more information.

#### Other Objects
Any key in `properties` that is an object, but whose value isn't a hook and isn't in the attributes key, will set the rendered elements property to the given object.

```javascript
createElement(new VNode('div', { style: { width: "100px", height: "100px"}}))
// When added to the dom, the resulting element will look like <div style="width: 100px; height: 100px"><div>
```

#### Other Values
Most attributes can be set using properties. During element creation, keys and values in the `properties.attributes` get set using `Element#setAttribute(key, value)`, whereas keys other than `attributes` present in properties get set using `element[key] = value`.

```javascript
foo = new VNode('div', { id: 'foo'})
// will render the same as
foo = new VNode('div', { attributes: { id: 'foo' }})
```

This can, however, cause some unexpected behavior, particularly if you are unfamiliar with the differences between setting an element property directly vs. setting it using an attribute. Refer to [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) for some common property behaviors.

#### properties.style vs properties.attributes.style
`properties.style` expects an object, while `properties.attributes.style` expects a string.

```javascript
// using attributes
var redBox = new VNode('div', { attributes: { style: "width: 100px; height: 100px; background-color: #FF0000;" }})
// using a property
var anotherRedBox = new VNode('div', { style: { width: "100px", height: "100px", backgroundColor: "#FF0000" }})
```

When using `properties.style`, if the styles have changed since the last render and are being updated, keys that are not in the new style definition will be set to an empty string. This differs from the normal behavior, which sets old object keys to undefined.

#### properties.className vs properties.attributes.class
Set the class attribute value using `className` key in properties, and the `class` key in attributes.

```javascript
var redBox = new VNode('div', { className: "red box" })
// will render the same as
var anotherRedBox = new VNode('div', { attributes: { "class": "red box" }})
```

#### Custom attributes (data-\*)
Custom attributes won't be rendered if set directly in properties. Set them in properties.attributes.

```javascript
var doThis = new VNode('div', { attributes: { "data-example": "I will render" }})
var notThis = new VNode('div', { "data-example": "I will not render" })
```

Alternately, you can use the [dataset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.dataset) property to set data-\* attributes.

```javascript
new VNode('div', { attributes: { "data-greeting": "Hello" }})
new VNode('div', { dataset: { greeting: "Hello" }})
// Will both generate <div data-greeting="Hello"></div>
```

#### ARIA attributes
Like custom attributes, ARIA attributes are also set in `properties.attributes`.

```javascript
var ariaExample = new VNode('div', { attributes: { "aria-checked": "true" }})
```

Unlike data-\* attributes, they cannot be set directly via properties.

#### properties.value, properties.defaultValue, and properties.attributes.value
If an input element is reset, its value will be returned to its value set in `properties.attributes.value`. If you've set the value using `properties.value`, this will not happen. However, you may set `properties.defaultValue` to get the desired result.


================================================
FILE: docs/vtext.md
================================================
# VText
VText is a representation of a Text node. 

In virtual-dom, a VText is turned into an actual text node with the `createElement` function. You can read the code at [vdom/create-element](https://github.com/Matt-Esch/virtual-dom/blob/master/vdom/create-element.js)

`createElement` turns a VText into a Text node using [document#createTextNode](https://developer.mozilla.org/en-US/docs/Web/API/document.createTextNode). 

## Full Example
```javascript
var createElement = require("virtual-dom").create
var VNode = require("virtual-dom/vnode/vnode")
var VText = require("virtual-dom/vnode/vtext")
var myText = new VText("Hello, World")

// Pass our VText as a child of our VNode
var myNode = new VNode("div", { id: "my-node" }, [myText])

var myElem = createElement(myNode)
document.body.appendChild(myElem)
// Will result in a dom string that looks like <div id="my-node">Hello, World</div>
```

## Arguments
**text**  
The string you would like the text node to contain.

## HTML Injection
`document#createTextNode` will defend against HTML injection. You could use the innerHTML property, but it will most likely break virtual dom.

```javascript
escapedText = new VText('<span>Example</span>')
escapedNode = new VNode('div', null, [escapedText])
// Will enter the dom as <div>&lt;span&gt;Example&lt;/span&gt;</div>

unescapedNode = new VNode('div', { innerHTML: "<span>Example</span>" })
// Will enter the dom as <div><span>Example</span></div>
// You should probably never do this
```


================================================
FILE: docs/widget.md
================================================
# Widget
Widgets are used to take control of the patching process, allowing the user to create stateful components, control sub-tree rendering, and hook into element removal.

## Widget Interface
**type**  
Must be the string "Widget"

**init**  
The function called when the widget is being created. Should return a DOM Element.

**update**  
The function called when the widget is being updated.

**destroy**  
The function called when the widget is being removed from the dom.

```javascript
// Boilerplate widget
var Widget = function (){}
Widget.prototype.type = "Widget"
Widget.prototype.init = function(){}
Widget.prototype.update = function(previous, domNode){}
Widget.prototype.destroy = function(domNode){}
```

### Update Arguments
The arguments passed to `Widget#update`

**previous**  
The previous Widget

**domNode**  
The previous DOM Element associated with this widget

### Destroy Argument
The argument passed to `Widget#destroy`

**domNode**  
The HTMLElement associated with the widget that will be removed

## Full Example
This example demonstrates one way to pass local component state and use `init`, `update`, and `destroy` to create a widget that counts each time it tries to update, only showing the odd numbers.

```javascript
var diff = require("virtual-dom").diff
var patch = require("virtual-dom").patch
var h = require("virtual-dom").h
var createElement = require("virtual-dom").create

var OddCounterWidget = function() {}
OddCounterWidget.prototype.type = "Widget"
OddCounterWidget.prototype.count = 1
OddCounterWidget.prototype.init = function() {
  // With widgets, you can use any method you would like to generate the DOM Elements.
  // We could get the same result using:
  // return createElement(h("div", "Count is: " + this.count))
  var divElem = document.createElement("div")
  var textElem = document.createTextNode("Count is: " + this.count)
  divElem.appendChild(textElem)
  return divElem
}

OddCounterWidget.prototype.update = function(previous, domNode) {
  this.count = previous.count + 1
  // Only re-render if the current count is odd
  if (this.count % 2) {
    // Returning a new element from widget#update
    // will replace the previous node
    return this.init()
  }
  return null
}

OddCounterWidget.prototype.destroy = function(domNode) {
  // While you can do any cleanup you would like here,
  // we don't really have to do anything in this case.
  // Instead, we'll log the current count
  console.log(this.count)
}

var myCounter = new OddCounterWidget()
var currentNode = myCounter
var rootNode = createElement(currentNode)

// A simple function to diff your widgets, and patch the dom
var update = function(nextNode) {
  var patches = diff(currentNode, nextNode)
  rootNode = patch(rootNode, patches)
  currentNode = nextNode
}

document.body.appendChild(rootNode)
setInterval(function(){
  update(new OddCounterWidget())
}, 1000)
```


================================================
FILE: docs.jsig
================================================
--  A VHook is an object with a hook and unhook method.
--  A VHook can be used to define your own apply property logic.
--
--  When a hook is found in the VProperties object in either
--      `patch()` or `createElement()`, instead of setting
--      or unsetting the property we will invoke `hook()` or
--      `unhook()` respectively so you can define what setting
--      and unsetting a property means.
type VHook : {
    hook: (node: DOMElement, propertyName: String) => void,
    unhook: (node: DOMElement, propertyName: String) => void
}

type VPropertyName : String
type VPropertyValue : String | Boolean | Number

--  A VProperties is a data structure that represents the
--      set of properties that can be attached to a virtual node
--
--  Properties can be many things. The simplest case is a string
--      property name and string or bool or number property value
--
--  You can also have hooks in your properties. You give a
--      string hook name and a hook value. The hook will then
--      be invoked as part of `patch()` and `createElement()`
--      which allows you to set custom properties.
--
--  Next up, attributes and style are treated slightly
--      differently. These keys are expected to have objects
--      of string keys and string values and will have the
--      correct `patch()` and `createElement()` logic for
--      setting attributes and styles.
--
--  Finally properties can also have nested arbitrary objects
--      of some key name to some value that is an object of
--      string to bool or number or string.
type VProperties :
    Object<propName: VPropertyName, propValue: VPropertyValue> &
    Object<hookName: String, hook: VHook> &
    Object<"attributes",
        Object<attrName: String, attrValue: String>> &
    Object<"style",
        Object<styleName: String, styleValue: String>> &
    Object<String, Object<
        nestedPropName: VPropertyName,
        nestedPropValue: VPropertyValue
    >

--  A VNode is a data structure that represents a virtual node
--      in a virtual tree.
--
--  A virtual node consists of a tagName, a set of properties
--      and a list of children. It also has meta data attached,
--      namely a key and a namespace.
type VNode : {
    tagName: String,
    properties: VProperties
    children: Array<VTree>,
    key: String | undefined,
    namespace: String | null
}

--  A VText is a data structure representing a text node in
--      a virtual tree.
type VText : {
    text: String,
    type: "VirtualText"
}

--  A Widget is a custom data structure for representing an
--      object in a virtual tree.
--
--  A Widget allows you to fully specify the semantics of
--      intitialization of the object, updating of the object
--      and destruction of the object.
--
--  A Widget should generally be used for custom, performance
--      critical code.
type Widget : {
    type: "Widget",
    init: () => DOMElement,
    update: (previous: Widget, domNode: DOMElement) => void,
    destroy: (node: DOMElement) => void
}

--  A Thunk is a custom data structure for representing an
--      unevaluated node in a virtual tree.
--
--  A Thunk allows you to overwrite the semantics of `diff()`
--      by implementing your own render method that takes the
--      previous node in the previous virtual tree.
--
--  Inside `render()` you can check whether the previous node
--      is conceptually the same as the current node and return
--      `previous.vnode` instead of re-computing and recreating
--      an equivelant vnode.
--
--  This allows you to implement caching in the virtual tree
--      and has significant performance improvements
type Thunk : {
    type: "Thunk",
    vnode: VTree,
    render: (previous: VTree | null) => VTree
}

--  A VTree is the union of all the types of nodes that can
--      exist in a virtual tree.
type VTree : VText | VNode | Widget | Thunk

--  A VPatch represents a patch object that is returned from
--      `diff()`. This patch object can be applied to an
--      existing DOM element tree.
type VPatch := {
    type: Number,
    vNode: VNode,
    patch: Any,
    type: 'VirtualPatch'
}

virtual-dom/create-element : (vnode: VTree, opts: {
    document?: DOMDocument,
    warn?: Boolean
}) => DOMElement | DOMTextNode

virtual-dom/diff : (left: VTree, right: VTree) => Array<VPatch>

virtual-dom/h : (
    tagSelector: String,
    properties?: {
        key: String,
        namespace: String
    } & VProperties,
    children?: Array<VTree> | Vtree | textContent: String
) => VNode

virtual-dom/patch : (
    rootNode: DOMElement,
    patches: Array<VPatch>
) => newRootNode: DOMElement


================================================
FILE: h.js
================================================
var h = require("./virtual-hyperscript/index.js")

module.exports = h


================================================
FILE: index.js
================================================
var diff = require("./diff.js")
var patch = require("./patch.js")
var h = require("./h.js")
var create = require("./create-element.js")
var VNode = require('./vnode/vnode.js')
var VText = require('./vnode/vtext.js')

module.exports = {
    diff: diff,
    patch: patch,
    h: h,
    create: create,
    VNode: VNode,
    VText: VText
}


================================================
FILE: package.json
================================================
{
  "name": "virtual-dom",
  "version": "2.1.1",
  "description": "A batched diff-based DOM rendering strategy",
  "keywords": [
    "virtual",
    "dom",
    "vdom",
    "vtree",
    "diff",
    "patch",
    "browser"
  ],
  "author": "Matt-Esch <matt@mattesch.info>",
  "repository": "git://github.com/Matt-Esch/virtual-dom.git",
  "main": "index",
  "homepage": "https://github.com/Matt-Esch/virtual-dom",
  "contributors": [
    {
      "name": "Matt-Esch"
    }
  ],
  "bugs": {
    "url": "https://github.com/Matt-Esch/virtual-dom/issues",
    "email": "matt@mattesch.info"
  },
  "dependencies": {
    "browser-split": "0.0.1",
    "error": "^4.3.0",
    "ev-store": "^7.0.0",
    "global": "^4.3.0",
    "is-object": "^1.0.1",
    "next-tick": "^0.2.2",
    "x-is-array": "0.1.0",
    "x-is-string": "0.1.0"
  },
  "devDependencies": {
    "browserify": "^9.0.7",
    "istanbul": "^0.3.13",
    "min-document": "^2.14.0",
    "opn": "^1.0.1",
    "run-browser": "^2.0.2",
    "tap-dot": "^1.0.0",
    "tap-spec": "^3.0.0",
    "tape": "^4.0.0",
    "zuul": "^2.1.1"
  },
  "license": "MIT",
  "scripts": {
    "test": "node ./test/index.js | tap-spec",
    "dot": "node ./test/index.js | tap-dot",
    "start": "node ./index.js",
    "cover": "istanbul cover --report html --print detail ./test/index.js",
    "view-cover": "istanbul report html && opn ./coverage/index.html",
    "browser": "run-browser test/index.js",
    "phantom": "run-browser test/index.js -b | tap-spec",
    "dist": "browserify --standalone virtual-dom index.js > dist/virtual-dom.js",
    "travis-test": "npm run phantom && npm run cover && istanbul report lcov && ((cat coverage/lcov.info | coveralls) || exit 0)",
    "release": "npm run release-patch",
    "release-patch": "git checkout master && npm version patch && git push origin master --tags && npm publish",
    "release-minor": "git checkout master && npm version minor && git push origin master --tags && npm publish",
    "release-major": "git checkout master && npm version major && git push origin master --tags && npm publish"
  },
  "testling": {
    "files": "test/*.js",
    "browsers": [
      "ie/8..latest",
      "firefox/17..latest",
      "firefox/nightly",
      "chrome/22..latest",
      "chrome/canary",
      "opera/12..latest",
      "opera/next",
      "safari/5.1..latest",
      "ipad/6.0..latest",
      "iphone/6.0..latest",
      "android-browser/4.2..latest"
    ]
  }
}


================================================
FILE: patch.js
================================================
var patch = require("./vdom/patch.js")

module.exports = patch


================================================
FILE: test/attributes.js
================================================
var test = require("tape")

var h = require("../h.js")
var createElement = require("../create-element.js")
var diff = require("../diff.js")
var patch = require("../patch.js")

test("attributes can be set", function (assert) {
    var leftTree = h("div")

    var rightTree = h("div",{
        attributes: {
            src: "test.jpg"
        }
    })

    var rootNode = createElement(leftTree)
    var patches = diff(leftTree, rightTree)

    var newRootNode = patch(rootNode, patches)

    assert.equal(newRootNode.getAttribute("src"), "test.jpg")
    assert.end()
})

test("individual attributes can be unset", function (assert) {
    var leftTree = h("div", {
        attributes: {
            a: "1",
            b: "2",
            c: "3"
        }
    })

    var rightTree = h("div", {
        attributes: {
            a: "1",
            c: "3"
        }
    })

    var rootNode = createElement(leftTree)
    var patches = diff(leftTree, rightTree)

    var newRootNode = patch(rootNode, patches)

    assert.equal(newRootNode, rootNode)
    assert.equal(newRootNode.getAttribute("a"), "1")
    assert.ok(newRootNode.getAttribute("b") == null)
    assert.equal(newRootNode.getAttribute("c"), "3")
    assert.end()
})

test("attributes can be completely unset", function (assert) {
    var leftTree = h("div", {
        attributes: {
            a: "1",
            b: "2",
            c: "3"
        }
    })

    var rightTree = h("div")

    var rootNode = createElement(leftTree)
    var patches = diff(leftTree, rightTree)


    var newRootNode = patch(rootNode, patches)

    assert.equal(newRootNode, rootNode)
    assert.ok(newRootNode.getAttribute("a") == null)
    assert.ok(newRootNode.getAttribute("b") == null)
    assert.ok(newRootNode.getAttribute("c") == null)
    assert.end()
})


================================================
FILE: test/hook.js
================================================
var test = require("tape")

var h = require("../h.js")
var Node = require("../vnode/vnode.js")
var create = require("../create-element.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var patchCount = require("./lib/patch-count.js")

test("Stateful hooks are added to a hooks object on a node", function (assert) {
    function StatefulHook() {}
    StatefulHook.prototype.hook = function () {}
    StatefulHook.prototype.unhook = function () {}
    var statefulValue = new StatefulHook()

    function StatelessHook() {}
    StatelessHook.prototype.hook = function () {}
    var statelessValue = new StatelessHook()

    var node = new Node("div", {
        stateful: statefulValue,
        stateless: statelessValue,
        value: "not a hook"
    }, [], null, null)

    assert.equal(node.hooks.stateful, statefulValue)
    assert.equal(node.hooks.stateless, undefined)
    assert.equal(node.descendantHooks, false)
    assert.end()
})

test("Node child stateless hooks are not identified", function (assert) {
    function Prop() {}
    Prop.prototype.hook = function () {}
    var propValue = new Prop()

    var node = new Node("div", {
        "id": propValue,
        "value": "not a hook"
    }, [], undefined, undefined)

    var parentNode = new Node("div", {
        "id": "not a hook"
    }, [node], undefined, undefined)

    assert.equal(node.hooks, undefined)
    assert.equal(parentNode.hooks, undefined)
    assert.notOk(parentNode.descendantHooks)
    assert.end()
})

test("Node child stateful hooks are identified", function (assert) {
    function Prop() {}
    Prop.prototype.hook = function () {}
    Prop.prototype.unhook = function () {}
    var propValue = new Prop()

    var node = new Node("div", {
        "id": propValue,
        "value": "not a hook"
    }, [], undefined, undefined)

    var parentNode = new Node("div", {
        "id": "not a hook"
    }, [node], undefined, undefined)

    assert.equal(node.hooks.id, propValue)
    assert.equal(parentNode.hooks, undefined)
    assert.ok(parentNode.descendantHooks)
    assert.end()
})

test("hooks get called in render", function (assert) {
    var counter = 0
    var vtree = h("div", {
        "some-key": hook(function (elem, prop) {
            counter++
            assert.equal(prop, "some-key")
            assert.equal(elem.tagName, "DIV")

            elem.className = "bar"
        })
    })

    var elem = create(vtree)
    assert.equal(elem.className, "bar")
    assert.equal(counter, 1)

    assert.end()
})

test("functions are not hooks in render", function (assert) {
    var counter = 0
    var fakeHook = function () {
        counter++
    }
    var vtree = h("div", {
        "someProp": fakeHook
    })

    var elem = create(vtree)
    assert.equal(elem.someProp, fakeHook)
    assert.equal(counter, 0)

    assert.end()
})

test("hooks get called in patch", function (assert) {
    var counter = 0
    var prev = h("div")
    var curr = h("div", {
        "some-key": hook(function (elem, prop) {
            counter++
            assert.equal(prop, "some-key")
            assert.equal(elem.tagName, "DIV")

            elem.className = "bar"
        })
    })

    var elem = createAndPatch(prev, curr)
    assert.equal(elem.className, "bar")
    assert.equal(counter, 1)

    assert.end()
})

test("hooks are called with DOM node, property name, and previous/next value", function (assert) {
    function Hook(name) {
        this.name = name
        this.hookArgs = []
        this.unhookArgs = []
    }
    Hook.prototype.hook = function() {
      this.hookArgs.push([].slice.call(arguments, 0))
    }
    Hook.prototype.unhook = function() {
      this.unhookArgs.push([].slice.call(arguments, 0))
    }

    var hook1 = new Hook('hook1')
    var hook2 = new Hook('hook2')

    var first = h("div", { id: 'first', hook: hook1 })
    var second = h("div", { id: 'second', hook: hook2 })
    var third = h("div")

    var elem = create(first)
    assert.equal(hook1.hookArgs.length, 1)
    assert.deepEqual(hook1.hookArgs[0], [elem, 'hook', undefined])
    assert.equal(hook1.unhookArgs.length, 0)

    var patches = diff(first, second)
    elem = patch(elem, patches)
    assert.equal(hook2.hookArgs.length, 1)
    assert.deepEqual(hook2.hookArgs[0], [elem, 'hook', hook1])
    assert.equal(hook1.unhookArgs.length, 1)
    assert.deepEqual(hook1.unhookArgs[0], [elem, 'hook', hook2])

    patches = diff(second, third)
    elem = patch(elem, patches)
    assert.equal(hook2.hookArgs.length, 1)
    assert.equal(hook2.unhookArgs.length, 1)
    assert.deepEqual(hook2.unhookArgs[0], [elem, 'hook', undefined])

    assert.end()
})

test("functions are not hooks in render", function (assert) {
    var counter = 0
    var fakeHook = function () {
        counter++
    }

    var prev = h("div")
    var curr = h("div", { someProp: fakeHook })

    var elem = createAndPatch(prev, curr)
    assert.equal(elem.someProp, fakeHook)
    assert.equal(counter, 0)

    assert.end()
})

test("two different hooks", function (assert) {
    var counters = { a: 0, b: 0 }
    var prev = h("div", { propA: hook(function () {
        counters.a++
    }) })
    var curr = h("div", { propB: hook(function () {
        counters.b++
    }) })

    var elem = createAndPatch(prev, curr)
    assert.equal(elem.propA, undefined)
    assert.equal(elem.propB, null)
    assert.equal(counters.a, 1)
    assert.equal(counters.b, 1)

    assert.end()
})

test("two hooks on same property", function (assert) {
    var counters = { a: 0, b: 0 }
    var prev = h("div", { propA: hook(function () {
        counters.a++
    }) })
    var curr = h("div", { propA: hook(function () {
        counters.b++
    }) })

    var elem = createAndPatch(prev, curr)
    assert.equal(elem.propA, undefined)
    assert.equal(counters.a, 1)
    assert.equal(counters.b, 1)

    assert.end()
})

test("two hooks of same interface", function (assert) {
    function Hook(key) {
        this.key = key
    }
    Hook.prototype.hook = function () {
        counters[this.key]++
    }

    var counters = { a: 0, b: 0 }
    var prev = h("div", { propA: new Hook("a") })
    var curr = h("div", { propA: new Hook("b") })

    var elem = createAndPatch(prev, curr)
    assert.equal(elem.propA, undefined)
    assert.equal(counters.a, 1, "counters.a")
    assert.equal(counters.b, 1, "counters.b")

    assert.end()
})

test("hooks are not called on trivial diff", function (assert) {
    var counters = {
        a: 0,
        b: 0,
        c: 0
    }

    var vnode = h("div", {
        test: hook(function () {
            counters.a++
        })
    }, [
        h("div", { test: hook(function () { counters.b++ }) }),
        h("div", { test: hook(function () { counters.c++ }) })
    ])

    var rootNode = create(vnode)
    assert.equal(counters.a, 1, "counters.a")
    assert.equal(counters.b, 1, "counters.b")
    assert.equal(counters.c, 1, "counters.c")

    var patches = diff(vnode, vnode)
    assert.equal(patchCount(patches), 0)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)
    assert.equal(counters.a, 1, "counters.a patch")
    assert.equal(counters.b, 1, "counters.b patch")
    assert.equal(counters.c, 1, "counters.c patch")
    assert.end()
})

test("property-replacing diff calls unhook", function (assert) {
  unhookCallCount = 0

  function zhook(x) {
    this.x = x
  }
  zhook.prototype.hook = function () {
    return null
  }

  zhook.prototype.unhook = function () {
    unhookCallCount += 1
  }

  hooker = new zhook('ONE')
  hooker2 = new zhook('TWO')

  var firstTree = h("div", {roothook: hooker})
  var secondTree = h("div", {roothook: hooker2})
  var thirdTree = h("span")

  var rootNode = create(firstTree)

  var firstPatches = diff(firstTree, secondTree)
  rootNode = patch(rootNode, firstPatches)


  var secondPatches = diff(secondTree, thirdTree)
  rootNode = patch(rootNode, secondPatches)

  assert.strictEqual(unhookCallCount, 2, "Missing unhook calls")

  assert.end()
})

test("unhook-only hook is a valid hook", function (assert) {
    var unhookCalled = false;

    function UnhookHook() {}

    UnhookHook.prototype.unhook = function() {
        unhookCalled = true
    }

    var hook = new UnhookHook()

    var firstTree = h('div', {hook: hook})
    var secondTree = h('div', {})

    var rootNode = create(firstTree)

    assert.notOk(unhookCalled)

    var patches = diff(firstTree, secondTree)
    rootNode = patch(rootNode, patches)

    assert.ok(unhookCalled)
    assert.end()
})

test("all hooks are unhooked", function (assert) {
    var hookCounts = {}
    var unhookCounts = {}

    function Hook(value) {
        if (!(this instanceof Hook)) {
            return new Hook(value)
        }
        this.value = value
    }

    Hook.prototype.hook = function hook() {
        var key = this.value
        if (key in hookCounts) {
            hookCounts[key]++
        } else {
            hookCounts[key] = 1;
        }
    }

    Hook.prototype.unhook = function unhook() {
        var key = this.value;
        if (key in unhookCounts) {
            unhookCounts[key]++
        } else {
            unhookCounts[key] = 1;
        }
    }

    var rootHook = Hook("rootHook")
    var childHookA = Hook("childHookA")
    var childHookB = Hook("childHookB")
    var childHookC = Hook("childHookC")
    var thunkyRootHook = Hook("thunkyRootHook")
    var thunkyChildHookA = Hook("thunkyChildHookA")
    var thunkyChildHookB = Hook("thunkyChildHookB")
    var thunkyChildHookC = Hook("thunkyChildHookC")

    function Thunky() {}

    Thunky.prototype.render = function () {
        return h("div", {
            rootHook: thunkyRootHook
        }, [
            h("div", {
                childHook: thunkyChildHookA
            }),
            h("div", {
                childHook: thunkyChildHookB
            }),
            h("div", {
                childHook: thunkyChildHookC
            })
        ])
    }

    Thunky.prototype.type = "Thunk"


    var firstTree = h("div", {
        rootHook: rootHook
    }, [
        h("div", {
            childHook: childHookA
        }),
        h("div", {
            childHook: childHookB
        }, [
            new Thunky()
        ]),
        h("div", {
            childHook: childHookC
        })
    ])

    var secondTree = h("div", {
        rootHook: rootHook
    }, [
        h("div", {
            childHook: childHookA
        }),
        h("div", {
            childHook: childHookB
        }, [
            new Thunky()
        ]),
        h("div", {
            childHook: childHookC
        })
    ])

    var thirdTree = h('span')

    var rootNode = create(firstTree)

    assertHooked();

    var firstPatches = diff(firstTree, secondTree)

    assertHooked();

    assert.strictEqual(patchCount(firstPatches), 0, "No patches for identical")
    rootNode = patch(rootNode, firstPatches)

    assertHooked();

    var secondPatches = diff(secondTree, thirdTree)

    assertHooked();

    // Expect 1 root patch, 3 unhook patches and a thunk patch
    assert.strictEqual(patchCount(secondPatches), 5, "Expect unhook patches")

    rootNode = patch(rootNode, secondPatches)

    assertUnhooked()

    assert.end()

    function assertHooked() {
        assert.strictEqual(hookCounts.rootHook, 1)
        assert.strictEqual(hookCounts.childHookA, 1)
        assert.strictEqual(hookCounts.childHookB, 1)
        assert.strictEqual(hookCounts.childHookC, 1)
        assert.strictEqual(hookCounts.thunkyRootHook, 1)
        assert.strictEqual(hookCounts.thunkyChildHookA, 1)
        assert.strictEqual(hookCounts.thunkyChildHookB, 1)
        assert.strictEqual(hookCounts.thunkyChildHookC, 1)
        assert.strictEqual(unhookCounts.rootHook, undefined)
        assert.strictEqual(unhookCounts.childHookA, undefined)
        assert.strictEqual(unhookCounts.childHookB, undefined)
        assert.strictEqual(unhookCounts.childHookC, undefined)
        assert.strictEqual(unhookCounts.thunkyRootHook, undefined)
        assert.strictEqual(unhookCounts.thunkyChildHookA, undefined)
        assert.strictEqual(unhookCounts.thunkyChildHookB, undefined)
        assert.strictEqual(unhookCounts.thunkyChildHookC, undefined)
    }

    function assertUnhooked() {
        assert.strictEqual(hookCounts.rootHook, 1)
        assert.strictEqual(hookCounts.childHookA, 1)
        assert.strictEqual(hookCounts.childHookB, 1)
        assert.strictEqual(hookCounts.childHookC, 1)
        assert.strictEqual(hookCounts.thunkyRootHook, 1)
        assert.strictEqual(hookCounts.thunkyChildHookA, 1)
        assert.strictEqual(hookCounts.thunkyChildHookB, 1)
        assert.strictEqual(hookCounts.thunkyChildHookC, 1)
        assert.strictEqual(unhookCounts.rootHook, 1)
        assert.strictEqual(unhookCounts.childHookA, 1)
        assert.strictEqual(unhookCounts.childHookB, 1)
        assert.strictEqual(unhookCounts.childHookC, 1)
        assert.strictEqual(unhookCounts.thunkyRootHook, 1)
        assert.strictEqual(unhookCounts.thunkyChildHookA, 1)
        assert.strictEqual(unhookCounts.thunkyChildHookB, 1)
        assert.strictEqual(unhookCounts.thunkyChildHookC, 1)
    }
})

function createAndPatch(prev, curr) {
    var elem = create(prev)
    var patches = diff(prev, curr)
    elem = patch(elem, patches)

    return elem
}

function Type(fn) {
    this.fn = fn
}

Type.prototype.hook = function () {
    this.fn.apply(this, arguments)
}

function hook(fn) {
    return new Type(fn)
}


================================================
FILE: test/index.js
================================================
require("./main.js")
require("./hook.js")
require("./nested-properties.js")
require("./undefined-properties.js")
require("./keys.js")
require("./thunk.js")
require("./style.js")
require("./attributes")
require("./non-string.js")

require("../vdom/test/")
require("../vtree/test/")
require("../virtual-hyperscript/test/")
require("../vnode/test/")


================================================
FILE: test/keys.js
================================================
var test = require("tape")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")

var patchCount = require("./lib/patch-count.js")
var assertEqualDom = require("./lib/assert-equal-dom.js")
var nodesFromArray = require("./lib/nodes-from-array.js")
var assertChildNodesFromArray = require("./lib/assert-childNodes-from-array.js")

var VPatch = require("../vnode/vpatch.js")

test("keys get reordered", function (assert) {
    var leftNode = nodesFromArray(["1", "2", "3", "4", "test", "6", "good", "7"])
    var rightNode = nodesFromArray(["7", "4", "3", "2", "6", "test", "good", "1"])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)
    assertReorderEquals(assert, patches, {
        removes: [
            {from: 0, key: '1'},
            {from: 0, key: '2'},
            {from: 1, key: '4'},
            {from: 2, key: '6'},
            {from: 3, key: '7'}
        ],
        inserts: [
            {to: 0, key: '7'},
            {to: 1, key: '4'},
            {to: 3, key: '2'},
            {to: 4, key: '6'},
            {to: 7, key: '1'}
        ]
    })

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.equal(newRoot.childNodes[3], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[5], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[0], childNodes[7])
    assert.end()
})

test("mix keys without keys", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div", { key: 1 })
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)
    assertReorderEquals(assert, patches, {
        removes: [{from: 0, key: '1'}],
        inserts: [{to: 7, key: '1'}]
    })

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.equal(newRoot.childNodes[2], childNodes[3])
    assert.equal(newRoot.childNodes[3], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[5], childNodes[6])
    assert.equal(newRoot.childNodes[6], childNodes[7])
    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.end()
})

test("avoid unnecessary reordering", function (assert) {
    var leftNode = h("div", [
        h("div"),
        h("div", { key: 1 }),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div", { key: 1 }),
        h("div")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 0)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.end()
})

test("missing key gets replaced", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.notEqual(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[3])
    assert.equal(newRoot.childNodes[4], childNodes[4])
    assert.equal(newRoot.childNodes[5], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[7], childNodes[7])
    assert.end()
})

test("widgets can be keyed", function (assert) {
    function DivWidget(key, state) {
        this.key = key
        this.state = state
    }

    DivWidget.prototype.init = function () {
        return render(h("div", this.state))
    }

    DivWidget.prototype.update = function (rootNode, prev) {
        if (this.state !== prev.state) {
            return render(h("div", this.state))
        }
    }

    DivWidget.prototype.type = "Widget"

    var leftNode = h("div", [
        new DivWidget("1", "a"),
        new DivWidget("2", "b"),
        new DivWidget("3", "c")
    ])

    var rightNode = h("div", [
        new DivWidget("3", "c"),
        new DivWidget("2", "b"),
        new DivWidget("1", "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)


    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 4)    // 1 reorder and 3 update patches

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assertEqualDom(assert, newRoot.childNodes[0], childNodes[2])
    assertEqualDom(assert, newRoot.childNodes[1], childNodes[1])
    assertEqualDom(assert, newRoot.childNodes[2], childNodes[0])
    assert.end()
})

test("delete key at the start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    // just a remove patch
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.end()
})

test("add key to start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[1], childNodes[0])
    assert.equal(newRoot.childNodes[2], childNodes[1])
    assert.end()
})

test("delete key at the end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    // just a remove patch
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add key to end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add to end and delete from center & reverse", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a", id: "a" }, "a"),
        h("div", { key: "b", id: "b" }, "b"),
        h("div", { key: "c", id: "c" }, "c"),
        h("div", { key: "d", id: "d" }, "d")
    ])

    var rightNode = h("div", [
        h("div", { key: "e", id: "e" }, "e"),
        h("div", { key: "d", id: "d" }, "d"),
        h("div", { key: "c", id: "c" }, "c"),
        h("div", { key: "a", id: "a" }, "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 4)

    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[0])
    assert.end()
})

test("add to front and remove", function (assert) {
    var leftNode = h("ul", [
        h("li", { key: "c" }, "c"),
        h("li", { key: "d" }, "d")
    ])

    var rightNode = h("ul", [
        h("li", { key: "a" }, "a"),
        h("li", { key: "b" }, "b"),
        h("li", { key: "c" }, "c"),
        h("li", { key: "e" }, "e")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 4)

    assert.equal(newRoot.childNodes[2], childNodes[0])
    assert.end()
})

test("adding multiple widgets", function (assert) {
    function FooWidget(foo) {
        this.foo = foo
        this.counter = 0
        this.key = foo
    }

    FooWidget.prototype.init = function () {
        return render(h("div", String(this.foo)))
    }

    FooWidget.prototype.update = function (prev, elem) {
        this.counter = prev.counter + 1
        elem.textContent = this.foo + this.counter
    }

    FooWidget.prototype.type = "Widget"

    var firstTree = h("div", [])
    var rootNode = render(firstTree)

    assert.equal(rootNode.tagName, "DIV")

    var secondTree = h("div", [
        new FooWidget("foo")
    ])
    rootNode = patch(rootNode, diff(firstTree, secondTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].tagName, "DIV")
    assert.equal(rootNode.childNodes[0].childNodes[0].data, "foo")

    var thirdTree = h("div", [
        new FooWidget("foo"),
        new FooWidget("bar")
    ])
    rootNode = patch(rootNode, diff(secondTree, thirdTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 2)

    assert.end()
})

var itemHelpers = {
    item: function (key) {
        key = key.toString()
        return h('div', { key: key, id: key }, ["" + key])
    },

    container: function (children) {
        return h('div', children)
    },

    itemsInContainer: function () {
        return {
            from: function (start) {
                return {
                    to: function (end) {
                        function withPredicate(predicate) {
                            var items = []
                            for (var i = start; i <= end; i++) {
                                if (!predicate(i)) continue
                                items.push(itemHelpers.item(i))
                            }
                            return itemHelpers.container(items)
                        }
                        return {
                            by: function (increment) {
                                return withPredicate(function (i) {
                                    return (i - start) % increment === 0
                                })
                            },
                            withPredicate: withPredicate
                        }
                    }
                }
            }
        }
    },

    expectTextOfChild: function (assert, rootNode, childNo, text) {
        assert.equal(rootNode.childNodes[childNo].id, text)
    }
}

test('3 elements in a container, insert an element after each', function (assert) {
    var threeItems = itemHelpers.itemsInContainer().from(0).to(4).by(2)
    var sixItems = itemHelpers.itemsInContainer().from(0).to(5).by(1)

    var rootNode = render(threeItems)
    rootNode = patch(rootNode, diff(threeItems, sixItems))

    for (var i = 0; i <= 5; i++) {
        itemHelpers.expectTextOfChild(assert, rootNode, i, i.toString())
    }

    assert.end()
})

test('10 elements in a container, remove every second element', function(assert) {
    var  fiveItems = itemHelpers.itemsInContainer().from(0).to(9).by(2)
    var tenItems = itemHelpers.itemsInContainer().from(0).to(9).by(1)

    var rootNode = render(tenItems)
    var patches = diff(tenItems, fiveItems)

    // 5 remove patches only
    assert.equal(patchCount(patches), 5)

    rootNode = patch(rootNode, patches)

    for (var i = 0; i < 5; i++) {
        itemHelpers.expectTextOfChild(assert, rootNode, i, (i * 2).toString())
    }

    assert.end()
})

test('3 elements in a container, add 3 elements after each', function (assert) {
    var first = itemHelpers.itemsInContainer().from(0).to(11).by(4)
    var second = itemHelpers.itemsInContainer().from(0).to(11).by(1)

    // Assert indices before
    assert.strictEqual(first.children.length, 3)

    var rootNode = render(first)

    for (var i = 0; i < 3; i++) {
        itemHelpers.expectTextOfChild(assert, rootNode, i, (4*i).toString())
    }

    // Assert indices after
    assert.strictEqual(second.children.length, 12)

    var newRoot = patch(rootNode, diff(first, second))

    for (var j = 0; j < 12; j++) {
        itemHelpers.expectTextOfChild(assert, newRoot, j, j.toString())
    }

    assert.end()
})

test('10 in container, add 1 after every 2nd element', function (assert) {
    function skipEveryThird(i) {
        return i % 3 === 0 || i % 3 === 1
    }

    var first = itemHelpers
        .itemsInContainer()
        .from(0)
        .to(14)
        .withPredicate(skipEveryThird)

    var second = itemHelpers.itemsInContainer().from(0).to(14).by(1)

    // Assert indices before
    assert.strictEqual(first.children.length, 10)

    var rootNode = render(first)
    var expectedIndices = [0, 1, 3, 4, 6, 7, 9, 10, 12, 13]

    for (var i = 0; i < 10; i++) {
        itemHelpers.expectTextOfChild(
            assert, rootNode, i, expectedIndices[i].toString()
        )
    }

    // Assert indices after
    assert.strictEqual(second.children.length, 15)

    var patches = diff(first, second)

    var newRoot = patch(rootNode, patches)

    for (var j = 0; j < 15; j++) {
        itemHelpers.expectTextOfChild(assert, newRoot, j, j.toString())
    }

    assert.end()
})

test('move a single element to the end', function (assert) {
    var start = nodesFromArray([0, 5, 1, 2, 3, 4])
    var end = nodesFromArray([0, 1, 2, 3, 4, 5])

    var patches = diff(start, end)

    assertReorderEquals(assert, patches, {
        removes: [{key: '5', from: 1}],
        inserts: [{key: '5', to: 5}]
    })
    assert.end()
})

test('move a single element to a later position', function (assert) {
    var start = nodesFromArray([0, 4, 1, 2, 3, 5])
    var end = nodesFromArray([0, 1, 2, 3, 4, 5])

    var patches = diff(start, end)

    assertReorderEquals(assert, patches, {
        removes: [{ key: '4', from: 1 }],
        inserts: [{ key: '4', to: 4 }]
    })
    assert.end()
})

test('remove a single element from early in the list', function (assert) {
    var start = nodesFromArray([0, 1, 2, 3, 4])
    var end = nodesFromArray([0, 2, 3, 4])

    var patches = diff(start, end)

    var reorderPatch = getReorderPatch(patches)
    assert.strictEqual(reorderPatch, null)
    assert.end()
})

test('move an element to a position after a removed element', function (assert) {
    var start = nodesFromArray([0, 1, 2, 3, 4, 5])
    var end = nodesFromArray([0, 2, 3, 5, 4])

    var patches = diff(start, end)

    assertReorderEquals(assert, patches, {
        removes: [
            {from: 1, key: null},
            {from: 4, key: '5'}
        ],
        inserts: [{to: 3, key: '5'}]
    })
    assert.end()
})

test('mixed keys move from i>0 to i<length-1', function (assert) {
    var start = nodesFromArray([undefined, undefined, 'key', undefined, undefined, undefined])
    var end = nodesFromArray([undefined, undefined, undefined, undefined, 'key', undefined])

    var patches = diff(start, end)

    assertReorderEquals(assert, patches, {
        removes: [{from: 2, key: 'key'}],
        inserts: [{to: 4, key: 'key'}]
    })
    assert.end()
})

/*
keyTest(
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42],
    [41, 3, 34, 36, 1, 40, 39, 7, 37, 14, 23, 26, 15, 6, 25, 24, 19, 8, 9, 22, 29, 27, 38, 35, 11, 20, 33, 31, 17, 32, 4, 28, 12, 2, 10, 0, 42, 21, 5, 16, 30, 18, 13]
)
*/

keyTest(
    [,8289652839303017,21512342290952802,6586509908083826,,7,8,9,2,5,6,1,,4,2379920135717839,26182894385419786,],
    [,1,2,3,4,5,6,7,8,9,]
)

automateTests({
    numTests: 1000,
    minLength: 5,
    maxLength: 12,
    minMutations: 15,
    maxMutations: 20,
    keyRatio: 0.8,
    mutations: [addKeyed, addNonKeyed, remove, swap, reverse, move]
})

function addKeyed(list) {
    list.push(('' + Math.random()).substring(2))

    return list
}

function addNonKeyed(list) {
    list.push(undefined)
    return list
}

function remove(list) {
    var index = Math.floor(Math.random() * list.length)

    list.splice(index, 1)

    return list
}

function swap(list) {
    var index1 = Math.floor(Math.random() * list.length)
    var index2 = Math.floor(Math.random() * list.length)

    var item1 = list[index1]
    list[index1] = list[index2]
    list[index2] = item1

    return list
}

function reverse(list) {
    return list.reverse()
}

function move(list) {
    var from = Math.floor(Math.random() * list.length)
    var to = Math.floor(Math.random() * list.length)

    list.splice(to, 0, list.splice(from, 1)[0])

    return list
}

function automateTests(options) {
    options = options || {}

    var numTests = options.numTests || 10
    var minLength = options.minLength || 5
    var maxLength = options.maxLength || 10
    var minMutations = options.minMutations || 3
    var maxMutations = options.maxMutations || 5
    var keyRatio = options.keyRatio || 1
    var mutations = options.mutations || []

    while (numTests--) {
        var length = Math.ceil(Math.random() * (maxLength - minLength)) + minLength
        var count = 0
        var end = []

        do {
            var isKeyed = Math.random() < keyRatio
            end[count] = isKeyed ? count : undefined
        }
        while (++count < length)

        var start = end.slice()

        if (mutations.length) {
            var numMutations = Math.ceil(Math.random() * (maxMutations - minMutations)) + minMutations
            while (numMutations--) {
                var mutation = mutations[Math.floor(Math.random() * mutations.length)]
                start = mutation(start)
            }
        }

        keyTest(start, end)
    }
}

function keyTest(itemsA, itemsB) {
    test(
        'keyTest([' + itemsA.join() + '], [' + itemsB.join() + '])',
        assertKeys
    )

    function assertKeys(assert) {
        var nodesA = nodesFromArray(itemsA)
        var nodesB = nodesFromArray(itemsB)

        var patches = diff(nodesA, nodesB)

        var rootNode = render(nodesA)
        patch(rootNode, patches)

        var childNodes = rootNode.childNodes

        assertChildNodesFromArray(assert, itemsB, childNodes)


        assert.end()
    }
}

function childNodesArray(node) {
    var childNodes = []
    for (var i = 0; i < node.childNodes.length; i++) {
        childNodes.push(node.childNodes[i])
    }
    return childNodes
}

function getReorderPatch(patches) {
    for (var key in patches) {
        if (key !== "a" && patches.hasOwnProperty(key)) {
            var patch = patches[key]
            if (patch.type === VPatch.ORDER) {
                return patch
            }
        }
    }

    return null
}

function assertReorderEquals(assert, patches, expected) {
    var reorderPatch = getReorderPatch(patches)

    assert.deepEqual(reorderPatch.patch, expected)
}


================================================
FILE: test/lib/assert-childNodes-from-array.js
================================================
module.exports = assertChildNodesFromArray;

function assertChildNodesFromArray(assert, items, childNodes) {
    // ensure that the output has the same number of nodes as required
    assert.equal(childNodes.length, items.length)

    for (var i = 0; i < items.length; i++) {
        var key = items[i]
        assert.equal(childNodes[i].id, key != null ? String(key) : 'no-key-' + i)
    }
}


================================================
FILE: test/lib/assert-equal-dom.js
================================================
module.exports = assertEqualDom

function assertEqualDom(assert, a, b) {
    assert.ok(areEqual(a, b) && areEqual(b, a), "Dom structures are equal")
}

function areEqual(a, b) {
    for (var key in a) {
        if (key !== "parentNode" &&
            key !== "parentElement" &&
            key !== "defaultView" &&
            key !== "ownerElement" &&
            key !== "nextElementSibling" &&
            key !== "nextSibling" &&
            key !== "previousElementSibling" &&
            key !== "previousSibling" &&
            key !== "document" &&
            key !== "window" &&
            key !== "frames" &&
            key !== "top" &&
            key !== "parent" &&
            key !== "self" &&
            key !== "outerHTML" &&
            key !== "innerHTML" &&
            key !== "spellcheck" &&
            key !== "bind" &&
            "" + parseInt(key, 10) !== key
        ) {
            if (key === "ownerDocument") {
                return a[key] === b[key]
            }
            if (key === "style") {
                return equalStyle(a[key], b[key])
            }
            if (typeof a === "object" || typeof a === "function") {
                if (!areEqual(a[key], b[key])) {
                    return false
                }
            } else {
                if (a !== b) {
                    return false
                }
            }
        }
    }

    return true
}

// CssStyleDeclaration indexes the styles, which could be out of order
// This is a left sided check. Note that we call equal(a, b) and equal(b, a)
function equalStyle(a, b) {
    var keys = []
    for (var key in a) {
        if ("" + parseInt(key, 10) === key) {
            continue
        } else {
            keys.push(key)
        }
    }

    keys.sort()

    for (var i = 0; i < keys.length; i++) {
        if (a[key] !== b[key]) {
            return false
        }
    }

    return true
}


================================================
FILE: test/lib/nodes-from-array.js
================================================
var h = require("../../h.js")

module.exports = nodesFromArray

function nodesFromArray(array) {
    var i =0
    var children = []
    var key
    var properties

    for(; i < array.length; i++) {
        key = array[i]

        if (key != null) {
            properties = {
                key: key,
                id: String(key)
            }
        }
        else {
            properties = {
                id: 'no-key-' + i
            }
        }

        children.push(h('div', properties, properties.id))
    }

    return h('div', children)
}


================================================
FILE: test/lib/patch-count.js
================================================
module.exports = patchCount

function patchCount(patch) {
    var count = 0

    for (var key in patch) {
        if (key !== "a" && patch.hasOwnProperty(key)) {
            count++
        }
    }

    return count
}


================================================
FILE: test/main.js
================================================
var test = require("tape")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")
var Node = require("../vnode/vnode")
var TextNode = require("../vnode/vtext")
var version = require("../vnode/version")
var assertEqualDom = require("./lib/assert-equal-dom.js")
var patchCount = require("./lib/patch-count.js")



// VirtualNode tests
test("Node is a function", function (assert) {
    assert.equal(typeof Node, "function")
    assert.end()
})

test("Node type and version are set", function (assert) {
    assert.equal(Node.prototype.type, "VirtualNode")
    assert.deepEqual(Node.prototype.version, version)
    assert.end()
})

test("TextNode is a function", function (assert) {
    assert.equal(typeof TextNode, "function")
    assert.end()
})

test("TextNode type and version are set", function (assert) {
    assert.equal(TextNode.prototype.type, "VirtualText")
    assert.deepEqual(TextNode.prototype.version, version)
    assert.end()
})

// h tests

test("h is a function", function (assert) {
    assert.equal(typeof h, "function")
    assert.end()
})

test("defaults to div node", function (assert) {
    var node = h()
    assertNode(assert, node, "DIV")
    assert.end()
})

test("can use class selector", function (assert) {
    var node = h("div.pretty")
    assertNode(assert, node, "DIV", { className: "pretty" })
    assert.end()
})

test("can use non-ascii class selector", function (assert) {
    var node = h("div.ΑΒΓΔΕΖ")
    assertNode(assert, node, "DIV", { className: "ΑΒΓΔΕΖ" })
    assert.end()
})

test("class selectors combine with className property", function (assert) {
    var node = h("div.very", { className: "pretty" })
    assertNode(assert, node, "DIV", { className: "very pretty" })
    assert.end()
})

test("can use id selector", function (assert) {
    var node = h("div#important")
    assertNode(assert, node, "DIV", { id: "important" })
    assert.end()
})

test("can use non-ascii id selector", function (assert) {
    var node = h("div#ΑΒΓΔΕΖ")
    assertNode(assert, node, "DIV", { id: "ΑΒΓΔΕΖ" })
    assert.end()
})

test("properties id overrides selector id", function (assert) {
    var node = h("div#very", { id: "important" })
    assertNode(assert, node, "DIV", { id: "important" })
    assert.end()
})

test("defaults to div when using selectors", function (assert) {
    var node1 = h("#important")
    var node2 = h(".pretty")
    var node3 = h("#important.pretty")
    var node4 = h(".pretty#important")

    assertNode(assert, node1, "DIV", { id: "important" })
    assertNode(assert, node2, "DIV", { className: "pretty" })
    assertNode(assert, node3, "DIV", { id: "important", className: "pretty" })
    assertNode(assert, node4, "DIV", { id: "important", className: "pretty" })
    assert.end()
})

test("second argument can be children", function (assert) {
    var node1 = h("#important.pretty", "test")
    var node2 = h("#important.pretty", ["test"])
    var node3 = h("#important.pretty", h("p", "testing"))
    var node4 = h("#important.pretty", [h("p", "testing")])

    var props = { id: "important", className: "pretty" }

    assertNode(assert, node1, "DIV", props, ["test"])
    assertNode(assert, node2, "DIV", props, ["test"])
    assertNode(assert, node3, "DIV", props, [["P", {}, ["testing"]]])
    assertNode(assert, node4, "DIV", props, [["P", {}, ["testing"]]])
    assert.end()
})

test("third argument can be child or children", function (assert) {
    var node1 = h("#important.pretty", { a: "b" }, "test")
    var node2 = h("#important.pretty", { a: "b" }, ["test"])
    var node3 = h("#important.pretty", { a: "b" }, h("p", "testing"))
    var node4 = h("#important.pretty", { a: "b" }, [h("p", "testing")])

    var props = { a: "b", id: "important", className: "pretty" }

    assertNode(assert, node1, "DIV", props, ["test"])
    assertNode(assert, node2, "DIV", props, ["test"])
    assertNode(assert, node3, "DIV", props, [["P", {}, ["testing"]]])
    assertNode(assert, node4, "DIV", props, [["P", {}, ["testing"]]])
    assert.end()
})

function assertNode(assert, node, tagName, properties, children) {
    properties = properties || {}
    children = children || []

    assert.ok(node instanceof Node, "node is a VirtualNode")
    assert.equal(node.tagName, tagName, "tag names are equal")
    assert.deepEqual(node.properties, properties, "propeties are equal")
    assert.equal(node.children.length, children.length, "child count equal")
    for (var i = 0; i < children.length; i++) {
        var child = children[i]

        if (typeof child === "string") {
            assert.equal(node.children[i].text, child)
        } else {
            assertNode(assert,
                node.children[i],
                child[0],
                child[1],
                child[2])
        }
    }
}



// render tests
test("render is a function", function (assert) {
    assert.equal(typeof h, "function")
    assert.end()
})

test("render text node", function (assert) {
    var vdom = h("span", "hello")
    var dom = render(vdom)
    assert.equal(dom.tagName, "SPAN")
    assert.notOk(dom.id)
    assert.notOk(dom.className)
    assert.equal(dom.childNodes.length, 1)
    assert.equal(dom.childNodes[0].data, "hello")
    assert.end()
})

test("render div", function (assert) {
    var vdom = h()
    var dom = render(vdom)
    assert.notOk(dom.id)
    assert.notOk(dom.className)
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("node id is applied correctly", function (assert) {
    var vdom = h("#important")
    var dom = render(vdom)
    assert.equal(dom.id, "important")
    assert.notOk(dom.className)
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("node class name is applied correctly", function (assert) {
    var vdom = h(".pretty")
    var dom = render(vdom)
    assert.notOk(dom.id)
    assert.equal(dom.className, "pretty")
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("mixture of node/classname applied correctly", function (assert) {
    var vdom = h("#override.very", { id: "important", className: "pretty"})
    var dom = render(vdom)
    assert.equal(dom.id, "important")
    assert.equal(dom.className, "very pretty")
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("style object is applied correctly", function (assert) {
    var vdom = h("#important.pretty", { style: {
        border: "1px solid rgb(0, 0, 0)",
        padding: "2px"
    } })
    var dom = render(vdom)
    assert.equal(dom.id, "important")
    assert.equal(dom.className, "pretty")
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.style.border, style("border", "1px solid rgb(0, 0, 0)"))
    assert.equal(dom.style.padding, style("padding", "2px"))
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("children are added", function (assert) {
    var vdom = h("div", [
        h("div", [
            "just testing",
            "multiple",
            h("b", "nodes")
        ]),
        "hello",
        h("span", "test")
    ])

    var dom = render(vdom)

    assert.equal(dom.childNodes.length, 3)

    var nodes = dom.childNodes
    assert.equal(nodes.length, 3)
    assert.equal(nodes[0].tagName, "DIV")
    assert.equal(nodes[1].data, "hello")
    assert.equal(nodes[2].tagName, "SPAN")

    var subNodes0 = nodes[0].childNodes
    assert.equal(subNodes0.length, 3)
    assert.equal(subNodes0[0].data, "just testing")
    assert.equal(subNodes0[1].data, "multiple")
    assert.equal(subNodes0[2].tagName, "B")

    var subNodes0_2 = subNodes0[2].childNodes
    assert.equal(subNodes0_2.length, 1)
    assert.equal(subNodes0_2[0].data, "nodes")

    var subNodes2 = nodes[2].childNodes
    assert.equal(subNodes2.length, 1)
    assert.equal(subNodes2[0].data, "test")
    assert.end()
})

test("incompatible children are ignored", function (assert) {
    var vdom = h("#important.pretty", {
        style: {
            "cssText": "color: red;"
        }
    }, [
        null
    ])
    var dom = render(vdom)
    assert.equal(dom.id, "important")
    assert.equal(dom.className, "pretty")
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.style.cssText, style("cssText", "color: red;"))
    assert.equal(dom.childNodes.length, 0)
    assert.end()
})

test("injected document object is used", function (assert) {
    var vdom = h("div", "hello")
    var count = 0
    var doc = {
        createElement: function createElement(tagName) {
            assert.equal(tagName, "DIV")
            count++
            return { tagName: "DIV", appendChild: function (t) {
                assert.equal(t, "hello")
                count++
            } }
        },
        createTextNode: function createTextNode(text) {
            assert.equal(text, "hello")
            count++
            return text
        }
    }
    render(vdom, { document: doc })
    assert.equal(count, 3)
    assert.end()
})

test("injected warning is used", function (assert) {
    var badObject = {}
    var vdom = h("#important.pretty", {
        style: {
            cssText: "color: red;"
        }
    })

    vdom.children = [
        badObject, null
    ]

    var i = 0
    function warn(warning, node) {
        assert.equal(warning, "Item is not a valid virtual dom node")

        if (i === 0) {
            assert.equal(node, badObject)
        } else if (i === 1) {
            assert.equal(node, null)
        } else {
            assert.error("Too many warnings")
        }

        i++
    }

    var dom = render(vdom, { warn: warn })
    assert.equal(dom.id, "important")
    assert.equal(dom.className, "pretty")
    assert.equal(dom.tagName, "DIV")
    assert.equal(dom.style.cssText, style("cssText", "color: red;"))
    assert.equal(dom.childNodes.length, 0)
    assert.equal(i, 2)
    assert.end()
})

// Complete patch tests
test("textnode update test", function (assert) {
    var hello = h("div", "hello")
    var goodbye = h("div", "goodbye")
    var rootNode = render(hello)
    var equalNode = render(goodbye)
    var patches = diff(hello, goodbye)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("textnode replace test", function (assert) {
    var hello = h("div", "hello")
    var goodbye = h("div", [h("span", "goodbye")])
    var rootNode = render(hello)
    var equalNode = render(goodbye)
    var patches = diff(hello, goodbye)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("textnode insert test", function (assert) {
    var hello = h("div", "hello")
    var again = h("span", ["hello", "again"])
    var rootNode = render(hello)
    var equalNode = render(again)
    var patches = diff(hello, again)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("textnode remove", function (assert) {
    var again = h("span", ["hello", "again"])
    var hello = h("div", "hello")
    var rootNode = render(again)
    var equalNode = render(hello)
    var patches = diff(again, hello)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("dom node update test", function (assert) {
    var hello = h("div.hello", "hello")
    var goodbye = h("div.goodbye", "goodbye")
    var rootNode = render(hello)
    var equalNode = render(goodbye)
    var patches = diff(hello, goodbye)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("dom node replace test", function (assert) {
    var hello = h("div", "hello")
    var goodbye = h("span", "goodbye")
    var rootNode = render(hello)
    var equalNode = render(goodbye)
    var patches = diff(hello, goodbye)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("dom node insert", function (assert) {
    var hello = h("div", [h("span", "hello")])
    var again = h("div", [h("span", "hello"), h("span", "again")])
    var rootNode = render(hello)
    var equalNode = render(again)
    var patches = diff(hello, again)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})

test("dom node remove", function (assert) {
    var hello = h("div", [h("span", "hello")])
    var again = h("div", [h("span", "hello"), h("span", "again")])
    var rootNode = render(again)
    var equalNode = render(hello)
    var patches = diff(again, hello)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)
    assert.end()
})


test("reuse dom node without breaking", function (assert) {
    var hSpan = h("span", "hello")
    var hello = h("div", [hSpan, hSpan, hSpan])
    var goodbye = h("div", [h("span", "hello"), hSpan, h("span", "goodbye")])
    var rootNode = render(hello)
    var equalNode = render(goodbye)
    var patches = diff(hello, goodbye)
    var newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, equalNode)

    // Undo the rendering with new trees
    hello = h("div", [hSpan, hSpan, hSpan])
    goodbye = h("div", [h("span", "hello"), hSpan, h("span", "goodbye")])
    patches = diff(goodbye, hello)
    newRoot = patch(rootNode, patches)
    assertEqualDom(assert, newRoot, rootNode)

    assert.end()
})

test("Allow empty textnode", function (assert) {
    var empty = h("span", "")
    var rootNode = render(empty)
    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].data, "")
    assert.end()
})

test("Can replace vnode with vtext", function (assert) {

    var leftNode = h("div", h("div"))
    var rightNode = h("div", "text")

    var rootNode = render(leftNode)

    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].nodeType, 1)

    var patches = diff(leftNode, rightNode)

    var newRoot = patch(rootNode, patches)

    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 1)
    assert.equal(newRoot.childNodes[0].nodeType, 3)

    assert.end()
})

// Widget tests
test("Widget is initialised on render", function (assert) {
    var initCount = 0
    var testNode = render(h("div"))
    var Widget = {
        init: function () {
            initCount++
            return testNode
        },
        update: function () {
            initCount = 1000000
        },
        type: "Widget"
    }

    var result = render(Widget)

    assert.equal(initCount, 1)
    assert.equal(result, testNode)
    assert.end()
})

test("Nested widget is initialised on render", function (assert) {
    var initCount = 0
    var testNode = render(h("div"))
    var Widget = {
        init: function () {
            initCount++
            return testNode
        },
        update: function () {
            initCount = 1000000
        },
        type: "Widget"
    }

    var vdom = h("div", [
        h("span", "text"),
        h("div.widgetContainer", [
            Widget
        ]),
        h("p", "more text")
    ])

    var result = render(vdom)

    assert.equal(initCount, 1)
    assert.equal(result.childNodes[1].childNodes[0], testNode)
    assert.end()
})

test("Patch widgets at the root", function (assert) {
    var initCount = 0
    var updateCount = 0
    var leftState = { a: 1 }
    var rightState = { a: 2 }
    var domNode

    function Widget(state) {
        this.state = state
        this.vdom = this.render(state)
    }

    Widget.prototype.init = function () {
        initCount++
        return render(this.vdom)
    }

    Widget.prototype.update = function (leftNode, dom) {
        updateCount++
        assert.equal(this.state, rightState)
        assert.equal(leftNode.state, leftState)
        assert.equal(dom, domNode)
        patch(dom, diff(leftNode.vdom, this.vdom))
    }

    Widget.prototype.render = function (state) {
        return h("div", "" + state.a)
    }

    Widget.prototype.type = "Widget"

    var leftTree = new Widget(leftState)
    var rightTree = new Widget(rightState)
    domNode = render(leftTree)
    assert.equal(initCount, 1, "initCount after left render")
    assert.equal(updateCount, 0, "updateCount after left render")

    var patches = diff(leftTree, rightTree)
    assert.equal(patchCount(patches), 1)
    assert.equal(initCount, 1, "initCount after diff")
    assert.equal(updateCount, 0, "updateCount after diff")

    var newRoot = patch(domNode, patches)
    assert.equal(initCount, 1, "initCount after patch")
    assert.equal(updateCount, 1, "updateCount after patch")

    // The patch should only update sibling value in this use case
    var expectedNode = render(rightTree)
    assert.equal(newRoot, domNode)
    assertEqualDom(assert, newRoot, expectedNode)
    assert.end()
})

test("Patch nested widgets", function (assert) {
    var initCount = 0
    var updateCount = 0
    var leftState = { a: 1 }
    var rightState = { a: 2 }
    var domNode

    function Widget(state) {
        this.state = state
        this.vdom = this.render(state)
    }

    Widget.prototype.init = function () {
        initCount++
        return render(this.vdom)
    }

    Widget.prototype.update = function (leftNode, dom) {
        updateCount++
        assert.equal(this.state, rightState)
        assert.equal(leftNode.state, leftState)
        assert.equal(dom, domNode.childNodes[1].childNodes[0])
        patch(dom, diff(leftNode.vdom, this.vdom))
    }

    Widget.prototype.render = function (state) {
        return h("div", "" + state.a)
    }

    Widget.prototype.type = "Widget"

    var leftWidget = new Widget(leftState)
    var rightWidget = new Widget(rightState)

    var leftTree = h("div", [
        h("span", "text"),
        h("div.widgetContainer", [
            leftWidget
        ]),
        h("p", "more text")
    ])

    var rightTree = h("div", [
        h("span", "text"),
        h("div.widgetContainer", [
            rightWidget
        ]),
        h("p", "more text")
    ])

    domNode = render(leftTree)
    assert.equal(initCount, 1, "initCount after left render")
    assert.equal(updateCount, 0, "updateCount after left render")

    var patches = diff(leftTree, rightTree)
    assert.equal(patchCount(patches), 1)
    assert.equal(initCount, 1, "initCount after diff")
    assert.equal(updateCount, 0, "updateCount after diff")

    var newRoot = patch(domNode, patches)
    assert.equal(initCount, 1, "initCount after patch")
    assert.equal(updateCount, 1, "updateCount after patch")

    // The patch should only update sibling value in this use case
    var expectedNode = render(rightTree)
    assert.equal(newRoot, domNode)
    assertEqualDom(assert, newRoot, expectedNode)
    assert.end()
})

test("Can replace stateful widget with vnode", function (assert) {
    var statefulWidget  = {
        init: function () {
            return render(h("div.widget"))
        },
        update: function () {},
        destroy: function () {},
        type: "Widget"
    }

    var leftNode = h("div", statefulWidget)
    var rightNode = h("div", h("div.vnode"))

    var rootNode = render(leftNode)

    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].className, 'widget')

    var patches = diff(leftNode, rightNode)

    var newRoot = patch(rootNode, patches)

    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 1)
    assert.equal(newRoot.childNodes[0].className, 'vnode')

    assert.end()
})

test("Can replace vnode with stateful widget with vnode", function (assert) {
    var statefulWidget  = {
        init: function () {
            return render(h("div.widget"))
        },
        update: function () {},
        destroy: function () {},
        type: "Widget"
    }

    var leftNode = h("div", h("div.vnode"))
    var rightNode = h("div", statefulWidget)

    var rootNode = render(leftNode)

    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].className, 'vnode')

    var patches = diff(leftNode, rightNode)

    var newRoot = patch(rootNode, patches)

    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 1)
    assert.equal(newRoot.childNodes[0].className, 'widget')

    assert.end()
})

test("Ensure children are not rendered more than once", function (assert) {
    var initCount = 0
    var updateCount = 0
    var rightState = { a: 1 }
    var domNode

    function Widget(state) {
        this.state = state
        this.vdom = this.render(state)
    }

    Widget.prototype.init = function () {
        initCount++
        return render(this.vdom)
    }

    Widget.prototype.update = function (leftNode, dom) {
        updateCount++
        patch(dom, diff(leftNode.vdom, this.vdom))
    }

    Widget.prototype.render = function (state) {
        return h("div", "" + state.a)
    }

    Widget.prototype.type = "Widget"

    var rightWidget = new Widget(rightState)

    var leftTree = h("div.container", [
        h("div")
    ])

    var rightTree = h("div.container", [
        h("section.widgetContainer", rightWidget)
    ])

    domNode = render(leftTree)
    assert.equal(initCount, 0, "initCount after left render")
    assert.equal(updateCount, 0, "updateCount after left render")

    var patches = diff(leftTree, rightTree)
    assert.equal(patchCount(patches), 1)
    assert.equal(initCount, 0, "initCount after diff")
    assert.equal(updateCount, 0, "updateCount after diff")

    var newRoot = patch(domNode, patches)
    assert.equal(initCount, 1, "initCount after patch")
    assert.equal(updateCount, 0, "updateCount after patch")

    // The patch should only update sibling value in this use case
    var expectedNode = render(rightTree)
    assert.equal(newRoot, domNode)
    assertEqualDom(assert, newRoot, expectedNode)
    assert.end()
})

test("VNode indicates stateful sibling", function (assert) {
    var statefulWidget  = {
        init: function () {},
        update: function () {},
        destroy: function () {},
        type: "Widget"
    }

    var pureWidget = {
        init: function () {},
        update: function () {},
        type: "Widget"
    }

    var stateful = h("div", [statefulWidget])
    var pure = h("div", [pureWidget])

    assert.ok(stateful.hasWidgets)
    assert.notOk(pure.hasWidgets)
    assert.end()
})

test("Replacing stateful widget with vnode calls destroy", function (assert) {
    var count = 0
    var statefulWidget  = {
        init: function () {},
        update: function () {},
        destroy: function () {
            count++
        },
        type: "Widget"
    }

    var rootNode = render(h("div"))
    patch(rootNode, diff(statefulWidget, h("div")))
    assert.equal(count, 1)
    assert.end()
})

test("Replacing stateful widget with stateful widget", function (assert) {
    var count = 0
    var statefulWidget  = {
        init: function () {},
        update: function () {},
        destroy: function () {
            count++
        },
        type: "Widget"
    }

    var newWidget = {
        init: function () {},
        update: function () {},
        destroy: function () {
            count = 10000000
        },
        type: "Widget"
    }

    var rootNode = render(h("div"))
    var patches = diff(statefulWidget, newWidget)
    patch(rootNode, patches)
    assert.equal(count, 1)
    assert.end()
})

test("Replacing stateful widget with pure widget", function (assert) {
    var count = 0
    var statefulWidget  = {
        init: function () {},
        update: function () {},
        destroy: function () {
            count++
        },
        type: "Widget"
    }

    var newWidget = {
        init: function () {},
        update: function () {},
        type: "Widget"
    }

    var rootNode = render(h("div"))
    patch(rootNode, diff(statefulWidget, newWidget))
    assert.equal(count, 1)
    assert.end()
})

test("Removing stateful widget calls destroy", function (assert) {
    var count = 0
    var statefulWidget  = {
        init: function () {},
        update: function () {},
        destroy: function () {
            count++
        },
        type: "Widget"
    }

    var rootNode = render(h("div"))
    patch(rootNode, diff(statefulWidget, null))
    assert.equal(count, 1)
    assert.end()
})

test("Patching parent destroys stateful sibling", function (assert) {
    var count = 0
    var widgetRoot = render(h("span"))
    var statefulWidget  = {
        init: function () {
            return widgetRoot
        },
        update: function () {
            assert.error()
        },
        destroy: function (domNode) {
            assert.equal(domNode, widgetRoot)
            count++
        },
        type: "Widget"
    }

    var deepTree = h("div", [
        "hello",
        h("span", "test"),
        h("div", [
            h("article", [statefulWidget])
        ]),
        h("div", [
            h("div", "test")
        ])
    ])

    var rootNode

    rootNode = render(deepTree)
    patch(rootNode, diff(deepTree, null))
    assert.equal(count, 1)

    rootNode = render(deepTree)
    patch(rootNode, diff(deepTree, h("span")))
    assert.equal(count, 2)

    rootNode = render(deepTree)
    patch(rootNode, diff(deepTree, h("div")))
    assert.equal(count, 3)

    assert.end()
})

test("Widget update can replace domNode", function (assert) {
    var widgetInit = render(h("span.init"))
    var widgetUpdate = render(h("span.update"))

    function Widget () {}
    Widget.prototype.init = function () {
        return widgetInit
    }
    Widget.prototype.update = function () {
        return widgetUpdate
    }
    Widget.prototype.destroy = function () {}
    Widget.prototype.type = "Widget"

    var initTree = h("div.init", [new Widget])
    var updateTree = h("div.update", [new Widget])
    var rootNode

    rootNode = render(initTree)
    assert.equal(rootNode.childNodes[0], widgetInit)

    patch(rootNode, diff(initTree, updateTree))

    assert.equal(rootNode.childNodes[0], widgetUpdate)
    assert.end()
})

test("Destroy widget nested in removed thunk", function (assert) {
    var count = 0
    var widgetRoot = render(h(".widget"))
    var statefulWidget  = {
        init: function () {
            return widgetRoot
        },
        update: function () {
            assert.error()
        },
        destroy: function (domNode) {
            assert.equal(domNode, widgetRoot)
            count++
        },
        type: "Widget"
    }
    var vnode = h(".wrapper", statefulWidget)

    function Thunk() {}

    Thunk.prototype.render = function () {
        return vnode
    }

    Thunk.prototype.type = "Thunk"

    var thunkTree = h(".page", [
        h(".section", [
            new Thunk()
        ])
    ])

    var empty = h(".empty")

    var rootNode = render(thunkTree)
    patch(rootNode, diff(thunkTree, empty))
    assert.equal(count, 1)

    assert.end()
})

test("Create element respects namespace", function (assert) {
    if (!supportsNamespace()) {
        assert.skip("browser doesn't support namespaces");
        return assert.end();
    }

    var svgURI = "http://www.w3.org/2000/svg"
    var vnode = new Node("svg", {}, [], null, svgURI)
    var node = render(vnode)

    assert.equal(node.tagName, "svg")
    assert.equal(node.namespaceURI, svgURI)
    assert.end()
})

test("Different namespaces creates a patch", function (assert) {
    if (!supportsNamespace()) {
        assert.skip("browser doesn't support namespaces");
        return assert.end();
    }

    var leftNode = new Node("div", {}, [], null, "testing")
    var rightNode = new Node("div", {}, [], null, "undefined")

    var rootNode = render(leftNode)
    assert.equal(rootNode.tagName, "div")
    assert.equal(rootNode.namespaceURI, "testing")

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    rootNode = patch(rootNode, patches)

    assert.equal(rootNode.tagName, "div")
    assert.equal(rootNode.namespaceURI, "undefined")

    assert.end()
})

// Safely translates style values using the DOM in the browser
function style(name, value) {
    var node = render(h())
    node.style[name] = value
    return node.style[name]
}

// Determine if namespace is supported by the DOM
function supportsNamespace() {
    var node = render(h())
    return 'namespaceURI' in node;
}



================================================
FILE: test/nested-properties.js
================================================
var test = require("tape")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")
var assertEqualDom = require("./lib/assert-equal-dom.js")

test("dom node style", function (assert) {
    var a = h("div", {
        style: {
            border: "none",
            className: "oops",
            display: "none"
        }
    })

    var b = h("div", {
        style: {
            border: "1px solid #000",
            className: "oops",
            display: ""
        }
    })

    var rootNode = render(a)
    assert.equal(rootNode.style.border, style("border", "none"))
    assert.equal(rootNode.style.className, style("className", "oops"))
    assert.equal(rootNode.style.display, style("display", "none"))
    var s1 = rootNode.style
    var equalNode = render(b)
    assert.equal(equalNode.style.border, style("border", "1px solid #000"))
    assert.equal(equalNode.style.className, style("className", "oops"))
    assert.equal(equalNode.style.display, style("display", ""))
    var newRoot = patch(rootNode, diff(a, b))
    var s2 = newRoot.style
    assertEqualDom(assert, newRoot, equalNode)
    assert.equal(newRoot.style.border, style("border", "1px solid #000"))
    assert.equal(newRoot.style.className, style("className", "oops"))
    assert.equal(newRoot.style.display, style("display", ""))
    assert.equal(s1, s2)
    assert.end()
})

test("dom node dataset", function (assert) {
    var a = h("div", { dataset: { foo: "bar", bar: "oops" } })
    var b = h("div", { dataset: { foo: "baz", bar: "oops" } })
    var rootNode = render(a)
    var d1 = rootNode.dataset
    assert.equal(rootNode.dataset.foo, "bar")
    assert.equal(rootNode.dataset.bar, "oops")
    var equalNode = render(b)
    var newRoot = patch(rootNode, diff(a, b))
    var d2 = newRoot.dataset
    assertEqualDom(assert, newRoot, equalNode)
    assert.equal(newRoot.dataset.foo, "baz")
    assert.equal(newRoot.dataset.bar, "oops")
    assert.equal(d1, d2)
    assert.end()
})

test("dom node attributes", function (assert) {
    var a = h("div", { attributes: { foo: "bar", bar: "oops" } })
    var b = h("div", { attributes: { foo: "baz", bar: "oops" } })
    var rootNode = render(a)
    var equalNode = render(b)

    var newRoot = patch(rootNode, diff(a, b))

    assertEqualDom(assert, newRoot, equalNode)
    assert.equal(newRoot.getAttribute("foo"), "baz")
    assert.equal(newRoot.getAttribute("bar"), "oops")
    assert.end()
})

test("patch nested properties in right only", function (assert) {
    var prev = h("div")
    var curr = h("div", { style: { display: "none" } })

    var elem = createAndPatch(prev, curr)

    assert.equal(elem.style.display, style("display", "none"))

    assert.end()
})

test("null properties", function (assert) {
    var prev = h("div", { propA: "bar", propC: {} })
    var curr = h("div", { propB: "apples" })

    var elem = createAndPatch(prev, curr)

    assert.equal(elem.propA, "")
    assert.equal(elem.propC, null)
    assert.equal(elem.propB, "apples")

    assert.end()
})

test("replace object with value", function (assert) {
    var prev = h("div", { propA: { foo: "bar" } })
    var curr = h("div", { propA: null })

    var elem = createAndPatch(prev, curr)

    assert.equal(elem.propA, null)
    assert.end()
})

test("create object on node for nested properties", function (assert) {
    var prev = h("div", { propA: null })
    var curr = h("div", { propA: { nested: true } })

    var elem = createAndPatch(prev, curr)

    assert.equal(elem.propA.nested, true)
    assert.end()
})

function createAndPatch(prev, curr) {
    var elem = render(prev)
    var patches = diff(prev, curr)
    return patch(elem, patches)
}

// Safely translates style values using the DOM in the browser
function style(name, value) {
    var node = render(h())
    node.style[name] = value
    return node.style[name]
}


================================================
FILE: test/non-string.js
================================================
var test = require("tape")
var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var createElement = require("../create-element.js")

test("coerce numbers to strings in children array", function (assert) {
    var leftNode = h("div", [ "clicked ", 1336, " times" ])
    var rightNode = h("div", [ "clicked ", 1337, " times" ])
    var rootNode = createElement(leftNode)
    var newRoot = patch(rootNode, diff(leftNode, rightNode))
    assert.strictEqual(
        newRoot.outerHTML || newRoot.toString(),
        '<div>clicked 1337 times</div>'
    )
    assert.end()
})


================================================
FILE: test/sort.js
================================================
//
// This tests the performance of vtree/diff.
//
// This test in it's current configuration will test every array permutation of
// N numbers from 0, 2, ... N  for N = 1 up to and including N = 9.
//
// Furthermore, arrays for N 10, 20, 30, ... 1000 are tested. Since the number
// of permutations is a factorial problem, we can't test all permutations for
// these. We generate 100 arrays instead and randomly shuffle them for testing.
//
// Each test is timed in a benchmark version of the test which removes the
// peripheral operations, and each test is also re-run against a validation
// version, which checks to ensure that the arrays are actually sorted.
//
// Essentially this test proves that the sorting algorithm for N 0-based
// consecutive integers works for ALL permutations up to length 9.
//

var PERMUTATION_START = 1;
var PERMUTATION_END = 9;

var SAMPLE_START = 10;
var SAMPLE_END = 1000;
var SAMPLE_COUNT = 100;
var SAMPLE_INTERVAL = 10;

var nodesFromArray = require('./lib/nodes-from-array.js');
var assertChildNodesFromArray = require('./lib/assert-childNodes-from-array.js');

var diff = require('../vtree/diff');
var render = require('../create-element.js');
var patch = require('../patch.js');

var assert = require('assert');

var document = require('global/document');

var testlingOutput = document.getElementById('__testling_output');
if (testlingOutput) {
    testlingOutput.parentNode.removeChild(testlingOutput);
}

runTest();
// validateOnly();
// benchmarkOnly();

function runTest() {
    forEachPermutation(PERMUTATION_START, PERMUTATION_END, testArrays);
    forEachSample(SAMPLE_START, SAMPLE_END, SAMPLE_COUNT, SAMPLE_INTERVAL, testArrays);
}

function testArrays(arrays) {
    runSort(arrays);
    runBench(arrays);
}

function validateOnly() {
    forEachPermutation(PERMUTATION_START, PERMUTATION_END, runSort);
    forEachSample(SAMPLE_START, SAMPLE_END, SAMPLE_COUNT, SAMPLE_INTERVAL, runSort);
}

function benchmarkOnly() {
    forEachPermutation(PERMUTATION_START, PERMUTATION_END, runBench);
    forEachSample(SAMPLE_START, SAMPLE_END, SAMPLE_COUNT, SAMPLE_INTERVAL, runBench);
}

function runBench(permutations) {
    var count = permutations.length;
    var arrayLength = permutations[0].goal.length;

    console.log('Benchmarking sort for length ', arrayLength);

    var startTime = Date.now();

    for (var i = 0; i < count; i++) {
        var item = permutations[i];
        var goal = nodesFromArray(item.goal);
        var shuffled = nodesFromArray(item.shuffled);

        var rootNode = render(shuffled);
        document.body.appendChild(rootNode);
        var reflow = rootNode.offsetWidth;
        var patches = diff(shuffled, goal);
        patch(rootNode, patches);
        reflow = rootNode.offsetWidth;
        document.body.removeChild(rootNode);
    }

    var totalTime = Date.now() - startTime;
    var average = totalTime / count >> 0

    console.log('All (' + count + ') arrays sorted in', totalTime, 'ms');
    console.log('An array of length', arrayLength, 'sorts in', average, 'ms');
}

function runSort(permutations) {
    var count = permutations.length;

    console.log('Testing sort for length ', permutations[0].goal.length);

    for (var i = 0; i < count; i++) {
        var item = permutations[i];
        var goal = nodesFromArray(item.goal);
        var shuffled = nodesFromArray(item.shuffled);

        var rootNode = render(shuffled);
        var patches = diff(shuffled, goal);
        patch(rootNode, patches);

        assertChildNodesFromArray(assert, item.goal, rootNode.childNodes);
    }

    console.log('All permutations sorted correctly');
}

function forEachPermutation(start, end, run) {
    for (var arrayLength = start; arrayLength <= end; arrayLength++) {
        var array = createArray(arrayLength);

        console.log('Generating test permutations for length', arrayLength);
        var permutations = permutator(array);

        run(permutations);
    }
}

function permutator(inputArr) {
    var results = [];

    function permute(arr, memo) {
        memo = memo || [];

        var cur;

        for (var i = 0; i < arr.length; i++) {
            cur = arr.splice(i, 1);
            if (arr.length === 0) {
                results.push({
                    goal: inputArr,
                    shuffled: memo.concat(cur)
                });
            }
            permute(arr.slice(), memo.concat(cur));
            arr.splice(i, 0, cur[0]);
        }

        return results;
    }

    return permute(inputArr);
}

function forEachSample(start, end, count, interval, run) {
    console.log(arguments);
    for (var i = start; i <= end; i += interval) {
        var samples = new Array(count);

        console.log("Generating", count, "sample arrays of length", i);

        for (var j = 0; j < count; j++) {
            var goal = createArray(i);
            samples[j] = {
                goal: goal,
                shuffled: shuffle(goal)
            };
        }

        run(samples);
    }
}

function createArray(arrayLength) {
    var array = new Array(arrayLength);

    for (var numberToAdd = 0; numberToAdd < arrayLength; numberToAdd++) {
        array[numberToAdd] = numberToAdd;
    }

    return array;
}

function shuffle(array) {
    var currentIndex = array.length;
    var temporaryValue;
    var randomIndex;

    while (0 !== currentIndex) {
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
    }

    return array;
}


================================================
FILE: test/style.js
================================================
var test = require("tape")
var document = require("global/document")

var h = require("../h")
var diff = require("../diff")
var patch = require("../patch")
var render = require("../create-element")

var patchCount = require("./lib/patch-count")


test("style patches correctly", function (assert) {
    var leftNode = h("div", {
        style: {
            border: "1px solid #000"
        }
    })

    var rightNode = h("div", {
        style: {
            padding: "5px"
        }
    })

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1);

    var rootNode = render(leftNode)
    assert.equal(rootNode.style.border, style("border", "1px solid #000"))

    var newRoot = patch(rootNode, patches)
    assert.equal(rootNode, newRoot)

    assert.equal(newRoot.style.padding, style("padding", "5px"))
    assert.equal(newRoot.style.border, style("border", ""))

    assert.end()
})

function style(name, setValue) {
    var div = document.createElement("div")
    div.style[name] = setValue
    return div.style[name]
}



================================================
FILE: test/thunk.js
================================================
var test = require("tape")

var isThunk = require("../vnode/is-thunk")
var isVNode = require("../vnode/is-vnode")
var VNode = require("../vnode/vnode")
var diff = require("../diff.js")

var patchCount = require("./lib/patch-count.js")

function Thunk(tagName) {
    this.tagName = tagName
}

Thunk.prototype.render = function () {
    return new VNode(this.tagName)
}

Thunk.prototype.type = "Thunk"

test("is thunk", function (assert) {
    var notThunk = {}
    var thunkLiteral = {
        type: "Thunk",
        render: function () {}
    }

    assert.notOk(isThunk(notThunk))
    assert.ok(isThunk(thunkLiteral))
    assert.ok(isThunk(new Thunk("div")))
    assert.end()
})

test("null or undefined previous renders thunk", function (assert) {
    var n = new Thunk("first")
    var u = new Thunk("second")
    var nullPatches = diff(null, n)
    var undefPatches = diff(undefined, u)

    assert.ok(isVNode(n.vnode))
    assert.ok(isVNode(u.vnode))
    assert.equal(n.vnode.tagName, "first")
    assert.equal(u.vnode.tagName, "second")
    assert.equal(patchCount(nullPatches), 1)
    assert.equal(patchCount(undefPatches), 1)
    assert.end()
})

test("previous thunk passed to render", function (assert) {
    var renderCount = 0

    var previousThunk = new Thunk("div")

    var nextThunk = {
        type: "Thunk",
        render: function (previous) {
            renderCount++
            assert.equal(previous, previousThunk)
            return new VNode("test")
        }
    }

    var patches = diff(previousThunk, nextThunk)

    assert.equal(renderCount, 1)
    assert.equal(patchCount(patches), 1)
    assert.ok(isVNode(nextThunk.vnode))
    assert.equal(nextThunk.vnode.tagName, "test")
    assert.end()
})


================================================
FILE: test/undefined-properties.js
================================================
var test = require("tape")
var isObject = require("is-object")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")

test("undefined props are not set in create-element", function (assert) {
    var node = h("div", { special: undefined })
    var rootNode = render(node)
    assert.ok(!("special" in rootNode))
    assert.end()
})

test("undefined removes all previous styles", function (assert) {
    var leftNode = h("div", {
        style: {
            display: "none",
            border: "1px solid #000"
        }
    })

    var rightNode = h("div", {
        style: undefined
    })

    var rootNode = createAndPatch(leftNode, rightNode)

    assert.equal(rootNode.style.display, style("display", ""))
    assert.equal(rootNode.style.border, style("border", ""))
    assert.end();
})

test("undefined style removes individual styles", function (assert) {
    var leftNode = h("div", { "style": { "display": "none" }})
    var rightNode = h("div", { "style": undefined })

    var rootNode = createAndPatch(leftNode, rightNode)

    assert.equal(rootNode.style.display, style("display", ""))
    assert.end()
})

test("undefined ignored for hooks", function (assert) {
    function CheckNodeBeforeSet(value) {
        this.value = value
    }
    CheckNodeBeforeSet.prototype.hook = function (rootNode, propName) {
        var value = this.value
        if (value !== rootNode[propName]) {
            rootNode[propName] = value
        }
    }

    var leftNode = h("input", { value: new CheckNodeBeforeSet("hello") })
    var rightNode = h("input", { value: undefined })

    var rootNode = render(leftNode)
    assert.equal(rootNode.value, "hello")

    var newRoot = patch(rootNode, diff(leftNode, rightNode))
    assert.equal(newRoot.value, "hello")

    assert.end()
})

test("undefined nulls other complex types", function (assert) {
    var leftNode = h("input", { special: {} })
    var rightNode = h("input", { special: null })

    var rootNode = render(leftNode)
    assert.ok(isObject(rootNode.special))


    var newRoot = patch(rootNode, diff(leftNode, rightNode))
    assert.equal(newRoot.special, null)

    assert.end()
})

test("null not ignored for value", function (assert) {
    var leftNode = h("input", { value: "hello" })
    var rightNode = h("input", { value: null })

    var rootNode = createAndPatch(leftNode, rightNode)

    assert.equal(rootNode.value, property("input", "value", null))
    assert.end()
})

test("null not ignored for objects", function (assert) {
    var leftNode = h("div", { "test": { "complex": "object" }})
    var rightNode = h("div", { "test": null })

    var rootNode = createAndPatch(leftNode, rightNode)

    assert.equal(rootNode.test, null)
    assert.end()
})

test("null not ignored for hooks", function (assert) {
    function CheckNodeBeforeSet(value) {
        this.value = value
    }
    CheckNodeBeforeSet.prototype.hook = function (rootNode, propName) {
        var value = this.value
        if (value !== rootNode[propName]) {
            rootNode.value = value
        }
    }

    var leftNode = h("input", { value: new CheckNodeBeforeSet("hello") })
    var rightNode = h("input", { value: null })

    var rootNode = render(leftNode)
    assert.equal(rootNode.value, "hello")

    var newRoot = patch(rootNode, diff(leftNode, rightNode))
    assert.equal(newRoot.value, property("input", "value", null))

    assert.end()
})

function createAndPatch(prev, curr) {
    var elem = render(prev)
    var patches = diff(prev, curr)
    return patch(elem, patches)
}

// Safely translates style values using the DOM in the browser
function style(name, value) {
    var node = render(h())
    node.style[name] = value
    return node.style[name]
}

// Safely transaltes node property using the DOM in the browser
function property(tag, prop, value) {
    var node = render(h(tag))
    node[prop] = value
    return node[prop]
}


================================================
FILE: vdom/README.md
================================================
# vdom

A DOM render and patch algorithm for vtree

## Motivation

Given a `vtree` structure representing a DOM structure, we would like to either
render the structure to a DOM node using `vdom/create-element` or we would like
to update the DOM using the results of `vtree/diff` by patching the DOM with
`vdom/patch`

## Example

```js
var h = require("virtual-dom/h")
var diff = require("virtual-dom/diff")

var createElement = require("virtual-dom/create-element")
var patch = require("virtual-dom/patch")

var leftNode = h("div")
var rightNode = h("text")

// Render the left node to a DOM node
var rootNode = createElement(leftNode)
document.body.appendChild(rootNode)

// Update the DOM with the results of a diff
var patches = diff(leftNode, rightNode)
patch(rootNode, patches)
```

## Installation

`npm install virtual-dom`

## Contributors

 - Matt Esch

## MIT Licenced


================================================
FILE: vdom/apply-properties.js
================================================
var isObject = require("is-object")
var isHook = require("../vnode/is-vhook.js")

module.exports = applyProperties

function applyProperties(node, props, previous) {
    for (var propName in props) {
        var propValue = props[propName]

        if (propValue === undefined) {
            removeProperty(node, propName, propValue, previous);
        } else if (isHook(propValue)) {
            removeProperty(node, propName, propValue, previous)
            if (propValue.hook) {
                propValue.hook(node,
                    propName,
                    previous ? previous[propName] : undefined)
            }
        } else {
            if (isObject(propValue)) {
                patchObject(node, props, previous, propName, propValue);
            } else {
                node[propName] = propValue
            }
        }
    }
}

function removeProperty(node, propName, propValue, previous) {
    if (previous) {
        var previousValue = previous[propName]

        if (!isHook(previousValue)) {
            if (propName === "attributes") {
                for (var attrName in previousValue) {
                    node.removeAttribute(attrName)
                }
            } else if (propName === "style") {
                for (var i in previousValue) {
                    node.style[i] = ""
                }
            } else if (typeof previousValue === "string") {
                node[propName] = ""
            } else {
                node[propName] = null
            }
        } else if (previousValue.unhook) {
            previousValue.unhook(node, propName, propValue)
        }
    }
}

function patchObject(node, props, previous, propName, propValue) {
    var previousValue = previous ? previous[propName] : undefined

    // Set attributes
    if (propName === "attributes") {
        for (var attrName in propValue) {
            var attrValue = propValue[attrName]

            if (attrValue === undefined) {
                node.removeAttribute(attrName)
            } else {
                node.setAttribute(attrName, attrValue)
            }
        }

        return
    }

    if(previousValue && isObject(previousValue) &&
        getPrototype(previousValue) !== getPrototype(propValue)) {
        node[propName] = propValue
        return
    }

    if (!isObject(node[propName])) {
        node[propName] = {}
    }

    var replacer = propName === "style" ? "" : undefined

    for (var k in propValue) {
        var value = propValue[k]
        node[propName][k] = (value === undefined) ? replacer : value
    }
}

function getPrototype(value) {
    if (Object.getPrototypeOf) {
        return Object.getPrototypeOf(value)
    } else if (value.__proto__) {
        return value.__proto__
    } else if (value.constructor) {
        return value.constructor.prototype
    }
}


================================================
FILE: vdom/create-element.js
================================================
var document = require("global/document")

var applyProperties = require("./apply-properties")

var isVNode = require("../vnode/is-vnode.js")
var isVText = require("../vnode/is-vtext.js")
var isWidget = require("../vnode/is-widget.js")
var handleThunk = require("../vnode/handle-thunk.js")

module.exports = createElement

function createElement(vnode, opts) {
    var doc = opts ? opts.document || document : document
    var warn = opts ? opts.warn : null

    vnode = handleThunk(vnode).a

    if (isWidget(vnode)) {
        return vnode.init()
    } else if (isVText(vnode)) {
        return doc.createTextNode(vnode.text)
    } else if (!isVNode(vnode)) {
        if (warn) {
            warn("Item is not a valid virtual dom node", vnode)
        }
        return null
    }

    var node = (vnode.namespace === null) ?
        doc.createElement(vnode.tagName) :
        doc.createElementNS(vnode.namespace, vnode.tagName)

    var props = vnode.properties
    applyProperties(node, props)

    var children = vnode.children

    for (var i = 0; i < children.length; i++) {
        var childNode = createElement(children[i], opts)
        if (childNode) {
            node.appendChild(childNode)
        }
    }

    return node
}


================================================
FILE: vdom/dom-index.js
================================================
// Maps a virtual DOM tree onto a real DOM tree in an efficient manner.
// We don't want to read all of the DOM nodes in the tree so we use
// the in-order tree indexing to eliminate recursion down certain branches.
// We only recurse into a DOM node if we know that it contains a child of
// interest.

var noChild = {}

module.exports = domIndex

function domIndex(rootNode, tree, indices, nodes) {
    if (!indices || indices.length === 0) {
        return {}
    } else {
        indices.sort(ascending)
        return recurse(rootNode, tree, indices, nodes, 0)
    }
}

function recurse(rootNode, tree, indices, nodes, rootIndex) {
    nodes = nodes || {}


    if (rootNode) {
        if (indexInRange(indices, rootIndex, rootIndex)) {
            nodes[rootIndex] = rootNode
        }

        var vChildren = tree.children

        if (vChildren) {

            var childNodes = rootNode.childNodes

            for (var i = 0; i < tree.children.length; i++) {
                rootIndex += 1

                var vChild = vChildren[i] || noChild
                var nextIndex = rootIndex + (vChild.count || 0)

                // skip recursion down the tree if there are no nodes down here
                if (indexInRange(indices, rootIndex, nextIndex)) {
                    recurse(childNodes[i], vChild, indices, nodes, rootIndex)
                }

                rootIndex = nextIndex
            }
        }
    }

    return nodes
}

// Binary search for an index in the interval [left, right]
function indexInRange(indices, left, right) {
    if (indices.length === 0) {
        return false
    }

    var minIndex = 0
    var maxIndex = indices.length - 1
    var currentIndex
    var currentItem

    while (minIndex <= maxIndex) {
        currentIndex = ((maxIndex + minIndex) / 2) >> 0
        currentItem = indices[currentIndex]

        if (minIndex === maxIndex) {
            return currentItem >= left && currentItem <= right
        } else if (currentItem < left) {
            minIndex = currentIndex + 1
        } else  if (currentItem > right) {
            maxIndex = currentIndex - 1
        } else {
            return true
        }
    }

    return false;
}

function ascending(a, b) {
    return a > b ? 1 : -1
}


================================================
FILE: vdom/patch-op.js
================================================
var applyProperties = require("./apply-properties")

var isWidget = require("../vnode/is-widget.js")
var VPatch = require("../vnode/vpatch.js")

var updateWidget = require("./update-widget")

module.exports = applyPatch

function applyPatch(vpatch, domNode, renderOptions) {
    var type = vpatch.type
    var vNode = vpatch.vNode
    var patch = vpatch.patch

    switch (type) {
        case VPatch.REMOVE:
            return removeNode(domNode, vNode)
        case VPatch.INSERT:
            return insertNode(domNode, patch, renderOptions)
        case VPatch.VTEXT:
            return stringPatch(domNode, vNode, patch, renderOptions)
        case VPatch.WIDGET:
            return widgetPatch(domNode, vNode, patch, renderOptions)
        case VPatch.VNODE:
            return vNodePatch(domNode, vNode, patch, renderOptions)
        case VPatch.ORDER:
            reorderChildren(domNode, patch)
            return domNode
        case VPatch.PROPS:
            applyProperties(domNode, patch, vNode.properties)
            return domNode
        case VPatch.THUNK:
            return replaceRoot(domNode,
                renderOptions.patch(domNode, patch, renderOptions))
        default:
            return domNode
    }
}

function removeNode(domNode, vNode) {
    var parentNode = domNode.parentNode

    if (parentNode) {
        parentNode.removeChild(domNode)
    }

    destroyWidget(domNode, vNode);

    return null
}

function insertNode(parentNode, vNode, renderOptions) {
    var newNode = renderOptions.render(vNode, renderOptions)

    if (parentNode) {
        parentNode.appendChild(newNode)
    }

    return parentNode
}

function stringPatch(domNode, leftVNode, vText, renderOptions) {
    var newNode

    if (domNode.nodeType === 3) {
        domNode.replaceData(0, domNode.length, vText.text)
        newNode = domNode
    } else {
        var parentNode = domNode.parentNode
        newNode = renderOptions.render(vText, renderOptions)

        if (parentNode && newNode !== domNode) {
            parentNode.replaceChild(newNode, domNode)
        }
    }

    return newNode
}

function widgetPatch(domNode, leftVNode, widget, renderOptions) {
    var updating = updateWidget(leftVNode, widget)
    var newNode

    if (updating) {
        newNode = widget.update(leftVNode, domNode) || domNode
    } else {
        newNode = renderOptions.render(widget, renderOptions)
    }

    var parentNode = domNode.parentNode

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    if (!updating) {
        destroyWidget(domNode, leftVNode)
    }

    return newNode
}

function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
    var parentNode = domNode.parentNode
    var newNode = renderOptions.render(vNode, renderOptions)

    if (parentNode && newNode !== domNode) {
        parentNode.replaceChild(newNode, domNode)
    }

    return newNode
}

function destroyWidget(domNode, w) {
    if (typeof w.destroy === "function" && isWidget(w)) {
        w.destroy(domNode)
    }
}

function reorderChildren(domNode, moves) {
    var childNodes = domNode.childNodes
    var keyMap = {}
    var node
    var remove
    var insert

    for (var i = 0; i < moves.removes.length; i++) {
        remove = moves.removes[i]
        node = childNodes[remove.from]
        if (remove.key) {
            keyMap[remove.key] = node
        }
        domNode.removeChild(node)
    }

    var length = childNodes.length
    for (var j = 0; j < moves.inserts.length; j++) {
        insert = moves.inserts[j]
        node = keyMap[insert.key]
        // this is the weirdest bug i've ever seen in webkit
        domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to])
    }
}

function replaceRoot(oldRoot, newRoot) {
    if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) {
        oldRoot.parentNode.replaceChild(newRoot, oldRoot)
    }

    return newRoot;
}


================================================
FILE: vdom/patch.js
================================================
var document = require("global/document")
var isArray = require("x-is-array")

var render = require("./create-element")
var domIndex = require("./dom-index")
var patchOp = require("./patch-op")
module.exports = patch

function patch(rootNode, patches, renderOptions) {
    renderOptions = renderOptions || {}
    renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch
        ? renderOptions.patch
        : patchRecursive
    renderOptions.render = renderOptions.render || render

    return renderOptions.patch(rootNode, patches, renderOptions)
}

function patchRecursive(rootNode, patches, renderOptions) {
    var indices = patchIndices(patches)

    if (indices.length === 0) {
        return rootNode
    }

    var index = domIndex(rootNode, patches.a, indices)
    var ownerDocument = rootNode.ownerDocument

    if (!renderOptions.document && ownerDocument !== document) {
        renderOptions.document = ownerDocument
    }

    for (var i = 0; i < indices.length; i++) {
        var nodeIndex = indices[i]
        rootNode = applyPatch(rootNode,
            index[nodeIndex],
            patches[nodeIndex],
            renderOptions)
    }

    return rootNode
}

function applyPatch(rootNode, domNode, patchList, renderOptions) {
    if (!domNode) {
        return rootNode
    }

    var newNode

    if (isArray(patchList)) {
        for (var i = 0; i < patchList.length; i++) {
            newNode = patchOp(patchList[i], domNode, renderOptions)

            if (domNode === rootNode) {
                rootNode = newNode
            }
        }
    } else {
        newNode = patchOp(patchList, domNode, renderOptions)

        if (domNode === rootNode) {
            rootNode = newNode
        }
    }

    return rootNode
}

function patchIndices(patches) {
    var indices = []

    for (var key in patches) {
        if (key !== "a") {
            indices.push(Number(key))
        }
    }

    return indices
}


================================================
FILE: vdom/test/dom-index.js
================================================
var test = require("tape")
var VNode = require("../../vnode/vnode")
var VText = require("../../vnode/vtext")
var diff = require("../../vtree/diff")

var createElement = require("../create-element")
var patch = require("../patch")

test("indexing over thunk root", function (assert) {
    var leftThunk = {
        type: "Thunk",
        render: function () {
            return new VNode("div", {
                className:"test"
            }, [new VText("Left")])
        }
    }

    var rightThunk = {
        type: "Thunk",
        render: function () {
            return new VNode("div", {
  
Download .txt
gitextract_pkovor08/

├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .zuul.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── create-element.js
├── diff.js
├── dist/
│   └── virtual-dom.js
├── docs/
│   ├── README.md
│   ├── css-animations.md
│   ├── faq.md
│   ├── hooks.md
│   ├── thunk.md
│   ├── vnode.md
│   ├── vtext.md
│   └── widget.md
├── docs.jsig
├── h.js
├── index.js
├── package.json
├── patch.js
├── test/
│   ├── attributes.js
│   ├── hook.js
│   ├── index.js
│   ├── keys.js
│   ├── lib/
│   │   ├── assert-childNodes-from-array.js
│   │   ├── assert-equal-dom.js
│   │   ├── nodes-from-array.js
│   │   └── patch-count.js
│   ├── main.js
│   ├── nested-properties.js
│   ├── non-string.js
│   ├── sort.js
│   ├── style.js
│   ├── thunk.js
│   └── undefined-properties.js
├── vdom/
│   ├── README.md
│   ├── apply-properties.js
│   ├── create-element.js
│   ├── dom-index.js
│   ├── patch-op.js
│   ├── patch.js
│   ├── test/
│   │   ├── dom-index.js
│   │   ├── index.js
│   │   ├── patch-index.js
│   │   └── patch-op-index.js
│   └── update-widget.js
├── virtual-hyperscript/
│   ├── README.md
│   ├── hooks/
│   │   ├── attribute-hook.js
│   │   ├── ev-hook.js
│   │   ├── focus-hook.js
│   │   └── soft-set-hook.js
│   ├── index.js
│   ├── parse-tag.js
│   ├── svg-attribute-namespace.js
│   ├── svg.js
│   └── test/
│       ├── attribute-hook.js
│       ├── ev-hook.js
│       ├── h.js
│       ├── index.js
│       └── svg.js
├── vnode/
│   ├── handle-thunk.js
│   ├── is-thunk.js
│   ├── is-vhook.js
│   ├── is-vnode.js
│   ├── is-vtext.js
│   ├── is-widget.js
│   ├── test/
│   │   ├── handle-thunk.js
│   │   └── index.js
│   ├── version.js
│   ├── vnode.js
│   ├── vpatch.js
│   └── vtext.js
└── vtree/
    ├── README.md
    ├── diff-props.js
    ├── diff.js
    └── test/
        ├── diff-props.js
        └── index.js
Download .txt
SYMBOL INDEX (198 symbols across 43 files)

FILE: dist/virtual-dom.js
  function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
  function EvStore (line 155) | function EvStore(elem) {
  function Individual (line 177) | function Individual(key, value) {
  function OneVersion (line 195) | function OneVersion(moduleName, version, defaultValue) {
  function isArray (line 244) | function isArray(obj) {
  function applyProperties (line 259) | function applyProperties(node, props, previous) {
  function removeProperty (line 282) | function removeProperty(node, propName, propValue, previous) {
  function patchObject (line 306) | function patchObject(node, props, previous, propName, propValue) {
  function getPrototype (line 342) | function getPrototype(value) {
  function createElement (line 364) | function createElement(vnode, opts) {
  function domIndex (line 411) | function domIndex(rootNode, tree, indices, nodes) {
  function recurse (line 420) | function recurse(rootNode, tree, indices, nodes, rootIndex) {
  function indexInRange (line 455) | function indexInRange(indices, left, right) {
  function ascending (line 483) | function ascending(a, b) {
  function applyPatch (line 497) | function applyPatch(vpatch, domNode, renderOptions) {
  function removeNode (line 527) | function removeNode(domNode, vNode) {
  function insertNode (line 539) | function insertNode(parentNode, vNode, renderOptions) {
  function stringPatch (line 549) | function stringPatch(domNode, leftVNode, vText, renderOptions) {
  function widgetPatch (line 567) | function widgetPatch(domNode, leftVNode, widget, renderOptions) {
  function vNodePatch (line 590) | function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
  function destroyWidget (line 601) | function destroyWidget(domNode, w) {
  function reorderChildren (line 607) | function reorderChildren(domNode, moves) {
  function replaceRoot (line 632) | function replaceRoot(oldRoot, newRoot) {
  function patch (line 649) | function patch(rootNode, patches, renderOptions) {
  function patchRecursive (line 659) | function patchRecursive(rootNode, patches, renderOptions) {
  function applyPatch (line 684) | function applyPatch(rootNode, domNode, patchList, renderOptions) {
  function patchIndices (line 710) | function patchIndices(patches) {
  function updateWidget (line 727) | function updateWidget(a, b) {
  function EvHook (line 746) | function EvHook(value) {
  function SoftSetHook (line 773) | function SoftSetHook(value) {
  function h (line 806) | function h(tagName, properties, children) {
  function addChild (line 850) | function addChild(c, childNodes, tag, props) {
  function transformProperties (line 874) | function transformProperties(props) {
  function isChild (line 891) | function isChild(x) {
  function isChildren (line 895) | function isChildren(x) {
  function UnexpectedVirtualElement (line 899) | function UnexpectedVirtualElement(data) {
  function errorString (line 918) | function errorString(obj) {
  function parseTag (line 936) | function parseTag(tag, props) {
  function handleThunk (line 990) | function handleThunk(a, b) {
  function renderThunk (line 1008) | function renderThunk(thunk, previous) {
  function isThunk (line 1027) | function isThunk(t) {
  function isHook (line 1034) | function isHook(hook) {
  function isVirtualNode (line 1045) | function isVirtualNode(x) {
  function isVirtualText (line 1054) | function isVirtualText(x) {
  function isWidget (line 1061) | function isWidget(w) {
  function VirtualNode (line 1080) | function VirtualNode(tagName, properties, children, key, namespace) {
  function VirtualPatch (line 1157) | function VirtualPatch(type, vNode, patch) {
  function VirtualText (line 1171) | function VirtualText(text) {
  function diffProps (line 1184) | function diffProps(a, b) {
  function getPrototype (line 1228) | function getPrototype(value) {
  function diff (line 1252) | function diff(a, b) {
  function walk (line 1258) | function walk(a, b, patch, index) {
  function diffChildren (line 1322) | function diffChildren(a, b, patch, apply, index) {
  function clearState (line 1363) | function clearState(vNode, patch, index) {
  function destroyWidgets (line 1371) | function destroyWidgets(vNode, patch, index) {
  function thunks (line 1398) | function thunks(a, b, patch, index) {
  function hasPatches (line 1406) | function hasPatches(patch) {
  function unhook (line 1417) | function unhook(vNode, patch, index) {
  function undefinedKeys (line 1449) | function undefinedKeys(obj) {
  function reorder (line 1460) | function reorder(aChildren, bChildren) {
  function remove (line 1623) | function remove(arr, index, key) {
  function keyIndex (line 1632) | function keyIndex(children) {
  function appendPatch (line 1653) | function appendPatch(apply, patch) {

FILE: test/hook.js
  function StatefulHook (line 11) | function StatefulHook() {}
  function StatelessHook (line 16) | function StatelessHook() {}
  function Prop (line 33) | function Prop() {}
  function Prop (line 53) | function Prop() {}
  function Hook (line 129) | function Hook(name) {
  function Hook (line 221) | function Hook(key) {
  function zhook (line 275) | function zhook(x) {
  function UnhookHook (line 310) | function UnhookHook() {}
  function Hook (line 336) | function Hook(value) {
  function Thunky (line 370) | function Thunky() {}
  function assertHooked (line 451) | function assertHooked() {
  function assertUnhooked (line 470) | function assertUnhooked() {
  function createAndPatch (line 490) | function createAndPatch(prev, curr) {
  function Type (line 498) | function Type(fn) {
  function hook (line 506) | function hook(fn) {

FILE: test/keys.js
  function DivWidget (line 188) | function DivWidget(key, state) {
  function FooWidget (line 409) | function FooWidget(foo) {
  function withPredicate (line 468) | function withPredicate(predicate) {
  function skipEveryThird (line 554) | function skipEveryThird(i) {
  function addKeyed (line 680) | function addKeyed(list) {
  function addNonKeyed (line 686) | function addNonKeyed(list) {
  function remove (line 691) | function remove(list) {
  function swap (line 699) | function swap(list) {
  function reverse (line 710) | function reverse(list) {
  function move (line 714) | function move(list) {
  function automateTests (line 723) | function automateTests(options) {
  function keyTest (line 759) | function keyTest(itemsA, itemsB) {
  function childNodesArray (line 783) | function childNodesArray(node) {
  function getReorderPatch (line 791) | function getReorderPatch(patches) {
  function assertReorderEquals (line 804) | function assertReorderEquals(assert, patches, expected) {

FILE: test/lib/assert-childNodes-from-array.js
  function assertChildNodesFromArray (line 3) | function assertChildNodesFromArray(assert, items, childNodes) {

FILE: test/lib/assert-equal-dom.js
  function assertEqualDom (line 3) | function assertEqualDom(assert, a, b) {
  function areEqual (line 7) | function areEqual(a, b) {
  function equalStyle (line 52) | function equalStyle(a, b) {

FILE: test/lib/nodes-from-array.js
  function nodesFromArray (line 5) | function nodesFromArray(array) {

FILE: test/lib/patch-count.js
  function patchCount (line 3) | function patchCount(patch) {

FILE: test/main.js
  function assertNode (line 130) | function assertNode(assert, node, tagName, properties, children) {
  function warn (line 317) | function warn(warning, node) {
  function Widget (line 539) | function Widget(state) {
  function Widget (line 592) | function Widget(state) {
  function Widget (line 721) | function Widget(state) {
  function Widget (line 927) | function Widget () {}
  function Thunk (line 968) | function Thunk() {}
  function style (line 1031) | function style(name, value) {
  function supportsNamespace (line 1038) | function supportsNamespace() {

FILE: test/nested-properties.js
  function createAndPatch (line 120) | function createAndPatch(prev, curr) {
  function style (line 127) | function style(name, value) {

FILE: test/sort.js
  function runTest (line 47) | function runTest() {
  function testArrays (line 52) | function testArrays(arrays) {
  function validateOnly (line 57) | function validateOnly() {
  function benchmarkOnly (line 62) | function benchmarkOnly() {
  function runBench (line 67) | function runBench(permutations) {
  function runSort (line 96) | function runSort(permutations) {
  function forEachPermutation (line 116) | function forEachPermutation(start, end, run) {
  function permutator (line 127) | function permutator(inputArr) {
  function forEachSample (line 153) | function forEachSample(start, end, count, interval, run) {
  function createArray (line 172) | function createArray(arrayLength) {
  function shuffle (line 182) | function shuffle(array) {

FILE: test/style.js
  function style (line 40) | function style(name, setValue) {

FILE: test/thunk.js
  function Thunk (line 10) | function Thunk(tagName) {

FILE: test/undefined-properties.js
  function CheckNodeBeforeSet (line 46) | function CheckNodeBeforeSet(value) {
  function CheckNodeBeforeSet (line 103) | function CheckNodeBeforeSet(value) {
  function createAndPatch (line 125) | function createAndPatch(prev, curr) {
  function style (line 132) | function style(name, value) {
  function property (line 139) | function property(tag, prop, value) {

FILE: vdom/apply-properties.js
  function applyProperties (line 6) | function applyProperties(node, props, previous) {
  function removeProperty (line 29) | function removeProperty(node, propName, propValue, previous) {
  function patchObject (line 53) | function patchObject(node, props, previous, propName, propValue) {
  function getPrototype (line 89) | function getPrototype(value) {

FILE: vdom/create-element.js
  function createElement (line 12) | function createElement(vnode, opts) {

FILE: vdom/dom-index.js
  function domIndex (line 11) | function domIndex(rootNode, tree, indices, nodes) {
  function recurse (line 20) | function recurse(rootNode, tree, indices, nodes, rootIndex) {
  function indexInRange (line 55) | function indexInRange(indices, left, right) {
  function ascending (line 83) | function ascending(a, b) {

FILE: vdom/patch-op.js
  function applyPatch (line 10) | function applyPatch(vpatch, domNode, renderOptions) {
  function removeNode (line 40) | function removeNode(domNode, vNode) {
  function insertNode (line 52) | function insertNode(parentNode, vNode, renderOptions) {
  function stringPatch (line 62) | function stringPatch(domNode, leftVNode, vText, renderOptions) {
  function widgetPatch (line 80) | function widgetPatch(domNode, leftVNode, widget, renderOptions) {
  function vNodePatch (line 103) | function vNodePatch(domNode, leftVNode, vNode, renderOptions) {
  function destroyWidget (line 114) | function destroyWidget(domNode, w) {
  function reorderChildren (line 120) | function reorderChildren(domNode, moves) {
  function replaceRoot (line 145) | function replaceRoot(oldRoot, newRoot) {

FILE: vdom/patch.js
  function patch (line 9) | function patch(rootNode, patches, renderOptions) {
  function patchRecursive (line 19) | function patchRecursive(rootNode, patches, renderOptions) {
  function applyPatch (line 44) | function applyPatch(rootNode, domNode, patchList, renderOptions) {
  function patchIndices (line 70) | function patchIndices(patches) {

FILE: vdom/test/patch-index.js
  function patchCustom (line 11) | function patchCustom(rootNode, patches, renderOptions) {
  function createElementCustom (line 18) | function createElementCustom(vnode) {}

FILE: vdom/test/patch-op-index.js
  function assertPachedNodeIsMarked (line 16) | function assertPachedNodeIsMarked(leftNode, rightNode, assert) {

FILE: vdom/update-widget.js
  function updateWidget (line 5) | function updateWidget(a, b) {

FILE: virtual-hyperscript/hooks/attribute-hook.js
  function AttributeHook (line 5) | function AttributeHook(namespace, value) {

FILE: virtual-hyperscript/hooks/ev-hook.js
  function EvHook (line 7) | function EvHook(value) {

FILE: virtual-hyperscript/hooks/focus-hook.js
  function MutableFocusHook (line 8) | function MutableFocusHook() {

FILE: virtual-hyperscript/hooks/soft-set-hook.js
  function SoftSetHook (line 5) | function SoftSetHook(value) {

FILE: virtual-hyperscript/index.js
  function h (line 19) | function h(tagName, properties, children) {
  function addChild (line 73) | function addChild(c, childNodes, tag, props) {
  function transformProperties (line 97) | function transformProperties(props) {
  function isChild (line 114) | function isChild(x) {
  function isChildren (line 118) | function isChildren(x) {
  function UnexpectedVirtualElement (line 122) | function UnexpectedVirtualElement(data) {
  function UnsupportedValueType (line 141) | function UnsupportedValueType(data) {
  function errorString (line 160) | function errorString(obj) {

FILE: virtual-hyperscript/parse-tag.js
  function parseTag (line 10) | function parseTag(tag, props) {

FILE: virtual-hyperscript/svg-attribute-namespace.js
  function SVGAttributeNamespace (line 309) | function SVGAttributeNamespace(value) {

FILE: virtual-hyperscript/svg.js
  function svg (line 15) | function svg(tagName, properties, children) {
  function isChildren (line 60) | function isChildren(x) {

FILE: virtual-hyperscript/test/attribute-hook.js
  function blankAttributeNS (line 129) | function blankAttributeNS() {

FILE: virtual-hyperscript/test/ev-hook.js
  function one (line 10) | function one() {}

FILE: virtual-hyperscript/test/svg.js
  function safeStyle (line 67) | function safeStyle(property, value) {

FILE: vnode/handle-thunk.js
  function handleThunk (line 8) | function handleThunk(a, b) {
  function renderThunk (line 26) | function renderThunk(thunk, previous) {

FILE: vnode/is-thunk.js
  function isThunk (line 3) | function isThunk(t) {

FILE: vnode/is-vhook.js
  function isHook (line 3) | function isHook(hook) {

FILE: vnode/is-vnode.js
  function isVirtualNode (line 5) | function isVirtualNode(x) {

FILE: vnode/is-vtext.js
  function isVirtualText (line 5) | function isVirtualText(x) {

FILE: vnode/is-widget.js
  function isWidget (line 3) | function isWidget(w) {

FILE: vnode/vnode.js
  function VirtualNode (line 12) | function VirtualNode(tagName, properties, children, key, namespace) {

FILE: vnode/vpatch.js
  function VirtualPatch (line 15) | function VirtualPatch(type, vNode, patch) {

FILE: vnode/vtext.js
  function VirtualText (line 5) | function VirtualText(text) {

FILE: vtree/diff-props.js
  function diffProps (line 6) | function diffProps(a, b) {
  function getPrototype (line 50) | function getPrototype(value) {

FILE: vtree/diff.js
  function diff (line 14) | function diff(a, b) {
  function walk (line 20) | function walk(a, b, patch, index) {
  function diffChildren (line 84) | function diffChildren(a, b, patch, apply, index) {
  function clearState (line 125) | function clearState(vNode, patch, index) {
  function destroyWidgets (line 133) | function destroyWidgets(vNode, patch, index) {
  function thunks (line 160) | function thunks(a, b, patch, index) {
  function hasPatches (line 168) | function hasPatches(patch) {
  function unhook (line 179) | function unhook(vNode, patch, index) {
  function undefinedKeys (line 211) | function undefinedKeys(obj) {
  function reorder (line 222) | function reorder(aChildren, bChildren) {
  function remove (line 385) | function remove(arr, index, key) {
  function keyIndex (line 394) | function keyIndex(children) {
  function appendPatch (line 415) | function appendPatch(apply, patch) {
Condensed preview — 81 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (272K chars).
[
  {
    "path": ".editorconfig",
    "chars": 207,
    "preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 4\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
  },
  {
    "path": ".gitignore",
    "chars": 164,
    "preview": ".idea/\n.DS_Store\n.monitor\n.*.swp\n.nodemonignore\nreleases\n*.log\n*.err\nfleet.json\npublic/browserify\nbin/*.json\n.bin\nbuild\n"
  },
  {
    "path": ".jshintrc",
    "chars": 866,
    "preview": "{\n    \"asi\": true,\n    \"bitwise\": false,\n    \"camelcase\": true,\n    \"curly\": false,\n    \"eqeqeq\": true,\n    \"eqnull\": tr"
  },
  {
    "path": ".travis.yml",
    "chars": 557,
    "preview": "language: node_js\nnode_js:\n- '0.10'\n- '0.12'\nbefore_install:\n- npm install npm -g\nbefore_script:\n- npm install\n- npm ins"
  },
  {
    "path": ".zuul.yml",
    "chars": 399,
    "preview": "ui: tape\nconcurrency: 1\nbrowsers:\n  - name: chrome\n    version: [26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, latest]\n  -"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1942,
    "preview": "# Release Notes\n\n## v2.0.0\n\n  Provides fundamental fixes and tests for reordering of keyed nodes.\n\n  Everyone is encoura"
  },
  {
    "path": "LICENSE",
    "chars": 1054,
    "preview": "Copyright (c) 2014 Matt-Esch.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this soft"
  },
  {
    "path": "README.md",
    "chars": 7722,
    "preview": "# virtual-dom\n\nA JavaScript [DOM model](#dom-model) supporting [element creation](#element-creation), [diff computation]"
  },
  {
    "path": "create-element.js",
    "chars": 88,
    "preview": "var createElement = require(\"./vdom/create-element.js\")\n\nmodule.exports = createElement\n"
  },
  {
    "path": "diff.js",
    "chars": 61,
    "preview": "var diff = require(\"./vtree/diff.js\")\n\nmodule.exports = diff\n"
  },
  {
    "path": "dist/virtual-dom.js",
    "chars": 46508,
    "preview": "!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();else if(\"function\"==typeof defin"
  },
  {
    "path": "docs/README.md",
    "chars": 1863,
    "preview": "# virtual-dom documentation\nThis documentation is aimed at people who would like to work with virtual-dom directly, or g"
  },
  {
    "path": "docs/css-animations.md",
    "chars": 2904,
    "preview": "# CSS animations\nBased on a discusison in [question](https://github.com/Matt-Esch/virtual-dom/issues/104#issuecomment-68"
  },
  {
    "path": "docs/faq.md",
    "chars": 3515,
    "preview": "# FAQ\n\nThese are frequently asked questions. If you have any questions\nnot on this list, then **please** [open an issue "
  },
  {
    "path": "docs/hooks.md",
    "chars": 1955,
    "preview": "# Hooks\nHooks are functions that execute after turning a VNode into an Element. They are set by passing a VNode any prop"
  },
  {
    "path": "docs/thunk.md",
    "chars": 5778,
    "preview": "# Thunk\nThunks allow the user to take control of the diff'ing process for a specific dom tree, usually to avoid doing ca"
  },
  {
    "path": "docs/vnode.md",
    "chars": 6265,
    "preview": "# VNode\n\nA VNode is a representation of a dom node. Most end users will probably have their VNodes generated through [vi"
  },
  {
    "path": "docs/vtext.md",
    "chars": 1494,
    "preview": "# VText\nVText is a representation of a Text node. \n\nIn virtual-dom, a VText is turned into an actual text node with the "
  },
  {
    "path": "docs/widget.md",
    "chars": 2903,
    "preview": "# Widget\nWidgets are used to take control of the patching process, allowing the user to create stateful components, cont"
  },
  {
    "path": "docs.jsig",
    "chars": 4636,
    "preview": "--  A VHook is an object with a hook and unhook method.\n--  A VHook can be used to define your own apply property logic."
  },
  {
    "path": "h.js",
    "chars": 70,
    "preview": "var h = require(\"./virtual-hyperscript/index.js\")\n\nmodule.exports = h\n"
  },
  {
    "path": "index.js",
    "chars": 352,
    "preview": "var diff = require(\"./diff.js\")\r\nvar patch = require(\"./patch.js\")\r\nvar h = require(\"./h.js\")\r\nvar create = require(\"./c"
  },
  {
    "path": "package.json",
    "chars": 2444,
    "preview": "{\n  \"name\": \"virtual-dom\",\n  \"version\": \"2.1.1\",\n  \"description\": \"A batched diff-based DOM rendering strategy\",\n  \"keyw"
  },
  {
    "path": "patch.js",
    "chars": 63,
    "preview": "var patch = require(\"./vdom/patch.js\")\n\nmodule.exports = patch\n"
  },
  {
    "path": "test/attributes.js",
    "chars": 1884,
    "preview": "var test = require(\"tape\")\r\n\r\nvar h = require(\"../h.js\")\r\nvar createElement = require(\"../create-element.js\")\r\nvar diff "
  },
  {
    "path": "test/hook.js",
    "chars": 13535,
    "preview": "var test = require(\"tape\")\n\nvar h = require(\"../h.js\")\nvar Node = require(\"../vnode/vnode.js\")\nvar create = require(\"../"
  },
  {
    "path": "test/index.js",
    "chars": 347,
    "preview": "require(\"./main.js\")\nrequire(\"./hook.js\")\nrequire(\"./nested-properties.js\")\nrequire(\"./undefined-properties.js\")\nrequire"
  },
  {
    "path": "test/keys.js",
    "chars": 22212,
    "preview": "var test = require(\"tape\")\n\nvar h = require(\"../h.js\")\nvar diff = require(\"../diff.js\")\nvar patch = require(\"../patch.js"
  },
  {
    "path": "test/lib/assert-childNodes-from-array.js",
    "chars": 393,
    "preview": "module.exports = assertChildNodesFromArray;\n\nfunction assertChildNodesFromArray(assert, items, childNodes) {\n    // ensu"
  },
  {
    "path": "test/lib/assert-equal-dom.js",
    "chars": 1922,
    "preview": "module.exports = assertEqualDom\n\nfunction assertEqualDom(assert, a, b) {\n    assert.ok(areEqual(a, b) && areEqual(b, a),"
  },
  {
    "path": "test/lib/nodes-from-array.js",
    "chars": 558,
    "preview": "var h = require(\"../../h.js\")\n\nmodule.exports = nodesFromArray\n\nfunction nodesFromArray(array) {\n    var i =0\n    var ch"
  },
  {
    "path": "test/lib/patch-count.js",
    "chars": 218,
    "preview": "module.exports = patchCount\n\nfunction patchCount(patch) {\n    var count = 0\n\n    for (var key in patch) {\n        if (ke"
  },
  {
    "path": "test/main.js",
    "chars": 28656,
    "preview": "var test = require(\"tape\")\n\nvar h = require(\"../h.js\")\nvar diff = require(\"../diff.js\")\nvar patch = require(\"../patch.js"
  },
  {
    "path": "test/nested-properties.js",
    "chars": 3938,
    "preview": "var test = require(\"tape\")\n\nvar h = require(\"../h.js\")\nvar diff = require(\"../diff.js\")\nvar patch = require(\"../patch.js"
  },
  {
    "path": "test/non-string.js",
    "chars": 611,
    "preview": "var test = require(\"tape\")\nvar h = require(\"../h.js\")\nvar diff = require(\"../diff.js\")\nvar patch = require(\"../patch.js\""
  },
  {
    "path": "test/sort.js",
    "chars": 5647,
    "preview": "//\n// This tests the performance of vtree/diff.\n//\n// This test in it's current configuration will test every array perm"
  },
  {
    "path": "test/style.js",
    "chars": 1103,
    "preview": "var test = require(\"tape\")\r\nvar document = require(\"global/document\")\r\n\r\nvar h = require(\"../h\")\r\nvar diff = require(\".."
  },
  {
    "path": "test/thunk.js",
    "chars": 1798,
    "preview": "var test = require(\"tape\")\r\n\r\nvar isThunk = require(\"../vnode/is-thunk\")\r\nvar isVNode = require(\"../vnode/is-vnode\")\r\nva"
  },
  {
    "path": "test/undefined-properties.js",
    "chars": 3995,
    "preview": "var test = require(\"tape\")\nvar isObject = require(\"is-object\")\n\nvar h = require(\"../h.js\")\nvar diff = require(\"../diff.j"
  },
  {
    "path": "vdom/README.md",
    "chars": 880,
    "preview": "# vdom\n\nA DOM render and patch algorithm for vtree\n\n## Motivation\n\nGiven a `vtree` structure representing a DOM structur"
  },
  {
    "path": "vdom/apply-properties.js",
    "chars": 2836,
    "preview": "var isObject = require(\"is-object\")\nvar isHook = require(\"../vnode/is-vhook.js\")\n\nmodule.exports = applyProperties\n\nfunc"
  },
  {
    "path": "vdom/create-element.js",
    "chars": 1237,
    "preview": "var document = require(\"global/document\")\n\nvar applyProperties = require(\"./apply-properties\")\n\nvar isVNode = require(\"."
  },
  {
    "path": "vdom/dom-index.js",
    "chars": 2253,
    "preview": "// Maps a virtual DOM tree onto a real DOM tree in an efficient manner.\n// We don't want to read all of the DOM nodes in"
  },
  {
    "path": "vdom/patch-op.js",
    "chars": 3972,
    "preview": "var applyProperties = require(\"./apply-properties\")\n\nvar isWidget = require(\"../vnode/is-widget.js\")\nvar VPatch = requir"
  },
  {
    "path": "vdom/patch.js",
    "chars": 1954,
    "preview": "var document = require(\"global/document\")\nvar isArray = require(\"x-is-array\")\n\nvar render = require(\"./create-element\")\n"
  },
  {
    "path": "vdom/test/dom-index.js",
    "chars": 2027,
    "preview": "var test = require(\"tape\")\r\nvar VNode = require(\"../../vnode/vnode\")\r\nvar VText = require(\"../../vnode/vtext\")\r\nvar diff"
  },
  {
    "path": "vdom/test/index.js",
    "chars": 76,
    "preview": "require(\"./dom-index\")\nrequire(\"./patch-index\")\nrequire(\"./patch-op-index\")\n"
  },
  {
    "path": "vdom/test/patch-index.js",
    "chars": 923,
    "preview": "var test = require(\"tape\")\nvar VNode = require(\"../../vnode/vnode\")\nvar VText = require(\"../../vnode/vtext\")\nvar diff = "
  },
  {
    "path": "vdom/test/patch-op-index.js",
    "chars": 2029,
    "preview": "var test = require(\"tape\")\nvar VNode = require(\"../../vnode/vnode\")\nvar VText = require(\"../../vnode/vtext\")\nvar diff = "
  },
  {
    "path": "vdom/update-widget.js",
    "chars": 313,
    "preview": "var isWidget = require(\"../vnode/is-widget.js\")\n\nmodule.exports = updateWidget\n\nfunction updateWidget(a, b) {\n    if (is"
  },
  {
    "path": "virtual-hyperscript/README.md",
    "chars": 2051,
    "preview": "# virtual-hyperscript\n\nA DSL for creating virtual trees\n\n## Example\n\n```js\nvar h = require('virtual-dom/h')\n\nvar tree = "
  },
  {
    "path": "virtual-hyperscript/hooks/attribute-hook.js",
    "chars": 941,
    "preview": "'use strict';\n\nmodule.exports = AttributeHook;\n\nfunction AttributeHook(namespace, value) {\n    if (!(this instanceof Att"
  },
  {
    "path": "virtual-hyperscript/hooks/ev-hook.js",
    "chars": 532,
    "preview": "'use strict';\n\nvar EvStore = require('ev-store');\n\nmodule.exports = EvHook;\n\nfunction EvHook(value) {\n    if (!(this ins"
  },
  {
    "path": "virtual-hyperscript/hooks/focus-hook.js",
    "chars": 430,
    "preview": "'use strict';\n\nvar document = require(\"global/document\");\nvar nextTick = require(\"next-tick\");\n\nmodule.exports = Mutable"
  },
  {
    "path": "virtual-hyperscript/hooks/soft-set-hook.js",
    "chars": 347,
    "preview": "'use strict';\n\nmodule.exports = SoftSetHook;\n\nfunction SoftSetHook(value) {\n    if (!(this instanceof SoftSetHook)) {\n  "
  },
  {
    "path": "virtual-hyperscript/index.js",
    "chars": 4419,
    "preview": "'use strict';\n\nvar isArray = require('x-is-array');\n\nvar VNode = require('../vnode/vnode.js');\nvar VText = require('../v"
  },
  {
    "path": "virtual-hyperscript/parse-tag.js",
    "chars": 1143,
    "preview": "'use strict';\n\nvar split = require('browser-split');\n\nvar classIdSplit = /([\\.#]?[a-zA-Z0-9\\u007F-\\uFFFF_:-]+)/;\nvar not"
  },
  {
    "path": "virtual-hyperscript/svg-attribute-namespace.js",
    "chars": 11244,
    "preview": "'use strict';\n\nvar DEFAULT_NAMESPACE = null;\nvar EV_NAMESPACE = 'http://www.w3.org/2001/xml-events';\nvar XLINK_NAMESPACE"
  },
  {
    "path": "virtual-hyperscript/svg.js",
    "chars": 1441,
    "preview": "'use strict';\n\nvar isArray = require('x-is-array');\n\nvar h = require('./index.js');\n\n\nvar SVGAttributeNamespace = requir"
  },
  {
    "path": "virtual-hyperscript/test/attribute-hook.js",
    "chars": 4599,
    "preview": "var test = require(\"tape\")\nvar doc = require(\"global/document\")\n\nvar attributeHook = require(\"../hooks/attribute-hook.js"
  },
  {
    "path": "virtual-hyperscript/test/ev-hook.js",
    "chars": 699,
    "preview": "var test = require(\"tape\")\nvar EvStore = require(\"ev-store\")\n\nvar h = require(\"../index.js\")\nvar createElement = require"
  },
  {
    "path": "virtual-hyperscript/test/h.js",
    "chars": 3891,
    "preview": "var test = require(\"tape\")\nvar EvStore = require('ev-store')\n\nvar h = require(\"../index\")\n\ntest(\"h is a function\", funct"
  },
  {
    "path": "virtual-hyperscript/test/index.js",
    "chars": 93,
    "preview": "require(\"./h.js\")\nrequire(\"./svg.js\")\nrequire(\"./ev-hook.js\")\nrequire(\"./attribute-hook.js\")\n"
  },
  {
    "path": "virtual-hyperscript/test/svg.js",
    "chars": 2137,
    "preview": "var test = require(\"tape\")\nvar doc = require(\"global/document\")\n\nvar svg = require(\"../svg\")\nvar attributeHook = require"
  },
  {
    "path": "vnode/handle-thunk.js",
    "chars": 834,
    "preview": "var isVNode = require(\"./is-vnode\")\nvar isVText = require(\"./is-vtext\")\nvar isWidget = require(\"./is-widget\")\nvar isThun"
  },
  {
    "path": "vnode/is-thunk.js",
    "chars": 90,
    "preview": "module.exports = isThunk\r\n\r\nfunction isThunk(t) {\r\n    return t && t.type === \"Thunk\"\r\n}\r\n"
  },
  {
    "path": "vnode/is-vhook.js",
    "chars": 220,
    "preview": "module.exports = isHook\n\nfunction isHook(hook) {\n    return hook &&\n      (typeof hook.hook === \"function\" && !hook.hasO"
  },
  {
    "path": "vnode/is-vnode.js",
    "chars": 164,
    "preview": "var version = require(\"./version\")\n\nmodule.exports = isVirtualNode\n\nfunction isVirtualNode(x) {\n    return x && x.type ="
  },
  {
    "path": "vnode/is-vtext.js",
    "chars": 164,
    "preview": "var version = require(\"./version\")\n\nmodule.exports = isVirtualText\n\nfunction isVirtualText(x) {\n    return x && x.type ="
  },
  {
    "path": "vnode/is-widget.js",
    "chars": 88,
    "preview": "module.exports = isWidget\n\nfunction isWidget(w) {\n    return w && w.type === \"Widget\"\n}\n"
  },
  {
    "path": "vnode/test/handle-thunk.js",
    "chars": 3708,
    "preview": "var test = require(\"tape\")\n\nvar handleThunk = require(\"../handle-thunk\")\nvar VNode = require(\"../vnode\")\nvar VText = req"
  },
  {
    "path": "vnode/test/index.js",
    "chars": 29,
    "preview": "require('./handle-thunk.js')\n"
  },
  {
    "path": "vnode/version.js",
    "chars": 21,
    "preview": "module.exports = \"2\"\n"
  },
  {
    "path": "vnode/vnode.js",
    "chars": 2073,
    "preview": "var version = require(\"./version\")\nvar isVNode = require(\"./is-vnode\")\nvar isWidget = require(\"./is-widget\")\nvar isThunk"
  },
  {
    "path": "vnode/vpatch.js",
    "chars": 485,
    "preview": "var version = require(\"./version\")\n\nVirtualPatch.NONE = 0\nVirtualPatch.VTEXT = 1\nVirtualPatch.VNODE = 2\nVirtualPatch.WID"
  },
  {
    "path": "vnode/vtext.js",
    "chars": 210,
    "preview": "var version = require(\"./version\")\n\nmodule.exports = VirtualText\n\nfunction VirtualText(text) {\n    this.text = String(te"
  },
  {
    "path": "vtree/README.md",
    "chars": 647,
    "preview": "# vtree\n\nA realtime tree diffing algorithm\n\n## Motivation\n\n`vtree` currently exists as part of `virtual-dom`. It is used"
  },
  {
    "path": "vtree/diff-props.js",
    "chars": 1415,
    "preview": "var isObject = require(\"is-object\")\nvar isHook = require(\"../vnode/is-vhook\")\n\nmodule.exports = diffProps\n\nfunction diff"
  },
  {
    "path": "vtree/diff.js",
    "chars": 11939,
    "preview": "var isArray = require(\"x-is-array\")\n\nvar VPatch = require(\"../vnode/vpatch\")\nvar isVNode = require(\"../vnode/is-vnode\")\n"
  },
  {
    "path": "vtree/test/diff-props.js",
    "chars": 489,
    "preview": "var test = require(\"tape\")\r\nvar diffProps = require(\"../diff-props\")\r\n\r\ntest(\"add attributes to empty attributes\", funct"
  },
  {
    "path": "vtree/test/index.js",
    "chars": 24,
    "preview": "require(\"./diff-props\")\n"
  }
]

About this extraction

This page contains the full source code of the Matt-Esch/virtual-dom GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 81 files (249.7 KB), approximately 65.7k tokens, and a symbol index with 198 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!