[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.monitor\n.*.swp\n.nodemonignore\nreleases\n*.log\n*.err\nfleet.json\npublic/browserify\nbin/*.json\n.bin\nbuild\ncompile\n.lock-wscript\ncoverage\nnode_modules\n"
  },
  {
    "path": ".jshintrc",
    "content": "{\n    \"maxdepth\": 4,\n    \"maxstatements\": 200,\n    \"maxcomplexity\": 12,\n    \"maxlen\": 80,\n    \"maxparams\": 5,\n\n    \"curly\": true,\n    \"eqeqeq\": true,\n    \"immed\": true,\n    \"latedef\": false,\n    \"noarg\": true,\n    \"noempty\": true,\n    \"nonew\": true,\n    \"undef\": true,\n    \"unused\": \"vars\",\n    \"trailing\": true,\n\n    \"quotmark\": true,\n    \"expr\": true,\n    \"asi\": true,\n\n    \"browser\": false,\n    \"esnext\": true,\n    \"devel\": false,\n    \"node\": false,\n    \"nonstandard\": false,\n\n    \"predef\": [\"require\", \"module\", \"__dirname\", \"__filename\"]\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - 0.8\n  - \"0.10\"\nbefore_script:\n - npm install\n - npm install istanbul coveralls\nscript: npm run travis-test\n"
  },
  {
    "path": "LICENCE",
    "content": "Copyright (c) 2014 Raynos.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# main-loop\n\n<!--\n    [![build status][1]][2]\n    [![NPM version][3]][4]\n    [![Coverage Status][5]][6]\n    [![gemnasium Dependency Status][7]][8]\n    [![Davis Dependency status][9]][10]\n-->\n\n<!-- [![browser support][11]][12] -->\n\nA rendering loop for diffable UIs\n\n`main-loop` is an optimization module for a virtual DOM system. Normally you would re-create the virtual tree every time your state changes. This is not optimum, with main-loop you will only update your virtual tree at most once per request animation frame.\n\n`main-loop` basically gives you batching of your virtual DOM changes, which means if you change your model multiple times it will be rendered once asynchronously on the next request animation frame.\n\n## Example\n\n```js\nvar mainLoop = require(\"main-loop\")\nvar h = require(\"virtual-dom/h\")\n\nvar initState = { fruits: [\"apple\", \"banana\"], name: \"Steve\" }\n\nfunction render(state) {\n    return h(\"div\", [\n        h(\"div\", [\n            h(\"span\", \"hello \"),\n            h(\"span.name\", state.name)\n        ]),\n        h(\"ul\", state.fruits.map(renderFruit))\n    ])\n\n    function renderFruit(fruitName) {\n        return h(\"li\", [\n            h(\"span\", fruitName)\n        ])\n    }\n}\n\n// set up a loop\nvar loop = mainLoop(initState, render, {\n    create: require(\"virtual-dom/create-element\"),\n    diff: require(\"virtual-dom/diff\"),\n    patch: require(\"virtual-dom/patch\")\n})\ndocument.body.appendChild(loop.target)\n\n// update the loop with the new application state\nloop.update({\n    fruits: [\"apple\", \"banana\", \"cherry\"],\n    name: \"Steve\"\n})\nloop.update({\n    fruits: [\"apple\", \"banana\", \"cherry\"],\n    name: \"Stevie\"\n})\n```\n\n## var loop = mainLoop(initState, render, opts)\n\nCreate a loop object with some initial state, a render function, and some\noptions. Your `function render (state) {}` receives the current state as its\nargument and must return a virtual-dom object.\n\nYou must supply: `opts.diff`, `opts.patch`, and `opts.create`. These can be\nobtained directly from `require(\"virtual-dom\")`.\n\nOptionally supply an `opts.target` and `opts.initialTree`.\n\n## loop.target\n\nThe main-loop root DOM element. Insert this element to the page.\n\n## loop.update(newState)\n\nUpdate the page state, automatically re-rendering the page as necessary.\n\n## loop.state\n\nRead the current main-loop state. To modify the loop state, use `loop.update()`.\n\n## Installation\n\n`npm install main-loop`\n\n## Contributors\n\n - Raynos\n\n## MIT Licenced\n\n  [1]: https://secure.travis-ci.org/Raynos/main-loop.png\n  [2]: https://travis-ci.org/Raynos/main-loop\n  [3]: https://badge.fury.io/js/main-loop.png\n  [4]: https://badge.fury.io/js/main-loop\n  [5]: https://coveralls.io/repos/Raynos/main-loop/badge.png\n  [6]: https://coveralls.io/r/Raynos/main-loop\n  [7]: https://gemnasium.com/Raynos/main-loop.png\n  [8]: https://gemnasium.com/Raynos/main-loop\n  [9]: https://david-dm.org/Raynos/main-loop.png\n  [10]: https://david-dm.org/Raynos/main-loop\n  [11]: https://ci.testling.com/Raynos/main-loop.png\n  [12]: https://ci.testling.com/Raynos/main-loop\n"
  },
  {
    "path": "docs.mli",
    "content": "import { VElem, VPatch } from \"vtree\"\nimport { DOMElement } from \"jsig.dom\"\n\ntype MainLoop<T> : (\n    initialState: T,\n    view: (T) => VElem,\n    opts?: {\n        create?: (VElem, opts: Object) => DOMElement,\n        diff?: (prev: VElem, curr: VElem, opts: Object) => Array<VPatch>,\n        patch?: (\n            target: DOMElement,\n            patches: Array<VPatch>,\n            opts: Object\n        ) => void,\n        initialTree?: VElem,\n        target?: DOMElement,\n        createOnly?: Boolean\n    }\n) => {\n    target: DOMElement,\n    update: (T) => void\n}\n\nmain-loop : MainLoop<T>\n"
  },
  {
    "path": "index.js",
    "content": "var raf = require(\"raf\")\nvar TypedError = require(\"error/typed\")\n\nvar InvalidUpdateInRender = TypedError({\n    type: \"main-loop.invalid.update.in-render\",\n    message: \"main-loop: Unexpected update occurred in loop.\\n\" +\n        \"We are currently rendering a view, \" +\n            \"you can't change state right now.\\n\" +\n        \"The diff is: {stringDiff}.\\n\" +\n        \"SUGGESTED FIX: find the state mutation in your view \" +\n            \"or rendering function and remove it.\\n\" +\n        \"The view should not have any side effects.\\n\" +\n        \"This may also have happened if rendering did not complete due to an error.\\n\",\n    diff: null,\n    stringDiff: null\n})\n\nmodule.exports = main\n\nfunction main(initialState, view, opts) {\n    opts = opts || {}\n\n    var currentState = initialState\n    var create = opts.create\n    var diff = opts.diff\n    var patch = opts.patch\n    var redrawScheduled = false\n\n    var tree = opts.initialTree || view(currentState, 0);\n    var target = opts.target || create(tree, opts)\n    var inRenderingTransaction = false\n\n    currentState = null\n\n    var loop = {\n        state: initialState,\n        target: target,\n        update: update\n    }\n    return loop\n\n    function update(state) {\n        if (inRenderingTransaction) {\n            throw InvalidUpdateInRender({\n                diff: state._diff,\n                stringDiff: JSON.stringify(state._diff)\n            })\n        }\n\n        if (currentState === null && !redrawScheduled) {\n            redrawScheduled = true\n            raf(redraw)\n        }\n\n        currentState = state\n        loop.state = state\n    }\n\n    function redraw(time) {\n        redrawScheduled = false\n        if (currentState === null) {\n            return\n        }\n\n        inRenderingTransaction = true\n        var newTree = view(currentState, time)\n\n        if (opts.createOnly) {\n            inRenderingTransaction = false\n            create(newTree, opts)\n        } else {\n            var patches = diff(tree, newTree, opts)\n            inRenderingTransaction = false\n            target = patch(target, patches, opts)\n        }\n\n        tree = newTree\n        currentState = null\n    }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"main-loop\",\n  \"version\": \"3.4.0\",\n  \"description\": \"A rendering loop for diffable UIs\",\n  \"keywords\": [],\n  \"author\": \"Raynos <raynos2@gmail.com>\",\n  \"repository\": \"git://github.com/Raynos/main-loop.git\",\n  \"main\": \"index\",\n  \"homepage\": \"https://github.com/Raynos/main-loop\",\n  \"contributors\": [\n    {\n      \"name\": \"Raynos\"\n    }\n  ],\n  \"bugs\": {\n    \"url\": \"https://github.com/Raynos/main-loop/issues\",\n    \"email\": \"raynos2@gmail.com\"\n  },\n  \"dependencies\": {\n    \"error\": \"^4.1.1\",\n    \"raf\": \"^2.0.1\"\n  },\n  \"devDependencies\": {\n    \"global\": \"^3.0.0\",\n    \"istanbul\": \"^0.3.0\",\n    \"tape\": \"^2.13.3\",\n    \"virtual-dom\": \"0.0.23\"\n  },\n  \"licenses\": [\n    {\n      \"type\": \"MIT\",\n      \"url\": \"http://github.com/Raynos/main-loop/raw/master/LICENSE\"\n    }\n  ],\n  \"scripts\": {\n    \"test\": \"node ./test/index.js\",\n    \"start\": \"node ./index.js\",\n    \"watch\": \"nodemon -w ./index.js index.js\",\n    \"travis-test\": \"istanbul cover ./test/index.js && ((cat coverage/lcov.info | coveralls) || exit 0)\",\n    \"cover\": \"istanbul cover --report none --print detail ./test/index.js\",\n    \"view-cover\": \"istanbul report html && google-chrome ./coverage/index.html\",\n    \"test-browser\": \"testem-browser ./test/browser/index.js\",\n    \"testem\": \"testem-both -b=./test/browser/index.js\"\n  },\n  \"testling\": {\n    \"files\": \"test/index.js\",\n    \"browsers\": [\n      \"ie/8..latest\",\n      \"firefox/16..latest\",\n      \"firefox/nightly\",\n      \"chrome/22..latest\",\n      \"chrome/canary\",\n      \"opera/12..latest\",\n      \"opera/next\",\n      \"safari/5.1..latest\",\n      \"ipad/6.0..latest\",\n      \"iphone/6.0..latest\",\n      \"android-browser/4.2..latest\"\n    ]\n  }\n}\n"
  },
  {
    "path": "test/index.js",
    "content": "var test = require(\"tape\")\nvar h = require(\"virtual-dom/virtual-hyperscript\")\nvar document = require(\"global/document\")\nvar raf = require(\"raf\")\n\nvar mainLoop = require(\"../index\")\n\ntest(\"mainLoop is a function\", function (assert) {\n    assert.equal(typeof mainLoop, \"function\")\n    assert.end()\n})\n\ntest(\"can set up main loop\", function (assert) {\n    var initState = { fruits: [\"apple\", \"banana\"], name: \"Steve\" }\n\n    function render(state) {\n        return h(\"div\", [\n            h(\"div\", [\n                h(\"span\", \"hello \"),\n                h(\"span.name\", state.name)\n            ]),\n            h(\"ul\", state.fruits.map(renderFruit))\n        ])\n\n        function renderFruit(fruitName) {\n            return h(\"li\", [\n                h(\"span\", fruitName)\n            ])\n        }\n    }\n\n    // set up a loop\n    var loop = mainLoop(initState, render, {\n        document: document,\n        create: require(\"virtual-dom/create-element\"),\n        diff: require(\"virtual-dom/diff\"),\n        patch: require(\"virtual-dom/patch\")\n    })\n    document.body.appendChild(loop.target)\n\n    var div = loop.target\n    var span = div.childNodes[0].childNodes[1]\n    var ul = div.childNodes[1]\n\n    assert.equal(div.tagName, \"DIV\")\n    assert.equal(span.childNodes[0].data, \"Steve\")\n    assert.equal(ul.childNodes.length, 2)\n\n    // update the loop with the new application state\n    loop.update({\n        fruits: [\"apple\", \"banana\", \"cherry\"],\n        name: \"Steve\"\n    })\n\n    raf(function () {\n        assert.equal(ul.childNodes.length, 3)\n\n        loop.update({\n            fruits: [\"apple\", \"banana\", \"cherry\"],\n            name: \"Stevie\"\n        })\n\n        raf(function () {\n            assert.equal(span.childNodes[0].data, \"Stevie\")\n\n            document.body.removeChild(loop.target)\n            assert.end()\n        })\n    })\n})\n\ntest(\"loop.state exposed\", function (assert) {\n    var loop = mainLoop({ n: 0 }, render, {\n        document: document,\n        create: require(\"virtual-dom/create-element\"),\n        diff: require(\"virtual-dom/diff\"),\n        patch: require(\"virtual-dom/patch\")\n    })\n    assert.equal(loop.state.n, 0)\n    loop.update({ n: 4 })\n    assert.equal(loop.state.n, 4)\n    assert.end()\n\n    function render(state) {\n        return h('div', String(state.n))\n    }\n})\n\ntest(\"render called with monotonically increasing times\", function(assert){\n    assert.plan(2)\n\n    var times = []\n    var loop = mainLoop({ n: 0 }, render, {\n        document: document,\n        create: require(\"virtual-dom/create-element\"),\n        diff: require(\"virtual-dom/diff\"),\n        patch: require(\"virtual-dom/patch\")\n    })\n\n\n    function render(state, time) {\n        times.push(time);\n        return h('div', String(state.n))\n    }\n\n\n    raf(function () {\n\n        loop.update({ n: 1})\n\n        raf(function () {\n            loop.update({ n: 2})\n\n            assert.equal(times.length, 2)\n            assert.ok(times[0] < times[1], \"should be increasing\")\n            assert.end()\n        })\n    })\n});\n"
  }
]