[
  {
    "path": ".gitignore",
    "content": "**/*.browser.js\nnode_modules\n*.log"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n- '12'\naddons:\n  apt:\n    packages:\n      - xvfb\ninstall:\n  - export DISPLAY=':99.0'\n  - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &\n  - npm install\nscript: npm run test"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog for fastn\n\n\n## v2\n\nVersion two of fastn changes the way the component constructors work, to allow for better composition of components.\n\nIn fastn v1, component constructors would create a component, modify it, then return it.\n\nIn fastn v2, component constructors are passed a component, and they may extend it with functionality.\n\nThe v1 way:\n\n```\n\nfunction myCoolComponentConstructor(fastn, type, settings, children){\n    var component = fastn.base(type, settings, children);\n\n    // Add properties, implement/override methods, etc...\n\n    return component;\n}\n\n```\n\nThe v2 way:\n\n```\n\nfunction myCoolComponentConstructor(fastn, component, type, settings, children){\n    // Add properties, implement/override methods, etc...\n\n    return component;\n}\n\n```\n\n## Extending\n\nIn v1, if you wanted to make a component that was an extension of another component, you would do something like this:\n\n```\n\nfunction myCoolFancyList(fastn, type, settings, children){\n\n    // Create a list.\n    var component = fastn.createComponent('list', settings, children);\n\n    // Add properties, implement/override methods, etc...\n\n    return component;\n}\n\n```\n\nin v2, you can just call .extend()...\n\n\n```\n\nfunction myCoolComponentConstructor(fastn, component, type, settings, children){\n\n    // Become a list.\n    component.extend('list', settings, children);\n\n    // Add properties, implement/override methods, etc...\n\n    return component;\n}\n\n```\n\nWhich is extremely handy if you want features from multiple components:\n\n\n```\n\nfunction myCoolComponentConstructor(fastn, component, type, settings, children){\n\n    // Become a list.\n    component.extend('list', settings, children);\n\n    // Also be a modal\n    component.extend('modal', settings, children);\n\n    // Also be a whatever\n    component.extend('whatever', settings, children);\n\n    // Add properties, implement/override methods, etc...\n\n    return component;\n}\n\n```\n\n# Why\n\nIn fastn v2, you can mix components together when you create them, like so:\n\n```\nvar myMapList = fastn('list:map', { ... });\n```\n\nFastn will, under the covers, extend all the types together in the order they are listed, so the above example is equivilent to:\n\nfastn('list', settings, children...).extend('map', settings, children...);\n\n## Details\n\n### API\n\n#### Removed\n\n - fastn.createComponent\n\n#### Changed\n\n - componant constructor parameters (fastn, type, settings, children) -> (fastn, component, type, settings, children)\n - component.setProperty can now be passed only a key, which will use the existing property, or create a new default one for that key.\n\n#### Added\n\n - Mixin syntax, fastn('componantType1:componantType2')\n - componant.extend(componantType, settings, children)\n - componant.is(componantType) -> bool\n - fastn.componants._container is now defaulted to containerComponant.\n\n### Best Practice\n\n#### Composition\n\nIn v1, you could add functionality to a componant arbitrarily, with no real structure\n\nIn v2, obviously, using the fastn('foo:bar') style is recommended.\n\n#### Adding properties\n\nIn v1, properties were generally added via\n\n```\nproperty.addTo(componant, key);\n```\n\nIn v2 this is deprecated, and it is encouraged that you instead use:\n\n```\ncomponant.setProperty('key', property);\n```\n"
  },
  {
    "path": "README.md",
    "content": "# fastn\n\nCreate ultra-lightweight UI components\n\n[![Build Status](https://travis-ci.org/KoryNunn/fastn.svg?branch=master)](https://travis-ci.org/KoryNunn/fastn)\n[![Join the chat at https://gitter.im/KoryNunn/fastn](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/KoryNunn/fastn?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n\n![fastn](http://korynunn.github.io/fastn/images/fastn-sml.png)\n\n## [Homepage](http://korynunn.github.io/fastn/), [Try it](http://korynunn.github.io/fastn/try/), [Example app](http://korynunn.github.io/fastn/example/)\n\n# Usage\n\nThe absolute minimum required to make a fastn component:\n\ninitialise fastn with default DOM components:\n\n```javascript\nvar fastn = require('fastn')(\n    // Default components for rendering DOM.\n    require('fastn/domComponents')(/* optional extra constructors */)\n);\n```\n\nOr use your own selection of constructors:\n\n```javascript\n// Require and initialise fastn\nvar fastn = require('fastn')({\n    // component constructors.. Add what you need to use\n\n    text: require('fastn/textComponent'), // Renders text\n    _generic: require('fastn/genericComponent') // Renders DOM nodes\n});\n```\n\nMake components:\n```javascript\nvar something = fastn('h1', 'Hello World');\n\n```\n\nPut them on the screen:\n```javascript\nsomething.render();\n\nwindow.addEventListener('load', function(){\n    document.body.appendChild(something.element);\n});\n```\n[^ try it](http://korynunn.github.io/fastn/try/#InJldHVybiBmYXN0bignaDEnLCAnSGVsbG8gV29ybGQnKTsi)\n\n`fastn` is a function with the signature:\n\n```javascript\nfastn(type[, settings, children...])\n```\n\nwhich can be used to create a UI:\n\n```javascript\n// Create some component\nvar someComponent = fastn('section',\n        fastn('h1', 'I\\'m a component! :D'),\n        fastn('a', {href: 'http://google.com'}, 'An anchor')\n    );\n\nsomeComponent.render();\n\n// Append the components element to the DOM\ndocument.body.appendChild(someComponent.element);\n```\n[^ try it](http://korynunn.github.io/fastn/try/#InJldHVybiBmYXN0bignc2VjdGlvbicsXG5cdGZhc3RuKCdoMScsICdJXFwnbSBhIGNvbXBvbmVudCEgOkQnKSxcblx0ZmFzdG4oJ2EnLCB7aHJlZjogJ2h0dHA6Ly9nb29nbGUuY29tJ30sICdBbiBhbmNob3InKVxuKTsi)\n\nYou can assign bindings to properties:\n\n```javascript\n\nvar someComponent = fastn('section',\n        fastn('h1', 'I\\'m a component! :D'),\n        fastn('a', {href: fastn.binding('url')},\n            fastn('label', 'This link points to '),\n            fastn('label', fastn.binding('url'))\n        )\n    );\n\nsomeComponent.attach({\n    url: 'http://google.com'\n});\n\n```\n\nWhich can be updated via a number of methods.\n\n\n```javascript\n\nsomeComponent.scope().set('url', 'http://bing.com');\n\n\n```\n[^ try it](http://korynunn.github.io/fastn/try/#InZhciBzb21lQ29tcG9uZW50ID0gZmFzdG4oJ3NlY3Rpb24nLFxuICAgICAgICBmYXN0bignaDEnLCAnSVxcJ20gYSBjb21wb25lbnQhIDpEJyksXG4gICAgICAgIGZhc3RuKCdhJywge2hyZWY6IGZhc3RuLmJpbmRpbmcoJ3VybCcpfSxcbiAgICAgICAgICAgIGZhc3RuKCdsYWJlbCcsICdUaGlzIGxpbmsgcG9pbnRzIHRvICcpLFxuICAgICAgICAgICAgZmFzdG4oJ2xhYmVsJywgZmFzdG4uYmluZGluZygndXJsJykpXG4gICAgICAgIClcbiAgICApO1xuXG5zb21lQ29tcG9uZW50LmF0dGFjaCh7XG4gICAgdXJsOiAnaHR0cDovL2dvb2dsZS5jb20nXG59KTtcblxuc2V0VGltZW91dChmdW5jdGlvbigpe1xuXHRzb21lQ29tcG9uZW50LnNjb3BlKCkuc2V0KCd1cmwnLCAnaHR0cDovL2JpbmcuY29tJyk7XG59LCAyMDAwKTtcblxucmV0dXJuIHNvbWVDb21wb25lbnQ7Ig==)\n\n## Special component types\n\nThere are a few special component types that are used as shorthands for some situations:\n\n### `text`\n\nif a string or `binding` is added as a child into a containerComponent, fastn will look for a `text` component, set it's `text` to the string or `binding`, and insert it. This is handy as you don't need to write: `fastn('text', 'foo')` all over the place.\n[^ try it](http://korynunn.github.io/fastn/try/#InJldHVybiBmYXN0bignZGl2Jyxcblx0ZmFzdG4oJ3RleHQnLCB7dGV4dDogJ0V4cGxpY2l0IHRleHQsICd9KSxcblx0J0ltcGxpY2l0IHRleHQsICcsXG4gICAgZmFzdG4uYmluZGluZygnYm91bmRUZXh0JylcbikuYXR0YWNoKHtcbiAgXHRib3VuZFRleHQ6ICdCb3VuZCB0ZXh0J1xufSk7Ig==)\n\n### `_generic`\n\nIf the type passed to fastn does not exactly match any known components, fastn will check for a `_generic` component, and pass all the settings and children through to it.\n[^ try it](http://korynunn.github.io/fastn/try/#InJldHVybiBmYXN0bignZGl2JyxcbiAgICAgICAgICAgICBcblx0ZmFzdG4oJ3NwYW4nLCAnV29vIGEgc3BhbiEnKSxcbiAgICAgICAgICAgICBcblx0ZmFzdG4oJ2JyJyksIC8vIEJyIGJlY2F1c2Ugd2UgY2FuIVxuICAgICAgICAgICAgIFxuICAgIGZhc3RuKCdhJywge2hyZWY6ICdodHRwczovL2dpdGh1Yi5jb20va29yeW51bm4vZmFzdG4nfSwgJ0FuIGFuY2hvcicpLFxuICAgICAgICAgICAgIFxuXHRmYXN0bignYnInKSwgLy8gQW5vdGhlciBiciBmb3IgcmVhc29uc1xuICAgICAgICAgICAgIFxuICAgIGZhc3RuKCdpbWcnLCB7dGl0bGU6ICdBd2Vzb21lIGxvZ28nLCBzcmM6ICdodHRwOi8va29yeW51bm4uZ2l0aHViLmlvL2Zhc3RuL3RyeS9mYXN0bi1zbWwucG5nJ30pXG4pOyI=)\n\n## Default components\n\nfastn includes 4 extremely simple default components that render as DOM nodes. It is not necessary to use them, and you can replace them with your own, enabling you to render to anything you want to.\n\n### textComponent\n\nA default handler for the `text` component type that renders a textNode. e.g.:\n\n```javascript\nfastn('something', // render a thing\n    'Some string passed as a child' // falls into the `text` component, renders as a textNode\n)\n```\n\n### genericComponent\n\nA default handler for the `_generic` component type that renders DOM nodes based on the type passed, e.g.:\n\n```javascript\nfastn('div') // no component is assigned to 'div', fastn will search for _generic, and if this component is assigned to it, it will create a div element.\n```\n\n### listComponent\n\nTakes a template and inserts children based on the result of its `items` property, e.g.:\n\n```javascript\nfastn('list', {\n    items: [1,2,3],\n    template: function(){\n        return fastn.binding('item')\n    }\n})\n```\nTemplated components will be attached to a model that contains `key` and `item`, where `key` is the key in the set that they correspond to, and `item` is the data of the item in the set.\n\n#### Lazy templating\n\nIf you need to render a huge list of items, and you're noticing a UI hang, you can choose to enable\nlazy templating by setting a lists' `insertionFrameTime` to some value:\n\n```javascript\nfastn('list', {\n    insertionFrameTime: 32, // Only render items for 32 milliseconds at a time before awaiting idle time.\n    items: [1,2,3],\n    template: function(){\n        return fastn.binding('item')\n    }\n})\n```\n\n### templaterComponent\n\nTakes a template and replaces itself with the component rendered by the template. Returning null from the template indicates that nothing should be inserted.\n\nThe template function will be passed the last component that was rendered by it as the third parameter.\n\nNote: The template function will run immediately upon component creation. This means that if your data is a binding, the first run of the template function will always receive undefined as it's item.\n\n```javascript\nfastn('templater', {\n    data: 'foo',\n    template: function(model, scope, currentComponent){\n        if(model.get('item') === 'foo'){\n            return fastn('img');\n        }else{\n            return null;\n        }\n    }\n})\n```\n\nAn optional property of `attachTemplates` can be provided. When set to true (default), the children rendered from the template will be attached to a new scope that contains the templater data, under the key of `item`.\n\nWhen set to false, the children rendered from the template will inherit their attachment from the templator.\n\n```javascript\nvar appData = {\n    foo: 'bar'\n};\n\nfastn('templater', {\n    data: binding('foo'),\n    attachTemplates: false,\n    template: function(model, scope, currentComponent){\n        // model.get('item') -> is appData\n    }\n}).attach(appData)\n```\n\n## A little deeper..\n\nA component can be created by calling `fastn` with a `type`, like so:\n\n```javascript\nvar myComponent = fastn('myComponent');\n```\n\nThis will create a component registered in `components` with the key `'myComponent'`\n\nIf `'myComponent'` is not found, fastn will check for a `'_generic'` constructor, and use that if defined. The generic component will create a DOM element of the given type passed in, and is likely the most common component you will create.\n\n```javascript\nvar divComponent = fastn('div', {'class':'myDiv'});\n```\n\nThe above will create a `component`, that renders as a `div` with a class of `'myDiv'`\n\n__the default genericComponent will automatically convert all keys in the settings object to properties.__\n\n## `fastn.binding(key)`\n\nCreates a binding with the given key.\n\nA binding can be attached to data using `.attach(object)`.\n\n\n# The Bits..\n\nThere are very few parts to fastn, they are:\n\n`component`, `property`, and `binding`\n\nIf you are just want to render some DOM, you will probably be able to just use the default ones.\n\n## `component`\n\nA fastn `component` is an object that represents a chunk of UI.\n\n```javascript\n\nvar someComponent = fastn('componentType', settings (optional), children (optional)...)\n\n```\n\n## `property`\n\nA fastn `property` is a getterSetter function and EventEmitter.\n\n```javascript\n\nvar someProperty = fastn.property(defaultValue, changes (optional), updater (optional));\n\n// get it's value\nsomeProperty(); // returns it's value;\n\n// set it's value\nsomeProperty(anything); // sets anything and returns the property.\n\n// add a change handler\nsomeProperty.on('change', function(value){\n    // value is the properties new value.\n});\n\n```\n\nProperties can be added to components in a number of ways:\n\nvia the settings object:\n\n```javascript\n\nvar component = fastn('div', {\n        property: someProperty\n    });\n\n```\nat a later point via property.addTo(component, key);\n\n```javascript\n\nsomeProperty.addTo(component, 'someProperty');\n\n```\n\n## `binding`\n\nA fastn `binding` is a getterSetter function and `EventEmitter`.\n\nIt is used as a mapping between an object and a key or path on that object.\n\nThe path syntax is identical to that used in [enti](https://github.com/KoryNunn/enti#paths)\n\n```javascript\n\nvar someBinding = fastn.binding('foo');\n\n// get it's value\nsomeBinding(); // returns it's value;\n\n// set it's value\nsomeBinding(anything); // sets anything and returns the binding.\n\n// add a change handler\nsomeBinding.on('change', function(value){\n    // value is the properties new value.\n});\n\n```\n\nYou can pass multiple paths or other bindings to a binding, along with a fuse function, to combine them into a single result:\n\n```javascript\n\nvar anotherBinding = fastn.binding('bar', 'baz', someBinding, function(bar, baz, foo){\n    return bar + foo;\n});\n\n```\n\n### `binding.from(value)`\n\n - if value is a binding: return `value`,\n - else: return a binding who's value is `value`.\n\nuseful when you don't know what something is, but you need it in a binding:\n\n```javascript\n\nvar someBinding = fastn.binding('someKey', fastn.binding.from(couldBeAnything), function(someValue, valueOfAnything){\n\n    });\n\n```\n\n### A note on the difference between `properties` and `bindings`\n\nOn the surface, properties and bindings look very similar.\nThey can both be used like getter/setter functions, and they both emit change events.\n\nThey differ both in usage and implementation in that properties don't have any awareness of a model or paths,\nand bindings don't have any awareness of components.\n\nThis distinction shines when you design your application with 'services' or 'controllers' that encapsulate models and how to interact with them.\nCheck out the example applications [search service](https://github.com/KoryNunn/fastn/blob/gh-pages/example/search.js) and\n[search bar component](https://github.com/KoryNunn/fastn/blob/gh-pages/example/searchBar.js).\nThe service only deals with data, and the component only deals with UI.\n\n# Browser Support\n\nFastn works in all the latest evergreen browsers.\n\nFastn *May* work in other browsers, but will almost certainly need a few polyfills like WeakMap, Map, WeakSet, Set, etc...\n"
  },
  {
    "path": "baseComponent.js",
    "content": "var is = require('./is'),\n    GENERIC = '_generic',\n    EventEmitter = require('events').EventEmitter,\n    slice = Array.prototype.slice;\n\nfunction flatten(item){\n    return Array.isArray(item) ? item.reduce(function(result, element){\n        if(element == null){\n            return result;\n        }\n        return result.concat(flatten(element));\n    },[]) : item;\n}\n\nfunction attachProperties(object, firm){\n    for(var key in this._properties){\n        this._properties[key].attach(object, firm);\n    }\n}\n\nfunction onRender(){\n\n    // Ensure all bindings are somewhat attached just before rendering\n    this.attach(undefined, 0);\n\n    for(var key in this._properties){\n        this._properties[key].update();\n    }\n}\n\nfunction detachProperties(firm){\n    for(var key in this._properties){\n        this._properties[key].detach(firm);\n    }\n}\n\nfunction destroyProperties(){\n    for(var key in this._properties){\n        this._properties[key].destroy();\n    }\n}\n\nfunction clone(){\n    return this.fastn(this.component._type, this.component._settings, this.component._children.filter(function(child){\n            return !child._templated;\n        }).map(function(child){\n            return typeof child === 'object' ? child.clone() : child;\n        })\n    );\n}\n\nfunction getSetBinding(newBinding){\n    if(!arguments.length){\n        return this.binding;\n    }\n\n    if(!is.binding(newBinding)){\n        newBinding = this.fastn.binding(newBinding);\n    }\n\n    if(this.binding && this.binding !== newBinding){\n        this.binding.removeListener('change', this.emitAttach);\n        newBinding.attach(this.binding._model, this.binding._firm);\n    }\n\n    this.binding = newBinding;\n\n    this.binding.on('change', this.emitAttach);\n    this.binding.on('detach', this.emitDetach);\n\n    this.emitAttach();\n\n    return this.component;\n};\n\nfunction emitAttach(){\n    var newBound = this.binding();\n    if(newBound !== this.lastBound){\n        this.lastBound = newBound;\n        this.scope.attach(this.lastBound);\n        this.component.emit('attach', this.scope, 1);\n    }\n}\n\nfunction emitDetach(){\n    this.component.emit('detach', 1);\n}\n\nfunction getScope(){\n    return this.scope;\n}\n\nfunction destroy(){\n    if(this.destroyed){\n        return;\n    }\n    this.destroyed = true;\n\n    this.component\n        .removeAllListeners('render')\n        .removeAllListeners('attach');\n\n    this.component.emit('destroy');\n    this.component.element = null;\n    this.scope.destroy();\n    this.binding.destroy(true);\n\n    return this.component;\n}\n\nfunction attachComponent(object, firm){\n    this.binding.attach(object, firm);\n    return this.component;\n}\n\nfunction detachComponent(firm){\n    this.binding.detach(firm);\n    return this.component;\n}\n\nfunction isDestroyed(){\n    return this.destroyed;\n}\n\nfunction setProperty(key, property){\n\n    // Add a default property or use the one already there\n    if(!property){\n        property = this.component[key] || this.fastn.property();\n    }\n\n    this.component[key] = property;\n    this.component._properties[key] = property;\n\n    return this.component;\n}\n\nfunction bindInternalProperty(component, model, propertyName, propertyTransform){\n    if(!(propertyName in component)){\n        component.setProperty(propertyName);\n    }\n    component[propertyName].on('change', function(value){\n        model.set(propertyName, propertyTransform ? propertyTransform(value) : value);\n    });\n}\n\nfunction createInternalScope(data, propertyTransforms){\n    var componentScope = this;\n    var model = new componentScope.fastn.Model(data);\n\n    for(var key in data){\n        bindInternalProperty(componentScope.component, model, key, propertyTransforms[key]);\n    }\n\n    return {\n        binding: function(){\n            return componentScope.fastn.binding.apply(null, arguments).attach(model);\n        },\n        model: model\n    };\n}\n\nfunction extendComponent(type, settings, children){\n    var component = this.component;\n\n    if(type in this.types){\n        return component;\n    }\n\n    if(!(type in this.fastn.components)){\n\n        if(!(GENERIC in this.fastn.components)){\n            throw new Error('No component of type \"' + type + '\" is loaded');\n        }\n\n        component = this.fastn.components._generic(this.fastn, this.component, type, settings, children, createInternalScope.bind(this));\n\n        if(component){\n            this.types._generic = true;\n        }\n    }else{\n\n        component = this.fastn.components[type](this.fastn, this.component, type, settings, children, createInternalScope.bind(this));\n    }\n\n    if(component){\n        this.types[type] = true;\n    }\n\n    return component;\n};\n\nfunction isType(type){\n    return type in this.types;\n}\n\nfunction FastnComponent(fastn, type, settings, children){\n    var component = this;\n\n    var componentScope = {\n        types: {},\n        fastn: fastn,\n        component: component,\n        binding: fastn.binding('.'),\n        destroyed: false,\n        scope: new fastn.Model(false),\n        lastBound: null\n    };\n\n    componentScope.emitAttach = emitAttach.bind(componentScope);\n    componentScope.emitDetach = emitDetach.bind(componentScope);\n    componentScope.binding._default_binding = true;\n\n    component._type = type;\n    component._properties = {};\n    component._settings = settings || {};\n    component._children = children ? flatten(children) : [];\n\n    component.attach = attachComponent.bind(componentScope);\n    component.detach = detachComponent.bind(componentScope);\n    component.scope = getScope.bind(componentScope);\n    component.destroy = destroy.bind(componentScope);\n    component.destroyed = isDestroyed.bind(componentScope);\n    component.binding = getSetBinding.bind(componentScope);\n    component.setProperty = setProperty.bind(componentScope);\n    component.clone = clone.bind(componentScope);\n    component.children = slice.bind(component._children);\n    component.extend = extendComponent.bind(componentScope);\n    component.is = isType.bind(componentScope);\n\n    component.binding(componentScope.binding);\n\n    component.on('attach', attachProperties.bind(this));\n    component.on('render', onRender.bind(this));\n    component.on('detach', detachProperties.bind(this));\n    component.on('destroy', destroyProperties.bind(this));\n\n    if(fastn.debug){\n        component.on('render', function(){\n            if(component.element && typeof component.element === 'object'){\n                component.element._component = component;\n            }\n        });\n    }\n}\nFastnComponent.prototype = Object.create(EventEmitter.prototype);\nFastnComponent.prototype.constructor = FastnComponent;\nFastnComponent.prototype._fastn_component = true;\n\nmodule.exports = FastnComponent;"
  },
  {
    "path": "binding.js",
    "content": "var is = require('./is'),\n    firmer = require('./firmer'),\n    functionEmitter = require('function-emitter'),\n    setPrototypeOf = require('setprototypeof'),\n    same = require('same-value');\n\nfunction noop(x){\n    return x;\n}\n\nfunction fuseBinding(){\n    var fastn = this,\n        args = Array.prototype.slice.call(arguments);\n\n    var bindings = args.slice(),\n        transform = bindings.pop(),\n        updateTransform,\n        resultBinding = createBinding.call(fastn),\n        selfChanging;\n\n    resultBinding._arguments = args;\n\n    if(typeof bindings[bindings.length-1] === 'function' && !is.binding(bindings[bindings.length-1])){\n        updateTransform = transform;\n        transform = bindings.pop();\n    }\n\n    resultBinding._model.removeAllListeners();\n    resultBinding._set = function(value){\n        if(updateTransform){\n            selfChanging = true;\n            var newValue = updateTransform(value);\n            if(!same(newValue, bindings[0]())){\n                bindings[0](newValue);\n                resultBinding._change(newValue);\n            }\n            selfChanging = false;\n        }else{\n            resultBinding._change(value);\n        }\n    };\n\n    function change(){\n        if(selfChanging){\n            return;\n        }\n        resultBinding(transform.apply(null, bindings.map(function(binding){\n            return binding();\n        })));\n    }\n\n    resultBinding.on('detach', function(firm){\n        bindings.forEach(function(binding, index){\n            binding.detach(firm);\n        });\n    });\n\n    resultBinding.once('destroy', function(soft){\n        bindings.forEach(function(binding, index){\n            binding.removeListener('change', change);\n            binding.destroy(soft);\n        });\n    });\n\n    bindings.forEach(function(binding, index){\n        if(!is.binding(binding)){\n            binding = createBinding.call(fastn, binding);\n            bindings[index] = binding;\n        }\n        binding.on('change', change);\n    });\n\n    var lastAttached;\n    resultBinding.attach = function(object, firm){\n        if(firmer(resultBinding, firm)){\n            return resultBinding;\n        }\n\n        resultBinding._firm = firm;\n\n        selfChanging = true;\n        bindings.forEach(function(binding){\n            binding.attach(object, 1);\n        });\n        selfChanging = false;\n        if(lastAttached !== object){\n            change();\n        }\n        lastAttached = object;\n        resultBinding._model.attach(object);\n        resultBinding.emit('attach', object, firm);\n        return resultBinding;\n    }\n\n    return resultBinding;\n}\n\nfunction createValueBinding(fastn){\n    var valueBinding = createBinding.call(fastn, 'value');\n    valueBinding.attach = function(){return valueBinding;};\n    valueBinding.detach = function(){return valueBinding;};\n    return valueBinding;\n}\n\nfunction bindingTemplate(newValue){\n    if(!arguments.length){\n        return this.value;\n    }\n\n    if(this.binding._fastn_binding === '.'){\n        return;\n    }\n\n    this.binding._set(newValue);\n    return this.binding;\n}\n\nfunction modelAttachHandler(data){\n    var bindingScope = this;\n    bindingScope.binding._model.attach(data);\n    bindingScope.binding._change(bindingScope.binding._model.get(bindingScope.path));\n    bindingScope.binding.emit('attach', data, 1);\n}\n\nfunction modelDetachHandler(){\n    this.binding._model.detach();\n}\n\nfunction attach(object, firm){\n    var bindingScope = this;\n    var binding = bindingScope.binding;\n    // If the binding is being asked to attach loosly to an object,\n    // but it has already been defined as being firmly attached, do not attach.\n    if(firmer(binding, firm)){\n        return binding;\n    }\n\n    binding._firm = firm;\n\n    var isModel = bindingScope.fastn.isModel(object);\n\n    if(isModel && bindingScope.attachedModel === object){\n        return binding;\n    }\n\n    if(bindingScope.attachedModel){\n        bindingScope.attachedModel.removeListener('attach', bindingScope.modelAttachHandler);\n        bindingScope.attachedModel.removeListener('detach', bindingScope.modelDetachHandler);\n        bindingScope.attachedModel = null;\n    }\n\n    if(isModel){\n        bindingScope.attachedModel = object;\n        bindingScope.attachedModel.on('attach', bindingScope.modelAttachHandler);\n        bindingScope.attachedModel.on('detach', bindingScope.modelDetachHandler);\n        object = object._model;\n    }\n\n    if(binding._model._model === object){\n        return binding;\n    }\n\n    bindingScope.modelAttachHandler(object);\n\n    return binding;\n};\n\nfunction detach(firm){\n    if(firmer(this.binding, firm)){\n        return this.binding;\n    }\n\n    this.value = undefined;\n    if(this.binding._model.isAttached()){\n        this.binding._model.detach();\n    }\n    this.binding.emit('detach', 1);\n    return this.binding;\n}\n\nfunction set(newValue){\n    var bindingScope = this;\n    if(same(bindingScope.binding._model.get(bindingScope.path), newValue)){\n        return;\n    }\n    if(!bindingScope.binding._model.isAttached()){\n        bindingScope.binding._model.attach(bindingScope.binding._model.get('.'));\n    }\n    bindingScope.binding._model.set(bindingScope.path, newValue);\n}\n\nfunction change(newValue){\n    var bindingScope = this;\n    if(newValue === undefined && bindingScope.value === newValue && !bindingScope.binding._model._model){\n        return;\n    }\n    bindingScope.value = newValue;\n    bindingScope.binding.emit('change', bindingScope.binding());\n}\n\nfunction clone(keepAttachment){\n    var bindingScope = this;\n    var newBinding = createBinding.apply(bindingScope.fastn, bindingScope.binding._arguments);\n\n    if(keepAttachment){\n        newBinding.attach(bindingScope.attachedModel || bindingScope.binding._model._model, bindingScope.binding._firm);\n    }\n\n    return newBinding;\n}\n\nfunction destroy(soft){\n    var bindingScope = this;\n    if(bindingScope.isDestroyed){\n        return;\n    }\n    if(soft){\n        return;\n    }\n    bindingScope.isDestroyed = true;\n    bindingScope.binding.emit('destroy', true);\n    bindingScope.binding.detach();\n    bindingScope.binding._model.destroy();\n}\n\nfunction destroyed(){\n    return this.isDestroyed;\n}\n\nfunction createBinding(path, more){\n    var fastn = this;\n\n    if(more){ // used instead of arguments.length for performance\n        return fuseBinding.apply(fastn, arguments);\n    }\n\n    if(is.binding(path)){\n        return createBinding.call(this, path, noop);\n    }\n\n    if(arguments.length === 0){\n        return createValueBinding(fastn);\n    }\n\n    if(!(typeof path === 'string' || typeof path === 'number')){\n        throw new Error('Invalid path for fastn.binding(String/Number), saw: ', JSON.stringify(path))\n    }\n\n    var bindingScope = {\n            fastn: fastn,\n            path: path\n        },\n        binding = bindingScope.binding = bindingTemplate.bind(bindingScope);\n\n    setPrototypeOf(binding, functionEmitter);\n    binding.setMaxListeners(10000);\n    binding._arguments = [path];\n    binding._model = new fastn.Model(false);\n    binding._fastn_binding = path;\n    binding._firm = -Infinity;\n\n    bindingScope.modelAttachHandler = modelAttachHandler.bind(bindingScope);\n    bindingScope.modelDetachHandler = modelDetachHandler.bind(bindingScope);\n\n    binding.attach = attach.bind(bindingScope);\n    binding.detach = detach.bind(bindingScope);\n    binding._set = set.bind(bindingScope);\n    binding._change = change.bind(bindingScope);\n    binding.clone = clone.bind(bindingScope);\n    binding.destroy = destroy.bind(bindingScope);\n    binding.destroyed = destroyed.bind(bindingScope);\n\n    if(path !== '.'){\n        binding._model.on(path, binding._change);\n    }\n\n    return binding;\n}\n\nfunction from(valueOrBinding){\n    if(is.binding(valueOrBinding)){\n        return valueOrBinding;\n    }\n\n    var result = this();\n    result(valueOrBinding)\n\n    return result;\n}\n\nmodule.exports = function(fastn){\n    var binding = createBinding.bind(fastn);\n    binding.from = from.bind(binding);\n    return binding;\n};\n"
  },
  {
    "path": "containerComponent.js",
    "content": "function insertChild(fastn, container, child, index){\n    if(child == null || child === false){\n        return;\n    }\n\n    if(child.destroyed && child.destroyed()){\n        throw new Error('Attempted to mount a destroyed componet. Are you re-using a componet that you stored in a variable?')\n    }\n\n    var currentIndex = container._children.indexOf(child),\n        newComponent = fastn.toComponent(child);\n\n    if(newComponent !== child && ~currentIndex){\n        container._children.splice(currentIndex, 1, newComponent);\n    }\n\n    if(!~currentIndex || newComponent !== child){\n        newComponent.attach(container.scope(), 1);\n    }\n\n    if(currentIndex !== index){\n        if(~currentIndex){\n            container._children.splice(currentIndex, 1);\n        }\n        container._children.splice(index, 0, newComponent);\n    }\n\n    if(container.element){\n        if(!newComponent.element){\n            newComponent.render();\n        }\n        container._insert(newComponent.element, index);\n        newComponent.emit('insert', container);\n        container.emit('childInsert', newComponent);\n    }\n}\n\nfunction getContainerElement(){\n    return this.containerElement || this.element;\n}\n\nfunction insert(child, index){\n    var childComponent = child,\n        container = this.container,\n        fastn = this.fastn;\n\n    if(index && typeof index === 'object'){\n        childComponent = Array.prototype.slice.call(arguments);\n    }\n\n    if(isNaN(index)){\n        index = container._children.length;\n    }\n\n    if(Array.isArray(childComponent)){\n        for (var i = 0; i < childComponent.length; i++) {\n            container.insert(childComponent[i], i + index);\n        }\n    }else{\n        insertChild(fastn, container, childComponent, index);\n    }\n\n    return container;\n}\n\nmodule.exports = function(fastn, component, type, settings, children){\n    component.insert = insert.bind({\n        container: component,\n        fastn: fastn\n    });\n\n    component._insert = function(element, index){\n        var containerElement = component.getContainerElement();\n        if(!containerElement){\n            return;\n        }\n\n        if(containerElement.childNodes[index] === element){\n            return;\n        }\n\n        containerElement.insertBefore(element, containerElement.childNodes[index]);\n    };\n\n    component.remove = function(childComponent){\n        var index = component._children.indexOf(childComponent);\n        if(~index){\n            component._children.splice(index,1);\n        }\n\n        childComponent.detach(1);\n\n        if(childComponent.element){\n            component._remove(childComponent.element);\n            childComponent.emit('remove', component);\n        }\n        component.emit('childRemove', childComponent);\n    };\n\n    component._remove = function(element){\n        var containerElement = component.getContainerElement();\n\n        if(!element || !containerElement || element.parentNode !== containerElement){\n            return;\n        }\n\n        containerElement.removeChild(element);\n    };\n\n    component.empty = function(){\n        while(component._children.length){\n            component.remove(component._children.pop());\n        }\n    };\n\n    component.replaceChild = function(oldChild, newChild){\n        var index = component._children.indexOf(oldChild);\n\n        if(!~index){\n            return;\n        }\n\n        component.remove(oldChild);\n        component.insert(newChild, index);\n    };\n\n    component.getContainerElement = getContainerElement.bind(component);\n\n    component.on('render', component.insert.bind(null, component._children, 0));\n\n    component.on('attach', function(model, firm){\n        for(var i = 0; i < component._children.length; i++){\n            if(fastn.isComponent(component._children[i])){\n                component._children[i].attach(model, firm);\n            }\n        }\n    });\n\n    component.on('destroy', function(data, firm){\n        for(var i = 0; i < component._children.length; i++){\n            if(fastn.isComponent(component._children[i])){\n                component._children[i].destroy(firm);\n            }\n        }\n    });\n\n    return component;\n};"
  },
  {
    "path": "domComponents.js",
    "content": "module.exports = function(extra){\n    var components = {\n        // The _generic component is a catch-all for any component type that\n        //  doesnt match any other component constructor, eg: 'div'\n        _generic: require('./genericComponent'),\n\n        // The text component is used to render text or bindings passed as children to other components.\n        text: require('./textComponent'),\n\n        // The list component is used to render items based on a set of data.\n        list: require('./listComponent'),\n\n        // The templater component is used to render one item based on some value.\n        templater: require('./templaterComponent')\n    };\n\n    if(extra){\n        Object.keys(extra).forEach(function(key){\n            components[key] = extra[key];\n        });\n    }\n\n    return components;\n}"
  },
  {
    "path": "fancyProps.js",
    "content": "var setify = require('setify'),\n    classist = require('classist');\n\nfunction updateTextProperty(generic, element, value){\n    if(arguments.length === 2){\n        return element.textContent;\n    }\n    element.textContent = (value == null ? '' : value);\n}\n\nmodule.exports = {\n    class: function(generic, element, value){\n        if(!generic._classist){\n            generic._classist = classist(element);\n        }\n\n        if(arguments.length < 3){\n            return generic._classist();\n        }\n\n        generic._classist(value);\n    },\n    display: function(generic, element, value){\n        if(arguments.length === 2){\n            return element.style.display !== 'none';\n        }\n        element.style.display = value ? null : 'none';\n    },\n    disabled: function(generic, element, value){\n        if(arguments.length === 2){\n            return element.hasAttribute('disabled');\n        }\n        if(value){\n            element.setAttribute('disabled', 'disabled');\n        }else{\n            element.removeAttribute('disabled');\n        }\n    },\n    innerHTML: function(generic, element, value){\n        if(arguments.length === 2){\n            return element.innerHTML;\n        }\n        element.innerHTML = (value == null ? '' : value);\n    },\n    value: function(generic, element, value){\n        var inputType = element.type;\n\n        if(element.nodeName === 'INPUT' && inputType === 'date'){\n            if(arguments.length === 2){\n                return element.value ? new Date(element.value.replace(/-/g,'/').replace('T',' ')) : null;\n            }\n\n            value = value != null ? new Date(value) : null;\n\n            if(!value || isNaN(value)){\n                element.value = null;\n            }else{\n                element.value = [\n                    value.getFullYear(),\n                    ('0' + (value.getMonth() + 1)).slice(-2),\n                    ('0' + value.getDate()).slice(-2)\n                ].join('-');\n            }\n            return;\n        }\n\n        if(arguments.length === 2){\n            return element.value;\n        }\n        if(value === undefined){\n            value = null;\n        }\n\n        if(element.nodeName === 'PROGRESS'){\n            value = parseFloat(value) || 0;\n        }\n\n        setify(element, value);\n    },\n    max: function(generic, element, value) {\n        if(arguments.length === 2){\n            return element.value;\n        }\n\n        if(element.nodeName === 'PROGRESS'){\n            value = parseFloat(value) || 0;\n        }\n\n        element.max = value;\n    },\n    style: function(generic, element, value){\n        if(arguments.length === 2){\n            return element.style;\n        }\n\n        if(typeof value === 'string'){\n            element.style = value;\n            return;\n        }\n\n        for(var key in value){\n            element.style[key] = value[key];\n        }\n    },\n    type: function(generic, element, value){\n        if(arguments.length === 2){\n            return element.type;\n        }\n        element.setAttribute('type', value);\n    }\n};"
  },
  {
    "path": "firmer.js",
    "content": "// Is the entity firmer than the new firmness\nmodule.exports = function(entity, firm){\n    if(firm != null && (entity._firm === undefined || firm < entity._firm)){\n        return true;\n    }\n};"
  },
  {
    "path": "genericComponent.js",
    "content": "var containerComponent = require('./containerComponent'),\n    schedule = require('./schedule'),\n    fancyProps = require('./fancyProps'),\n    matchDomHandlerName = /^((?:el\\.)?)([^. ]+)(?:\\.(capture))?$/,\n    GENERIC = '_generic';\n\nfunction createProperties(fastn, component, settings){\n    for(var key in settings){\n        var setting = settings[key];\n\n        if(typeof setting === 'function' && !fastn.isProperty(setting) && !fastn.isBinding(setting)){\n            continue;\n        }\n\n        component.addDomProperty(key);\n    }\n}\n\nfunction trackKeyEvents(component, element, event){\n    if('_lastStates' in component && 'charCode' in event){\n        component._lastStates.unshift(element.value);\n        component._lastStates.pop();\n    }\n}\n\nfunction addDomHandler(component, element, handlerName, eventName, capture){\n    var eventParts = handlerName.split('.');\n\n    if(eventParts[0] === 'on'){\n        eventParts.shift();\n    }\n\n    var handler = function(event){\n            trackKeyEvents(component, element, event);\n            component.emit(handlerName, event, component.scope());\n        };\n\n    element.addEventListener(eventName, handler, capture);\n\n    component.on('destroy', function(){\n        element.removeEventListener(eventName, handler, capture);\n    });\n}\n\nfunction addDomHandlers(component, element, eventNames){\n    var events = eventNames.split(' ');\n\n    for(var i = 0; i < events.length; i++){\n        var eventName = events[i],\n            match = eventName.match(matchDomHandlerName);\n\n        if(!match){\n            continue;\n        }\n\n        if(match[1] || 'on' + match[2] in element){\n            addDomHandler(component, element, eventNames, match[2], match[3]);\n        }\n    }\n}\n\nfunction addAutoHandler(component, element, key, settings){\n    if(!settings[key]){\n        return;\n    }\n\n    var eventName = key.slice(2);\n    var handler = settings[key];\n    delete settings[key];\n\n    if(typeof handler === 'function'){\n        var innerHandler = handler;\n        handler = function(event){\n            trackKeyEvents(component, element, event);\n            innerHandler.call(component, event, component.scope());\n        };\n    }\n\n    if (typeof handler === 'string') {\n        var autoEvent = handler.split(':');\n\n        handler = function(event){\n            var fancyProp = fancyProps[autoEvent[1]],\n                value = fancyProp ? fancyProp(component, element) : element[autoEvent[1]];\n\n            trackKeyEvents(component, element, event);\n\n            component[autoEvent[0]](value);\n        };\n    }\n\n    element.addEventListener(eventName, handler);\n\n    component.on('destroy', function(){\n        element.removeEventListener(eventName, handler);\n    });\n}\n\nfunction addDomProperty(fastn, key, property){\n    var component = this,\n        timeout;\n\n    property = property || component[key] || fastn.property();\n    component.setProperty(key, property);\n\n    function update(){\n        var element = component.getPropertyElement(key),\n            value = property();\n\n        if(!element || component.destroyed()){\n            return;\n        }\n\n        if(\n            key === 'value' &&\n            component._lastStates &&\n            ~component._lastStates.indexOf(value)\n        ){\n            clearTimeout(timeout);\n            timeout = setTimeout(update, 50);\n            return;\n        }\n\n        var isProperty = key in element.constructor.prototype || !('getAttribute' in element),\n            fancyProp = component._fancyProps && component._fancyProps(key) || fancyProps[key],\n            previous = fancyProp ? fancyProp(component, element) : isProperty ? element[key] : element.getAttribute(key);\n\n        if(!fancyProp && !isProperty && value === null){\n            value = '';\n        }\n\n        if(value !== previous){\n            if(fancyProp){\n                fancyProp(component, element, value);\n                return;\n            }\n\n            if(isProperty){\n                element[key] = value;\n                return;\n            }\n\n            if(typeof value !== 'function' && typeof value !== 'object'){\n                if(value === undefined) {\n                    element.removeAttribute(key);                    \n                } else {\n                    element.setAttribute(key, value);                    \n                }\n            }\n        }\n    }\n\n    property.updater(update);\n}\n\nfunction onRender(){\n    var component = this,\n        element;\n\n    for(var key in component._settings){\n        element = component.getEventElement(key);\n        if(key.slice(0,2) === 'on' && key in element){\n            addAutoHandler(component, element, key, component._settings);\n        }\n    }\n\n    for(var eventKey in component._events){\n        element = component.getEventElement(key);\n        addDomHandlers(component, element, eventKey);\n    }\n}\n\nfunction render(){\n    this.element = this.element || this.createElement(this._settings.tagName || this._tagName);\n\n    if('value' in this.element){\n        this._lastStates = new Array(2);\n    }\n\n    this.emit('render');\n\n    return this;\n};\n\nfunction genericComponent(fastn, component, type, settings, children){\n    if(component.is(type)){\n        return component;\n    }\n\n    if(global.Element && type instanceof global.Element){\n        component.element = type;\n        type = component.element.tagName;\n    }\n\n    if(global.Node && type instanceof global.Node){\n        return fastn('text', { text: type }, type.textContent);\n    }\n\n    if(typeof type !== 'string'){\n        return;\n    }\n\n    if(type === GENERIC){\n        component._tagName = component._tagName || 'div';\n    }else{\n        component._tagName = type;\n    }\n\n    if(component.is(GENERIC)){\n        return component;\n    }\n\n    component.extend('_container', settings, children);\n\n    component.addDomProperty = addDomProperty.bind(component, fastn);\n    component.getEventElement = component.getContainerElement;\n    component.getPropertyElement = component.getContainerElement;\n    component.updateProperty = genericComponent.updateProperty;\n    component.createElement = genericComponent.createElement;\n\n    createProperties(fastn, component, settings);\n\n    component.render = render.bind(component);\n\n    component.on('render', onRender);\n\n    return component;\n}\n\ngenericComponent.updateProperty = function(component, property, update){\n    if(typeof document !== 'undefined' && document.contains(component.element)){\n        schedule(property, update);\n    }else{\n        update();\n    }\n};\n\ngenericComponent.createElement = function(tagName){\n    if(tagName instanceof Node){\n        return tagName;\n    }\n    return document.createElement(tagName);\n};\n\nmodule.exports = genericComponent;"
  },
  {
    "path": "index.js",
    "content": "var createProperty = require('./property'),\n    createBinding = require('./binding'),\n    BaseComponent = require('./baseComponent'),\n    Enti = require('enti'),\n    objectAssign = require('object-assign'),\n    is = require('./is');\n\nfunction inflateProperties(component, settings){\n    for(var key in settings){\n        var setting = settings[key],\n            property = component[key];\n\n        if(is.property(settings[key])){\n\n            if(is.property(property)){\n                property.destroy();\n            }\n\n            setting.addTo(component, key);\n\n        }else if(is.property(property)){\n\n            if(is.binding(setting)){\n                property.binding(setting);\n            }else{\n                property(setting);\n            }\n\n            property.addTo(component, key);\n        }\n    }\n}\n\nfunction validateExpectedComponents(components, componentName, expectedComponents){\n    expectedComponents = expectedComponents.filter(function(componentName){\n        return !(componentName in components);\n    });\n\n    if(expectedComponents.length){\n        console.warn([\n            'fastn(\"' + componentName + '\") uses some components that have not been registered with fastn',\n            'Expected component constructors: ' + expectedComponents.join(', ')\n        ].join('\\n\\n'));\n    }\n}\n\nmodule.exports = function(components, debug){\n\n    if(!components || typeof components !== 'object'){\n        throw new Error('fastn must be initialised with a components object');\n    }\n\n    components._container = components._container || require('./containerComponent');\n\n    function fastn(type){\n        var args = Array.prototype.slice.call(arguments);\n\n        var settings = args[1],\n            childrenIndex = 2,\n            settingsChild = fastn.toComponent(args[1]);\n\n        if(Array.isArray(args[1]) || settingsChild || !args[1]){\n            if(args.length > 1){\n                args[1] = settingsChild || args[1];\n            }\n            childrenIndex--;\n            settings = null;\n        }\n\n        settings = objectAssign({}, settings || {});\n\n        var types = typeof type === 'string' ? type.split(':') : Array.isArray(type) ? type : [type],\n            baseType,\n            children = args.slice(childrenIndex),\n            component = fastn.base(type, settings, children);\n\n        while(component && (baseType = types.shift())){\n            component = component.extend(baseType, settings, children);\n        }\n\n        if(!component){\n            // Type was not a component.\n            return;\n        }\n\n        component._properties = {};\n\n        inflateProperties(component, settings);\n\n        return component;\n    }\n\n    fastn.toComponent = function(component){\n        if(component == null || Array.isArray(component)){\n            return;\n        }\n\n        if(is.component(component)){\n            return component;\n        }\n\n        if(typeof component !== 'object' || component instanceof Date){\n            return fastn('text', { text: component }, component);\n        }\n\n        return fastn(component)\n    };\n\n    fastn.debug = debug;\n    fastn.property = createProperty.bind(fastn);\n    fastn.binding = createBinding(fastn);\n    fastn.isComponent = is.component;\n    fastn.isBinding = is.binding;\n    fastn.isDefaultBinding = is.defaultBinding;\n    fastn.isBindingObject = is.bindingObject;\n    fastn.isProperty = is.property;\n    fastn.components = components;\n    fastn.Model = Enti;\n    fastn.isModel = Enti.isEnti.bind(Enti);\n\n    fastn.base = function(type, settings, children){\n        return new BaseComponent(fastn, type, settings, children);\n    };\n\n    for(var key in components){\n        var componentConstructor = components[key];\n\n        if(componentConstructor.expectedComponents){\n            validateExpectedComponents(components, key, componentConstructor.expectedComponents);\n        }\n    }\n\n    return fastn;\n};\n"
  },
  {
    "path": "is.js",
    "content": "var FUNCTION = 'function',\n    OBJECT = 'object',\n    FASTNBINDING = '_fastn_binding',\n    FASTNPROPERTY = '_fastn_property',\n    FASTNCOMPONENT = '_fastn_component',\n    DEFAULTBINDING = '_default_binding';\n\nfunction isComponent(thing){\n    return thing && typeof thing === OBJECT && FASTNCOMPONENT in thing;\n}\n\nfunction isBindingObject(thing){\n    return thing && typeof thing === OBJECT && FASTNBINDING in thing;\n}\n\nfunction isBinding(thing){\n    return typeof thing === FUNCTION && FASTNBINDING in thing;\n}\n\nfunction isProperty(thing){\n    return typeof thing === FUNCTION && FASTNPROPERTY in thing;\n}\n\nfunction isDefaultBinding(thing){\n    return typeof thing === FUNCTION && FASTNBINDING in thing && DEFAULTBINDING in thing;\n}\n\nmodule.exports = {\n    component: isComponent,\n    bindingObject: isBindingObject,\n    binding: isBinding,\n    defaultBinding: isDefaultBinding,\n    property: isProperty\n};"
  },
  {
    "path": "listComponent.js",
    "content": "var MultiMap = require('multimap'),\n    merge = require('flat-merge');\n\nvar requestIdleCallback = global.requestIdleCallback || global.requestAnimationFrame || global.setTimeout;\n\nMultiMap.Map = Map;\n\nfunction each(value, fn){\n    if(!value || typeof value !== 'object'){\n        return;\n    }\n\n    if(Array.isArray(value)){\n        for(var i = 0; i < value.length; i++){\n            fn(value[i], i)\n        }\n    }else{\n        for(var key in value){\n            fn(value[key], key);\n        }\n    }\n}\n\nfunction keyFor(object, value){\n    if(!object || typeof object !== 'object'){\n        return false;\n    }\n\n    if(Array.isArray(object)){\n        var index = object.indexOf(value);\n        return index >=0 ? index : false;\n    }\n\n    for(var key in object){\n        if(object[key] === value){\n            return key;\n        }\n    }\n\n    return false;\n}\n\nmodule.exports = function(fastn, component, type, settings, children){\n\n    if(fastn.components._generic){\n        component.extend('_generic', settings, children);\n    }else{\n        component.extend('_container', settings, children);\n    }\n\n    if(!('template' in settings)){\n        console.warn('No \"template\" function was set for this templater component');\n    }\n\n    var itemsMap = new MultiMap(),\n        dataMap = new WeakMap(),\n        lastTemplate,\n        existingItem = {};\n\n    var insertQueue = [];\n    var inserting;\n\n    function updateOrCreateChild(template, item, key){\n        var child,\n            existing;\n\n        if(Array.isArray(item) && item[0] === existingItem){\n            existing = true;\n            child = item[2];\n            item = item[1];\n        }\n\n        var childModel;\n\n        if(!existing){\n            childModel = new fastn.Model({\n                item: item,\n                key: key\n            });\n\n            child = fastn.toComponent(template(childModel, component.scope()));\n            if(!child){\n                child = fastn('template');\n            }\n            child._listItem = item;\n            child._templated = true;\n\n            dataMap.set(child, childModel);\n            itemsMap.set(item, child);\n        }else{\n            childModel = dataMap.get(child);\n            childModel.set('key', key);\n        }\n\n        if(fastn.isComponent(child) && component._settings.attachTemplates !== false){\n            child.attach(childModel, 2);\n        }\n\n        return child;\n    }\n\n    function insertNextItems(template, insertionFrameTime){\n        if(inserting){\n            return;\n        }\n\n        inserting = true;\n        component.emit('insertionStart', insertQueue.length);\n\n        insertQueue.sort(function(a, b){\n            return a[2] - b[2];\n        });\n\n        function insertNext(){\n            var startTime = Date.now();\n\n            while(insertQueue.length && Date.now() - startTime < insertionFrameTime) {\n                var nextInsersion = insertQueue.shift();\n                var child = updateOrCreateChild(template, nextInsersion[0], nextInsersion[1]);\n                component.insert(child, nextInsersion[2]);\n            }\n\n            if(!insertQueue.length || component.destroyed()){\n                inserting = false;\n                if(!component.destroyed()){\n                    component.emit('insertionComplete');\n                }\n                return;\n            }\n\n            requestIdleCallback(insertNext);\n        }\n\n        insertNext();\n    }\n\n    function updateItems(){\n        insertQueue = [];\n\n        var value = component.items(),\n            template = component.template(),\n            emptyTemplate = component.emptyTemplate(),\n            insertionFrameTime = component.insertionFrameTime() || Infinity,\n            newTemplate = lastTemplate !== template;\n\n        var currentItems = merge(template ? value : []);\n\n        itemsMap.forEach(function(childComponent, item){\n            var currentKey = keyFor(currentItems, item);\n\n            if(!newTemplate && currentKey !== false){\n                currentItems[currentKey] = [existingItem, item, childComponent];\n            }else{\n                removeComponent(childComponent);\n                itemsMap.delete(item, childComponent);\n            }\n        });\n\n        var index = 0;\n        var templateIndex = 0;\n\n        function updateItem(item, key){\n            while(index < component._children.length && !component._children[index]._templated){\n                index++;\n            }\n\n            insertQueue.push([item, key, index + templateIndex]);\n            templateIndex++;\n        }\n\n        each(currentItems, updateItem);\n\n        template && insertNextItems(template, insertionFrameTime);\n\n        lastTemplate = template;\n\n        if(templateIndex === 0 && emptyTemplate){\n            var child = fastn.toComponent(emptyTemplate(component.scope()));\n            if(!child){\n                child = fastn('template');\n            }\n            child._templated = true;\n\n            itemsMap.set({}, child);\n\n            component.insert(child);\n        }\n    }\n\n    function removeComponent(childComponent){\n        component.remove(childComponent);\n        childComponent.destroy();\n    }\n\n    component.setProperty('insertionFrameTime');\n\n    component.setProperty('items',\n        fastn.property([], settings.itemChanges || 'type keys shallowStructure')\n            .on('change', updateItems)\n    );\n\n    component.setProperty('template',\n        fastn.property().on('change', updateItems)\n    );\n\n    component.setProperty('emptyTemplate',\n        fastn.property().on('change', updateItems)\n    );\n\n    return component;\n};"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"fastn\",\n  \"version\": \"2.14.5\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"node test\",\n    \"watch\": \"watchify test/index.js -o test/index.browser.js -d\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/KoryNunn/fastn.git\"\n  },\n  \"dependencies\": {\n    \"classist\": \"^1.1.1\",\n    \"enti\": \"^6.1.3\",\n    \"flat-merge\": \"^1.0.0\",\n    \"function-emitter\": \"^1.0.0\",\n    \"multimap\": \"^1.0.2\",\n    \"object-assign\": \"^4.1.1\",\n    \"same-value\": \"^1.0.2\",\n    \"setify\": \"^1.0.3\",\n    \"setprototypeof\": \"^1.1.0\",\n    \"what-changed\": \"^2.3.0\"\n  },\n  \"devDependencies\": {\n    \"browserify\": \"^14.5.0\",\n    \"console-watch\": \"^1.0.2\",\n    \"crel\": \"^4.0.1\",\n    \"dom-lightning\": \"^1.0.2\",\n    \"dom-lite\": \"^0.5.1\",\n    \"tape\": \"^5.0.1\",\n    \"tape-run\": \"^6.0.1\",\n    \"watchify\": \"^3.11.0\"\n  }\n}\n"
  },
  {
    "path": "property.js",
    "content": "var WhatChanged = require('what-changed'),\n    same = require('same-value'),\n    firmer = require('./firmer'),\n    functionEmitter = require('function-emitter'),\n    setPrototypeOf = require('setprototypeof');\n\nvar propertyProto = Object.create(functionEmitter);\n\npropertyProto._fastn_property = true;\npropertyProto._firm = 1;\n\nfunction propertyTemplate(value){\n    if(!arguments.length){\n        return this.observable && typeof this.observable === 'function' && this.observable() || this.property._value;\n    }\n\n    if(!this.destroyed){\n        if(this.observable){\n            typeof this.observable === 'function' && this.observable(value);\n            return this.property;\n        }\n\n        this.valueUpdate(value);\n    }\n\n    return this.property;\n}\n\nfunction changeChecker(current, changes){\n    if(changes){\n        var changes = new WhatChanged(current, changes);\n\n        return function(value){\n            return changes.update(value).any;\n        };\n    }else{\n        var lastValue = current;\n        return function(newValue){\n            if(!same(lastValue, newValue)){\n                lastValue = newValue;\n                return true;\n            }\n        };\n    }\n}\n\nfunction propertyBinding(newBinding){\n    if(!arguments.length){\n        return this.observable;\n    }\n\n    if(typeof newBinding === 'string' || typeof newBinding === 'number'){\n        newBinding = this.fastn.binding(newBinding);\n    }\n\n    if(newBinding === this.observable){\n        return this.property;\n    }\n\n    if(this.observable){\n        this.observable.removeListener('change', this.valueUpdate);\n    }\n\n    this.observable = newBinding;\n\n    if(this.model){\n        this.property.attach(this.model, this.property._firm);\n    }\n\n    this.observable.on('change', this.valueUpdate);\n    if(typeof this.observable === 'function'){\n        this.valueUpdate(this.observable());\n    } else {\n        this.valueUpdate(undefined);\n    }\n\n    return this.property;\n}\n\nfunction attachProperty(object, firm){\n    if(firmer(this.property, firm)){\n        return this.property;\n    }\n\n    this.property._firm = firm;\n\n    if(!(object instanceof Object)){\n        object = {};\n    }\n\n    if(this.observable){\n        this.model = object;\n        this.observable.attach && this.observable.attach(object, 1);\n    }\n\n    if(this.property._events && 'attach' in this.property._events){\n        this.property.emit('attach', object, 1);\n    }\n\n    return this.property;\n};\n\nfunction detachProperty(firm){\n    if(firmer(this.property, firm)){\n        return this.property;\n    }\n\n    if(this.observable){\n        this.observable.removeListener('change', this.valueUpdate);\n        this.observable.detach && this.observable.detach(1);\n        this.model = null;\n    }\n\n    if(this.property._events && 'detach' in this.property._events){\n        this.property.emit('detach', 1);\n    }\n\n    return this.property;\n};\n\nfunction updateProperty(){\n    if(!this.destroyed){\n\n        if(this.property._update){\n            this.property._update(this.property._value, this.property);\n        }\n\n        this.property.emit('update', this.property._value);\n    }\n    return this.property;\n};\n\nfunction propertyUpdater(fn){\n    if(!arguments.length){\n        return this.property._update;\n    }\n    this.property._update = fn;\n    return this.property;\n};\n\nfunction destroyProperty(){\n    if(!this.destroyed){\n        this.destroyed = true;\n\n        this.property\n            .removeAllListeners('change')\n            .removeAllListeners('update')\n            .removeAllListeners('attach');\n\n        this.property.emit('destroy');\n        this.property.detach();\n        if(this.observable){\n            this.observable.destroy && this.observable.destroy(true);\n        }\n    }\n    return this.property;\n};\n\nfunction propertyDestroyed(){\n    return this.destroyed;\n};\n\nfunction addPropertyTo(component, key){\n    component.setProperty(key, this.property);\n\n    return this.property;\n};\n\nfunction createProperty(currentValue, changes, updater){\n    if(typeof changes === 'function'){\n        updater = changes;\n        changes = null;\n    }\n\n    var propertyScope = {\n            fastn: this,\n            hasChanged: changeChecker(currentValue, changes)\n        },\n        property = propertyTemplate.bind(propertyScope);\n\n    propertyScope.valueUpdate = function(value){\n        property._value = value;\n        if(!propertyScope.hasChanged(value)){\n            return;\n        }\n        property.emit('change', property._value);\n        property.update();\n    };\n\n    var property = propertyScope.property = propertyTemplate.bind(propertyScope);\n\n    property._value = currentValue;\n    property._update = updater;\n\n    setPrototypeOf(property, propertyProto);\n\n    property.binding = propertyBinding.bind(propertyScope);\n    property.attach = attachProperty.bind(propertyScope);\n    property.detach = detachProperty.bind(propertyScope);\n    property.update = updateProperty.bind(propertyScope);\n    property.updater = propertyUpdater.bind(propertyScope);\n    property.destroy = destroyProperty.bind(propertyScope);\n    property.destroyed = propertyDestroyed.bind(propertyScope);\n    property.addTo = addPropertyTo.bind(propertyScope);\n\n    return property;\n};\n\nmodule.exports = createProperty;"
  },
  {
    "path": "schedule.js",
    "content": "var todo = [],\n    todoKeys = [],\n    scheduled,\n    updates = 0;\n\nfunction run(){\n    var startTime = Date.now();\n\n    while(todo.length && Date.now() - startTime < 16){\n        todoKeys.shift();\n        todo.shift()();\n    }\n\n    if(todo.length){\n        requestAnimationFrame(run);\n    }else{\n        scheduled = false;\n    }\n}\n\nfunction schedule(key, fn){\n    if(~todoKeys.indexOf(key)){\n        return;\n    }\n\n    todo.push(fn);\n    todoKeys.push(key);\n\n    if(!scheduled){\n        scheduled = true;\n        requestAnimationFrame(run);\n    }\n}\n\nmodule.exports = schedule;"
  },
  {
    "path": "templaterComponent.js",
    "content": "module.exports = function(fastn, component, type, settings, children){\n    var itemModel = new fastn.Model({});\n\n    if(!('template' in settings)){\n        console.warn('No \"template\" function was set for this templater component');\n    }\n\n    function replaceElement(element){\n        if(component.element && component.element.parentNode){\n            component.element.parentNode.replaceChild(element, component.element);\n        }\n        component.element = element;\n    }\n\n    function update(){\n\n        var value = component.data(),\n            template = component.template();\n\n        itemModel.set('item', value);\n\n        var newComponent;\n\n        if(template){\n           newComponent = fastn.toComponent(template(itemModel, component.scope(), component._currentComponent));\n        }\n\n        if(component._currentComponent && component._currentComponent !== newComponent){\n            if(fastn.isComponent(component._currentComponent)){\n                component._currentComponent.destroy();\n            }\n        }\n\n        component._currentComponent = newComponent;\n\n        if(!newComponent){\n            replaceElement(component.emptyElement);\n            return;\n        }\n\n        if(fastn.isComponent(newComponent)){\n            if(component._settings.attachTemplates !== false){\n                newComponent.attach(itemModel, 2);\n            }else{\n                newComponent.attach(component.scope(), 1);\n            }\n\n            if(component.element && component.element !== newComponent.element){\n                if(newComponent.element == null){\n                    newComponent.render();\n                }\n                replaceElement(component._currentComponent.element);\n            }\n        }\n    }\n\n    component.render = function(){\n        var element;\n        component.emptyElement = document.createTextNode('');\n        if(component._currentComponent){\n            component._currentComponent.render();\n            element = component._currentComponent.element;\n        }\n        component.element = element || component.emptyElement;\n        component.emit('render');\n        return component;\n    };\n\n    component.setProperty('data',\n        fastn.property(undefined, settings.dataChanges || 'value structure')\n            .on('change', update)\n    );\n\n    component.setProperty('template',\n        fastn.property(undefined, 'value reference')\n            .on('change', update)\n    );\n\n    component.on('destroy', function(){\n        if(fastn.isComponent(component._currentComponent)){\n            component._currentComponent.destroy();\n        }\n    });\n\n    component.on('attach', function(data){\n        if(fastn.isComponent(component._currentComponent)){\n            component._currentComponent.attach(component.scope(), 1);\n        }\n    });\n\n    return component;\n};"
  },
  {
    "path": "test/attach.js",
    "content": "var test = require('tape'),\n    Enti = require('enti'),\n    createFastn = require('./createFastn');\n\ntest('manual attach', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span')\n        );\n\n    parent.attach({\n        foo:'bar'\n    });\n\n    t.deepEqual(parent.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.deepEqual(child.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.equal(parent.scope().get('.'), child.scope().get('.'));\n\n});\n\ntest('weak attach attempt', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span')\n        );\n\n    parent.attach({\n        foo:'bar'\n    });\n\n    child.attach({\n        baz: 'inga'\n    }, 0);\n\n    t.deepEqual(parent.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.deepEqual(child.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.equal(parent.scope().get('.'), child.scope().get('.'));\n});\n\ntest('firmer attach attempt', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span')\n        );\n\n    parent.attach({\n        foo:'bar'\n    });\n\n    child.attach({\n        baz: 'inga'\n    }, 1);\n\n    t.deepEqual(parent.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.deepEqual(child.scope().get('.'), {\n        baz:'inga'\n    });\n\n    t.notEqual(parent.scope().get('.'), child.scope().get('.'));\n});\n\ntest('firmest attach', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span')\n        );\n\n    parent.attach({\n        foo:'bar'\n    });\n\n    child.attach({\n        baz: 'inga'\n    });\n\n    t.deepEqual(parent.scope().get('.'), {\n        foo:'bar'\n    });\n\n    t.deepEqual(child.scope().get('.'), {\n        baz:'inga'\n    });\n\n    t.notEqual(parent.scope().get('.'), child.scope().get('.'));\n});\n"
  },
  {
    "path": "test/binding.js",
    "content": "var test = require('tape'),\n    createBinding = require('../index')({}).binding,\n    Enti = require('enti');\n\ntest('invalid path', function(t){\n    t.plan(5);\n\n    t.throws(function(){\n        createBinding(true);\n    });\n    t.throws(function(){\n        createBinding({});\n    });\n    t.throws(function(){\n        createBinding(function(){});\n    });\n    t.throws(function(){\n        createBinding(null);\n    });\n    t.throws(function(){\n        createBinding(undefined);\n    });\n});\n\ntest('simple binding initialisation', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    var model = {},\n        enti = new Enti(model);\n\n    t.equal(binding(), undefined);\n\n    enti.set('foo', 'bar');\n\n    t.equal(binding(), undefined);\n\n    binding.attach(model);\n\n    t.equal(binding(), 'bar');\n});\n\ntest('initial attach doesnt cause emit', function(t){\n    t.plan(1);\n\n    var binding = createBinding('foo');\n\n    binding.on('change', () => t.pass('Recieved change'));\n\n    binding.attach();\n    binding.attach({});\n});\n\ntest('simple binding set', function(t){\n    t.plan(2);\n\n    var binding = createBinding('foo');\n\n    binding.attach({});\n\n    t.equal(binding(), undefined);\n\n    binding('bazinga');\n\n    t.equal(binding(), 'bazinga');\n});\n\ntest('simple binding event', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    var model = {},\n        enti = new Enti(model);\n\n    binding.attach(model);\n\n    binding.once('change', function(value){\n        t.equal(value, 'bar');\n        t.equal(binding(), 'bar');\n    });\n\n    enti.set('foo', 'bar');\n\n    binding.once('detach', function(){\n        t.equal(binding(), undefined);\n    });\n\n    binding.detach();\n\n    enti.set('foo', 'baz');\n});\n\ntest('no model', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    t.equal(binding(), undefined);\n\n    binding.on('change', function(value){\n        t.equal(value, 'bar');\n        console.log(value)\n    });\n\n    binding('bar');\n    console.log(binding())\n\n    t.equal(binding(), 'bar');\n});\n\ntest('drill get', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n    binding.attach(data);\n\n    t.equal(binding(), 123);\n\n    model.set('foo', {\n        bar: 456\n    });\n\n    t.equal(binding(), 456);\n});\n\ntest('drill change', function(t){\n    t.plan(1);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n    binding.attach(data);\n\n    binding.on('change', function(){\n        t.pass('target changed');\n    });\n\n    model.set('foo', {\n        bar: 456\n    });\n});\n\ntest('drill attach', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n\n    binding.once('change', function(value){\n        t.equal(value, 123);\n    });\n\n    binding.attach(data);\n\n    binding.once('change', function(value){\n        t.equal(value, 456);\n    });\n\n    model.set('foo', {\n        bar: 456\n    });\n});\n\ntest('drill set', function(t){\n    t.plan(1);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        fooModel = new Enti(data.foo),\n        binding = createBinding('foo.bar');\n\n\n    fooModel.on('bar', function(value){\n        t.equal(value, 456);\n    });\n\n    binding.attach(data);\n\n    binding(456);\n});\n\ntest('drill multiple', function(t){\n    t.plan(3);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        fooModel = new Enti(data.foo),\n        binding = createBinding('foo.bar');\n\n\n    fooModel.once('bar', function(value){\n        t.equal(value, 456);\n    });\n\n    binding.attach(data);\n\n    binding(456);\n\n    binding.once('change', function(value){\n        t.equal(value, 789);\n    });\n\n    fooModel.set('bar', 789);\n\n    binding.once('change', function(value){\n        t.equal(value, 987);\n    });\n\n    binding(987);\n});\n\ntest('fuse', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: 1,\n            bar: 2,\n            baz: 3\n        },\n        model = new Enti(data),\n        binding = createBinding('foo', 'bar', 'baz', function(foo, bar, baz){\n            return foo + bar + baz;\n        });\n\n    binding.attach(data);\n\n    binding(2);\n\n    binding.once('change', function(value){\n        t.equal(value, 7);\n    });\n\n    model.set('bar', 3);\n\n    binding.once('change', function(value){\n        t.equal(value, 3);\n    });\n\n    binding(3);\n});\n\ntest('fuse attached to object with result', function(t){\n    t.plan(1);\n\n    var data = {\n            foo: 1,\n            bar: 2,\n            result: 'result'\n        },\n        binding = createBinding('foo', 'bar', function(foo, bar){\n            return foo + bar;\n        });\n\n    binding.on('change', value => t.equal(value, 3));\n    binding.attach(data);\n});\n\ntest('fuse firmness', function(t){\n    t.plan(1);\n\n    var data1 = {\n            foo: 1\n        },\n        binding = createBinding('foo', function(foo){\n            return foo;\n        });\n\n    binding.on('change', value => t.equal(value, 1));\n\n    binding.attach({ foo: 1 });\n\n    binding.attach({ foo: 2 }, 1);\n});\n\ntest('fuse destroy inner used', function(t){\n    t.plan(4);\n\n    var data1 = {\n            foo: 1\n        },\n        data2 = {\n            bar: 1\n        },\n        innerBinding = createBinding('foo').attach(data1),\n        binding = createBinding(innerBinding, 'bar', function(foo, bar){\n            return foo + bar;\n        });\n\n    // Add a listener to the inner binding\n    // This should prevent destruction.\n    innerBinding.on('change', function(){\n        t.pass('Inner binding changed');\n    });\n\n    binding.attach(data2);\n\n    binding.once('change', function(value){\n        t.equal(value, 3);\n    });\n\n    Enti.set(data1, 'foo', 2);\n\n    binding.once('change', function(value){\n        t.fail('No event should occur since the binding is detached');\n    });\n\n    binding.destroy();\n\n    t.notOk(innerBinding.destroyed(), 'inner binding should not be destroyed');\n\n    Enti.set(data1, 'foo', 3);\n});\n\ntest('fuse set', function(t){\n    t.plan(1);\n\n    var data = {\n        a: 1,\n        b: 2,\n        c: 3\n    };\n\n    var binding = createBinding('a', 'b', 'c', function(a, b, c){\n        return a + b + c;\n    }, x => x).attach(data);\n\n    binding(2);\n\n    t.equal(data.a, 2);\n});\n\ntest('filter', function(t){\n    t.plan(2);\n\n    var data = {},\n        model = new Enti(data),\n        binding = createBinding('foo|*');\n\n    binding.attach(data);\n\n    binding.on('change', function(value){\n        t.pass();\n    });\n\n    model.set('foo', []);\n\n    Enti.set(data.foo, 0, {});\n});\n\ntest('things', function(t){\n    t.plan(2);\n\n    var data = {},\n        model = new Enti(data),\n        binding = createBinding('foo|*.bar');\n\n    binding.attach(data);\n\n    binding.on('change', function(value){\n        t.pass();\n    });\n\n    model.set('foo', [{}]);\n\n    Enti.set(data.foo[0], 'bar', true);\n});\n\ntest('clone', function(t){\n    t.plan(4);\n\n    var data1 = {foo:1},\n        data2 = {foo:2},\n        binding = createBinding('foo');\n\n    binding.attach(data1);\n\n    t.equal(binding(), 1, 'Original binding has correct data');\n\n    var newBinding = binding.clone();\n\n    t.equal(newBinding(), undefined, 'New binding has no data');\n\n    newBinding.attach(data2);\n\n    t.equal(newBinding(), 2, 'New binding has new data');\n\n    t.equal(binding(), 1, 'Original binding still has original data');\n});\n\ntest('clone with attachment', function(t){\n    t.plan(2);\n\n    var data1 = {foo:1},\n        binding = createBinding('foo');\n\n    binding.attach(data1);\n\n    t.equal(binding(), 1, 'Original binding has correct data');\n\n    var newBinding = binding.clone(true);\n\n    t.equal(newBinding(), 1, 'New binding has same data');\n});\n\ntest('clone fuse', function(t){\n    t.plan(2);\n\n    var data1 = {foo:1, bar:2},\n        binding = createBinding('foo', 'bar', function(foo, bar){\n            return foo + bar;\n        });\n\n    binding.attach(data1);\n\n    t.equal(binding(), 3, 'Original binding has correct data');\n\n    var newBinding = binding.clone(true);\n\n    t.equal(newBinding(), 3, 'New binding has same data');\n});\n\ntest('binding as a bindings target', function(t){\n    t.plan(1);\n\n    var binding1 = createBinding('foo'),\n        binding2 = createBinding('bar');\n\n    binding1(binding2);\n\n    t.equal(binding1(), binding2, 'binding1 value correctly set to binding2');\n});\n\ntest('binding as own target', function(t){\n    t.plan(1);\n\n    var binding = createBinding('foo');\n\n    binding(binding);\n\n    t.equal(binding(), binding, 'binding value correctly set to self');\n});\n\ntest('value-only binding', function(t){\n    t.plan(1);\n\n    var binding = createBinding();\n\n    binding('foo');\n\n    t.equal(binding(), 'foo', 'binding value correctly set to foo');\n});\n\ntest('value-only binding cannot be attached', function(t){\n    t.plan(1);\n\n    var binding = createBinding();\n\n    binding('foo');\n\n    binding.attach({\n        value: 'bar'\n    });\n\n    t.equal(binding(), 'foo', 'binding value correctly set to foo');\n});\n\ntest('destroy', function(t){\n    t.plan(1);\n\n    var binding = createBinding().on('change', function(){\n        t.pass('binding changed');\n    });\n\n    binding('foo');\n\n    binding.destroy();\n\n    binding('bar');\n});\n\ntest('soft destroy', function(t){\n    t.plan(2);\n\n    var binding = createBinding().on('change', function(){\n        t.pass('binding changed');\n    });\n\n    binding('foo');\n\n    binding.destroy(true);\n\n    binding('bar');\n});\n\ntest('soft destroy 2', function(t){\n    t.plan(1);\n\n    function changeHandler(){\n        t.pass('binding changed');\n    }\n\n    var binding = createBinding().on('change', changeHandler);\n\n    binding('foo');\n\n    binding.removeListener('change', changeHandler);\n    binding.destroy(true);\n\n    binding('bar');\n});\n\ntest('model attach', function(t){\n    t.plan(2);\n\n    var model = new Enti();\n\n    var binding = createBinding('a');\n\n    binding.attach(model);\n\n    t.equal(binding(), undefined);\n\n    model.attach({\n        a: 2\n    });\n\n    t.equal(binding(), 2);\n\n});\n\ntest('from', function(t){\n    t.plan(3);\n\n    var binding = createBinding(),\n        value = 5;\n\n    binding(10);\n\n    var from1 = createBinding.from(binding);\n    var from2 = createBinding.from(value);\n\n    t.equal(from1(), 10);\n    t.equal(from1, binding);\n    t.equal(from2(), 5);\n\n});\n\ntest('binding as path', function(t){\n    t.plan(1);\n\n    var binding1 = createBinding(),\n        binding2 = createBinding(binding1);\n\n    binding1(10);\n\n    t.equal(binding2(), 10);\n\n});\n\ntest('detach memory usage', function(t){\n    t.plan(1);\n\n    var data = {\n        foo: null\n    };\n\n    var runs = 0;\n\n    function run(){\n        var bindings = [];\n        for(var i = 0; i < 1000; i++){\n            bindings.push(createBinding('foo').attach(data));\n        }\n\n        Enti.set(data, 'foo', runs);\n\n        bindings.map(function(binding){\n            binding.detach();\n        });\n\n        setTimeout(function(){\n            if(runs++ < 10){\n                run();\n            } else {\n                t.pass();\n            }\n        });\n    }\n\n    run();\n});\n"
  },
  {
    "path": "test/changes.js",
    "content": "const righto = require('righto');\r\n\r\nconst outputOnError = error => { error && console.log(error); };\r\n\r\nfunction runTest (fn) {\r\n  if (fn.constructor.name === 'GeneratorFunction') {\r\n    return function () {\r\n      const generator = righto.iterate(fn);\r\n      const result = righto.apply(null, [generator].concat(Array.from(arguments)));\r\n      result(outputOnError);\r\n    };\r\n  }\r\n\r\n  return fn;\r\n}\r\n\r\nfunction rightoTest (name, fn) {\r\n  test(name, runTest(fn));\r\n}\r\n\r\nmodule.exports = rightoTest;"
  },
  {
    "path": "test/component.js",
    "content": "var test = require('tape'),\n    Enti = require('enti'),\n    createFastn = require('./createFastn');\n\ntest('binding', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var data = {\n            foo:{\n                bar:1\n            }\n        },\n        component = fastn('div');\n\n    component.attach(data);\n\n    t.equal(component.scope().get('.'), data);\n\n    component.binding('foo');\n\n    t.equal(component.scope().get('.'), data.foo);\n});\n\ntest('pre-created component', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn({\n        custom: function(fastn, component, type, settings, children){\n            t.pass('Used custom constructor');\n            return component;\n        }\n    });\n\n    var data = {\n            foo:{\n                bar:1\n            }\n        },\n        component = fastn('custom');\n\n    component.attach(data);\n\n    t.equal(component.scope().get('.'), data);\n\n    component.binding('foo');\n\n    t.equal(component.scope().get('.'), data.foo);\n});\n\ntest('auto extend component', function(t){\n\n    t.plan(6);\n\n    var fastn = createFastn({\n        foo: function(fastn, component, type, settings, children){\n            t.pass('Used foo constructor');\n            return component;\n        },\n        bar: function(fastn, component, type, settings, children){\n            t.pass('Used bar constructor');\n            return component;\n        },\n        baz: function(fastn, component, type, settings, children){\n            t.pass('Used baz constructor');\n            return component;\n        }\n    });\n\n    var component = fastn('foo:bar:baz');\n\n    t.ok(component.is('foo'), 'componant is foo');\n    t.ok(component.is('bar'), 'componant is bar');\n    t.ok(component.is('baz'), 'componant is baz');\n});\n\ntest('manual extend component', function(t){\n\n    t.plan(6);\n\n    var fastn = createFastn({\n        foo: function(fastn, component, type, settings, children){\n            t.pass('Used foo constructor');\n            return component;\n        },\n        bar: function(fastn, component, type, settings, children){\n            t.pass('Used bar constructor');\n            return component;\n        },\n        baz: function(fastn, component, type, settings, children){\n            t.pass('Used baz constructor');\n            return component;\n        }\n    });\n\n    var component = fastn('foo');\n\n    component.extend('bar', {});\n\n    component.extend('baz', {});\n\n    t.ok(component.is('foo'), 'componant is foo');\n    t.ok(component.is('bar'), 'componant is bar');\n    t.ok(component.is('baz'), 'componant is baz');\n});\n\ntest('cannot double-extend component', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn({\n        foo: function(fastn, component, type, settings, children){\n            t.pass('Used foo constructor');\n            return component;\n        },\n        bar: function(fastn, component, type, settings, children){\n            t.pass('Used bar constructor');\n            return component;\n        }\n    });\n\n    var component = fastn('foo');\n\n    component.extend('bar', {});\n\n    // Shouldn't cause another call to bar constructor.\n    component.extend('bar', {});\n\n    t.ok(component.is('foo'), 'componant is foo');\n    t.ok(component.is('bar'), 'componant is bar');\n});"
  },
  {
    "path": "test/components.js",
    "content": "module.exports = function(components){\n    if(!components){\n        components = {};\n    }\n\n    var genericComponent = require('../genericComponent'),\n        textComponent = require('../textComponent');\n\n    // dont do fancy requestAnimationFrame scheduling that is hard to test.\n    genericComponent.updateProperty = function(generic, property, update){\n        update();\n    };\n\n    genericComponent.createElement = function(tagName){\n        if(tagName instanceof Node){\n            return tagName;\n        }\n        return document.createElement(tagName);\n    };\n\n    textComponent.createTextNode = document.createTextNode.bind(document);\n\n    components._generic = genericComponent;\n    components.list = require('../listComponent');\n    components.templater = require('../templaterComponent');\n    components.text = textComponent;\n\n    return components;\n};"
  },
  {
    "path": "test/container.js",
    "content": "var test = require('tape'),\n    createFastn = require('./createFastn');\n\ntest('children are added', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span')\n        );\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(parent.element.childNodes.length, 1);\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\ntest('undefined or null children are ignored', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var child,\n        parent = fastn('div',\n            child = fastn('span'),\n            undefined,\n            null\n        );\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(parent.element.childNodes.length, 1);\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\ntest('flatten children', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var parent = fastn('div',\n            [fastn('span'), fastn('span')],\n            fastn('span')\n        );\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(parent.element.childNodes.length, 3);\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\ntest('insert many after current', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var parent = fastn('div',\n            fastn('span', '1'),\n            fastn('span', '2')\n        );\n\n    parent.insert(\n        fastn('span', '3'),\n        fastn('span', '4')\n    );\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\ntest('insert returns container', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var container = fastn('div');\n\n    t.equal(container.insert(fastn('span')), container);\n\n    container.destroy();\n\n});\n\ntest('children passed attachment', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var container = fastn('div', fastn.binding('foo'));\n\n    container.render();\n\n    container.attach({foo: 'bar'});\n\n    t.equal(container.element.textContent, 'bar');\n\n    container.attach({foo: 'baz'});\n\n    t.equal(container.element.textContent, 'baz');\n\n    container.destroy();\n\n});\n\ntest('children passed model change attachment', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var container = fastn('div', fastn.binding('foo')),\n        model = new fastn.Model({foo: 'bar'});\n\n    container.render();\n\n    container.attach(model);\n\n    t.equal(container.element.textContent, 'bar');\n\n    model.attach({foo: 'baz'});\n\n    t.equal(container.element.textContent, 'baz');\n\n    container.destroy();\n\n});\n\ntest('insert undefined', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var container = fastn('div');\n\n    container.insert(undefined);\n\n    t.equal(container.children().length, 0, 'Nothing was added');\n\n});\n\ntest('insert undefined in array', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var container = fastn('div');\n\n    container.insert([1, undefined, 2]);\n\n    t.equal(container.children().length, 2, 'Only values added');\n\n});\n\ntest('insert mixed array', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var container = fastn('div');\n\n    container.insert([\n        undefined,\n        null,\n        false,\n        1,\n        '2',\n        NaN\n    ]);\n\n    t.equal(container.children().length, 3, 'Only values added');\n\n});\n\ntest('insert destroyed component throws', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var container = fastn('div');\n    var child = fastn('div');\n    child.destroy();\n\n    t.throws(function(){\n        container.insert(child);\n    }); \n});"
  },
  {
    "path": "test/createFastn.js",
    "content": "var merge = require('flat-merge');\n\nmodule.exports = function createFastn(components){\n    return require('../')(require('./components')(components));\n};"
  },
  {
    "path": "test/customBinding.js",
    "content": "var test = require('tape'),\n    createBinding = require('../binding'),\n    Enti = require('enti');\n\ntest('simple binding initialisation', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    var model = {},\n        enti = new Enti(model);\n\n    t.equal(binding(), undefined);\n\n    enti.set('foo', 'bar');\n\n    t.equal(binding(), undefined);\n\n    binding.attach(model);\n\n    t.equal(binding(), 'bar');\n});\n\ntest('simple binding set', function(t){\n    t.plan(2);\n\n    var binding = createBinding('foo');\n\n    binding.attach({});\n\n    t.equal(binding(), undefined);\n\n    binding('bazinga');\n\n    t.equal(binding(), 'bazinga');\n});\n\ntest('simple binding event', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    var model = {},\n        enti = new Enti(model);\n\n    binding.attach(model);\n\n    binding.once('change', function(value){\n        t.equal(value, 'bar');\n        t.equal(binding(), 'bar');\n    });\n\n    enti.set('foo', 'bar');\n\n    binding.once('detach', function(){\n        t.equal(binding(), undefined);\n    });\n\n    binding.detach();\n\n    enti.set('foo', 'baz');\n});\n\ntest('no model', function(t){\n    t.plan(3);\n\n    var binding = createBinding('foo');\n\n    t.equal(binding(), undefined);\n\n    binding.on('change', function(value){\n        t.equal(value, 'bar');\n        console.log(value)\n    });\n\n    binding('bar');\n    console.log(binding())\n\n    t.equal(binding(), 'bar');\n});\n\ntest('drill get', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n    binding.attach(data);\n\n    t.equal(binding(), 123);\n\n    model.set('foo', {\n        bar: 456\n    });\n\n    t.equal(binding(), 456);\n});\n\ntest('drill change', function(t){\n    t.plan(1);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n    binding.attach(data);\n\n    binding.on('change', function(){\n        t.pass('target changed');\n    });\n\n    model.set('foo', {\n        bar: 456\n    });\n});\n\ntest('drill attach', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        binding = createBinding('foo.bar');\n\n\n    binding.once('change', function(value){\n        t.equal(value, 123);\n    });\n\n    binding.attach(data);\n\n    binding.once('change', function(value){\n        t.equal(value, 456);\n    });\n\n    model.set('foo', {\n        bar: 456\n    });\n});\n\ntest('drill set', function(t){\n    t.plan(1);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        fooModel = new Enti(data.foo),\n        binding = createBinding('foo.bar');\n\n\n    fooModel.on('bar', function(value){\n        t.equal(value, 456);\n    });\n\n    binding.attach(data);\n\n    binding(456);\n});\n\ntest('drill multiple', function(t){\n    t.plan(3);\n\n    var data = {\n            foo: {\n                bar: 123\n            }\n        },\n        model = new Enti(data),\n        fooModel = new Enti(data.foo),\n        binding = createBinding('foo.bar');\n\n\n    fooModel.once('bar', function(value){\n        t.equal(value, 456);\n    });\n\n    binding.attach(data);\n\n    binding(456);\n\n    binding.once('change', function(value){\n        t.equal(value, 789);\n    });\n\n    fooModel.set('bar', 789);\n\n    binding.once('change', function(value){\n        t.equal(value, 987);\n    });\n\n    binding(987);\n});\n\ntest('fuse', function(t){\n    t.plan(2);\n\n    var data = {\n            foo: 1,\n            bar: 2,\n            baz: 3\n        },\n        model = new Enti(data),\n        binding = createBinding('foo', 'bar', 'baz', function(foo, bar, baz){\n            return foo + bar + baz;\n        });\n\n    binding.attach(data);\n\n    binding(2);\n\n    binding.once('change', function(value){\n        t.equal(value, 7);\n    });\n\n    model.set('bar', 3);\n\n    binding.once('change', function(value){\n        t.equal(value, 3);\n    });\n\n    binding(3);\n});\n\ntest('filter', function(t){\n    t.plan(2);\n\n    var data = {},\n        model = new Enti(data),\n        binding = createBinding('foo|*');\n\n    binding.attach(data);\n\n    binding.on('change', function(value){\n        t.pass();\n    });\n\n    model.set('foo', []);\n\n    Enti.set(data.foo, 0, {});\n});\n\ntest('things', function(t){\n    t.plan(2);\n\n    var data = {},\n        model = new Enti(data),\n        binding = createBinding('foo|*.bar');\n\n    binding.attach(data);\n\n    binding.on('change', function(value){\n        t.pass();\n    });\n\n    model.set('foo', [{}]);\n\n    Enti.set(data.foo[0], 'bar', true);\n});\n\ntest('clone', function(t){\n    t.plan(4);\n\n    var data1 = {foo:1},\n        data2 = {foo:2},\n        binding = createBinding('foo');\n\n    binding.attach(data1);\n\n    t.equal(binding(), 1, 'Original binding has correct data');\n\n    var newBinding = binding.clone();\n\n    t.equal(newBinding(), undefined, 'New binding has no data');\n\n    newBinding.attach(data2);\n\n    t.equal(newBinding(), 2, 'New binding has new data');\n\n    t.equal(binding(), 1, 'Original binding still has original data');\n});\n\ntest('clone with attachment', function(t){\n    t.plan(2);\n\n    var data1 = {foo:1},\n        binding = createBinding('foo');\n\n    binding.attach(data1);\n\n    t.equal(binding(), 1, 'Original binding has correct data');\n\n    var newBinding = binding.clone(true);\n\n    t.equal(newBinding(), 1, 'New binding has same data');\n});\n\ntest('clone fuse', function(t){\n    t.plan(2);\n\n    var data1 = {foo:1, bar:2},\n        binding = createBinding('foo', 'bar', function(foo, bar){\n            return foo + bar;\n        });\n\n    binding.attach(data1);\n\n    t.equal(binding(), 3, 'Original binding has correct data');\n\n    var newBinding = binding.clone(true);\n\n    t.equal(newBinding(), 3, 'New binding has same data');\n});\n\ntest('binding as a bindings target', function(t){\n    t.plan(1);\n\n    var binding1 = createBinding('foo'),\n        binding2 = createBinding('bar');\n\n    binding1(binding2);\n\n    t.equal(binding1(), binding2, 'binding1 value correctly set to binding2');\n});\n\ntest('binding as own target', function(t){\n    t.plan(1);\n\n    var binding = createBinding('foo');\n\n    binding(binding);\n\n    t.equal(binding(), binding, 'binding value correctly set to self');\n});\n\ntest('value-only binding', function(t){\n    t.plan(1);\n\n    var binding = createBinding();\n\n    binding('foo');\n\n    t.equal(binding(), 'foo', 'binding value correctly set to foo');\n});\n\ntest('value-only binding cannot be attached', function(t){\n    t.plan(1);\n\n    var binding = createBinding();\n\n    binding('foo');\n\n    binding.attach({\n        value: 'bar'\n    });\n\n    t.equal(binding(), 'foo', 'binding value correctly set to foo');\n});\n\ntest('destroy', function(t){\n    t.plan(1);\n\n    var binding = createBinding().on('change', function(){\n        t.pass('binding changed');\n    });\n\n    binding('foo');\n\n    binding.destroy();\n\n    binding('bar');\n});\n\ntest('soft destroy', function(t){\n    t.plan(2);\n\n    var binding = createBinding().on('change', function(){\n        t.pass('binding changed');\n    });\n\n    binding('foo');\n\n    binding.destroy(true);\n\n    binding('bar');\n});\n\ntest('soft destroy 2', function(t){\n    t.plan(1);\n\n    function changeHandler(){\n        t.pass('binding changed');\n    }\n\n    var binding = createBinding().on('change', changeHandler);\n\n    binding('foo');\n\n    binding.removeListener('change', changeHandler);\n    binding.destroy(true);\n\n    binding('bar');\n});\n\ntest('model attach', function(t){\n    t.plan(2);\n\n    var model = new Enti();\n\n    var binding = createBinding('a');\n\n    binding.attach(model);\n\n    t.equal(binding(), undefined);\n\n    model.attach({\n        a: 2\n    });\n\n    t.equal(binding(), 2);\n\n});"
  },
  {
    "path": "test/customModel.js",
    "content": "var test = require('tape'),\n    EventEmitter = require('events'),\n    createFastn = require('../index');\n\nvar allModels = new Set();\n\nfunction CustomModel(instance){\n    allModels.add(this);\n\n    this._model = instance;\n\n    this;\n\n    return this;\n}\nCustomModel.get = function(target, key){\n    var match = key.match(matchKeys);\n\n    if(!match){\n        return;\n    }\n\n    while(match[2]){\n        if(!target){\n            return;\n        }\n\n        target = target[match[1]];\n        match = match[2].match(matchKeys);\n    }\n\n    if(!target){\n        return;\n    }\n\n    return target[match[1]];\n};\nCustomModel.set = function(target, key, value){\n    var instance = target,\n        match = key.match(matchKeys);\n\n    if(!match){\n        return;\n    }\n\n    while(match[2]){\n        if(!target){\n            return;\n        }\n\n        target = target[match[1]];\n        match = match[2].match(matchKeys);\n    }\n\n    if(!target){\n        return;\n    }\n\n    target[match[1]] = value;\n    allModels.forEach(function(model){\n        if(model.isAttached() && model._model === instance){\n            model._events && Object.keys(model._events).forEach(function(key){\n                if(model.get(key.match(/(.*?)\\./)[1]) === target){\n                    model.emit(key, value);\n                }\n            });\n        }\n    });\n};\nCustomModel.remove = function(target, key){\n    var instance = target,\n        match = key.match(matchKeys);\n\n    if(!match){\n        return;\n    }\n\n    while(match[2]){\n        if(!target){\n            return;\n        }\n\n        target = target[match[1]];\n        match = match[2].match(matchKeys);\n    }\n\n    if(!target){\n        return;\n    }\n\n    delete target[match[1]];\n    allModels.forEach(function(model){\n        if(model.isAttached() && model._model === instance){\n            model._events && Object.keys(model._events).forEach(function(key){\n                if(model.get(key.match(/(.*?)\\./)[1]) === target){\n                    model.emit(key);\n                }\n            });\n        }\n    });\n};\nCustomModel.prototype = Object.create(EventEmitter.prototype);\nCustomModel.prototype.constructor = CustomModel;\nCustomModel.prototype._maxListeners = 100;\nCustomModel.prototype.constructor = CustomModel;\nCustomModel.prototype.attach = function(instance){\n    if(this._model !== instance){\n        this.detach();\n    }\n\n    allModels.add(this);\n    this._attached = true;\n    this._model = instance;\n    this.emit('attach', instance);\n};\nCustomModel.prototype.detach = function(){\n    allModels.delete(this);\n\n    this._model = {};\n    this._attached = false;\n    this.emit('detach');\n};\nCustomModel.prototype.destroy = function(){\n    this.detach();\n    this._events = null;\n    this.emit('destroy');\n};\nvar matchKeys = /(.*?)(?:\\.(.*)|$)/;\nCustomModel.prototype.get = function(key){\n    return CustomModel.get(this._model, key);\n};\nCustomModel.prototype.set = function(key, value){\n    return CustomModel.set(this._model, key, value);\n};\nCustomModel.prototype.remove = function(key){\n    return CustomModel.remove(this._model, key);\n};\nCustomModel.prototype.isAttached = function(){\n    return !!this._model;\n};\nCustomModel.isModel = function(target){\n    return target && target instanceof CustomModel;\n};\n\n\ntest('binding with custom model', function(t){\n    t.plan(4);\n\n    var fastn = createFastn({});\n    fastn.Model = CustomModel;\n    fastn.isModel = CustomModel.isModel;\n\n    var binding = fastn.binding('foo');\n\n    var model = {},\n        enti = new CustomModel(model);\n\n    t.equal(binding(), undefined);\n\n    enti.set('foo', 'bar');\n\n    t.equal(binding(), undefined);\n\n    binding.attach(model);\n\n    t.equal(binding(), 'bar');\n\n    binding.detach();\n\n    t.equal(binding(), undefined);\n});"
  },
  {
    "path": "test/document.js",
    "content": "module.exports = function(){\n    var domLite = require('dom-lightning');\n\n    document = domLite.document;\n    document.body = document.createElement('body');\n    \n    global.Node = domLite.Node;\n    global.document = document;\n    global.Element = domLite.Element;\n    global.HTMLElement = domLite.HTMLElement;\n};"
  },
  {
    "path": "test/fancyProps.js",
    "content": "var test = require('tape'),\n    crel = require('crel'),\n    fancyProps = require('../fancyProps');\n\ntest('date input', function(t){\n\n    t.plan(2);\n\n    var input = crel('input', {type: 'date'});\n\n    t.equal(fancyProps.value({}, input), null);\n\n    fancyProps.value({}, input, new Date('2000-1-1'));\n\n    t.equal(fancyProps.value({}, input).toString(), new Date('2000-1-1').toString());\n});\n\ntest('class', function(t){\n\n    t.plan(3);\n\n    var component = {},\n        span = crel('span');\n\n    t.equal(fancyProps.class(component, span), '');\n\n    fancyProps.class(component, span, 'foo');\n\n    t.equal(fancyProps.class(component, span), 'foo');\n\n    fancyProps.class(component, span, ['bar']);\n\n    t.equal(fancyProps.class(component, span), 'bar');\n});\n\ntest('class 2', function(t){\n\n    t.plan(6);\n\n    var component = {},\n        span = crel('span', {class: 'majigger'});\n\n    t.equal(fancyProps.class(component, span), '');\n    t.equal(span.className, 'majigger');\n\n    fancyProps.class(component, span, 'foo');\n\n    t.equal(fancyProps.class(component, span), 'foo');\n    t.equal(span.className, 'majigger foo');\n\n    span.className += ' whatsits';\n\n    fancyProps.class(component, span, ['bar']);\n\n    t.equal(fancyProps.class(component, span), 'bar');\n    t.equal(span.className, 'majigger whatsits bar');\n});\n\ntest('style string', function(t){\n\n    t.plan(4);\n\n    var component = {},\n        span = crel('span');\n\n    t.equal(fancyProps.style(component, span).background, '');\n    t.equal(span.style.background, '');\n\n    fancyProps.style(component, span, 'background: red');\n\n    t.equal(fancyProps.style(component, span).background, 'red');\n    t.equal(span.style.background, 'red');\n});\n\ntest('style object', function(t){\n\n    t.plan(4);\n\n    var component = {},\n        span = crel('span');\n\n    t.equal(fancyProps.style(component, span).background, '');\n    t.equal(span.style.background, '');\n\n    fancyProps.style(component, span, { background: 'red' });\n\n    t.equal(fancyProps.style(component, span).background, 'red');\n    t.equal(span.style.background, 'red');\n});"
  },
  {
    "path": "test/firmer.js",
    "content": "var test = require('tape'),\n    firmer = require('../firmer');\n\ntest('default (0) firmness', function(t){\n\n    t.plan(2);\n\n    var entitiy = {_firm:0};\n\n    t.notOk(firmer(entitiy, 1));\n    t.notOk(firmer(entitiy, 0));\n});\n\ntest('template (1) firmness', function(t){\n\n    t.plan(2);\n\n    var entitiy = {_firm:1};\n\n    t.notOk(firmer(entitiy, 1));\n    t.ok(firmer(entitiy, 0));\n});\n\ntest('custom (2) firmness', function(t){\n\n    t.plan(2);\n\n    var entitiy = {_firm:2};\n\n    t.ok(firmer(entitiy, 1));\n    t.ok(firmer(entitiy, 0));\n});\n\ntest('attach() (undefined) firmness', function(t){\n\n    t.plan(3);\n\n    var entitiy = {_firm:undefined};\n\n    t.ok(firmer(entitiy, 0));\n    t.ok(firmer(entitiy, 1));\n    t.ok(firmer(entitiy, Infinity));\n});"
  },
  {
    "path": "test/generic.js",
    "content": "var test = require('tape'),\n    crel = require('crel'),\n    createFastn = require('./createFastn');\n\ntest('div', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var div = fastn('div');\n\n    div.render();\n\n    document.body.appendChild(div.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'DIV');\n\n    div.element.remove();\n    div.destroy();\n\n});\n\ntest('undefined attribute is removed', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var div = fastn('div', { someattr: true });\n\n    div.render();\n\n    t.equal(div.element.hasAttribute('someattr'), true);\n\n    div.someattr(undefined);\n\n    t.equal(div.element.hasAttribute('someattr'), false, 'undefined attribute is removed');\n\n    div.destroy();\n});\n\n\ntest('special properties - input value - undefined', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var input = fastn('input', {value: undefined});\n\n    input.render();\n\n    document.body.appendChild(input.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'INPUT');\n    t.equal(document.body.childNodes[0].value, '');\n\n    input.element.remove();\n    input.destroy();\n\n});\n\ntest('special properties - input value - dates', function(t){\n\n    t.plan(8);\n\n    var fastn = createFastn();\n\n    var input = fastn('input', {\n        type: 'date',\n        value: new Date('2015/01/01'),\n        onchange: 'value:value',\n        onclick: 'value:value' // so I can trigger events..\n    });\n\n    input.render();\n\n    document.body.appendChild(input.element);\n\n    t.equal(document.body.childNodes.length, 1, 'node added');\n    t.equal(document.body.childNodes[0].tagName, 'INPUT', 'correct tagName');\n    t.equal(document.body.childNodes[0].value, '2015-01-01', 'correct initial input.value');\n    t.deepEqual(input.value(), new Date('2015/01/01'), 'correct initial property()');\n\n    input.value(new Date('2015/02/02'));\n\n    t.equal(document.body.childNodes[0].value, '2015-02-02', 'correctly set new input.value');\n    t.deepEqual(input.value(), new Date('2015/02/02'), 'correctly set new property()');\n\n    input.element.value = '2016-02-02';\n    input.element.click();\n\n    t.equal(document.body.childNodes[0].value, '2016-02-02', 'correctly set new input.value 2');\n    t.deepEqual(input.value(), new Date('2016/02/02'), 'correctly set new property() 2');\n\n    input.element.remove();\n    input.destroy();\n\n});\n\ntest('special properties - disabled', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn();\n\n    var button = fastn('button', {\n        type: 'button',\n        disabled: false\n    });\n\n    button.render();\n\n    document.body.appendChild(button.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'BUTTON');\n    t.equal(document.body.childNodes[0].getAttribute('disabled'), null);\n\n    button.disabled(true);\n\n    t.equal(document.body.childNodes[0].getAttribute('disabled'), 'disabled');\n\n    button.element.remove();\n    button.destroy();\n\n});\n\ntest('special properties - textContent', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn();\n\n    var label = fastn('label', {\n        textContent: 'foo'\n    });\n\n    label.render();\n\n    document.body.appendChild(label.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'LABEL');\n    t.equal(document.body.childNodes[0].textContent, 'foo');\n\n    label.textContent(null);\n\n    t.equal(document.body.childNodes[0].textContent, '');\n\n    label.element.remove();\n    label.destroy();\n\n});\n\ntest('special properties - innerHTML', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn();\n\n    var label = fastn('label', {\n        innerHTML: 'foo'\n    });\n\n    label.render();\n\n    document.body.appendChild(label.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'LABEL');\n    t.equal(document.body.childNodes[0].innerHTML, 'foo');\n\n    label.innerHTML(null);\n\n    t.equal(document.body.childNodes[0].innerHTML, '');\n\n    label.element.remove();\n    label.destroy();\n\n});\n\ntest('preexisting element', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn();\n\n    var element = crel('label'),\n        label = fastn(element, {\n            textContent: 'foo'\n        });\n\n    label.render();\n\n    document.body.appendChild(label.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'LABEL');\n    t.equal(document.body.childNodes[0].textContent, 'foo');\n\n    label.textContent(null);\n\n    t.equal(document.body.childNodes[0].textContent, '');\n\n    label.element.remove();\n    label.destroy();\n\n});\n\ntest('DOM children', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var label = fastn('div',\n            crel('h1', 'DOM Child')\n        );\n\n    label.render();\n\n    document.body.appendChild(label.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'DIV');\n    t.equal(document.body.childNodes[0].textContent, 'DOM Child');\n\n    label.element.remove();\n    label.destroy();\n\n});\n\ntest('same scope', function(t){\n\n    t.plan(4);\n\n    var fastn = createFastn();\n\n    var thing = fastn('label', {}, fastn.binding('x'));\n\n    thing.render();\n    document.body.appendChild(thing.element);\n\n    t.equal(document.body.childNodes.length, 1);\n    t.equal(document.body.childNodes[0].tagName, 'LABEL');\n\n    thing.attach({\n        x: 10\n    });\n\n    t.equal(document.body.childNodes[0].textContent, '10');\n\n    thing.attach({\n        x: 20\n    });\n\n    t.equal(document.body.childNodes[0].textContent, '20');\n\n    thing.element.remove();\n    thing.destroy();\n\n});\n\ntest('default type', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var thing = fastn('_generic').render();\n\n    t.equal(thing.element.tagName, 'DIV');\n\n    thing.destroy();\n\n});\n\ntest('override type', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var thing = fastn('span:div:section').render();\n\n    t.equal(thing.element.tagName, 'SECTION');\n\n    thing.destroy();\n\n});\n\ntest('custom fancyProps', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn({\n        custom: function(fastn, component, type, settings, children){\n            // Map all settings to data-{name} as an example\n            component.extend('_generic', settings, children);\n            component._fancyProps = function(attribute){\n                if(attribute === 'ignore'){\n                    return;\n                }\n\n                return function(component, element, value){\n                    if(arguments.length < 3){\n                        return element.getAttribute('data-' + attribute);\n                    }\n\n                    return element.setAttribute('data-' + attribute, value);\n                }\n            }\n            return component;\n        }\n    });\n\n    var thing = fastn('div:custom', { property: 'foo', ignore: 'bar' }).render();\n\n    t.equal(thing.element.tagName, 'DIV');\n    t.equal(thing.element.getAttribute('data-property'), 'foo');\n    t.equal(thing.element.getAttribute('ignore'), 'bar');\n\n    thing.destroy();\n\n});\n\ntest('event handling - auto handler', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var input = fastn('input', {\n        value: 'a',\n        onclick: 'value:value',\n    });\n\n    input.render();\n\n    document.body.appendChild(input.element);\n\n    input.element.value = 'b';\n    input.element.click();\n\n    t.equal(input.value(), 'b')\n\n    input.element.remove();\n    input.destroy();\n\n});\n\ntest('event handling - function handler', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var button = fastn('button', {\n        onclick: (event, scope) => t.pass('recieved click')\n    });\n\n    button.render();\n\n    document.body.appendChild(button.element);\n\n    button.element.click();\n\n    button.element.remove();\n    button.destroy();\n\n});\n\ntest('event handling - function handler - this', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var input = fastn('input', {\n        value: 'a',\n        onclick: function(event, scope){ this.value('b') }\n    });\n\n    input.render();\n\n    document.body.appendChild(input.element);\n\n    input.element.value = 'b';\n    input.element.click();\n\n    t.equal(input.value(), 'b')\n\n    input.element.remove();\n    input.destroy();\n\n});\n\ntest('event handling - component handler', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var button = fastn('button')\n        .on('click', (event, scope) => t.pass('recieved click'))\n\n    button.render();\n\n    document.body.appendChild(button.element);\n\n    button.element.click();\n\n    button.element.remove();\n    button.destroy();\n\n});\n\ntest('event handling - function handler - this', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var input = fastn('input', {\n        value: 'a'\n    })\n    .on('click', function(event, scope){ this.value('b') })\n\n    input.render();\n\n    document.body.appendChild(input.element);\n\n    input.element.value = 'b';\n    input.element.click();\n\n    t.equal(input.value(), 'b')\n\n    input.element.remove();\n    input.destroy();\n\n});\n"
  },
  {
    "path": "test/index.html",
    "content": "<meta name=\"viewport\" content=\"user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height\">\n<script>\n    var oldLog = console.log,\n        logs = [];\n\n    function printResults(){\n        logs.forEach(function(log){\n            document.write(log + '<br>');\n        });\n        document.body.scrollTop = Number.MAX_VALUE;\n    }\n\n    console.log = function(line){\n        logs.push(line);\n        oldLog.apply(this, arguments);\n\n        if(line.match(/^# ok$/)){\n            printResults();\n        }\n        if(line.match(/^# fail .*?$/)){\n            printResults();\n        }\n    };\n\n    window.onerror = printResults;\n</script>\n<body>\n<script src=\"index.browser.js\"></script>\n</body>\n"
  },
  {
    "path": "test/index.js",
    "content": "\"use strict\"\n\nfunction run(){\n    document.body.innerHTML = '';\n\n    require('./firmer.js');\n    require('./binding.js');\n    require('./property.js');\n    require('./component.js');\n    require('./text.js');\n    require('./list.js');\n    require('./templater.js');\n    require('./container.js');\n    require('./generic.js');\n    require('./attach.js');\n    require('./fancyProps.js');\n    require('./customModel.js');\n}\n\nif(typeof document !== 'undefined'){\n    window.onload = run;\n}else{\n    require('./document')();\n    run();\n}"
  },
  {
    "path": "test/list.js",
    "content": "var test = require('tape'),\n    consoleWatch = require('console-watch'),\n    Enti = require('enti'),\n    createFastn = require('./createFastn');\n\ntest('value items', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: [1,2,3,4],\n            template: function(model){\n                return fastn.binding('item');\n            }\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('value items duplicate values', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: [1,1,2,2],\n            template: function(model){\n                return fastn.binding('item');\n            }\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1122');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('bound items', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        });\n\n    list.attach({\n        items: [1,2,3,4]\n    });\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\n\ntest('bound items changing', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        }),\n        model = new Enti({\n            items: [1,2,3,4]\n        });\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    model.set('items.1', 5);\n\n    t.equal(document.body.textContent, '1534');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('bound items add', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        }),\n        model = new Enti({\n            items: [1,2,3,4]\n        });\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    model.set('items.4', 5);\n\n    t.equal(document.body.textContent, '12345');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('bound items remove', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        }),\n        model = new Enti({\n            items: [1,2,3,4]\n        });\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    model.remove('items.3');\n\n    t.equal(document.body.textContent, '123');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('bound items remove same instance', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item.name');\n            }\n        }),\n        model = new Enti({\n            items: []\n        });\n\n    var testItem = {\n        name: 'foo'\n    };\n\n    model.push('items', testItem);\n    model.push('items', testItem);\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, 'foofoo');\n\n    model.remove('items.0');\n\n    t.equal(document.body.textContent, 'foo');\n\n    model.remove('items.0');\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('null items', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: null,\n            template: function(model){\n                return fastn.binding('item');\n            }\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('null template', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: [1,2,3,4],\n            template: function(model){}\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('array to undefined', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        }),\n        model = new Enti({\n            items: [1,2,3,4]\n        });\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    model.remove('items');\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('array to null', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: fastn.binding('items|*'),\n            template: function(model){\n                return fastn.binding('item');\n            }\n        }),\n        model = new Enti({\n            items: [1,2,3,4]\n        });\n\n    list.attach(model);\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    model.set('items', null);\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('reattach list with templates', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var data = {foo: [\n            {a:1}\n        ]},\n        list = fastn('list', {\n            items: fastn.binding('.|*'),\n            template: function(model, scope, lastTemplate){\n                return fastn.binding('item.a');\n            }\n        })\n        .attach(data)\n        .binding('foo');\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1');\n\n    fastn.Model.set(data, 'foo', [{\n        a: 2\n    }]);\n\n    t.equal(document.body.textContent, '2');\n\n    fastn.Model.set(data, 'foo', [{\n        a: 3\n    }]);\n\n    t.equal(document.body.textContent, '3');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('dynamic template removed', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var templateBinding = fastn.binding();\n    templateBinding(function(model){\n        return fastn.binding('item');\n    });\n\n    var list = fastn('list', {\n            items: [1,2,3,4],\n            template: templateBinding\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    templateBinding(null);\n\n    t.equal(document.body.textContent, '');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('dynamic template', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var templateBinding = fastn.binding();\n    templateBinding(function(model){\n        return fastn.binding('item');\n    });\n\n    var list = fastn('list', {\n            items: [1,2,3,4],\n            template: templateBinding\n        });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    t.equal(document.body.textContent, '1234');\n\n    templateBinding(function(model){\n        return '*';\n    });\n\n    t.equal(document.body.textContent, '****');\n\n    list.element.remove();\n    list.destroy();\n\n});\n\ntest('object item keys', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var list = fastn('list', {\n            items: {foo:'bar'},\n            template: function(model){\n                t.equal(model.get('item'), 'bar');\n                t.equal(model.get('key'), 'foo');\n            }\n        });\n\n    list.attach();\n\n    list.destroy();\n\n});\n\ntest('warns on no template', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    consoleWatch(function(getResults) {\n        var list = fastn('list');\n\n        t.deepEqual(getResults(), {warn: ['No \"template\" function was set for this templater component']})\n    });\n\n});\n\ntest('Lazy item templating', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var items = [];\n\n    while(items.length < 100){\n        items.push(items.length);\n    }\n\n    var list = fastn('list', {\n        insertionFrameTime: 100,\n        items: items,\n        template: function(model){\n            if(model.get('item') < 10){\n                var start = new Date();\n                while(Date.now() - start < 10){}\n            }\n            return fastn.binding('item');\n        }\n    });\n\n    list.render();\n\n    document.body.appendChild(list.element);\n\n    var expectedEventualText = items.join('');\n\n    t.equal(expectedEventualText.indexOf(document.body.textContent), 0);\n    t.notEqual(document.body.textContent, expectedEventualText);\n\n    setTimeout(function(){\n\n        t.equal(document.body.textContent, expectedEventualText);\n\n        list.element.remove();\n        list.destroy();\n    }, 100);\n});"
  },
  {
    "path": "test/property.js",
    "content": "var test = require('tape'),\n    fastn = require('../index')({}),\n    createBinding = fastn.binding,\n    createProperty = fastn.property,\n    Enti = require('enti'),\n    EventEmitter = require('events');\n\ntest('simple property initialisation', function(t){\n    t.plan(3);\n\n    var property = createProperty();\n\n    t.equal(property(), undefined);\n\n    property('bar');\n\n    t.equal(property(), 'bar');\n\n    property.on('change', function(value){\n        t.equal(value, 'foo');\n    });\n\n    property('foo');\n});\n\ntest('bound property', function(t){\n    t.plan(5);\n\n    var property = createProperty();\n\n    var binding = createBinding('foo');\n\n    t.equal(property(), undefined, 'No initial value');\n\n    property('bar');\n\n    t.equal(property(), 'bar', 'bar set');\n\n    property.binding(binding);\n\n    t.equal(property(), undefined, 'bar overridden by binding');\n\n    binding('baz');\n\n    t.equal(property(), 'baz', 'baz set via binding');\n\n    property.on('change', function(value){\n        t.equal(value, 'foo', 'property changed');\n    });\n\n    binding('foo');\n});\n\ntest('bound property with model', function(t){\n    t.plan(3);\n\n    var data = {\n            foo: 'bar'\n        },\n        model = new Enti(data),\n        currentValue;\n\n    var property = createProperty();\n\n    property.on('change', function(value){\n        t.equal(value, currentValue);\n    });\n\n    var binding = createBinding('foo');\n\n    binding('baz');\n    currentValue = 'baz';\n\n    property.binding(binding);\n\n    currentValue = 'bar';\n    property.attach(model);\n\n    currentValue = 'foo';\n    model.set('foo', 'foo');\n});\n\ntest('bound property with model and drill', function(t){\n    t.plan(1);\n\n    var data = {},\n        model = new Enti(data);\n\n    var property = createProperty();\n\n    var binding = createBinding('foo.bar');\n\n    binding.attach(model);\n\n    property.binding(binding);\n\n    property.on('change', function(value){\n        t.equal(value, 123);\n    });\n\n    model.set('foo', {bar: 123});\n});\n\ntest('cyclic value', function(t){\n    t.plan(1);\n\n    var model = new Enti();\n\n    var property = createProperty(null, 'keys');\n\n    var binding = createBinding('.|*');\n\n    binding.attach(model);\n\n    property.binding(binding);\n\n    property.on('change', function(value){\n        t.equal(value, model.get('.'));\n    });\n\n    model.set('self', model.get('.'));\n});\n\ntest('cyclic value with structure changes', function(t){\n    t.plan(1);\n\n    var model = new Enti();\n\n    var property = createProperty(null, 'structure');\n\n    var binding = createBinding('.|*');\n\n    binding.attach(model);\n\n    property.binding(binding);\n\n    property.on('change', function(value){\n        t.equal(value, model.get('.'));\n    });\n\n    model.set('self', model.get('.'));\n});\n\ntest('bound property to EventEmitter', function(t){\n    t.plan(5);\n\n    var property = createProperty();\n\n    var observable = new EventEmitter();\n\n    t.equal(property(), undefined, 'No initial value');\n\n    property('bar');\n\n    t.equal(property(), 'bar', 'bar set');\n\n    property.binding(observable);\n\n    t.equal(property(), undefined, 'bar overridden by observable');\n\n    observable.emit('change', 'baz');\n\n    t.equal(property(), 'baz', 'baz set via observable');\n\n    property.on('change', function(value){\n        t.equal(value, 'foo', 'property changed');\n    });\n\n    observable.emit('change', 'foo');\n});\n\ntest('bound property to EventEmitter with custom attach', function(t){\n    t.plan(2);\n\n    var property = createProperty();\n\n    function customObservable(path) {\n        var observable = new EventEmitter();\n        observable.attach = function(data){\n            this.emit('change', data[path])\n        };\n        return observable;\n    }\n\n    var observable = customObservable('foo')\n\n    property.binding(observable);\n\n    t.equal(property(), undefined, 'no value');\n\n    property.attach({\n        foo: 'bar'\n    })\n\n    t.equal(property(), 'bar', 'bar set via observable attach');\n});"
  },
  {
    "path": "test/templater.js",
    "content": "var test = require('tape'),\n    consoleWatch = require('console-watch'),\n    Enti = require('enti'),\n    createFastn = require('./createFastn');\n\ntest('value data', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: {foo:'bar'},\n            template: function(model){\n                return fastn.binding('item.foo');\n            }\n        });\n\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, 'bar');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\n\ntest('bound data', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: fastn.binding('data|*'),\n            template: function(model){\n                return fastn.binding('item.foo');\n            }\n        });\n\n    template.attach({\n        data: {\n            foo: 'bar'\n        }\n    });\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, 'bar');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\n\ntest('bound data changing', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: fastn.binding('data|*'),\n            template: function(model){\n                return fastn.binding('item.foo');\n            }\n        }),\n        model = new Enti({\n            data: {\n                foo: 'bar'\n            }\n        });\n\n    template.attach(model);\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, 'bar');\n\n    model.set('data.foo', 'baz');\n\n    t.equal(document.body.textContent, 'baz');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\ntest('null data', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: null,\n            template: function(model){}\n        });\n\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, '');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\ntest('undefined template', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: null,\n            template: function(model){}\n        });\n\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, '');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\ntest('reuse template', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: 'foo',\n            template: function(model, scope, lastTemplate){\n                if(lastTemplate){\n                    return lastTemplate;\n                }\n                t.pass();\n                return fastn('text');\n            }\n        });\n\n    template.render();\n\n    template.data('bar');\n\n});\n\ntest('reuse template same element', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var template = fastn('templater', {\n            data: 'foo',\n            template: function(model, scope, lastTemplate){\n                if(lastTemplate){\n                    return lastTemplate;\n                }\n                return fastn.binding('item');\n            }\n        });\n\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, 'foo');\n\n    var lastNode = document.body.childNodes[1];\n\n    // Don't re-render or re-insert the template if it is already rendered or inserted\n    document.body.replaceChild = function(){\n        debugger\n        t.fail();\n    };\n\n    template.data('bar');\n\n    t.equal(document.body.textContent, 'bar');\n\n    t.equal(lastNode, document.body.childNodes[1]);\n\n    template.element.remove();\n    template.destroy();\n\n});\n\ntest('reattach templater with attachTemplates = false', function(t){\n\n    t.plan(3);\n\n    var fastn = createFastn();\n\n    var data = {foo: {bar: 1}},\n        template = fastn('templater', {\n            data: fastn.binding('nothing'),\n            attachTemplates: false,\n            template: function(model, scope, lastTemplate){\n                return fastn.binding('bar');\n            }\n        })\n        .attach(data)\n        .binding('foo');\n\n    template.render();\n\n    document.body.appendChild(template.element);\n\n    t.equal(document.body.textContent, '1');\n\n    fastn.Model.set(data, 'foo', {\n        bar: 2\n    });\n\n    t.equal(document.body.textContent, '2');\n\n    fastn.Model.set(data, 'foo', {\n        bar: 3\n    });\n\n    t.equal(document.body.textContent, '3');\n\n    template.element.remove();\n    template.destroy();\n\n});\n\ntest('warns on no template', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    consoleWatch(function(getResults) {\n        var list = fastn('templater');\n\n        t.deepEqual(getResults(), {warn: ['No \"template\" function was set for this templater component']})\n    });\n\n});"
  },
  {
    "path": "test/text.js",
    "content": "var test = require('tape'),\n    Enti = require('enti'),\n    createFastn = require('./createFastn');\n\ntest('value text', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var text = fastn('text', {text: 'foo'});\n\n    text.render();\n\n    document.body.appendChild(text.element);\n\n    t.equal(document.body.textContent, 'foo');\n\n    text.element.remove();\n    text.destroy();\n\n\n});\n\ntest('bound text', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var text = fastn('text', {text: fastn.binding('value')});\n\n    text.attach({\n        value: 'foo'\n    });\n    text.render();\n\n    document.body.appendChild(text.element);\n\n    t.equal(document.body.textContent, 'foo');\n\n    text.element.remove();\n    text.destroy();\n\n\n});\n\ntest('bound text changing', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var text = fastn('text', {text: fastn.binding('value')}),\n        model = new Enti({\n            value: 'foo'\n        });\n\n    text.attach(model);\n    text.render();\n\n    document.body.appendChild(text.element);\n\n    t.equal(document.body.textContent, 'foo');\n\n    model.set('value', 'bar');\n\n    t.equal(document.body.textContent, 'bar');\n\n    text.element.remove();\n    text.destroy();\n\n});\n\ntest('auto binding text', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var parent = fastn('span', fastn.binding('value')),\n        model = new Enti({\n            value: 'foo'\n        });\n\n    parent.attach(model);\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.textContent, 'foo');\n\n    model.set('value', 'bar');\n\n    t.equal(document.body.textContent, 'bar');\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\ntest('undefined text', function(t){\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var text = fastn('text', {text: undefined});\n\n    text.render();\n\n    document.body.appendChild(text.element);\n\n    t.equal(document.body.textContent, '');\n\n    text.element.remove();\n    text.destroy();\n});\n\n\ntest('auto text Date', function(t){\n\n    t.plan(1);\n\n    var fastn = createFastn();\n\n    var date = new Date(),\n        parent = fastn('span', date);\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.textContent, date.toString());\n\n    parent.element.remove();\n    parent.destroy();\n\n});\n\n\ntest('clone text', function(t){\n\n    t.plan(2);\n\n    var fastn = createFastn();\n\n    var parent = fastn('span', 'text');\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.textContent, 'text');\n\n    parent.element.remove();\n\n    var newParent = parent.clone();\n    parent.destroy();\n\n    newParent.render();\n\n    document.body.appendChild(newParent.element);\n\n    t.equal(document.body.textContent, 'text');\n\n    newParent.element.remove();\n\n    newParent.destroy();\n\n});\n\n\ntest('clone text binding', function(t){\n\n    t.plan(2);\n\n    var data = {\n        foo: 'bar'\n    };\n\n    var fastn = createFastn();\n\n    var binding = fastn.binding('foo').attach(data);\n\n    var parent = fastn('span', binding);\n\n    parent.render();\n\n    document.body.appendChild(parent.element);\n\n    t.equal(document.body.textContent, 'bar');\n\n    parent.element.remove();\n\n    var newParent = parent.clone();\n    parent.destroy();\n\n    newParent.render();\n\n    document.body.appendChild(newParent.element);\n\n    t.equal(document.body.textContent, 'bar');\n\n    newParent.element.remove();\n\n    newParent.destroy();\n\n});"
  },
  {
    "path": "textComponent.js",
    "content": "function updateText(){\n    if(!this.element){\n        return;\n    }\n\n    var value = this.text();\n\n    this.element.data = (value == null ? '' : value);\n}\n\nfunction autoRender(content){\n    this.element = document.createTextNode(content);\n}\n\nfunction autoText(text, fastn, content) {\n    text.render = autoRender.bind(text, content);\n\n    return text;\n}\n\nfunction render(){\n    this.element = this.createTextNode(this.text());\n    this.emit('render');\n};\n\nfunction textComponent(fastn, component, type, settings, children){\n    component.createTextNode = textComponent.createTextNode;\n    component.render = render.bind(component);\n\n    component.setProperty('text', fastn.property('', updateText.bind(component)));\n\n    return component;\n}\n\ntextComponent.createTextNode = function(text){\n    return document.createTextNode(text);\n};\n\nmodule.exports = textComponent;\n"
  }
]