[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\"react\"]\n}"
  },
  {
    "path": ".gitignore",
    "content": "build\nnode_modules\nyarn.lock\nnpm-debug.log"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015, Flipboard\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice, this\n  list of conditions and the following disclaimer in the documentation and/or\n  other materials provided with the distribution.\n\n* Neither the name of Flipboard nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "README.md",
    "content": "# react-canvas\n\n[Introductory blog post](http://engineering.flipboard.com/2015/02/mobile-web)\n\nReact Canvas adds the ability for React components to render to `<canvas>` rather than DOM.\n\nThis project is a work-in-progress. Though much of the code is in production on flipboard.com, the React canvas bindings are relatively new and the API is subject to change.\n\n## Motivation\n\nHaving a long history of building interfaces geared toward mobile devices, we found that the reason mobile web apps feel slow when compared to native apps is the DOM. CSS animations and transitions are the fastest path to smooth animations on the web, but they have several limitations. React Canvas leverages the fact that most modern mobile browsers now have hardware accelerated canvas.\n\nWhile there have been other attempts to bind canvas drawing APIs to React, they are more focused on visualizations and games. Where React Canvas differs is in the focus on building application user interfaces. The fact that it renders to canvas is an implementation detail.\n\nReact Canvas brings some of the APIs web developers are familiar with and blends them with a high performance drawing engine.\n\n## Installation\n\nReact Canvas is available through npm:\n\n```npm install react-canvas```\n\n## React Canvas Components\n\nReact Canvas provides a set of standard React components that abstract the underlying rendering implementation.\n\n### &lt;Surface&gt;\n\n**Surface** is the top-level component. Think of it as a drawing canvas in which you can place other components.\n\n### &lt;Layer&gt;\n\n**Layer** is the the base component by which other components build upon. Common styles and properties such as top, width, left, height, backgroundColor and zIndex are expressed at this level.\n\n### &lt;Group&gt;\n\n**Group** is a container component. Because React enforces that all components return a single component in `render()`, Groups can be useful for parenting a set of child components. The Group is also an important component for optimizing scrolling performance, as it allows the rendering engine to cache expensive drawing operations.\n\n### &lt;Text&gt;\n\n**Text** is a flexible component that supports multi-line truncation, something which has historically been difficult and very expensive to do in DOM.\n\n### &lt;Image&gt;\n\n**Image** is exactly what you think it is. However, it adds the ability to hide an image until it is fully loaded and optionally fade it in on load.\n\n### &lt;Gradient&gt;\n\n**Gradient** can be used to set the background of a group or surface. \n```javascript\n  render() {\n    ...\n    return (\n      <Group style={this.getStyle()}>\n        <Gradient style={this.getGradientStyle()} \n                  colorStops={this.getGradientColors()} />\n      </Group>\n    );\n  }\n  getGradientColors(){\n    return [\n      { color: \"transparent\", position: 0 },\n      { color: \"#000\", position: 1 }\n    ]\n  }\n``` \n\n### &lt;ListView&gt;\n\n**ListView** is a touch scrolling container that renders a list of elements in a column. Think of it like UITableView for the web. It leverages many of the same optimizations that make table views on iOS and list views on Android fast.\n\n## Events\n\nReact Canvas components support the same event model as normal React components. However, not all event types are currently supported.\n\nFor a full list of supported events see [EventTypes](lib/EventTypes.js).\n\n## Building Components\n\nHere is a very simple component that renders text below an image:\n\n```javascript\nvar React = require('react');\nvar ReactCanvas = require('react-canvas');\n\nvar Surface = ReactCanvas.Surface;\nvar Image = ReactCanvas.Image;\nvar Text = ReactCanvas.Text;\n\nvar MyComponent = React.createClass({\n\n  render: function () {\n    var surfaceWidth = window.innerWidth;\n    var surfaceHeight = window.innerHeight;\n    var imageStyle = this.getImageStyle();\n    var textStyle = this.getTextStyle();\n\n    return (\n      <Surface width={surfaceWidth} height={surfaceHeight} left={0} top={0}>\n        <Image style={imageStyle} src='...' />\n        <Text style={textStyle}>\n          Here is some text below an image.\n        </Text>\n      </Surface>\n    );\n  },\n\n  getImageHeight: function () {\n    return Math.round(window.innerHeight / 2);\n  },\n\n  getImageStyle: function () {\n    return {\n      top: 0,\n      left: 0,\n      width: window.innerWidth,\n      height: this.getImageHeight()\n    };\n  },\n\n  getTextStyle: function () {\n    return {\n      top: this.getImageHeight() + 10,\n      left: 0,\n      width: window.innerWidth,\n      height: 20,\n      lineHeight: 20,\n      fontSize: 12\n    };\n  }\n\n});\n```\n\n## ListView\n\nMany mobile interfaces involve an infinitely long scrolling list of items. React Canvas provides the ListView component to do just that.\n\nBecause ListView virtualizes elements outside of the viewport, passing children to it is different than a normal React component where children are declared in render().\n\nThe `numberOfItemsGetter`, `itemHeightGetter` and `itemGetter` props are all required.\n\n```javascript\nvar ListView = ReactCanvas.ListView;\n\nvar MyScrollingListView = React.createClass({\n\n  render: function () {\n    return (\n      <ListView\n        numberOfItemsGetter={this.getNumberOfItems}\n        itemHeightGetter={this.getItemHeight}\n        itemGetter={this.renderItem} />\n    );\n  },\n\n  getNumberOfItems: function () {\n    // Return the total number of items in the list\n  },\n\n  getItemHeight: function () {\n    // Return the height of a single item\n  },\n\n  renderItem: function (index) {\n    // Render the item at the given index, usually a <Group>\n  },\n\n});\n```\n\nSee the [timeline example](examples/timeline/app.js) for a more complete example.\n\nCurrently, ListView requires that each item is of the same height. Future versions will support variable height items.\n\n## Text sizing\n\nReact Canvas provides the `measureText` function for computing text metrics.\n\nThe [Page component](examples/timeline/components/Page.js) in the timeline example contains an example of using measureText to achieve precise multi-line ellipsized text.\n\nCustom fonts are not currently supported but will be added in a future version.\n\n## css-layout\n\nThere is experimental support for using [css-layout](https://github.com/facebook/css-layout) to style React Canvas components. This is a more expressive way of defining styles for a component using standard CSS styles and flexbox.\n\nFuture versions may not support css-layout out of the box. The performance implications need to be investigated before baking this in as a core layout principle.\n\nSee the [css-layout example](examples/css-layout).\n\n## Accessibility\n\nThis area needs further exploration. Using fallback content (the canvas DOM sub-tree) should allow screen readers such as VoiceOver to interact with the content. We've seen mixed results with the iOS devices we've tested. Additionally there is a standard for [focus management](http://www.w3.org/TR/2010/WD-2dcontext-20100304/#dom-context-2d-drawfocusring) that is not supported by browsers yet.\n\nOne approach that was raised by [Bespin](http://vimeo.com/3195079) in 2009 is to keep a [parallel DOM](http://robertnyman.com/2009/04/03/mozilla-labs-online-code-editor-bespin/#comment-560310) in sync with the elements rendered in canvas.\n\n## Running the examples\n\n```\nnpm install\nnpm start\n```\n\nThis will start a live reloading server on port 8080. To override the default server and live reload ports, run `npm start` with PORT and/or RELOAD_PORT environment variables.\n\n**A note on NODE_ENV and React**: running the examples with `NODE_ENV=production` will noticeably improve scrolling performance. This is because React skips propType validation in production mode.\n\n\n## Using with webpack\n\nThe [brfs](https://github.com/substack/brfs) transform is required in order to use the project with webpack.\n\n```bash\nnpm install -g brfs\nnpm install --save-dev transform-loader brfs\n```\n\nThen add the [brfs](https://github.com/substack/brfs) transform to your webpack config\n\n```javascript\nmodule: {\n  postLoaders: [\n    { loader: \"transform?brfs\" }\n  ]\n}\n```\n\n## Contributing\n\nWe welcome pull requests for bug fixes, new features, and improvements to React Canvas. Contributors to the main repository must accept Flipboard's Apache-style [Individual Contributor License Agreement (CLA)](https://docs.google.com/forms/d/1gh9y6_i8xFn6pA15PqFeye19VqasuI9-bGp_e0owy74/viewform) before any changes can be merged.\n"
  },
  {
    "path": "examples/common/data.js",
    "content": "module.exports = [\n  {\n    title: '10 Unbelievable Secrets That Will Make Your Airline Pilot Nervous',\n    excerpt: 'With these words the Witch fell down in a brown, melted, shapeless mass and began to spread over the clean boards of the kitchen floor.  Seeing that she had really melted away to nothing, Dorothy drew another bucket of water and threw it over the mess.  She then swept it all out the door.  After picking out the silver shoe, which was all that was left of the old woman, she cleaned and dried it with a cloth, and put it on her foot again.  Then, being at last free to do as she chose, she ran out to the courtyard to tell the Lion that the Wicked Witch of the West had come to an end, and that they were no longer prisoners in a strange land.',\n    imageUrl: 'https://placekitten.com/360/420'\n  },\n  {\n    title: 'Will Batman Save Leaf Blowing?',\n    excerpt: 'The splendid fellow sprang to his feet, and grasping me by the shoulder raised his sword on high, exclaiming: \"And had the choice been left to me I could not have chosen a more fitting mate for the first princess of Barsoom.  Here is my hand upon your shoulder, John Carter, and my word that Sab Than shall go out at the point of my sword for the sake of my love for Helium, for Dejah Thoris, and for you.  This very night I shall try to reach his quarters in the palace.\" \"How?\" I asked.  \"You are strongly guarded and a quadruple force patrols the sky.\" He bent his head in thought a moment, then raised it with an air of confidence.',\n    imageUrl: 'https://placekitten.com/361/421'\n  },\n  {\n    title: '8 Scary Things Your Professor Is Using Against You',\n    excerpt: 'For a minute he scarcely realised what this meant, and, although the heat was excessive, he clambered down into the pit close to the bulk to see the Thing more clearly.  He fancied even then that the cooling of the body might account for this, but what disturbed that idea was the fact that the ash was falling only from the end of the cylinder. And then he perceived that, very slowly, the circular top of the cylinder was rotating on its body.  It was such a gradual movement that he discovered it only through noticing that a black mark that had been near him five minutes ago was now at the other side of the circumference.',\n    imageUrl: 'https://placekitten.com/362/422'\n  },\n  {\n    title: 'Kanye West\\'s Top 10 Scandalous Microsoft Excel Secrets',\n    excerpt: 'My wife was curiously silent throughout the drive, and seemed oppressed with forebodings of evil.  I talked to her reassuringly, pointing out that the Martians were tied to the Pit by sheer heaviness, and at the utmost could but crawl a little out of it; but she answered only in monosyllables.  Had it not been for my promise to the innkeeper, she would, I think, have urged me to stay in Leatherhead that night.  Would that I had!  Her face, I remember, was very white as we parted. For my own part, I had been feverishly excited all day.',\n    imageUrl: 'https://placekitten.com/363/423'\n  },\n  {\n    title: 'The Embarassing Secrets Of Julia Roberts',\n    excerpt: 'Passepartout heard the street door shut once; it was his new master going out.  He heard it shut again; it was his predecessor, James Forster, departing in his turn.  Passepartout remained alone in the house in Saville Row. \"Faith,\" muttered Passepartout, somewhat flurried, \"I\\'ve seen people at Madame Tussaud\\'s as lively as my new master!\" Madame Tussaud\\'s \"people,\" let it be said, are of wax, and are much visited in London; speech is all that is wanting to make them human. During his brief interview with Mr. Fogg, Passepartout had been carefully observing him.',\n    imageUrl: 'https://placekitten.com/364/424'\n  },\n  {\n    title: '20 Unbelievable Things Girlfriends Won\\'t Tell Their Friends',\n    excerpt: 'On March 3, 1866, Powell and I packed his provisions on two of our burros, and bidding me good-bye he mounted his horse, and started down the mountainside toward the valley, across which led the first stage of his journey. The morning of Powell\\'s departure was, like nearly all Arizona mornings, clear and beautiful; I could see him and his little pack animals picking their way down the mountainside toward the valley, and all during the morning I would catch occasional glimpses of them as they topped a hog back or came out upon a level plateau.',\n    imageUrl: 'https://placekitten.com/365/425'\n  },\n  {\n    title: 'Can Vladimir Putin Save Beard Care?',\n    excerpt: 'So powerfully did the whole grim aspect of Ahab affect me, and the livid brand which streaked it, that for the first few moments I hardly noted that not a little of this overbearing grimness was owing to the barbaric white leg upon which he partly stood. It had previously come to me that this ivory leg had at sea been fashioned from the polished bone of the sperm whale\\'s jaw. \"Aye, he was dismasted off Japan,\" said the old Gay-Head Indian once; \"but like his dismasted craft, he shipped another mast without coming home for it.',\n    imageUrl: 'https://placekitten.com/366/426'\n  },\n  {\n    title: '15 Truths That Will Make Your Psychiatrist Feel Ashamed',\n    excerpt: 'Again was I suddenly recalled to my immediate surroundings by a repetition of the weird moan from the depths of the cave.  Naked and unarmed as I was, I had no desire to face the unseen thing which menaced me. My revolvers were strapped to my lifeless body which, for some unfathomable reason, I could not bring myself to touch.  My carbine was in its boot, strapped to my saddle, and as my horse had wandered off I was left without means of defense.  My only alternative seemed to lie in flight and my decision was crystallized by a recurrence of the rustling sound.',\n    imageUrl: 'https://placekitten.com/367/427'\n  },\n  {\n    title: '6 Terrible Facts That Make Boyfriends Stronger',\n    excerpt: 'First they came to a great hall in which were many ladies and gentlemen of the court, all dressed in rich costumes.  These people had nothing to do but talk to each other, but they always came to wait outside the Throne Room every morning, although they were never permitted to see Oz.  As Dorothy entered they looked at her curiously, and one of them whispered: \"Are you really going to look upon the face of Oz the Terrible?\" \"Of course,\" answered the girl, \"if he will see me.\" \"Oh, he will see you,\" said the soldier who had taken her message to the Wizard.',\n    imageUrl: 'https://placekitten.com/368/428'\n  },\n  {\n    title: '5 Surprising Dental Care Tips From Robert De Niro',\n    excerpt: 'At once, with a quick mental leap, he linked the Thing with the flash upon Mars. The thought of the confined creature was so dreadful to him that he forgot the heat and went forward to the cylinder to help turn.  But luckily the dull radiation arrested him before he could burn his hands on the still-glowing metal.  At that he stood irresolute for a moment, then turned, scrambled out of the pit, and set off running wildly into Woking.  The time then must have been somewhere about six o\\'clock. He met a waggoner and tried to make him understand, but the tale he told and his appearance were so wild--his hat had fallen off in the pit--that the man simply drove on.',\n    imageUrl: 'https://placekitten.com/369/429'\n  },\n];\n"
  },
  {
    "path": "examples/common/examples.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n  font: 16px Helvetica, sans-serif;\n  height: 100%;\n  overflow: hidden;\n  background: #ddd;\n}\n\n#main {\n  background: #fff;\n  position: relative;\n  height: 100%;\n  max-width: 420px;\n  max-height: 700px;\n}\n"
  },
  {
    "path": "examples/common/touch-emulator.js",
    "content": "// https://github.com/hammerjs/touchemulator\n\n(function(window, document, exportName, undefined) {\n    \"use strict\";\n\n    var isMultiTouch = false;\n    var multiTouchStartPos;\n    var eventTarget;\n    var touchElements = {};\n\n    // polyfills\n    if(!document.createTouch) {\n        document.createTouch = function(view, target, identifier, pageX, pageY, screenX, screenY, clientX, clientY) {\n            // auto set\n            if(clientX == undefined || clientY == undefined) {\n                clientX = pageX - window.pageXOffset;\n                clientY = pageY - window.pageYOffset;\n            }\n\n            return new Touch(target, identifier, {\n                pageX: pageX,\n                pageY: pageY,\n                screenX: screenX,\n                screenY: screenY,\n                clientX: clientX,\n                clientY: clientY\n            });\n        };\n    }\n\n    if(!document.createTouchList) {\n        document.createTouchList = function() {\n            var touchList = new TouchList();\n            for (var i = 0; i < arguments.length; i++) {\n                touchList[i] = arguments[i];\n            }\n            touchList.length = arguments.length;\n            return touchList;\n        };\n    }\n\n    /**\n     * create an touch point\n     * @constructor\n     * @param target\n     * @param identifier\n     * @param pos\n     * @param deltaX\n     * @param deltaY\n     * @returns {Object} touchPoint\n     */\n    function Touch(target, identifier, pos, deltaX, deltaY) {\n        deltaX = deltaX || 0;\n        deltaY = deltaY || 0;\n\n        this.identifier = identifier;\n        this.target = target;\n        this.clientX = pos.clientX + deltaX;\n        this.clientY = pos.clientY + deltaY;\n        this.screenX = pos.screenX + deltaX;\n        this.screenY = pos.screenY + deltaY;\n        this.pageX = pos.pageX + deltaX;\n        this.pageY = pos.pageY + deltaY;\n    }\n\n    /**\n     * create empty touchlist with the methods\n     * @constructor\n     * @returns touchList\n     */\n    function TouchList() {\n        var touchList = [];\n\n        touchList.item = function(index) {\n            return this[index] || null;\n        };\n\n        // specified by Mozilla\n        touchList.identifiedTouch = function(id) {\n            return this[id + 1] || null;\n        };\n\n        return touchList;\n    }\n\n\n    /**\n     * Simple trick to fake touch event support\n     * this is enough for most libraries like Modernizr and Hammer\n     */\n    function fakeTouchSupport() {\n        var objs = [window, document.documentElement];\n        var props = ['ontouchstart', 'ontouchmove', 'ontouchcancel', 'ontouchend'];\n\n        for(var o=0; o<objs.length; o++) {\n            for(var p=0; p<props.length; p++) {\n                if(objs[o] && objs[o][props[p]] == undefined) {\n                    objs[o][props[p]] = null;\n                }\n            }\n        }\n    }\n\n    /**\n     * we don't have to emulate on a touch device\n     * @returns {boolean}\n     */\n    function hasTouchSupport() {\n        return (\"ontouchstart\" in window) || // touch events\n               (window.Modernizr && window.Modernizr.touch) || // modernizr\n               (navigator.msMaxTouchPoints || navigator.maxTouchPoints) > 2; // pointer events\n    }\n\n    /**\n     * disable mouseevents on the page\n     * @param ev\n     */\n    function preventMouseEvents(ev) {\n        ev.preventDefault();\n        ev.stopPropagation();\n    }\n\n    /**\n     * only trigger touches when the left mousebutton has been pressed\n     * @param touchType\n     * @returns {Function}\n     */\n    function onMouse(touchType) {\n        return function(ev) {\n            // prevent mouse events\n            preventMouseEvents(ev);\n\n            if (ev.which !== 1) {\n                return;\n            }\n\n            // The EventTarget on which the touch point started when it was first placed on the surface,\n            // even if the touch point has since moved outside the interactive area of that element.\n            // also, when the target doesnt exist anymore, we update it\n            if (ev.type == 'mousedown' || !eventTarget || (eventTarget && !eventTarget.dispatchEvent)) {\n                eventTarget = ev.target;\n            }\n\n            // shiftKey has been lost, so trigger a touchend\n            if (isMultiTouch && !ev.shiftKey) {\n                triggerTouch('touchend', ev);\n                isMultiTouch = false;\n            }\n\n            triggerTouch(touchType, ev);\n\n            // we're entering the multi-touch mode!\n            if (!isMultiTouch && ev.shiftKey) {\n                isMultiTouch = true;\n                multiTouchStartPos = {\n                    pageX: ev.pageX,\n                    pageY: ev.pageY,\n                    clientX: ev.clientX,\n                    clientY: ev.clientY,\n                    screenX: ev.screenX,\n                    screenY: ev.screenY\n                };\n                triggerTouch('touchstart', ev);\n            }\n\n            // reset\n            if (ev.type == 'mouseup') {\n                multiTouchStartPos = null;\n                isMultiTouch = false;\n                eventTarget = null;\n            }\n        }\n    }\n\n    /**\n     * trigger a touch event\n     * @param eventName\n     * @param mouseEv\n     */\n    function triggerTouch(eventName, mouseEv) {\n        var touchEvent = document.createEvent('Event');\n        touchEvent.initEvent(eventName, true, true);\n\n        touchEvent.altKey = mouseEv.altKey;\n        touchEvent.ctrlKey = mouseEv.ctrlKey;\n        touchEvent.metaKey = mouseEv.metaKey;\n        touchEvent.shiftKey = mouseEv.shiftKey;\n\n        touchEvent.touches = getActiveTouches(mouseEv, eventName);\n        touchEvent.targetTouches = getActiveTouches(mouseEv, eventName);\n        touchEvent.changedTouches = getChangedTouches(mouseEv, eventName);\n\n        eventTarget.dispatchEvent(touchEvent);\n    }\n\n    /**\n     * create a touchList based on the mouse event\n     * @param mouseEv\n     * @returns {TouchList}\n     */\n    function createTouchList(mouseEv) {\n        var touchList = new TouchList();\n\n        if (isMultiTouch) {\n            var f = TouchEmulator.multiTouchOffset;\n            var deltaX = multiTouchStartPos.pageX - mouseEv.pageX;\n            var deltaY = multiTouchStartPos.pageY - mouseEv.pageY;\n\n            touchList.push(new Touch(eventTarget, 1, multiTouchStartPos, (deltaX*-1) - f, (deltaY*-1) + f));\n            touchList.push(new Touch(eventTarget, 2, multiTouchStartPos, deltaX+f, deltaY-f));\n        } else {\n            touchList.push(new Touch(eventTarget, 1, mouseEv, 0, 0));\n        }\n\n        return touchList;\n    }\n\n    /**\n     * receive all active touches\n     * @param mouseEv\n     * @returns {TouchList}\n     */\n    function getActiveTouches(mouseEv, eventName) {\n        // empty list\n        if (mouseEv.type == 'mouseup') {\n            return new TouchList();\n        }\n\n        var touchList = createTouchList(mouseEv);\n        if(isMultiTouch && mouseEv.type != 'mouseup' && eventName == 'touchend') {\n            touchList.splice(1, 1);\n        }\n        return touchList;\n    }\n\n    /**\n     * receive a filtered set of touches with only the changed pointers\n     * @param mouseEv\n     * @param eventName\n     * @returns {TouchList}\n     */\n    function getChangedTouches(mouseEv, eventName) {\n        var touchList = createTouchList(mouseEv);\n\n        // we only want to return the added/removed item on multitouch\n        // which is the second pointer, so remove the first pointer from the touchList\n        //\n        // but when the mouseEv.type is mouseup, we want to send all touches because then\n        // no new input will be possible\n        if(isMultiTouch && mouseEv.type != 'mouseup' &&\n            (eventName == 'touchstart' || eventName == 'touchend')) {\n            touchList.splice(0, 1);\n        }\n\n        return touchList;\n    }\n\n    /**\n     * show the touchpoints on the screen\n     */\n    function showTouches(ev) {\n        var touch, i, el, styles;\n\n        // first all visible touches\n        for(i = 0; i < ev.touches.length; i++) {\n            touch = ev.touches[i];\n            el = touchElements[touch.identifier];\n            if(!el) {\n                el = touchElements[touch.identifier] = document.createElement(\"div\");\n                document.body.appendChild(el);\n            }\n\n            styles = TouchEmulator.template(touch);\n            for(var prop in styles) {\n                el.style[prop] = styles[prop];\n            }\n        }\n\n        // remove all ended touches\n        if(ev.type == 'touchend' || ev.type == 'touchcancel') {\n            for(i = 0; i < ev.changedTouches.length; i++) {\n                touch = ev.changedTouches[i];\n                el = touchElements[touch.identifier];\n                if(el) {\n                    el.parentNode.removeChild(el);\n                    delete touchElements[touch.identifier];\n                }\n            }\n        }\n    }\n\n    /**\n     * TouchEmulator initializer\n     */\n    function TouchEmulator() {\n        if (hasTouchSupport()) {\n            return;\n        }\n\n        fakeTouchSupport();\n\n        window.addEventListener(\"mousedown\", onMouse('touchstart'), true);\n        window.addEventListener(\"mousemove\", onMouse('touchmove'), true);\n        window.addEventListener(\"mouseup\", onMouse('touchend'), true);\n\n        window.addEventListener(\"mouseenter\", preventMouseEvents, true);\n        window.addEventListener(\"mouseleave\", preventMouseEvents, true);\n        window.addEventListener(\"mouseout\", preventMouseEvents, true);\n        window.addEventListener(\"mouseover\", preventMouseEvents, true);\n\n        // it uses itself!\n        window.addEventListener(\"touchstart\", showTouches, false);\n        window.addEventListener(\"touchmove\", showTouches, false);\n        window.addEventListener(\"touchend\", showTouches, false);\n        window.addEventListener(\"touchcancel\", showTouches, false);\n    }\n\n    // start distance when entering the multitouch mode\n    TouchEmulator.multiTouchOffset = 75;\n\n    /**\n     * css template for the touch rendering\n     * @param touch\n     * @returns object\n     */\n    TouchEmulator.template = function(touch) {\n        var size = 30;\n        var transform = 'translate('+ (touch.clientX-(size/2)) +'px, '+ (touch.clientY-(size/2)) +'px)';\n        return {\n            position: 'fixed',\n            left: 0,\n            top: 0,\n            background: '#fff',\n            border: 'solid 1px #999',\n            opacity: .6,\n            borderRadius: '100%',\n            height: size + 'px',\n            width: size + 'px',\n            padding: 0,\n            margin: 0,\n            display: 'block',\n            overflow: 'hidden',\n            pointerEvents: 'none',\n            webkitUserSelect: 'none',\n            mozUserSelect: 'none',\n            userSelect: 'none',\n            webkitTransform: transform,\n            mozTransform: transform,\n            transform: transform\n        }\n    };\n\n    // export\n    if (typeof define == \"function\" && define.amd) {\n        define(function() {\n            return TouchEmulator;\n        });\n    } else if (typeof module != \"undefined\" && module.exports) {\n        module.exports = TouchEmulator;\n    } else {\n        window[exportName] = TouchEmulator;\n    }\n})(window, document, \"TouchEmulator\");"
  },
  {
    "path": "examples/css-layout/app.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar ReactCanvas = require('react-canvas');\n\nvar Surface = ReactCanvas.Surface;\nvar Group = ReactCanvas.Group;\nvar Image = ReactCanvas.Image;\nvar Text = ReactCanvas.Text;\nvar FontFace = ReactCanvas.FontFace;\n\nvar App = React.createClass({\n\n  componentDidMount: function () {\n    window.addEventListener('resize', this.handleResize, true);\n  },\n\n  render: function () {\n    var size = this.getSize();\n    return (\n      <Surface top={0} left={0} width={size.width} height={size.height} enableCSSLayout={true}>\n        <Group style={this.getPageStyle()}>\n          <Text style={this.getTitleStyle()}>\n            Professor PuddinPop\n          </Text>\n          <Group style={this.getImageGroupStyle()}>\n            <Image src='https://placekitten.com/720/840' style={this.getImageStyle()} fadeIn={true} />\n          </Group>\n          <Text style={this.getExcerptStyle()}>\n            With these words the Witch fell down in a brown, melted, shapeless mass and began to spread over the clean boards of the kitchen floor.  Seeing that she had really melted away to nothing, Dorothy drew another bucket of water and threw it over the mess.  She then swept it all out the door.  After picking out the silver shoe, which was all that was left of the old woman, she cleaned and dried it with a cloth, and put it on her foot again.  Then, being at last free to do as she chose, she ran out to the courtyard to tell the Lion that the Wicked Witch of the West had come to an end, and that they were no longer prisoners in a strange land.\n          </Text>\n        </Group>\n      </Surface>\n    );\n  },\n\n  // Styles\n  // ======\n\n  getSize: function () {\n    return document.getElementById('main').getBoundingClientRect();\n  },\n\n  getPageStyle: function () {\n    var size = this.getSize();\n    return {\n      position: 'relative',\n      padding: 14,\n      width: size.width,\n      height: size.height,\n      backgroundColor: '#f7f7f7',\n      flexDirection: 'column'\n    };\n  },\n\n  getImageGroupStyle: function () {\n    return {\n      position: 'relative',\n      flex: 1,\n      backgroundColor: '#eee'\n    };\n  },\n\n  getImageStyle: function () {\n    return {\n      position: 'absolute',\n      left: 0,\n      top: 0,\n      right: 0,\n      bottom: 0\n    };\n  },\n\n  getTitleStyle: function () {\n    return {\n      fontFace: FontFace('Georgia'),\n      fontSize: 22,\n      lineHeight: 28,\n      height: 28,\n      marginBottom: 10,\n      color: '#333',\n      textAlign: 'center'\n    };\n  },\n\n  getExcerptStyle: function () {\n    return {\n      fontFace: FontFace('Georgia'),\n      fontSize: 17,\n      lineHeight: 25,\n      marginTop: 15,\n      flex: 1,\n      color: '#333'\n    };\n  },\n\n  // Events\n  // ======\n\n  handleResize: function () {\n    this.forceUpdate();\n  }\n\n});\n\nReactDOM.render(<App />, document.getElementById('main'));\n"
  },
  {
    "path": "examples/css-layout/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n  <title>ReactCanvas: css-layout</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/examples/common/examples.css\">\n  <script src=\"/examples/common/touch-emulator.js\"></script>\n  <script type=\"text/javascript\">\n    TouchEmulator();\n  </script>\n</head>\n<body>\n  <div id=\"main\"></div>\n  <script src=\"/build/css-layout.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/gradient/app.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar ReactCanvas = require('react-canvas');\n\nvar Gradient = ReactCanvas.Gradient;\nvar Surface = ReactCanvas.Surface;\n\nvar App = React.createClass({\n\n  render: function () {\n    var size = this.getSize();\n    return (\n      <Surface top={0} left={0} width={size.width} height={size.height}>\n        <Gradient style={this.getGradientStyle()}\n                  colorStops={this.getGradientColors()} />\n      </Surface>\n    );\n  },\n\n  getGradientStyle: function(){\n    var size = this.getSize();\n    return {\n      top: 0,\n      left: 0,\n      width: size.width,\n      height: size.height\n    };\n  },\n\n  getGradientColors: function(){\n    return [\n      { color: \"transparent\", position: 0 },\n      { color: \"#000\", position: 1 }\n    ];\n  },\n\n  getSize: function () {\n    return document.getElementById('main').getBoundingClientRect();\n  }\n\n});\n\nReactDOM.render(<App />, document.getElementById('main'));\n"
  },
  {
    "path": "examples/gradient/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n  <title>ReactCanvas: ListView</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/examples/common/examples.css\">\n  <script src=\"/examples/common/touch-emulator.js\"></script>\n  <script type=\"text/javascript\">\n    TouchEmulator();\n  </script>\n</head>\n<body>\n  <div id=\"main\"></div>\n  <script src=\"/build/gradient.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/listview/app.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar ReactCanvas = require('react-canvas');\nvar Item = require('./components/Item');\nvar articles = require('../common/data');\n\nvar Surface = ReactCanvas.Surface;\nvar ListView = ReactCanvas.ListView;\n\nvar App = React.createClass({\n\n  render: function () {\n    var size = this.getSize();\n    return (\n      <Surface top={0} left={0} width={size.width} height={size.height}>\n        <ListView\n          style={this.getListViewStyle()}\n          numberOfItemsGetter={this.getNumberOfItems}\n          itemHeightGetter={Item.getItemHeight}\n          itemGetter={this.renderItem} />\n      </Surface>\n    );\n  },\n\n  renderItem: function (itemIndex, scrollTop) {\n    var article = articles[itemIndex % articles.length];\n      return (\n      <Item\n        width={this.getSize().width}\n        height={Item.getItemHeight()}\n        imageUrl={article.imageUrl}\n        title={article.title}\n        itemIndex={itemIndex} />\n    );\n  },\n\n  getSize: function () {\n    return document.getElementById('main').getBoundingClientRect();\n  },\n\n  // ListView\n  // ========\n\n  getListViewStyle: function () {\n    return {\n      top: 0,\n      left: 0,\n      width: window.innerWidth,\n      height: window.innerHeight\n    };\n  },\n\n  getNumberOfItems: function () {\n    return 1000;\n  },\n\n});\n\nReactDOM.render(<App />, document.getElementById('main'));\n"
  },
  {
    "path": "examples/listview/components/Item.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactCanvas = require('react-canvas');\n\nvar Group = ReactCanvas.Group;\nvar Image = ReactCanvas.Image;\nvar Text = ReactCanvas.Text;\n\nvar Item = React.createClass({\n\n  propTypes: {\n    width: React.PropTypes.number.isRequired,\n    height: React.PropTypes.number.isRequired,\n    imageUrl: React.PropTypes.string.isRequired,\n    title: React.PropTypes.string.isRequired,\n    itemIndex: React.PropTypes.number.isRequired,\n  },\n\n  statics: {\n    getItemHeight: function () {\n      return 80;\n    }\n  },\n\n  render: function () {\n    return (\n      <Group style={this.getStyle()}>\n        <Image style={this.getImageStyle()} src={this.props.imageUrl} />\n        <Text style={this.getTitleStyle()}>{this.props.title}</Text>\n      </Group>\n    );\n  },\n\n  getStyle: function () {\n    return {\n      width: this.props.width,\n      height: Item.getItemHeight(),\n      backgroundColor: (this.props.itemIndex % 2) ? '#eee' : '#a5d2ee'\n    };\n  },\n\n  getImageStyle: function () {\n    return {\n      top: 10,\n      left: 10,\n      width: 60,\n      height: 60,\n      backgroundColor: '#ddd',\n      borderColor: '#999',\n      borderWidth: 1\n    };\n  },\n\n  getTitleStyle: function () {\n    return {\n      top: 32,\n      left: 80,\n      width: this.props.width - 90,\n      height: 18,\n      fontSize: 14,\n      lineHeight: 18\n    };\n  }\n\n});\n\nmodule.exports = Item;\n"
  },
  {
    "path": "examples/listview/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n  <title>ReactCanvas: ListView</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/examples/common/examples.css\">\n  <script src=\"/examples/common/touch-emulator.js\"></script>\n  <script type=\"text/javascript\">\n    TouchEmulator();\n  </script>\n</head>\n<body>\n  <div id=\"main\"></div>\n  <script src=\"/build/listview.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "examples/timeline/app.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar ReactCanvas = require('react-canvas');\nvar Page = require('./components/Page');\nvar articles = require('../common/data');\n\nvar Surface = ReactCanvas.Surface;\nvar ListView = ReactCanvas.ListView;\n\nvar App = React.createClass({\n\n  render: function () {\n    var size = this.getSize();\n    return (\n      <Surface top={0} left={0} width={size.width} height={size.height}>\n        <ListView\n          style={this.getListViewStyle()}\n          snapping={true}\n          scrollingDeceleration={0.92}\n          scrollingPenetrationAcceleration={0.13}\n          numberOfItemsGetter={this.getNumberOfPages}\n          itemHeightGetter={this.getPageHeight}\n          itemGetter={this.renderPage} />\n      </Surface>\n    );\n  },\n\n  renderPage: function (pageIndex, scrollTop) {\n    var size = this.getSize();\n    var article = articles[pageIndex % articles.length];\n    var pageScrollTop = pageIndex * this.getPageHeight() - scrollTop;\n    return (\n      <Page\n        width={size.width}\n        height={size.height}\n        article={article}\n        pageIndex={pageIndex}\n        scrollTop={pageScrollTop} />\n    );\n  },\n\n  getSize: function () {\n    return document.getElementById('main').getBoundingClientRect();\n  },\n\n  // ListView\n  // ========\n\n  getListViewStyle: function () {\n    var size = this.getSize();\n    return {\n      top: 0,\n      left: 0,\n      width: size.width,\n      height: size.height\n    };\n  },\n\n  getNumberOfPages: function () {\n    return 1000;\n  },\n\n  getPageHeight: function () {\n    return this.getSize().height;\n  }\n\n});\n\nReactDOM.render(<App />, document.getElementById('main'));\n"
  },
  {
    "path": "examples/timeline/components/Page.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactCanvas = require('react-canvas');\n\nvar Group = ReactCanvas.Group;\nvar Image = ReactCanvas.Image;\nvar Text = ReactCanvas.Text;\nvar FontFace = ReactCanvas.FontFace;\nvar measureText = ReactCanvas.measureText;\n\nvar CONTENT_INSET = 14;\nvar TEXT_SCROLL_SPEED_MULTIPLIER = 0.6;\nvar TEXT_ALPHA_SPEED_OUT_MULTIPLIER = 1.25;\nvar TEXT_ALPHA_SPEED_IN_MULTIPLIER = 2.6;\nvar IMAGE_LAYER_INDEX = 2;\nvar TEXT_LAYER_INDEX = 1;\n\nvar Page = React.createClass({\n\n  propTypes: {\n    width: React.PropTypes.number.isRequired,\n    height: React.PropTypes.number.isRequired,\n    article: React.PropTypes.object.isRequired,\n    scrollTop: React.PropTypes.number.isRequired\n  },\n\n  componentWillMount: function () {\n    // Pre-compute headline/excerpt text dimensions.\n    var article = this.props.article;\n    var maxWidth = this.props.width - 2 * CONTENT_INSET;\n    var titleStyle = this.getTitleStyle();\n    var excerptStyle = this.getExcerptStyle();\n    this.titleMetrics = measureText(article.title, maxWidth, titleStyle.fontFace, titleStyle.fontSize, titleStyle.lineHeight);\n    this.excerptMetrics = measureText(article.excerpt, maxWidth, excerptStyle.fontFace, excerptStyle.fontSize, excerptStyle.lineHeight);\n  },\n\n  render: function () {\n    var groupStyle = this.getGroupStyle();\n    var imageStyle = this.getImageStyle();\n    var titleStyle = this.getTitleStyle();\n    var excerptStyle = this.getExcerptStyle();\n\n    // Layout title and excerpt below image.\n    titleStyle.height = this.titleMetrics.height;\n    excerptStyle.top = titleStyle.top + titleStyle.height + CONTENT_INSET;\n    excerptStyle.height = this.props.height - excerptStyle.top - CONTENT_INSET;\n\n    return (\n      <Group style={groupStyle}>\n        <Image style={imageStyle} src={this.props.article.imageUrl} fadeIn={true} useBackingStore={true} />\n        <Group style={this.getTextGroupStyle()} useBackingStore={true}>\n          <Text style={titleStyle}>{this.props.article.title}</Text>\n          <Text style={excerptStyle}>{this.props.article.excerpt}</Text>\n        </Group>\n      </Group>\n    );\n  },\n\n  // Styles\n  // ======\n\n  getGroupStyle: function () {\n    return {\n      top: 0,\n      left: 0,\n      width: this.props.width,\n      height: this.props.height,\n    };\n  },\n\n  getImageHeight: function () {\n    return Math.round(this.props.height * 0.5);\n  },\n\n  getImageStyle: function () {\n    return {\n      top: 0,\n      left: 0,\n      width: this.props.width,\n      height: this.getImageHeight(),\n      backgroundColor: '#eee',\n      zIndex: IMAGE_LAYER_INDEX\n    };\n  },\n\n  getTitleStyle: function () {\n    return {\n      top: this.getImageHeight() + CONTENT_INSET,\n      left: CONTENT_INSET,\n      width: this.props.width - 2 * CONTENT_INSET,\n      fontSize: 22,\n      lineHeight: 30,\n      fontFace: FontFace('Avenir Next Condensed, Helvetica, sans-serif', null, {weight: 500})\n    };\n  },\n\n  getExcerptStyle: function () {\n    return {\n      left: CONTENT_INSET,\n      width: this.props.width - 2 * CONTENT_INSET,\n      fontFace: FontFace('Georgia, serif'),\n      fontSize: 15,\n      lineHeight: 23\n    };\n  },\n\n  getTextGroupStyle: function () {\n    var imageHeight = this.getImageHeight();\n    var translateY = 0;\n    var alphaMultiplier = (this.props.scrollTop <= 0) ? -TEXT_ALPHA_SPEED_OUT_MULTIPLIER : TEXT_ALPHA_SPEED_IN_MULTIPLIER;\n    var alpha = 1 - (this.props.scrollTop / this.props.height) * alphaMultiplier;\n    alpha = Math.min(Math.max(alpha, 0), 1);\n    translateY = -this.props.scrollTop * TEXT_SCROLL_SPEED_MULTIPLIER;\n\n    return {\n      width: this.props.width,\n      height: this.props.height - imageHeight,\n      top: imageHeight,\n      left: 0,\n      alpha: alpha,\n      translateY: translateY,\n      zIndex: TEXT_LAYER_INDEX\n    };\n  }\n\n});\n\nmodule.exports = Page;\n"
  },
  {
    "path": "examples/timeline/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n  <meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n  <title>ReactCanvas: Timeline</title>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"/examples/common/examples.css\">\n  <script src=\"/examples/common/touch-emulator.js\"></script>\n  <script type=\"text/javascript\">\n    TouchEmulator();\n  </script>\n</head>\n<body>\n  <div id=\"main\"></div>\n  <script src=\"/build/timeline.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "gulpfile.js",
    "content": "var gulp = require('gulp');\nvar del = require('del');\nvar connect = require('gulp-connect');\nvar webpack = require('webpack-stream');\nvar webpackConfig = require('./webpack.config.js');\n\nvar port = process.env.PORT || 8080;\nvar reloadPort = process.env.RELOAD_PORT || 35729;\n\ngulp.task('clean', function () {\n  del(['build']);\n});\n\ngulp.task('build', function () {\n  return gulp.src(webpackConfig.entry.timeline[0])\n    .pipe(webpack(webpackConfig))\n    .pipe(gulp.dest('build/'));\n});\n\ngulp.task('serve', function () {\n  connect.server({\n    port: port,\n    livereload: {\n      port: reloadPort\n    }\n  });\n});\n\ngulp.task('reload-js', function () {\n  return gulp.src('./build/*.js')\n    .pipe(connect.reload());\n});\n\ngulp.task('watch', function () {\n  gulp.watch(['./build/*.js'], ['reload-js']);\n});\n\ngulp.task('default', ['clean', 'build', 'serve', 'watch']);\n"
  },
  {
    "path": "lib/Canvas.js",
    "content": "'use strict';\n\n// Note that this class intentionally does not use PooledClass.\n// DrawingUtils manages <canvas> pooling for more fine-grained control.\n\nfunction Canvas (width, height, scale) {\n  // Re-purposing an existing canvas element.\n  if (!this._canvas) {\n    this._canvas = document.createElement('canvas');\n  }\n\n  this.width = width;\n  this.height = height;\n  this.scale = scale || window.devicePixelRatio;\n\n  this._canvas.width = this.width * this.scale;\n  this._canvas.height = this.height * this.scale;\n  this._canvas.getContext('2d').scale(this.scale, this.scale);\n}\n\nObject.assign(Canvas.prototype, {\n\n  getRawCanvas: function () {\n    return this._canvas;\n  },\n\n  getContext: function () {\n    return this._canvas.getContext('2d');\n  }\n\n});\n\n// PooledClass:\n\n// Be fairly conserative - we are potentially drawing a large number of medium\n// to large size images.\nCanvas.poolSize = 30;\n\nmodule.exports = Canvas;\n"
  },
  {
    "path": "lib/CanvasUtils.js",
    "content": "'use strict';\n\nvar FontFace = require('./FontFace');\nvar clamp = require('./clamp');\nvar measureText = require('./measureText');\n\n/**\n * Draw an image into a <canvas>. This operation requires that the image\n * already be loaded.\n *\n * @param {CanvasContext} ctx\n * @param {Image} image The source image (from ImageCache.get())\n * @param {Number} x The x-coordinate to begin drawing\n * @param {Number} y The y-coordinate to begin drawing\n * @param {Number} width The desired width\n * @param {Number} height The desired height\n * @param {Object} options Available options are:\n *   {Number} originalWidth\n *   {Number} originalHeight\n *   {Object} focusPoint {x,y}\n *   {String} backgroundColor\n */\nfunction drawImage (ctx, image, x, y, width, height, options) {\n  options = options || {};\n\n  if (options.backgroundColor) {\n    ctx.save();\n    ctx.fillStyle = options.backgroundColor;\n    ctx.fillRect(x, y, width, height);\n    ctx.restore();\n  }\n\n  var dx = 0;\n  var dy = 0;\n  var dw = 0;\n  var dh = 0;\n  var sx = 0;\n  var sy = 0;\n  var sw = 0;\n  var sh = 0;\n  var scale;\n  var scaledSize;\n  var actualSize;\n  var focusPoint = options.focusPoint;\n\n  actualSize = {\n    width: image.getWidth(),\n    height: image.getHeight()\n  };\n\n  scale = Math.max(\n    width / actualSize.width,\n    height / actualSize.height\n  ) || 1;\n  scale = parseFloat(scale.toFixed(4), 10);\n\n  scaledSize = {\n    width: actualSize.width * scale,\n    height: actualSize.height * scale\n  };\n\n  if (focusPoint) {\n    // Since image hints are relative to image \"original\" dimensions (original != actual),\n    // use the original size for focal point cropping.\n    if (options.originalHeight) {\n      focusPoint.x *= (actualSize.height / options.originalHeight);\n      focusPoint.y *= (actualSize.height / options.originalHeight);\n    }\n  } else {\n    // Default focal point to [0.5, 0.5]\n    focusPoint = {\n      x: actualSize.width * 0.5,\n      y: actualSize.height * 0.5\n    };\n  }\n\n  // Clip the image to rectangle (sx, sy, sw, sh).\n  sx = Math.round(clamp(width * 0.5 - focusPoint.x * scale, width - scaledSize.width, 0)) * (-1 / scale);\n  sy = Math.round(clamp(height * 0.5 - focusPoint.y * scale, height - scaledSize.height, 0)) * (-1 / scale);\n  sw = Math.round(actualSize.width - (sx * 2));\n  sh = Math.round(actualSize.height - (sy * 2));\n\n  // Scale the image to dimensions (dw, dh).\n  dw = Math.round(width);\n  dh = Math.round(height);\n\n  // Draw the image on the canvas at coordinates (dx, dy).\n  dx = Math.round(x);\n  dy = Math.round(y);\n\n  ctx.drawImage(image.getRawImage(), sx, sy, sw, sh, dx, dy, dw, dh);\n}\n\n/**\n * @param {CanvasContext} ctx\n * @param {String} text The text string to render\n * @param {Number} x The x-coordinate to begin drawing\n * @param {Number} y The y-coordinate to begin drawing\n * @param {Number} width The maximum allowed width\n * @param {Number} height The maximum allowed height\n * @param {FontFace} fontFace The FontFace to to use\n * @param {Object} options Available options are:\n *   {Number} fontSize\n *   {Number} lineHeight\n *   {String} textAlign\n *   {String} color\n *   {String} backgroundColor\n */\nfunction drawText (ctx, text, x, y, width, height, fontFace, options) {\n  var textMetrics;\n  var currX = x;\n  var currY = y;\n  var currText;\n  var options = options || {};\n\n  options.fontSize = options.fontSize || 16;\n  options.lineHeight = options.lineHeight || 18;\n  options.textAlign = options.textAlign || 'left';\n  options.backgroundColor = options.backgroundColor || 'transparent';\n  options.color = options.color || '#000';\n\n  textMetrics = measureText(\n    text,\n    width,\n    fontFace,\n    options.fontSize,\n    options.lineHeight\n  );\n\n  ctx.save();\n\n  // Draw the background\n  if (options.backgroundColor !== 'transparent') {\n    ctx.fillStyle = options.backgroundColor;\n    ctx.fillRect(0, 0, width, height);\n  }\n\n  ctx.fillStyle = options.color;\n  ctx.font = fontFace.attributes.style + ' ' + fontFace.attributes.weight + ' ' + options.fontSize + 'px ' + fontFace.family;\n\n  textMetrics.lines.forEach(function (line, index) {\n    currText = line.text;\n    currY = (index === 0) ? y + options.fontSize :\n      (y + options.fontSize + options.lineHeight * index);\n\n    // Account for text-align: left|right|center\n    switch (options.textAlign) {\n      case 'center':\n        currX = x + (width / 2) - (line.width / 2);\n        break;\n      case 'right':\n        currX = x + width - line.width;\n        break;\n      default:\n        currX = x;\n    }\n\n    if ((index < textMetrics.lines.length - 1) &&\n      ((options.fontSize + options.lineHeight * (index + 1)) > height)) {\n      currText = currText.replace(/\\,?\\s?\\w+$/, '…');\n    }\n\n    if (currY <= (height + y)) {\n      ctx.fillText(currText, currX, currY);\n    }\n  });\n\n  ctx.restore();\n}\n\n/**\n * Draw a linear gradient\n *\n * @param {CanvasContext} ctx\n * @param {Number} x1 gradient start-x coordinate\n * @param {Number} y1 gradient start-y coordinate\n * @param {Number} x2 gradient end-x coordinate\n * @param {Number} y2 gradient end-y coordinate\n * @param {Array} colorStops Array of {(String)color, (Number)position} values\n * @param {Number} x x-coordinate to begin fill\n * @param {Number} y y-coordinate to begin fill\n * @param {Number} width how wide to fill\n * @param {Number} height how tall to fill\n */\nfunction drawGradient(ctx, x1, y1, x2, y2, colorStops, x, y, width, height) {\n  var grad;\n\n  ctx.save();\n  grad = ctx.createLinearGradient(x1, y1, x2, y2);\n\n  colorStops.forEach(function (colorStop) {\n    grad.addColorStop(colorStop.position, colorStop.color);\n  });\n\n  ctx.fillStyle = grad;\n  ctx.fillRect(x, y, width, height);\n  ctx.restore();\n}\n\nmodule.exports = {\n  drawImage: drawImage,\n  drawText: drawText,\n  drawGradient: drawGradient,\n};\n\n"
  },
  {
    "path": "lib/ContainerMixin.js",
    "content": "'use strict';\n\n// Adapted from ReactART:\n// https://github.com/reactjs/react-art\n\nvar React = require('react');\nvar ReactMultiChild = require('react-dom/lib/ReactMultiChild');\nvar emptyObject = require('fbjs/lib/emptyObject');\n\nvar ContainerMixin = Object.assign({}, ReactMultiChild.Mixin, {\n\n  /**\n   * Moves a child component to the supplied index.\n   *\n   * @param {ReactComponent} child Component to move.\n   * @param {number} toIndex Destination index of the element.\n   * @protected\n   */\n  moveChild: function(child, afterNode, toIndex, lastIndex) {\n    var childNode = child._mountImage;\n    var mostRecentlyPlacedChild = this._mostRecentlyPlacedChild;\n    if (mostRecentlyPlacedChild == null) {\n      // I'm supposed to be first.\n      if (childNode.previousSibling) {\n        if (this.node.firstChild) {\n          childNode.injectBefore(this.node.firstChild);\n        } else {\n          childNode.inject(this.node);\n        }\n      }\n    } else {\n      // I'm supposed to be after the previous one.\n      if (mostRecentlyPlacedChild.nextSibling !== childNode) {\n        if (mostRecentlyPlacedChild.nextSibling) {\n          childNode.injectBefore(mostRecentlyPlacedChild.nextSibling);\n        } else {\n          childNode.inject(this.node);\n        }\n      }\n    }\n    this._mostRecentlyPlacedChild = childNode;\n  },\n\n  /**\n   * Creates a child component.\n   *\n   * @param {ReactComponent} child Component to create.\n   * @param {object} childNode ART node to insert.\n   * @protected\n   */\n  createChild: function(child, afterNode, childNode) {\n    child._mountImage = childNode;\n    var mostRecentlyPlacedChild = this._mostRecentlyPlacedChild;\n    if (mostRecentlyPlacedChild == null) {\n      // I'm supposed to be first.\n      if (this.node.firstChild) {\n        childNode.injectBefore(this.node.firstChild);\n      } else {\n        childNode.inject(this.node);\n      }\n    } else {\n      // I'm supposed to be after the previous one.\n      if (mostRecentlyPlacedChild.nextSibling) {\n        childNode.injectBefore(mostRecentlyPlacedChild.nextSibling);\n      } else {\n        childNode.inject(this.node);\n      }\n    }\n    this._mostRecentlyPlacedChild = childNode;\n  },\n\n  /**\n   * Removes a child component.\n   *\n   * @param {ReactComponent} child Child to remove.\n   * @protected\n   */\n  removeChild: function(child) {\n    child._mountImage.remove();\n    child._mountImage = null;\n    this.node.invalidateLayout();\n  },\n\n  updateChildrenAtRoot: function(nextChildren, transaction) {\n    this.updateChildren(nextChildren, transaction, emptyObject);\n  },\n\n  mountAndInjectChildrenAtRoot: function(children, transaction) {\n    this.mountAndInjectChildren(children, transaction, emptyObject);\n  },\n\n  /**\n   * Override to bypass batch updating because it is not necessary.\n   *\n   * @param {?object} nextChildren.\n   * @param {ReactReconcileTransaction} transaction\n   * @internal\n   * @override {ReactMultiChild.Mixin.updateChildren}\n   */\n  updateChildren: function(nextChildren, transaction, context) {\n    this._mostRecentlyPlacedChild = null;\n    this._updateChildren(nextChildren, transaction, context);\n  },\n\n  // Shorthands\n\n  mountAndInjectChildren: function(children, transaction, context) {\n    var mountedImages = this.mountChildren(\n      children,\n      transaction,\n      context\n    );\n\n    // Each mount image corresponds to one of the flattened children\n    var i = 0;\n    for (var key in this._renderedChildren) {\n      if (this._renderedChildren.hasOwnProperty(key)) {\n        var child = this._renderedChildren[key];\n        child._mountImage = mountedImages[i];\n        mountedImages[i].inject(this.node);\n        i++;\n      }\n    }\n  },\n  getHostNode: function () { return this.node },\n  getNativeNode: function () { return this.node },\n\n});\n\nmodule.exports = ContainerMixin;\n"
  },
  {
    "path": "lib/DrawingUtils.js",
    "content": "'use strict';\n\nvar ImageCache = require('./ImageCache');\nvar FontUtils = require('./FontUtils');\nvar FontFace = require('./FontFace');\nvar FrameUtils = require('./FrameUtils');\nvar CanvasUtils = require('./CanvasUtils');\nvar Canvas = require('./Canvas');\n\n// Global backing store <canvas> cache\nvar _backingStores = [];\n\n/**\n * Maintain a cache of backing <canvas> for RenderLayer's which are accessible\n * through the RenderLayer's `backingStoreId` property.\n *\n * @param {String} id The unique `backingStoreId` for a RenderLayer\n * @return {HTMLCanvasElement}\n */\nfunction getBackingStore (id) {\n  for (var i=0, len=_backingStores.length; i < len; i++) {\n    if (_backingStores[i].id === id) {\n      return _backingStores[i].canvas;\n    }\n  }\n  return null;\n}\n\n/**\n * Purge a layer's backing store from the cache.\n *\n * @param {String} id The layer's backingStoreId\n */\nfunction invalidateBackingStore (id) {\n  for (var i=0, len=_backingStores.length; i < len; i++) {\n    if (_backingStores[i].id === id) {\n      _backingStores.splice(i, 1);\n      break;\n    }\n  }\n}\n\n/**\n * Purge the entire backing store cache.\n */\nfunction invalidateAllBackingStores () {\n  _backingStores = [];\n}\n\n/**\n * Find the nearest backing store ancestor for a given layer.\n *\n * @param {RenderLayer} layer\n */\nfunction getBackingStoreAncestor (layer) {\n  while (layer) {\n    if (layer.backingStoreId) {\n      return layer;\n    }\n    layer = layer.parentLayer;\n  }\n  return null;\n}\n\n/**\n * Check if a layer is using a given image URL.\n *\n * @param {RenderLayer} layer\n * @param {String} imageUrl\n * @return {Boolean}\n */\nfunction layerContainsImage (layer, imageUrl) {\n  // Check the layer itself.\n  if (layer.type === 'image' && layer.imageUrl === imageUrl) {\n    return layer;\n  }\n\n  // Check the layer's children.\n  if (layer.children) {\n    for (var i=0, len=layer.children.length; i < len; i++) {\n      if (layerContainsImage(layer.children[i], imageUrl)) {\n        return layer.children[i];\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Check if a layer is using a given FontFace.\n *\n * @param {RenderLayer} layer\n * @param {FontFace} fontFace\n * @return {Boolean}\n */\nfunction layerContainsFontFace (layer, fontFace) {\n  // Check the layer itself.\n  if (layer.type === 'text' && layer.fontFace && layer.fontFace.id === fontFace.id) {\n    return layer;\n  }\n\n  // Check the layer's children.\n  if (layer.children) {\n    for (var i=0, len=layer.children.length; i < len; i++) {\n      if (layerContainsFontFace(layer.children[i], fontFace)) {\n        return layer.children[i];\n      }\n    }\n  }\n\n  return false;\n}\n\n/**\n * Invalidates the backing stores for layers which contain an image layer\n * associated with the given imageUrl.\n *\n * @param {String} imageUrl\n */\nfunction handleImageLoad (imageUrl) {\n  _backingStores.forEach(function (backingStore) {\n    if (layerContainsImage(backingStore.layer, imageUrl)) {\n      invalidateBackingStore(backingStore.id);\n    }\n  });\n}\n\n/**\n * Invalidates the backing stores for layers which contain a text layer\n * associated with the given font face.\n *\n * @param {FontFace} fontFace\n */\nfunction handleFontLoad (fontFace) {\n  _backingStores.forEach(function (backingStore) {\n    if (layerContainsFontFace(backingStore.layer, fontFace)) {\n      invalidateBackingStore(backingStore.id);\n    }\n  });\n}\n\n/**\n * Draw a RenderLayer instance to a <canvas> context.\n *\n * @param {CanvasRenderingContext2d} ctx\n * @param {RenderLayer} layer\n */\nfunction drawRenderLayer (ctx, layer) {\n  var customDrawFunc;\n\n  // Performance: avoid drawing hidden layers.\n  if (typeof layer.alpha === 'number' && layer.alpha <= 0) {\n    return;\n  }\n\n  switch (layer.type) {\n    case 'image':\n      customDrawFunc = drawImageRenderLayer;\n      break;\n\n    case 'text':\n      customDrawFunc = drawTextRenderLayer;\n      break;\n\n    case 'gradient':\n      customDrawFunc = drawGradientRenderLayer;\n      break;\n  }\n\n  // Establish drawing context for certain properties:\n  // - alpha\n  // - translate\n  var saveContext = (layer.alpha !== null && layer.alpha < 1) ||\n                    (layer.translateX || layer.translateY);\n\n  if (saveContext) {\n    ctx.save();\n\n    // Alpha:\n    if (layer.alpha !== null && layer.alpha < 1) {\n      ctx.globalAlpha = layer.alpha;\n    }\n\n    // Translation:\n    if (layer.translateX || layer.translateY) {\n      ctx.translate(layer.translateX || 0, layer.translateY || 0);\n    }\n  }\n\n  // If the layer is bitmap-cacheable, draw in a pooled off-screen canvas.\n  // We disable backing stores on pad since we flip there.\n  if (layer.backingStoreId) {\n    drawCacheableRenderLayer(ctx, layer, customDrawFunc);\n  } else {\n    // Draw default properties, such as background color.\n    ctx.save();\n    drawBaseRenderLayer(ctx, layer);\n\n    // Draw custom properties if needed.\n    customDrawFunc && customDrawFunc(ctx, layer);\n    ctx.restore();\n\n    // Draw child layers, sorted by their z-index.\n    if (layer.children) {\n      layer.children.slice().sort(sortByZIndexAscending).forEach(function (childLayer) {\n        drawRenderLayer(ctx, childLayer);\n      });\n    }\n  }\n\n  // Pop the context state if we established a new drawing context.\n  if (saveContext) {\n    ctx.restore();\n  }\n}\n\n/**\n * Draw base layer properties into a rendering context.\n * NOTE: The caller is responsible for calling save() and restore() as needed.\n *\n * @param {CanvasRenderingContext2d} ctx\n * @param {RenderLayer} layer\n */\nfunction drawBaseRenderLayer (ctx, layer) {\n  var frame = layer.frame;\n\n  // Border radius:\n  if (layer.borderRadius) {\n    ctx.beginPath();\n    ctx.moveTo(frame.x + layer.borderRadius, frame.y);\n    ctx.arcTo(frame.x + frame.width, frame.y, frame.x + frame.width, frame.y + frame.height, layer.borderRadius);\n    ctx.arcTo(frame.x + frame.width, frame.y + frame.height, frame.x, frame.y + frame.height, layer.borderRadius);\n    ctx.arcTo(frame.x, frame.y + frame.height, frame.x, frame.y, layer.borderRadius);\n    ctx.arcTo(frame.x, frame.y, frame.x + frame.width, frame.y, layer.borderRadius);\n    ctx.closePath();\n\n    // Create a clipping path when drawing an image or using border radius.\n    if (layer.type === 'image') {\n      ctx.clip();\n    }\n\n    // Border with border radius:\n    if (layer.borderColor) {\n      ctx.lineWidth = layer.borderWidth || 1;\n      ctx.strokeStyle = layer.borderColor;\n      ctx.stroke();\n    }\n  }\n\n  // Border color (no border radius):\n  if (layer.borderColor && !layer.borderRadius) {\n    ctx.lineWidth = layer.borderWidth || 1;\n    ctx.strokeStyle = layer.borderColor;\n    ctx.strokeRect(frame.x, frame.y, frame.width, frame.height);\n  }\n\n  // Shadow:\n  ctx.shadowBlur = layer.shadowBlur;\n  ctx.shadowColor = layer.shadowColor;\n  ctx.shadowOffsetX = layer.shadowOffsetX;\n  ctx.shadowOffsetY = layer.shadowOffsetY;\n\n  // Background color:\n  if (layer.backgroundColor) {\n    ctx.fillStyle = layer.backgroundColor;\n    if (layer.borderRadius) {\n      // Fill the current path when there is a borderRadius set.\n      ctx.fill();\n    } else {\n      ctx.fillRect(frame.x, frame.y, frame.width, frame.height);\n    }\n  }\n}\n\n/**\n * Draw a bitmap-cacheable layer into a pooled <canvas>. The result will be\n * drawn into the given context. This will populate the layer backing store\n * cache with the result.\n *\n * @param {CanvasRenderingContext2d} ctx\n * @param {RenderLayer} layer\n * @param {Function} customDrawFunc\n * @private\n */\nfunction drawCacheableRenderLayer (ctx, layer, customDrawFunc) {\n  // See if there is a pre-drawn canvas in the pool.\n  var backingStore = getBackingStore(layer.backingStoreId);\n  var backingStoreScale = layer.scale || window.devicePixelRatio;\n  var frameOffsetY = layer.frame.y;\n  var frameOffsetX = layer.frame.x;\n  var backingContext;\n\n  if (!backingStore) {\n    if (_backingStores.length >= Canvas.poolSize) {\n      // Re-use the oldest backing store once we reach the pooling limit.\n      backingStore = _backingStores[0].canvas;\n      Canvas.call(backingStore, layer.frame.width, layer.frame.height, backingStoreScale);\n\n      // Move the re-use canvas to the front of the queue.\n      _backingStores[0].id = layer.backingStoreId;\n      _backingStores[0].canvas = backingStore;\n      _backingStores.push(_backingStores.shift());\n    } else {\n      // Create a new backing store, we haven't yet reached the pooling limit\n      backingStore = new Canvas(layer.frame.width, layer.frame.height, backingStoreScale);\n      _backingStores.push({\n        id: layer.backingStoreId,\n        layer: layer,\n        canvas: backingStore\n      });\n    }\n\n    // Draw into the backing <canvas> at (0, 0) - we will later use the\n    // <canvas> to draw the layer as an image at the proper coordinates.\n    backingContext = backingStore.getContext('2d');\n    layer.translate(-frameOffsetX, -frameOffsetY);\n\n    // Draw default properties, such as background color.\n    backingContext.save();\n    drawBaseRenderLayer(backingContext, layer);\n\n    // Custom drawing operations\n    customDrawFunc && customDrawFunc(backingContext, layer);\n    backingContext.restore();\n\n    // Draw child layers, sorted by their z-index.\n    if (layer.children) {\n      layer.children.slice().sort(sortByZIndexAscending).forEach(function (childLayer) {\n        drawRenderLayer(backingContext, childLayer);\n      });\n    }\n\n    // Restore layer's original frame.\n    layer.translate(frameOffsetX, frameOffsetY);\n  }\n\n  // We have the pre-rendered canvas ready, draw it into the destination canvas.\n  if (layer.clipRect) {\n    // Fill the clipping rect in the destination canvas.\n    var sx = (layer.clipRect.x - layer.frame.x) * backingStoreScale;\n    var sy = (layer.clipRect.y - layer.frame.y) * backingStoreScale;\n    var sw = layer.clipRect.width * backingStoreScale;\n    var sh = layer.clipRect.height * backingStoreScale;\n    var dx = layer.clipRect.x;\n    var dy = layer.clipRect.y;\n    var dw = layer.clipRect.width;\n    var dh = layer.clipRect.height;\n\n    // No-op for zero size rects. iOS / Safari will throw an exception.\n    if (sw > 0 && sh > 0) {\n      ctx.drawImage(backingStore.getRawCanvas(), sx, sy, sw, sh, dx, dy, dw, dh);\n    }\n  } else {\n    // Fill the entire canvas\n    ctx.drawImage(backingStore.getRawCanvas(), layer.frame.x, layer.frame.y, layer.frame.width, layer.frame.height);\n  }\n}\n\n/**\n * @private\n */\nfunction sortByZIndexAscending (layerA, layerB) {\n  return (layerA.zIndex || 0) - (layerB.zIndex || 0);\n}\n\n/**\n * @private\n */\nfunction drawImageRenderLayer (ctx, layer) {\n  if (!layer.imageUrl) {\n    return;\n  }\n\n  // Don't draw until loaded\n  var image = ImageCache.get(layer.imageUrl);\n  if (!image.isLoaded()) {\n    return;\n  }\n\n  CanvasUtils.drawImage(ctx, image, layer.frame.x, layer.frame.y, layer.frame.width, layer.frame.height);\n}\n\n/**\n * @private\n */\nfunction drawTextRenderLayer (ctx, layer) {\n  // Fallback to standard font.\n  var fontFace = layer.fontFace || FontFace.Default();\n\n  // Don't draw text until loaded\n  if (!FontUtils.isFontLoaded(fontFace)) {\n    return;\n  }\n\n  CanvasUtils.drawText(ctx, layer.text, layer.frame.x, layer.frame.y, layer.frame.width, layer.frame.height, fontFace, {\n    fontSize: layer.fontSize,\n    lineHeight: layer.lineHeight,\n    textAlign: layer.textAlign,\n    color: layer.color\n  });\n}\n\n/**\n * @private\n */\nfunction drawGradientRenderLayer (ctx, layer) {\n  // Default to linear gradient from top to bottom.\n  var x1 = layer.x1 || layer.frame.x;\n  var y1 = layer.y1 || layer.frame.y;\n  var x2 = layer.x2 || layer.frame.x;\n  var y2 = layer.y2 || layer.frame.y + layer.frame.height;\n  CanvasUtils.drawGradient(ctx, x1, y1, x2, y2, layer.colorStops, layer.frame.x, layer.frame.y, layer.frame.width, layer.frame.height);\n}\n\nmodule.exports = {\n  drawRenderLayer: drawRenderLayer,\n  invalidateBackingStore: invalidateBackingStore,\n  invalidateAllBackingStores: invalidateAllBackingStores,\n  handleImageLoad: handleImageLoad,\n  handleFontLoad: handleFontLoad,\n  layerContainsImage: layerContainsImage,\n  layerContainsFontFace: layerContainsFontFace\n};\n"
  },
  {
    "path": "lib/Easing.js",
    "content": "// Penner easing equations\n// https://gist.github.com/gre/1650294\n\nvar Easing = {\n\n  linear: function (t) {\n    return t;\n  },\n\n  easeInQuad: function (t) {\n    return Math.pow(t, 2);\n  },\n\n  easeOutQuad: function (t) {\n    return t * (2-t);\n  },\n\n  easeInOutQuad: function (t) {\n    return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n  },\n\n  easeInCubic: function (t) {\n    return t * t * t;\n  },\n\n  easeOutCubic: function (t) {\n    return (--t) * t * t + 1;\n  },\n\n  easeInOutCubic: function (t) {\n    return t < .5 ? 4 * t * t * t : (t-1) * (2*t - 2) * (2*t - 2) + 1;\n  }\n\n};\n\nmodule.exports = Easing;\n"
  },
  {
    "path": "lib/EventTypes.js",
    "content": "'use strict';\n\n// Supported events that RenderLayer's can subscribe to.\n\nmodule.exports = {\n  onTouchStart: 'touchstart',\n  onTouchMove: 'touchmove',\n  onTouchEnd: 'touchend',\n  onTouchCancel: 'touchcancel',\n  onClick: 'click',\n  onContextMenu: 'contextmenu',\n  onDoubleClick: 'dblclick'\n};\n"
  },
  {
    "path": "lib/FontFace.js",
    "content": "'use strict';\n\nvar _fontFaces = {};\n\n/**\n * @param {String} family The CSS font-family value\n * @param {String} url The remote URL for the font file\n * @param {Object} attributes Font attributes supported: style, weight\n * @return {Object}\n */\nfunction FontFace (family, url, attributes) {\n  var fontFace;\n  var fontId;\n\n  attributes = attributes || {};\n  attributes.style = attributes.style || 'normal';\n  attributes.weight = attributes.weight || 400;\n\n  fontId = getCacheKey(family, url, attributes);\n  fontFace = _fontFaces[fontId];\n\n  if (!fontFace) {\n    fontFace = {};\n    fontFace.id = fontId;\n    fontFace.family = family;\n    fontFace.url = url;\n    fontFace.attributes = attributes;\n    _fontFaces[fontId] = fontFace;\n  }\n\n  return fontFace;\n}\n\n/**\n * Helper for retrieving the default family by weight.\n *\n * @param {Number} fontWeight\n * @return {FontFace}\n */\nFontFace.Default = function (fontWeight) {\n  return FontFace('sans-serif', null, {weight: fontWeight});\n};\n\n/**\n * @internal\n */\nfunction getCacheKey (family, url, attributes) {\n  return family + url + Object.keys(attributes).sort().map(function (key) {\n    return attributes[key];\n  });\n}\n\nmodule.exports = FontFace;\n"
  },
  {
    "path": "lib/FontUtils.js",
    "content": "'use strict';\n\nvar FontFace = require('./FontFace');\n\nvar _useNativeImpl = (typeof window.FontFace !== 'undefined');\nvar _pendingFonts = {};\nvar _loadedFonts = {};\nvar _failedFonts = {};\n\nvar kFontLoadTimeout = 3000;\n\n/**\n * Check if a font face has loaded\n * @param {FontFace} fontFace\n * @return {Boolean}\n */\nfunction isFontLoaded (fontFace) {\n  // For remote URLs, check the cache. System fonts (sans url) assume loaded.\n  return _loadedFonts[fontFace.id] !== undefined || !fontFace.url;\n}\n\n/**\n * Load a remote font and execute a callback.\n * @param {FontFace} fontFace The font to Load\n * @param {Function} callback Function executed upon font Load\n */\nfunction loadFont (fontFace, callback) {\n  var defaultNode;\n  var testNode;\n  var checkFont;\n\n  // See if we've previously loaded it.\n  if (_loadedFonts[fontFace.id]) {\n    return callback(null);\n  }\n\n  // See if we've previously failed to load it.\n  if (_failedFonts[fontFace.id]) {\n    return callback(_failedFonts[fontFace.id]);\n  }\n\n  // System font: assume already loaded.\n  if (!fontFace.url) {\n    return callback(null);\n  }\n\n  // Font load is already in progress:\n  if (_pendingFonts[fontFace.id]) {\n    _pendingFonts[fontFace.id].callbacks.push(callback);\n    return;\n  }\n\n  // Create the test <span>'s for measuring.\n  defaultNode = createTestNode('Helvetica', fontFace.attributes);\n  testNode = createTestNode(fontFace.family, fontFace.attributes);\n  document.body.appendChild(testNode);\n  document.body.appendChild(defaultNode);\n\n  _pendingFonts[fontFace.id] = {\n    startTime: Date.now(),\n    defaultNode: defaultNode,\n    testNode: testNode,\n    callbacks: [callback]\n  };\n\n  // Font watcher\n  checkFont = function () {\n    var currWidth = testNode.getBoundingClientRect().width;\n    var defaultWidth = defaultNode.getBoundingClientRect().width;\n    var loaded = currWidth !== defaultWidth;\n\n    if (loaded) {\n      handleFontLoad(fontFace, null);\n    } else {\n      // Timeout?\n      if (Date.now() - _pendingFonts[fontFace.id].startTime >= kFontLoadTimeout) {\n        handleFontLoad(fontFace, true);\n      } else {\n        requestAnimationFrame(checkFont);\n      }\n    }\n  };\n\n  // Start watching\n  checkFont();\n}\n\n// Internal\n// ========\n\n/**\n * Native FontFace loader implementation\n * @internal\n */\nfunction loadFontNative (fontFace, callback) {\n  var theFontFace;\n\n  // See if we've previously loaded it.\n  if (_loadedFonts[fontFace.id]) {\n    return callback(null);\n  }\n\n  // See if we've previously failed to load it.\n  if (_failedFonts[fontFace.id]) {\n    return callback(_failedFonts[fontFace.id]);\n  }\n\n  // System font: assume it's installed.\n  if (!fontFace.url) {\n    return callback(null);\n  }\n\n  // Font load is already in progress:\n  if (_pendingFonts[fontFace.id]) {\n    _pendingFonts[fontFace.id].callbacks.push(callback);\n    return;\n  }\n\n  _pendingFonts[fontFace.id] = {\n    startTime: Date.now(),\n    callbacks: [callback]\n  };\n\n  // Use font loader API\n  theFontFace = new window.FontFace(fontFace.family,\n    'url(' + fontFace.url + ')', fontFace.attributes);\n\n  theFontFace.load().then(function () {\n    _loadedFonts[fontFace.id] = true;\n    callback(null);\n  }, function (err) {\n    _failedFonts[fontFace.id] = err;\n    callback(err);\n  });\n}\n\n/**\n * Helper method for created a hidden <span> with a given font.\n * Uses TypeKit's default test string, which is said to result\n * in highly varied measured widths when compared to the default font.\n * @internal\n */\nfunction createTestNode (family, attributes) {\n  var span = document.createElement('span');\n  span.setAttribute('data-fontfamily', family);\n  span.style.cssText = 'position:absolute; left:-5000px; top:-5000px; visibility:hidden;' +\n    'font-size:100px; font-family:\"' + family + '\", Helvetica;font-weight: ' + attributes.weight + ';' +\n    'font-style:' + attributes.style + ';';\n  span.innerHTML = 'BESs';\n  return span;\n}\n\n/**\n * @internal\n */\nfunction handleFontLoad (fontFace, timeout) {\n  var error = timeout ? 'Exceeded load timeout of ' + kFontLoadTimeout + 'ms' : null;\n\n  if (!error) {\n    _loadedFonts[fontFace.id] = true;\n  } else {\n    _failedFonts[fontFace.id] = error;\n  }\n\n  // Execute pending callbacks.\n  _pendingFonts[fontFace.id].callbacks.forEach(function (callback) {\n    callback(error);\n  });\n\n  // Clean up DOM\n  if (_pendingFonts[fontFace.id].defaultNode) {\n    document.body.removeChild(_pendingFonts[fontFace.id].defaultNode);\n  }\n  if (_pendingFonts[fontFace.id].testNode) {\n    document.body.removeChild(_pendingFonts[fontFace.id].testNode);\n  }\n\n  // Clean up waiting queue\n  delete _pendingFonts[fontFace.id];\n}\n\nmodule.exports = {\n  isFontLoaded: isFontLoaded,\n  loadFont: _useNativeImpl ? loadFontNative : loadFont\n};\n"
  },
  {
    "path": "lib/FrameUtils.js",
    "content": "'use strict';\n\nfunction Frame (x, y, width, height) {\n  this.x = x;\n  this.y = y;\n  this.width = width;\n  this.height = height;\n}\n\n/**\n * Get a frame object\n *\n * @param {Number} x\n * @param {Number} y\n * @param {Number} width\n * @param {Number} height\n * @return {Frame}\n */\nfunction make (x, y, width, height) {\n  return new Frame(x, y, width, height);\n}\n\n/**\n * Return a zero size anchored at (0, 0).\n *\n * @return {Frame}\n */\nfunction zero () {\n  return make(0, 0, 0, 0);\n}\n\n/**\n * Return a cloned frame\n *\n * @param {Frame} frame\n * @return {Frame}\n */\nfunction clone (frame) {\n  return make(frame.x, frame.y, frame.width, frame.height);\n}\n\n/**\n * Creates a new frame by a applying edge insets. This method accepts CSS\n * shorthand notation e.g. inset(myFrame, 10, 0);\n *\n * @param {Frame} frame\n * @param {Number} top\n * @param {Number} right\n * @param {?Number} bottom\n * @param {?Number} left\n * @return {Frame}\n */\nfunction inset (frame, top, right, bottom, left) {\n  var frameCopy = clone(frame);\n\n  // inset(myFrame, 10, 0) => inset(myFrame, 10, 0, 10, 0)\n  if (typeof bottom === 'undefined') {\n    bottom = top;\n    left = right;\n  }\n\n  // inset(myFrame, 10) => inset(myFrame, 10, 10, 10, 10)\n  if (typeof right === 'undefined') {\n    right = bottom = left = top;\n  }\n\n  frameCopy.x += left;\n  frameCopy.y += top;\n  frameCopy.height -= (top + bottom);\n  frameCopy.width -= (left + right);\n\n  return frameCopy;\n}\n\n/**\n * Compute the intersection region between 2 frames.\n *\n * @param {Frame} frame\n * @param {Frame} otherFrame\n * @return {Frame}\n */\nfunction intersection (frame, otherFrame) {\n  var x = Math.max(frame.x, otherFrame.x);\n  var width = Math.min(frame.x + frame.width, otherFrame.x + otherFrame.width);\n  var y = Math.max(frame.y, otherFrame.y);\n  var height = Math.min(frame.y + frame.height, otherFrame.y + otherFrame.height);\n  if (width >= x && height >= y) {\n    return make(x, y, width - x, height - y);\n  }\n  return null;\n}\n\n/**\n * Compute the union of two frames\n *\n * @param {Frame} frame\n * @param {Frame} otherFrame\n * @return {Frame}\n */\nfunction union (frame, otherFrame) {\n  var x1 = Math.min(frame.x, otherFrame.x);\n  var x2 = Math.max(frame.x + frame.width, otherFrame.x + otherFrame.width);\n  var y1 = Math.min(frame.y, otherFrame.y);\n  var y2 = Math.max(frame.y + frame.height, otherFrame.y + otherFrame.height);\n  return make(x1, y1, x2 - x1, y2 - y1);\n}\n\n/**\n * Determine if 2 frames intersect each other\n *\n * @param {Frame} frame\n * @param {Frame} otherFrame\n * @return {Boolean}\n */\nfunction intersects (frame, otherFrame) {\n  return !(otherFrame.x > frame.x + frame.width ||\n           otherFrame.x + otherFrame.width < frame.x ||\n           otherFrame.y > frame.y + frame.height ||\n           otherFrame.y + otherFrame.height < frame.y);\n}\n\nmodule.exports = {\n  make: make,\n  zero: zero,\n  clone: clone,\n  inset: inset,\n  intersection: intersection,\n  intersects: intersects,\n  union: union\n};\n\n"
  },
  {
    "path": "lib/Gradient.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar createComponent = require('./createComponent');\nvar LayerMixin = require('./LayerMixin');\n\nvar Gradient = createComponent('Gradient', LayerMixin, {\n\n  applyGradientProps: function (prevProps, props) {\n    var layer = this.node;\n    layer.type = 'gradient';\n    layer.colorStops = props.colorStops || [];\n    this.applyLayerProps(prevProps, props);\n  },\n\n  mountComponent: function (\n    transaction,\n    nativeParent,\n    nativeContainerInfo,\n    context\n  ) {\n    var props = this._currentElement.props;\n    var layer = this.node;\n    this.applyGradientProps({}, props);\n    return layer;\n  },\n\n  receiveComponent: function (nextComponent, transaction, context) {\n    var prevProps = this._currentElement.props;\n    var props = nextComponent.props;\n    this.applyGradientProps({}, props);\n    this._currentElement = nextComponent;\n    this.node.invalidateLayout();\n  },\n\n});\n\n\nmodule.exports = Gradient;\n"
  },
  {
    "path": "lib/Group.js",
    "content": "'use strict';\n\nvar createComponent = require('./createComponent');\nvar ContainerMixin = require('./ContainerMixin');\nvar LayerMixin = require('./LayerMixin');\nvar RenderLayer = require('./RenderLayer');\n\nvar Group = createComponent('Group', LayerMixin, ContainerMixin, {\n\n  mountComponent: function (\n    transaction,\n    nativeParent,\n    nativeContainerInfo,\n    context\n  ) {\n    var props = this._currentElement.props;\n    var layer = this.node;\n\n    this.applyLayerProps({}, props);\n    this.mountAndInjectChildren(props.children, transaction, context);\n\n    return layer;\n  },\n\n  receiveComponent: function (nextComponent, transaction, context) {\n    var props = nextComponent.props;\n    var prevProps = this._currentElement.props;\n    this.applyLayerProps(prevProps, props);\n    this.updateChildren(props.children, transaction, context);\n    this._currentElement = nextComponent;\n    this.node.invalidateLayout();\n  },\n\n  unmountComponent: function () {\n    LayerMixin.unmountComponent.call(this);\n    this.unmountChildren();\n  }\n\n});\n\nmodule.exports = Group;\n"
  },
  {
    "path": "lib/Image.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar createComponent = require('./createComponent');\nvar LayerMixin = require('./LayerMixin');\nvar Layer = require('./Layer');\nvar Group = require('./Group');\nvar ImageCache = require('./ImageCache');\nvar Easing = require('./Easing');\nvar clamp = require('./clamp');\n\nvar FADE_DURATION = 200;\n\nvar RawImage = createComponent('Image', LayerMixin, {\n\n  applyImageProps: function (prevProps, props) {\n    var layer = this.node;\n\n    layer.type = 'image';\n    layer.imageUrl = props.src;\n  },\n\n  mountComponent: function (\n    transaction,\n    nativeParent,\n    nativeContainerInfo,\n    context\n  ) {\n    var props = this._currentElement.props;\n    var layer = this.node;\n    this.applyLayerProps({}, props);\n    this.applyImageProps({}, props);\n    return layer;\n  },\n\n  receiveComponent: function (nextComponent, transaction, context) {\n    var prevProps = this._currentElement.props;\n    var props = nextComponent.props;\n    this.applyLayerProps(prevProps, props);\n    this.applyImageProps(prevProps, props);\n    this._currentElement = nextComponent;\n    this.node.invalidateLayout();\n  },\n\n});\n\nvar Image = React.createClass({\n\n  propTypes: {\n    src: React.PropTypes.string.isRequired,\n    style: React.PropTypes.object,\n    useBackingStore: React.PropTypes.bool,\n    fadeIn: React.PropTypes.bool,\n    fadeInDuration: React.PropTypes.number\n  },\n\n  getInitialState: function () {\n    var loaded = ImageCache.get(this.props.src).isLoaded();\n    return {\n      loaded: loaded,\n      imageAlpha: loaded ? 1 : 0\n    };\n  },\n\n  componentDidMount: function () {\n    ImageCache.get(this.props.src).on('load', this.handleImageLoad);\n  },\n\n  componentWillUpdate: function(nextProps, nextState) {\n    if(nextProps.src !== this.props.src) {\n      ImageCache.get(this.props.src).removeListener('load', this.handleImageLoad);\n      ImageCache.get(nextProps.src).on('load', this.handleImageLoad);\n      var loaded = ImageCache.get(nextProps.src).isLoaded();\n      this.setState({loaded: loaded});\n    }\n  },\n\n  componentWillUnmount: function () {\n    if (this._pendingAnimationFrame) {\n      cancelAnimationFrame(this._pendingAnimationFrame);\n    }\n    ImageCache.get(this.props.src).removeListener('load', this.handleImageLoad);\n  },\n\n  componentDidUpdate: function (prevProps, prevState) {\n    if (this.refs.image) {\n      this.refs.image.invalidateLayout();\n    }\n  },\n\n  render: function () {\n    var rawImage;\n    var imageStyle = Object.assign({}, this.props.style);\n    var style = Object.assign({}, this.props.style);\n    var backgroundStyle = Object.assign({}, this.props.style);\n    var useBackingStore = this.state.loaded ? this.props.useBackingStore : false;\n\n    // Hide the image until loaded.\n    imageStyle.alpha = this.state.imageAlpha;\n\n    // Hide opaque background if image loaded so that images with transparent\n    // do not render on top of solid color.\n    style.backgroundColor = imageStyle.backgroundColor = null;\n    backgroundStyle.alpha = clamp(1 - this.state.imageAlpha, 0, 1);\n\n    return (\n      React.createElement(Group, {ref: 'main', style: style},\n        React.createElement(Layer, {ref: 'background', style: backgroundStyle}),\n        React.createElement(RawImage, {ref: 'image', src: this.props.src, style: imageStyle, useBackingStore: useBackingStore})\n      )\n    );\n  },\n\n  handleImageLoad: function () {\n    var imageAlpha = 1;\n    if (this.props.fadeIn) {\n      imageAlpha = 0;\n      this._animationStartTime = Date.now();\n      this._pendingAnimationFrame = requestAnimationFrame(this.stepThroughAnimation);\n    }\n    this.setState({ loaded: true, imageAlpha: imageAlpha });\n  },\n\n  stepThroughAnimation: function () {\n    var fadeInDuration = this.props.fadeInDuration || FADE_DURATION;\n    var alpha = Easing.easeInCubic((Date.now() - this._animationStartTime) / fadeInDuration);\n    alpha = clamp(alpha, 0, 1);\n    this.setState({ imageAlpha: alpha });\n    if (alpha < 1) {\n      this._pendingAnimationFrame = requestAnimationFrame(this.stepThroughAnimation);\n    }\n  }\n\n});\n\nmodule.exports = Image;\n"
  },
  {
    "path": "lib/ImageCache.js",
    "content": "'use strict';\n\nvar EventEmitter = require('events');\n\nvar NOOP = function () {};\n\nfunction Img (src) {\n  this._originalSrc = src;\n  this._img = new Image();\n  this._img.onload = this.emit.bind(this, 'load');\n  this._img.onerror = this.emit.bind(this, 'error');\n  this._img.crossOrigin = true;\n  this._img.src = src;\n\n  // The default impl of events emitter will throw on any 'error' event unless\n  // there is at least 1 handler. Logging anything in this case is unnecessary\n  // since the browser console will log it too.\n  this.on('error', NOOP);\n\n  // Default is just 10.\n  this.setMaxListeners(100);\n}\n\nObject.assign(Img.prototype, EventEmitter.prototype, {\n\n  /**\n   * Pooling owner looks for this\n   */\n  destructor: function () {\n    // Make sure we aren't leaking callbacks.\n    this.removeAllListeners();\n  },\n\n  /**\n   * Retrieve the original image URL before browser normalization\n   *\n   * @return {String}\n   */\n  getOriginalSrc: function () {\n    return this._originalSrc;\n  },\n\n  /**\n   * Retrieve a reference to the underyling <img> node.\n   *\n   * @return {HTMLImageElement}\n   */\n  getRawImage: function () {\n    return this._img;\n  },\n\n  /**\n   * Retrieve the loaded image width\n   *\n   * @return {Number}\n   */\n  getWidth: function () {\n    return this._img.naturalWidth;\n  },\n\n  /**\n   * Retrieve the loaded image height\n   *\n   * @return {Number}\n   */\n  getHeight: function () {\n    return this._img.naturalHeight;\n  },\n\n  /**\n   * @return {Bool}\n   */\n  isLoaded: function () {\n    return this._img.naturalHeight > 0;\n  }\n\n});\n\nvar kInstancePoolLength = 300;\n\nvar _instancePool = {\n  length: 0,\n  // Keep all the nodes in memory.\n  elements: {\n    \n  },\n  \n  // Push with 0 frequency\n  push: function (hash, data) {\n    this.length++;\n    this.elements[hash] = {\n      hash: hash, // Helps identifying \n      freq: 0,\n      data: data\n    };\n  },\n  \n  get: function (path) {\n    var element = this.elements[path];\n    \n    if( element ){\n      element.freq++;\n      return element.data;\n    }\n    \n    return null;\n  },\n  \n  // used to explicitely remove the path\n  removeElement: function (path) {\n    // Now almighty GC can claim this soul\n    var element = this.elements[path];\n    delete this.elements[path];\n    this.length--;\n    return element;\n  },\n  \n  _reduceLeastUsed: function (least, currentHash) {\n    var current = _instancePool.elements[currentHash];\n    \n    if( least.freq > current.freq ){\n      return current;\n    }\n    \n    return least;\n  },\n  \n  popLeastUsed: function () {\n    var reducer = _instancePool._reduceLeastUsed;\n    var minUsed = Object.keys(this.elements).reduce(reducer, { freq: Infinity });\n    \n    if( minUsed.hash ){\n      return this.removeElement(minUsed.hash);  \n    }\n    \n    return null;\n  }\n};\n\nvar ImageCache = {\n\n  /**\n   * Retrieve an image from the cache\n   *\n   * @return {Img}\n   */\n  get: function (src) {\n    var image = _instancePool.get(src);\n    if (!image) {\n      // Awesome LRU\n      image = new Img(src);\n      if (_instancePool.length >= kInstancePoolLength) {\n        _instancePool.popLeastUsed().destructor();\n      }\n      _instancePool.push(image.getOriginalSrc(), image);\n    }\n    return image;\n  }\n\n};\n\nmodule.exports = ImageCache;\n"
  },
  {
    "path": "lib/Layer.js",
    "content": "'use strict';\n\nvar createComponent = require('./createComponent');\nvar LayerMixin = require('./LayerMixin');\n\nvar Layer = createComponent('Layer', LayerMixin, {\n\n  mountComponent: function (\n    transaction,\n    nativeParent,\n    nativeContainerInfo,\n    context\n  ) {\n    var props = this._currentElement.props;\n    var layer = this.node;\n    this.applyLayerProps({}, props);\n    return layer;\n  },\n\n  receiveComponent: function (nextComponent, transaction, context) {\n    var prevProps = this._currentElement.props;\n    var props = nextComponent.props;\n    this.applyLayerProps(prevProps, props);\n    this._currentElement = nextComponent;\n    this.node.invalidateLayout();\n  }\n\n});\n\nmodule.exports = Layer;\n"
  },
  {
    "path": "lib/LayerMixin.js",
    "content": "'use strict';\n\n// Adapted from ReactART:\n// https://github.com/reactjs/react-art\n\nvar FrameUtils = require('./FrameUtils');\nvar DrawingUtils = require('./DrawingUtils');\nvar EventTypes = require('./EventTypes');\n\nvar LAYER_GUID = 0;\n\nvar LayerMixin = {\n\n  construct: function(element) {\n    this._currentElement = element;\n    this._layerId = LAYER_GUID++;\n  },\n\n  getPublicInstance: function() {\n    return this.node;\n  },\n\n  putEventListener: function(type, listener) {\n    var subscriptions = this.subscriptions || (this.subscriptions = {});\n    var listeners = this.listeners || (this.listeners = {});\n    listeners[type] = listener;\n    if (listener) {\n      if (!subscriptions[type]) {\n        subscriptions[type] = this.node.subscribe(type, listener, this);\n      }\n    } else {\n      if (subscriptions[type]) {\n        subscriptions[type]();\n        delete subscriptions[type];\n      }\n    }\n  },\n\n  handleEvent: function(event) {\n    // TODO\n  },\n\n  destroyEventListeners: function() {\n    // TODO\n  },\n\n  applyLayerProps: function (prevProps, props) {\n    var layer = this.node;\n    var style = (props && props.style) ? props.style : {};\n    layer._originalStyle = style;\n\n    // Common layer properties\n    layer.alpha = style.alpha;\n    layer.backgroundColor = style.backgroundColor;\n    layer.borderColor = style.borderColor;\n    layer.borderWidth = style.borderWidth;\n    layer.borderRadius = style.borderRadius;\n    layer.clipRect = style.clipRect;\n    layer.frame = FrameUtils.make(style.left || 0, style.top || 0, style.width || 0, style.height || 0);\n    layer.scale = style.scale;\n    layer.translateX = style.translateX;\n    layer.translateY = style.translateY;\n    layer.zIndex = style.zIndex;\n\n    // Shadow\n    layer.shadowColor = style.shadowColor;\n    layer.shadowBlur = style.shadowBlur;\n    layer.shadowOffsetX = style.shadowOffsetX;\n    layer.shadowOffsetY = style.shadowOffsetY;\n\n    // Generate backing store ID as needed.\n    if (props.useBackingStore) {\n      layer.backingStoreId = this._layerId;\n    }\n\n    // Register events\n    for (var type in EventTypes) {\n      this.putEventListener(EventTypes[type], props[type]);\n    }\n  },\n\n  mountComponentIntoNode: function(rootID, container) {\n    throw new Error(\n      'You cannot render a Canvas component standalone. ' +\n      'You need to wrap it in a Surface.'\n    );\n  },\n\n  unmountComponent: function() {\n    this.destroyEventListeners();\n  },\n  getHostNode: function () { return this.node },\n  getNativeNode: function () { return this.node },\n\n};\n\nmodule.exports = LayerMixin;\n"
  },
  {
    "path": "lib/Layout.js",
    "content": "// https://github.com/facebook/css-layout\n\n/**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n\n  function capitalizeFirst(str) {\n    return str.charAt(0).toUpperCase() + str.slice(1);\n  }\n\n  function getSpacing(node, type, suffix, location) {\n    var key = type + capitalizeFirst(location) + suffix;\n    if (key in node.style) {\n      return node.style[key];\n    }\n\n    key = type + suffix;\n    if (key in node.style) {\n      return node.style[key];\n    }\n\n    return 0;\n  }\n\n  function getPositiveSpacing(node, type, suffix, location) {\n    var key = type + capitalizeFirst(location) + suffix;\n    if (key in node.style && node.style[key] >= 0) {\n      return node.style[key];\n    }\n\n    key = type + suffix;\n    if (key in node.style && node.style[key] >= 0) {\n      return node.style[key];\n    }\n\n    return 0;\n  }\n\n  function isUndefined(value) {\n    return value === undefined;\n  }\n\n  function getMargin(node, location) {\n    return getSpacing(node, 'margin', '', location);\n  }\n\n  function getPadding(node, location) {\n    return getPositiveSpacing(node, 'padding', '', location);\n  }\n\n  function getBorder(node, location) {\n    return getPositiveSpacing(node, 'border', 'Width', location);\n  }\n\n  function getPaddingAndBorder(node, location) {\n    return getPadding(node, location) + getBorder(node, location);\n  }\n\n  function getMarginAxis(node, axis) {\n    return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]);\n  }\n\n  function getPaddingAndBorderAxis(node, axis) {\n    return getPaddingAndBorder(node, leading[axis]) + getPaddingAndBorder(node, trailing[axis]);\n  }\n\n  function getJustifyContent(node) {\n    if ('justifyContent' in node.style) {\n      return node.style.justifyContent;\n    }\n    return 'flex-start';\n  }\n\n  function getAlignItem(node, child) {\n    if ('alignSelf' in child.style) {\n      return child.style.alignSelf;\n    }\n    if ('alignItems' in node.style) {\n      return node.style.alignItems;\n    }\n    return 'stretch';\n  }\n\n  function getFlexDirection(node) {\n    if ('flexDirection' in node.style) {\n      return node.style.flexDirection;\n    }\n    return 'column';\n  }\n\n  function getPositionType(node) {\n    if ('position' in node.style) {\n      return node.style.position;\n    }\n    return 'relative';\n  }\n\n  function getFlex(node) {\n    return node.style.flex;\n  }\n\n  function isFlex(node) {\n    return (\n      getPositionType(node) === CSS_POSITION_RELATIVE &&\n      getFlex(node) > 0\n    );\n  }\n\n  function isFlexWrap(node) {\n    return node.style.flexWrap === 'wrap';\n  }\n\n  function getDimWithMargin(node, axis) {\n    return node.layout[dim[axis]] + getMarginAxis(node, axis);\n  }\n\n  function isDimDefined(node, axis) {\n    return !isUndefined(node.style[dim[axis]]) && node.style[dim[axis]] >= 0;\n  }\n\n  function isPosDefined(node, pos) {\n    return !isUndefined(node.style[pos]);\n  }\n\n  function isMeasureDefined(node) {\n    return 'measure' in node.style;\n  }\n\n  function getPosition(node, pos) {\n    if (pos in node.style) {\n      return node.style[pos];\n    }\n    return 0;\n  }\n\n  // When the user specifically sets a value for width or height\n  function setDimensionFromStyle(node, axis) {\n    // The parent already computed us a width or height. We just skip it\n    if (!isUndefined(node.layout[dim[axis]])) {\n      return;\n    }\n    // We only run if there's a width or height defined\n    if (!isDimDefined(node, axis)) {\n      return;\n    }\n\n    // The dimensions can never be smaller than the padding and border\n    node.layout[dim[axis]] = fmaxf(\n      node.style[dim[axis]],\n      getPaddingAndBorderAxis(node, axis)\n    );\n  }\n\n  // If both left and right are defined, then use left. Otherwise return\n  // +left or -right depending on which is defined.\n  function getRelativePosition(node, axis) {\n    if (leading[axis] in node.style) {\n      return getPosition(node, leading[axis]);\n    }\n    return -getPosition(node, trailing[axis]);\n  }\n\n  var leading = {\n    row: 'left',\n    column: 'top'\n  };\n  var trailing = {\n    row: 'right',\n    column: 'bottom'\n  };\n  var pos = {\n    row: 'left',\n    column: 'top'\n  };\n  var dim = {\n    row: 'width',\n    column: 'height'\n  };\n\n  function fmaxf(a, b) {\n    if (a > b) {\n      return a;\n    }\n    return b;\n  }\n\n  var CSS_UNDEFINED = undefined;\n\n  var CSS_FLEX_DIRECTION_ROW = 'row';\n  var CSS_FLEX_DIRECTION_COLUMN = 'column';\n\n  var CSS_JUSTIFY_FLEX_START = 'flex-start';\n  var CSS_JUSTIFY_CENTER = 'center';\n  var CSS_JUSTIFY_FLEX_END = 'flex-end';\n  var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n  var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n  var CSS_ALIGN_FLEX_START = 'flex-start';\n  var CSS_ALIGN_CENTER = 'center';\n  var CSS_ALIGN_FLEX_END = 'flex-end';\n  var CSS_ALIGN_STRETCH = 'stretch';\n\n  var CSS_POSITION_RELATIVE = 'relative';\n  var CSS_POSITION_ABSOLUTE = 'absolute';\n\n  return function layoutNode(node, parentMaxWidth) {\n    var/*css_flex_direction_t*/ mainAxis = getFlexDirection(node);\n    var/*css_flex_direction_t*/ crossAxis = mainAxis === CSS_FLEX_DIRECTION_ROW ?\n      CSS_FLEX_DIRECTION_COLUMN :\n      CSS_FLEX_DIRECTION_ROW;\n\n    // Handle width and height style attributes\n    setDimensionFromStyle(node, mainAxis);\n    setDimensionFromStyle(node, crossAxis);\n\n    // The position is set by the parent, but we need to complete it with a\n    // delta composed of the margin and left/top/right/bottom\n    node.layout[leading[mainAxis]] += getMargin(node, leading[mainAxis]) +\n      getRelativePosition(node, mainAxis);\n    node.layout[leading[crossAxis]] += getMargin(node, leading[crossAxis]) +\n      getRelativePosition(node, crossAxis);\n\n    if (isMeasureDefined(node)) {\n      var/*float*/ width = CSS_UNDEFINED;\n      if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n        width = node.style.width;\n      } else if (!isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_ROW]])) {\n        width = node.layout[dim[CSS_FLEX_DIRECTION_ROW]];\n      } else {\n        width = parentMaxWidth -\n          getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n      }\n      width -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n\n      // We only need to give a dimension for the text if we haven't got any\n      // for it computed yet. It can either be from the style attribute or because\n      // the element is flexible.\n      var/*bool*/ isRowUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_ROW) &&\n        isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_ROW]]);\n      var/*bool*/ isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&\n        isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]);\n\n      // Let's not measure the text if we already know both dimensions\n      if (isRowUndefined || isColumnUndefined) {\n        var/*css_dim_t*/ measure_dim = node.style.measure(\n          /*(c)!node->context,*/\n          width\n        );\n        if (isRowUndefined) {\n          node.layout.width = measure_dim.width +\n            getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n        }\n        if (isColumnUndefined) {\n          node.layout.height = measure_dim.height +\n            getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n        }\n      }\n      return;\n    }\n\n    // Pre-fill some dimensions straight from the parent\n    for (var/*int*/ i = 0; i < node.children.length; ++i) {\n      var/*css_node_t**/ child = node.children[i];\n      // Pre-fill cross axis dimensions when the child is using stretch before\n      // we call the recursive layout pass\n      if (getAlignItem(node, child) === CSS_ALIGN_STRETCH &&\n          getPositionType(child) === CSS_POSITION_RELATIVE &&\n          !isUndefined(node.layout[dim[crossAxis]]) &&\n          !isDimDefined(child, crossAxis)) {\n        child.layout[dim[crossAxis]] = fmaxf(\n          node.layout[dim[crossAxis]] -\n            getPaddingAndBorderAxis(node, crossAxis) -\n            getMarginAxis(child, crossAxis),\n          // You never want to go smaller than padding\n          getPaddingAndBorderAxis(child, crossAxis)\n        );\n      } else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {\n        // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both\n        // left and right or top and bottom).\n        for (var/*int*/ ii = 0; ii < 2; ii++) {\n          var/*css_flex_direction_t*/ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;\n          if (!isUndefined(node.layout[dim[axis]]) &&\n              !isDimDefined(child, axis) &&\n              isPosDefined(child, leading[axis]) &&\n              isPosDefined(child, trailing[axis])) {\n            child.layout[dim[axis]] = fmaxf(\n              node.layout[dim[axis]] -\n              getPaddingAndBorderAxis(node, axis) -\n              getMarginAxis(child, axis) -\n              getPosition(child, leading[axis]) -\n              getPosition(child, trailing[axis]),\n              // You never want to go smaller than padding\n              getPaddingAndBorderAxis(child, axis)\n            );\n          }\n        }\n      }\n    }\n\n    var/*float*/ definedMainDim = CSS_UNDEFINED;\n    if (!isUndefined(node.layout[dim[mainAxis]])) {\n      definedMainDim = node.layout[dim[mainAxis]] -\n          getPaddingAndBorderAxis(node, mainAxis);\n    }\n\n    // We want to execute the next two loops one per line with flex-wrap\n    var/*int*/ startLine = 0;\n    var/*int*/ endLine = 0;\n    var/*int*/ nextOffset = 0;\n    var/*int*/ alreadyComputedNextLayout = 0;\n    // We aggregate the total dimensions of the container in those two variables\n    var/*float*/ linesCrossDim = 0;\n    var/*float*/ linesMainDim = 0;\n    while (endLine < node.children.length) {\n      // <Loop A> Layout non flexible children and count children by type\n\n      // mainContentDim is accumulation of the dimensions and margin of all the\n      // non flexible children. This will be used in order to either set the\n      // dimensions of the node if none already exist, or to compute the\n      // remaining space left for the flexible children.\n      var/*float*/ mainContentDim = 0;\n\n      // There are three kind of children, non flexible, flexible and absolute.\n      // We need to know how many there are in order to distribute the space.\n      var/*int*/ flexibleChildrenCount = 0;\n      var/*float*/ totalFlexible = 0;\n      var/*int*/ nonFlexibleChildrenCount = 0;\n      for (var/*int*/ i = startLine; i < node.children.length; ++i) {\n        var/*css_node_t**/ child = node.children[i];\n        var/*float*/ nextContentDim = 0;\n\n        // It only makes sense to consider a child flexible if we have a computed\n        // dimension for the node.\n        if (!isUndefined(node.layout[dim[mainAxis]]) && isFlex(child)) {\n          flexibleChildrenCount++;\n          totalFlexible += getFlex(child);\n\n          // Even if we don't know its exact size yet, we already know the padding,\n          // border and margin. We'll use this partial information to compute the\n          // remaining space.\n          nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +\n            getMarginAxis(child, mainAxis);\n\n        } else {\n          var/*float*/ maxWidth = CSS_UNDEFINED;\n          if (mainAxis === CSS_FLEX_DIRECTION_ROW) {\n            // do nothing\n          } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n            maxWidth = node.layout[dim[CSS_FLEX_DIRECTION_ROW]] -\n              getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n          } else {\n            maxWidth = parentMaxWidth -\n              getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -\n              getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n          }\n\n          // This is the main recursive call. We layout non flexible children.\n          if (alreadyComputedNextLayout === 0) {\n            layoutNode(child, maxWidth);\n          }\n\n          // Absolute positioned elements do not take part of the layout, so we\n          // don't use them to compute mainContentDim\n          if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n            nonFlexibleChildrenCount++;\n            // At this point we know the final size and margin of the element.\n            nextContentDim = getDimWithMargin(child, mainAxis);\n          }\n        }\n\n        // The element we are about to add would make us go to the next line\n        if (isFlexWrap(node) &&\n            !isUndefined(node.layout[dim[mainAxis]]) &&\n            mainContentDim + nextContentDim > definedMainDim &&\n            // If there's only one element, then it's bigger than the content\n            // and needs its own line\n            i !== startLine) {\n          alreadyComputedNextLayout = 1;\n          break;\n        }\n        alreadyComputedNextLayout = 0;\n        mainContentDim += nextContentDim;\n        endLine = i + 1;\n      }\n\n      // <Loop B> Layout flexible children and allocate empty space\n\n      // In order to position the elements in the main axis, we have two\n      // controls. The space between the beginning and the first element\n      // and the space between each two elements.\n      var/*float*/ leadingMainDim = 0;\n      var/*float*/ betweenMainDim = 0;\n\n      // The remaining available space that needs to be allocated\n      var/*float*/ remainingMainDim = 0;\n      if (!isUndefined(node.layout[dim[mainAxis]])) {\n        remainingMainDim = definedMainDim - mainContentDim;\n      } else {\n        remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;\n      }\n\n      // If there are flexible children in the mix, they are going to fill the\n      // remaining space\n      if (flexibleChildrenCount !== 0) {\n        var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;\n\n        // The non flexible children can overflow the container, in this case\n        // we should just assume that there is no space available.\n        if (flexibleMainDim < 0) {\n          flexibleMainDim = 0;\n        }\n        // We iterate over the full array and only apply the action on flexible\n        // children. This is faster than actually allocating a new array that\n        // contains only flexible children.\n        for (var/*int*/ i = startLine; i < endLine; ++i) {\n          var/*css_node_t**/ child = node.children[i];\n          if (isFlex(child)) {\n            // At this point we know the final size of the element in the main\n            // dimension\n            child.layout[dim[mainAxis]] = flexibleMainDim * getFlex(child) +\n              getPaddingAndBorderAxis(child, mainAxis);\n\n            var/*float*/ maxWidth = CSS_UNDEFINED;\n            if (mainAxis === CSS_FLEX_DIRECTION_ROW) {\n              // do nothing\n            } else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n              maxWidth = node.layout[dim[CSS_FLEX_DIRECTION_ROW]] -\n                getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n            } else {\n              maxWidth = parentMaxWidth -\n                getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -\n                getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n            }\n\n            // And we recursively call the layout algorithm for this child\n            layoutNode(child, maxWidth);\n          }\n        }\n\n      // We use justifyContent to figure out how to allocate the remaining\n      // space available\n      } else {\n        var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n        if (justifyContent === CSS_JUSTIFY_FLEX_START) {\n          // Do nothing\n        } else if (justifyContent === CSS_JUSTIFY_CENTER) {\n          leadingMainDim = remainingMainDim / 2;\n        } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n          leadingMainDim = remainingMainDim;\n        } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n          remainingMainDim = fmaxf(remainingMainDim, 0);\n          if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 !== 0) {\n            betweenMainDim = remainingMainDim /\n              (flexibleChildrenCount + nonFlexibleChildrenCount - 1);\n          } else {\n            betweenMainDim = 0;\n          }\n        } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n          // Space on the edges is half of the space between elements\n          betweenMainDim = remainingMainDim /\n            (flexibleChildrenCount + nonFlexibleChildrenCount);\n          leadingMainDim = betweenMainDim / 2;\n        }\n      }\n\n      // <Loop C> Position elements in the main axis and compute dimensions\n\n      // At this point, all the children have their dimensions set. We need to\n      // find their position. In order to do that, we accumulate data in\n      // variables that are also useful to compute the total dimensions of the\n      // container!\n      var/*float*/ crossDim = 0;\n      var/*float*/ mainDim = leadingMainDim +\n        getPaddingAndBorder(node, leading[mainAxis]);\n\n      for (var/*int*/ i = startLine; i < endLine; ++i) {\n        var/*css_node_t**/ child = node.children[i];\n\n        if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n            isPosDefined(child, leading[mainAxis])) {\n          // In case the child is position absolute and has left/top being\n          // defined, we override the position to whatever the user said\n          // (and margin/border).\n          child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n            getBorder(node, leading[mainAxis]) +\n            getMargin(child, leading[mainAxis]);\n        } else {\n          // If the child is position absolute (without top/left) or relative,\n          // we put it at the current accumulated offset.\n          child.layout[pos[mainAxis]] += mainDim;\n        }\n\n        // Now that we placed the element, we need to update the variables\n        // We only need to do that for relative elements. Absolute elements\n        // do not take part in that phase.\n        if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n          // The main dimension is the sum of all the elements dimension plus\n          // the spacing.\n          mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n          // The cross dimension is the max of the elements dimension since there\n          // can only be one element in that cross dimension.\n          crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n        }\n      }\n\n      var/*float*/ containerMainAxis = node.layout[dim[mainAxis]];\n      // If the user didn't specify a width or height, and it has not been set\n      // by the container, then we set it via the children.\n      if (isUndefined(node.layout[dim[mainAxis]])) {\n        containerMainAxis = fmaxf(\n          // We're missing the last padding at this point to get the final\n          // dimension\n          mainDim + getPaddingAndBorder(node, trailing[mainAxis]),\n          // We can never assign a width smaller than the padding and borders\n          getPaddingAndBorderAxis(node, mainAxis)\n        );\n      }\n\n      var/*float*/ containerCrossAxis = node.layout[dim[crossAxis]];\n      if (isUndefined(node.layout[dim[crossAxis]])) {\n        containerCrossAxis = fmaxf(\n          // For the cross dim, we add both sides at the end because the value\n          // is aggregate via a max function. Intermediate negative values\n          // can mess this computation otherwise\n          crossDim + getPaddingAndBorderAxis(node, crossAxis),\n          getPaddingAndBorderAxis(node, crossAxis)\n        );\n      }\n\n      // <Loop D> Position elements in the cross axis\n\n      for (var/*int*/ i = startLine; i < endLine; ++i) {\n        var/*css_node_t**/ child = node.children[i];\n\n        if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n            isPosDefined(child, leading[crossAxis])) {\n          // In case the child is absolutely positionned and has a\n          // top/left/bottom/right being set, we override all the previously\n          // computed positions to set it correctly.\n          child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n            getBorder(node, leading[crossAxis]) +\n            getMargin(child, leading[crossAxis]);\n\n        } else {\n          var/*float*/ leadingCrossDim = getPaddingAndBorder(node, leading[crossAxis]);\n\n          // For a relative children, we're either using alignItems (parent) or\n          // alignSelf (child) in order to determine the position in the cross axis\n          if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n            var/*css_align_t*/ alignItem = getAlignItem(node, child);\n            if (alignItem === CSS_ALIGN_FLEX_START) {\n              // Do nothing\n            } else if (alignItem === CSS_ALIGN_STRETCH) {\n              // You can only stretch if the dimension has not already been set\n              // previously.\n              if (!isDimDefined(child, crossAxis)) {\n                child.layout[dim[crossAxis]] = fmaxf(\n                  containerCrossAxis -\n                    getPaddingAndBorderAxis(node, crossAxis) -\n                    getMarginAxis(child, crossAxis),\n                  // You never want to go smaller than padding\n                  getPaddingAndBorderAxis(child, crossAxis)\n                );\n              }\n            } else {\n              // The remaining space between the parent dimensions+padding and child\n              // dimensions+margin.\n              var/*float*/ remainingCrossDim = containerCrossAxis -\n                getPaddingAndBorderAxis(node, crossAxis) -\n                getDimWithMargin(child, crossAxis);\n\n              if (alignItem === CSS_ALIGN_CENTER) {\n                leadingCrossDim += remainingCrossDim / 2;\n              } else { // CSS_ALIGN_FLEX_END\n                leadingCrossDim += remainingCrossDim;\n              }\n            }\n          }\n\n          // And we apply the position\n          child.layout[pos[crossAxis]] += linesCrossDim + leadingCrossDim;\n        }\n      }\n\n      linesCrossDim += crossDim;\n      linesMainDim = fmaxf(linesMainDim, mainDim);\n      startLine = endLine;\n    }\n\n    // If the user didn't specify a width or height, and it has not been set\n    // by the container, then we set it via the children.\n    if (isUndefined(node.layout[dim[mainAxis]])) {\n      node.layout[dim[mainAxis]] = fmaxf(\n        // We're missing the last padding at this point to get the final\n        // dimension\n        linesMainDim + getPaddingAndBorder(node, trailing[mainAxis]),\n        // We can never assign a width smaller than the padding and borders\n        getPaddingAndBorderAxis(node, mainAxis)\n      );\n    }\n\n    if (isUndefined(node.layout[dim[crossAxis]])) {\n      node.layout[dim[crossAxis]] = fmaxf(\n        // For the cross dim, we add both sides at the end because the value\n        // is aggregate via a max function. Intermediate negative values\n        // can mess this computation otherwise\n        linesCrossDim + getPaddingAndBorderAxis(node, crossAxis),\n        getPaddingAndBorderAxis(node, crossAxis)\n      );\n    }\n\n    // <Loop E> Calculate dimensions for absolutely positioned elements\n\n    for (var/*int*/ i = 0; i < node.children.length; ++i) {\n      var/*css_node_t**/ child = node.children[i];\n      if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {\n        // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both\n        // left and right or top and bottom).\n        for (var/*int*/ ii = 0; ii < 2; ii++) {\n          var/*css_flex_direction_t*/ axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;\n          if (!isUndefined(node.layout[dim[axis]]) &&\n              !isDimDefined(child, axis) &&\n              isPosDefined(child, leading[axis]) &&\n              isPosDefined(child, trailing[axis])) {\n            child.layout[dim[axis]] = fmaxf(\n              node.layout[dim[axis]] -\n              getPaddingAndBorderAxis(node, axis) -\n              getMarginAxis(child, axis) -\n              getPosition(child, leading[axis]) -\n              getPosition(child, trailing[axis]),\n              // You never want to go smaller than padding\n              getPaddingAndBorderAxis(child, axis)\n            );\n          }\n        }\n        for (var/*int*/ ii = 0; ii < 2; ii++) {\n          var/*css_flex_direction_t*/ axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;\n          if (isPosDefined(child, trailing[axis]) &&\n              !isPosDefined(child, leading[axis])) {\n            child.layout[leading[axis]] =\n              node.layout[dim[axis]] -\n              child.layout[dim[axis]] -\n              getPosition(child, trailing[axis]);\n          }\n        }\n      }\n    }\n  };\n})();\n\nif (typeof module === 'object') {\n  module.exports = computeLayout;\n}\n"
  },
  {
    "path": "lib/ListView.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar Scroller = require('scroller');\nvar Group = require('./Group');\nvar clamp = require('./clamp');\n\nvar ListView = React.createClass({\n\n  propTypes: {\n    style: React.PropTypes.object,\n    numberOfItemsGetter: React.PropTypes.func.isRequired,\n    itemHeightGetter: React.PropTypes.func.isRequired,\n    itemGetter: React.PropTypes.func.isRequired,\n    snapping: React.PropTypes.bool,\n    scrollingDeceleration: React.PropTypes.number,\n    scrollingPenetrationAcceleration: React.PropTypes.number,\n    onScroll: React.PropTypes.func\n  },\n\n  getDefaultProps: function () {\n    return {\n      style: { left: 0, top: 0, width: 0, height: 0 },\n      snapping: false,\n      scrollingDeceleration: 0.95,\n      scrollingPenetrationAcceleration: 0.08\n    };\n  },\n\n  getInitialState: function () {\n    return {\n      scrollTop: 0\n    };\n  },\n\n  componentDidMount: function () {\n    this.createScroller();\n    this.updateScrollingDimensions();\n  },\n\n  render: function () {\n    var items = this.getVisibleItemIndexes().map(this.renderItem);\n    return (\n      React.createElement(Group, {\n        style: this.props.style,\n        onTouchStart: this.handleTouchStart,\n        onTouchMove: this.handleTouchMove,\n        onTouchEnd: this.handleTouchEnd,\n        onTouchCancel: this.handleTouchEnd},\n        items\n      )\n    );\n  },\n\n  renderItem: function (itemIndex) {\n    var item = this.props.itemGetter(itemIndex, this.state.scrollTop);\n    var itemHeight = this.props.itemHeightGetter();\n    var style = {\n      top: 0,\n      left: 0,\n      width: this.props.style.width,\n      height: itemHeight,\n      translateY: (itemIndex * itemHeight) - this.state.scrollTop,\n      zIndex: itemIndex\n    };\n\n    return (\n      React.createElement(Group, {style: style, key: itemIndex},\n        item\n      )\n    );\n  },\n\n  // Events\n  // ======\n\n  handleTouchStart: function (e) {\n    if (this.scroller) {\n      this.scroller.doTouchStart(e.touches, e.timeStamp);\n    }\n  },\n\n  handleTouchMove: function (e) {\n    if (this.scroller) {\n      e.preventDefault();\n      this.scroller.doTouchMove(e.touches, e.timeStamp, e.scale);\n    }\n  },\n\n  handleTouchEnd: function (e) {\n    if (this.scroller) {\n      this.scroller.doTouchEnd(e.timeStamp);\n      if (this.props.snapping) {\n        this.updateScrollingDeceleration();\n      }\n    }\n  },\n\n  handleScroll: function (left, top) {\n    this.setState({ scrollTop: top });\n    if (this.props.onScroll) {\n      this.props.onScroll(top);\n    }\n  },\n\n  // Scrolling\n  // =========\n\n  createScroller: function () {\n    var options = {\n      scrollingX: false,\n      scrollingY: true,\n      decelerationRate: this.props.scrollingDeceleration,\n      penetrationAcceleration: this.props.scrollingPenetrationAcceleration,\n    };\n    this.scroller = new Scroller(this.handleScroll, options);\n  },\n\n  updateScrollingDimensions: function () {\n    var width = this.props.style.width;\n    var height = this.props.style.height;\n    var scrollWidth = width;\n    var scrollHeight = this.props.numberOfItemsGetter() * this.props.itemHeightGetter();\n    this.scroller.setDimensions(width, height, scrollWidth, scrollHeight);\n  },\n\n  getVisibleItemIndexes: function () {\n    var itemIndexes = [];\n    var itemHeight = this.props.itemHeightGetter();\n    var itemCount = this.props.numberOfItemsGetter();\n    var scrollTop = this.state.scrollTop;\n    var itemScrollTop = 0;\n\n    for (var index=0; index < itemCount; index++) {\n      itemScrollTop = (index * itemHeight) - scrollTop;\n\n      // Item is completely off-screen bottom\n      if (itemScrollTop >= this.props.style.height) {\n        continue;\n      }\n\n      // Item is completely off-screen top\n      if (itemScrollTop <= -this.props.style.height) {\n        continue;\n      }\n\n      // Part of item is on-screen.\n      itemIndexes.push(index);\n    }\n\n    return itemIndexes;\n  },\n\n  updateScrollingDeceleration: function () {\n    var currVelocity = this.scroller.__decelerationVelocityY;\n    var currScrollTop = this.state.scrollTop;\n    var targetScrollTop = 0;\n    var estimatedEndScrollTop = currScrollTop;\n\n    while (Math.abs(currVelocity).toFixed(6) > 0) {\n      estimatedEndScrollTop += currVelocity;\n      currVelocity *= this.props.scrollingDeceleration;\n    }\n\n    // Find the page whose estimated end scrollTop is closest to 0.\n    var closestZeroDelta = Infinity;\n    var pageHeight = this.props.itemHeightGetter();\n    var pageCount = this.props.numberOfItemsGetter();\n    var pageScrollTop;\n\n    for (var pageIndex=0, len=pageCount; pageIndex < len; pageIndex++) {\n      pageScrollTop = (pageHeight * pageIndex) - estimatedEndScrollTop;\n      if (Math.abs(pageScrollTop) < closestZeroDelta) {\n        closestZeroDelta = Math.abs(pageScrollTop);\n        targetScrollTop = pageHeight * pageIndex;\n      }\n    }\n\n    this.scroller.__minDecelerationScrollTop = targetScrollTop;\n    this.scroller.__maxDecelerationScrollTop = targetScrollTop;\n  }\n\n});\n\nmodule.exports = ListView;\n"
  },
  {
    "path": "lib/ReactCanvas.js",
    "content": "'use strict';\n\nvar ReactCanvas = {\n  Surface: require('./Surface'),\n\n  Layer: require('./Layer'),\n  Group: require('./Group'),\n  Image: require('./Image'),\n  Text: require('./Text'),\n  ListView: require('./ListView'),\n  Gradient: require('./Gradient'),\n\n  FontFace: require('./FontFace'),\n  measureText: require('./measureText')\n};\n\nmodule.exports = ReactCanvas;\n"
  },
  {
    "path": "lib/RenderLayer.js",
    "content": "'use strict';\n\nvar FrameUtils = require('./FrameUtils');\nvar DrawingUtils = require('./DrawingUtils');\nvar EventTypes = require('./EventTypes');\n\nfunction RenderLayer () {\n  this.children = [];\n  this.frame = FrameUtils.zero();\n}\n\nRenderLayer.prototype = {\n\n  /**\n   * Retrieve the root injection layer\n   *\n   * @return {RenderLayer}\n   */\n  getRootLayer: function () {\n    var root = this;\n    while (root.parentLayer) {\n      root = root.parentLayer;\n    }\n    return root;\n  },\n\n  /**\n   * RenderLayers are injected into a root owner layer whenever a Surface is\n   * mounted. This is the integration point with React internals.\n   *\n   * @param {RenderLayer} parentLayer\n   */\n  inject: function (parentLayer) {\n    if (this.parentLayer && this.parentLayer !== parentLayer) {\n      this.remove();\n    }\n    if (!this.parentLayer) {\n      parentLayer.addChild(this);\n    }\n  },\n\n  /**\n   * Inject a layer before a reference layer\n   *\n   * @param {RenderLayer} parentLayer\n   * @param {RenderLayer} referenceLayer\n   */\n  injectBefore: function (parentLayer, referenceLayer) {\n    // FIXME\n    this.inject(parentLayer);\n  },\n\n  /**\n   * Add a child to the render layer\n   *\n   * @param {RenderLayer} child\n   */\n  addChild: function (child) {\n    child.parentLayer = this;\n    this.children.push(child);\n  },\n\n  /**\n   * Remove a layer from it's parent layer\n   */\n  remove: function () {\n    if (this.parentLayer) {\n      this.parentLayer.children.splice(this.parentLayer.children.indexOf(this), 1);\n    }\n  },\n\n  /**\n   * Attach an event listener to a layer. Supported events are defined in\n   * lib/EventTypes.js\n   *\n   * @param {String} type\n   * @param {Function} callback\n   * @param {?Object} callbackScope\n   * @return {Function} invoke to unsubscribe the listener\n   */\n  subscribe: function (type, callback, callbackScope) {\n    // This is the integration point with React, called from LayerMixin.putEventListener().\n    // Enforce that only a single callbcak can be assigned per event type.\n    for (var eventType in EventTypes) {\n      if (EventTypes[eventType] === type) {\n        this[eventType] = callback;\n      }\n    }\n\n    // Return a function that can be called to unsubscribe from the event.\n    return this.removeEventListener.bind(this, type, callback, callbackScope);\n  },\n\n  /**\n   * @param {String} type\n   * @param {Function} callback\n   * @param {?Object} callbackScope\n   */\n  addEventListener: function (type, callback, callbackScope) {\n    for (var eventType in EventTypes) {\n      if (EventTypes[eventType] === type) {\n        delete this[eventType];\n      }\n    }\n  },\n\n  /**\n   * @param {String} type\n   * @param {Function} callback\n   * @param {?Object} callbackScope\n   */\n  removeEventListener: function (type, callback, callbackScope) {\n    var listeners = this.eventListeners[type];\n    var listener;\n    if (listeners) {\n      for (var index=0, len=listeners.length; index < len; index++) {\n        listener = listeners[index];\n        if (listener.callback === callback &&\n            listener.callbackScope === callbackScope) {\n          listeners.splice(index, 1);\n          break;\n        }\n      }\n    }\n  },\n\n  /**\n   * Translate a layer's frame\n   *\n   * @param {Number} x\n   * @param {Number} y\n   */\n  translate: function (x, y) {\n    if (this.frame) {\n      this.frame.x += x;\n      this.frame.y += y;\n    }\n\n    if (this.clipRect) {\n      this.clipRect.x += x;\n      this.clipRect.y += y;\n    }\n\n    if (this.children) {\n      this.children.forEach(function (child) {\n        child.translate(x, y);\n      });\n    }\n  },\n\n  /**\n   * Layers should call this method when they need to be redrawn. Note the\n   * difference here between `invalidateBackingStore`: updates that don't\n   * trigger layout should prefer `invalidateLayout`. For instance, an image\n   * component that is animating alpha level after the image loads would\n   * call `invalidateBackingStore` once after the image loads, and at each\n   * step in the animation would then call `invalidateRect`.\n   *\n   * @param {?Frame} frame Optional, if not passed the entire layer's frame\n   *   will be invalidated.\n   */\n  invalidateLayout: function () {\n    // Bubble all the way to the root layer.\n    this.getRootLayer().draw();\n  },\n\n  /**\n   * Layers should call this method when their backing <canvas> needs to be\n   * redrawn. For instance, an image component would call this once after the\n   * image loads.\n   */\n  invalidateBackingStore: function () {\n    if (this.backingStoreId) {\n      DrawingUtils.invalidateBackingStore(this.backingStoreId);\n    }\n    this.invalidateLayout();\n  },\n\n  /**\n   * Only the root owning layer should implement this function.\n   */\n  draw: function () {\n    // Placeholer\n  }\n\n};\n\nmodule.exports = RenderLayer;\n"
  },
  {
    "path": "lib/Surface.js",
    "content": "'use strict';\n\nvar React = require('react');\nvar ReactUpdates = require('react-dom/lib/ReactUpdates');\nvar invariant = require('fbjs/lib/invariant');\nvar ContainerMixin = require('./ContainerMixin');\nvar RenderLayer = require('./RenderLayer');\nvar FrameUtils = require('./FrameUtils');\nvar DrawingUtils = require('./DrawingUtils');\nvar hitTest = require('./hitTest');\nvar layoutNode = require('./layoutNode');\n\n/**\n * Surface is a standard React component and acts as the main drawing canvas.\n * ReactCanvas components cannot be rendered outside a Surface.\n */\n\nvar Surface = React.createClass({\n\n  mixins: [ContainerMixin],\n\n  propTypes: {\n    className: React.PropTypes.string,\n    id: React.PropTypes.string,\n    top: React.PropTypes.number.isRequired,\n    left: React.PropTypes.number.isRequired,\n    width: React.PropTypes.number.isRequired,\n    height: React.PropTypes.number.isRequired,\n    scale: React.PropTypes.number.isRequired,\n    enableCSSLayout: React.PropTypes.bool\n  },\n\n  getDefaultProps: function () {\n    return {\n      scale: window.devicePixelRatio || 1\n    };\n  },\n\n  componentDidMount: function () {\n    // Prepare the <canvas> for drawing.\n    this.scale();\n\n    // ContainerMixin expects `this.node` to be set prior to mounting children.\n    // `this.node` is injected into child components and represents the current\n    // render tree.\n    this.node = new RenderLayer();\n    this.node.frame = FrameUtils.make(this.props.left, this.props.top, this.props.width, this.props.height);\n    this.node.draw = this.batchedTick;\n\n    // This is the integration point between custom canvas components and React\n    var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();\n    transaction.perform(\n      this.mountAndInjectChildrenAtRoot,\n      this,\n      this.props.children,\n      transaction\n    );\n    ReactUpdates.ReactReconcileTransaction.release(transaction);\n\n    // Execute initial draw on mount.\n    this.node.draw();\n  },\n\n  componentWillUnmount: function () {\n    // Implemented in ReactMultiChild.Mixin\n    this.unmountChildren();\n  },\n\n  componentDidUpdate: function (prevProps, prevState) {\n    // We have to manually apply child reconciliation since child are not\n    // declared in render().\n    var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();\n    transaction.perform(\n      this.updateChildrenAtRoot,\n      this,\n      this.props.children,\n      transaction\n    );\n    ReactUpdates.ReactReconcileTransaction.release(transaction);\n\n    // Re-scale the <canvas> when changing size.\n    if (prevProps.width !== this.props.width || prevProps.height !== this.props.height) {\n      this.scale();\n    }\n\n    // Redraw updated render tree to <canvas>.\n    if (this.node) {\n      this.node.draw();\n    }\n  },\n\n  render: function () {\n    // Scale the drawing area to match DPI.\n    var width = this.props.width * this.props.scale;\n    var height = this.props.height * this.props.scale;\n    var style = {\n      width: this.props.width,\n      height: this.props.height\n    };\n\n    return (\n      React.createElement('canvas', {\n        ref: 'canvas',\n        className: this.props.className,\n        id: this.props.id,\n        width: width,\n        height: height,\n        style: style,\n        onTouchStart: this.handleTouchStart,\n        onTouchMove: this.handleTouchMove,\n        onTouchEnd: this.handleTouchEnd,\n        onTouchCancel: this.handleTouchEnd,\n        onClick: this.handleClick,\n        onContextMenu: this.handleContextMenu,\n        onDoubleClick: this.handleDoubleClick})\n    );\n  },\n\n  // Drawing\n  // =======\n\n  getContext: function () {\n    ('production' !== process.env.NODE_ENV ? invariant(\n      this.isMounted(),\n      'Tried to access drawing context on an unmounted Surface.'\n    ) : invariant(this.isMounted()));\n    return this.refs.canvas.getContext('2d');\n  },\n\n  scale: function () {\n    this.getContext().scale(this.props.scale, this.props.scale);\n  },\n\n  batchedTick: function () {\n    if (this._frameReady === false) {\n      this._pendingTick = true;\n      return;\n    }\n    this.tick();\n  },\n\n  tick: function () {\n    // Block updates until next animation frame.\n    this._frameReady = false;\n    this.clear();\n    this.draw();\n    requestAnimationFrame(this.afterTick);\n  },\n\n  afterTick: function () {\n    // Execute pending draw that may have been scheduled during previous frame\n    this._frameReady = true;\n    if (this._pendingTick) {\n      this._pendingTick = false;\n      this.batchedTick();\n    }\n  },\n\n  clear: function () {\n    this.getContext().clearRect(0, 0, this.props.width, this.props.height);\n  },\n\n  draw: function () {\n    var layout;\n    if (this.node) {\n      if (this.props.enableCSSLayout) {\n        layout = layoutNode(this.node);\n      }\n      DrawingUtils.drawRenderLayer(this.getContext(), this.node);\n    }\n  },\n\n  // Events\n  // ======\n\n  hitTest: function (e) {\n    var hitTarget = hitTest(e, this.node, this.refs.canvas);\n    if (hitTarget) {\n      hitTarget[hitTest.getHitHandle(e.type)](e);\n    }\n  },\n\n  handleTouchStart: function (e) {\n    var hitTarget = hitTest(e, this.node, this.refs.canvas);\n    var touch;\n    if (hitTarget) {\n      // On touchstart: capture the current hit target for the given touch.\n      this._touches = this._touches || {};\n      for (var i=0, len=e.touches.length; i < len; i++) {\n        touch = e.touches[i];\n        this._touches[touch.identifier] = hitTarget;\n      }\n      hitTarget[hitTest.getHitHandle(e.type)](e);\n    }\n  },\n\n  handleTouchMove: function (e) {\n    this.hitTest(e);\n  },\n\n  handleTouchEnd: function (e) {\n    // touchend events do not generate a pageX/pageY so we rely\n    // on the currently captured touch targets.\n    if (!this._touches) {\n      return;\n    }\n\n    var hitTarget;\n    var hitHandle = hitTest.getHitHandle(e.type);\n    for (var i=0, len=e.changedTouches.length; i < len; i++) {\n      hitTarget = this._touches[e.changedTouches[i].identifier];\n      if (hitTarget && hitTarget[hitHandle]) {\n        hitTarget[hitHandle](e);\n      }\n      delete this._touches[e.changedTouches[i].identifier];\n    }\n  },\n\n  handleClick: function (e) {\n    this.hitTest(e);\n  },\n\n  handleContextMenu: function (e) {\n    this.hitTest(e);\n  },\n\n  handleDoubleClick: function (e) {\n    this.hitTest(e);\n  },\n\n});\n\nmodule.exports = Surface;\n"
  },
  {
    "path": "lib/Text.js",
    "content": "'use strict';\n\nvar createComponent = require('./createComponent');\nvar LayerMixin = require('./LayerMixin');\n\nvar Text = createComponent('Text', LayerMixin, {\n\n  applyTextProps: function (prevProps, props) {\n    var style = (props && props.style) ? props.style : {};\n    var layer = this.node;\n\n    layer.type = 'text';\n    layer.text = childrenAsString(props.children);\n\n    layer.color = style.color;\n    layer.fontFace = style.fontFace;\n    layer.fontSize = style.fontSize;\n    layer.lineHeight = style.lineHeight;\n    layer.textAlign = style.textAlign;\n  },\n\n  mountComponent: function (\n    transaction,\n    nativeParent,\n    nativeContainerInfo,\n    context\n  ) {\n    var props = this._currentElement.props;\n    var layer = this.node;\n    this.applyLayerProps({}, props);\n    this.applyTextProps({}, props);\n    return layer;\n  },\n\n  receiveComponent: function (nextComponent, transaction, context) {\n    var props = nextComponent.props;\n    var prevProps = this._currentElement.props;\n    this.applyLayerProps(prevProps, props);\n    this.applyTextProps(prevProps, props);\n    this._currentElement = nextComponent;\n    this.node.invalidateLayout();\n  }\n\n});\n\nfunction childrenAsString(children) {\n  if (!children) {\n    return '';\n  }\n  if (typeof children === 'string') {\n    return children;\n  }\n  if (children.length) {\n    return children.join('\\n');\n  }\n  return '';\n}\n\nmodule.exports = Text;"
  },
  {
    "path": "lib/__tests__/clamp-test.js",
    "content": "jest.dontMock('../clamp.js');\n\nvar clamp = require('../clamp');\n\ndescribe('clamp', function() {\n  it('returns the min if n is less than min', function() {\n    expect(clamp(-1, 0, 1)).toBe(0);\n  });\n\n  it('returns the max if n is greater than max', function() {\n    expect(clamp(2, 0, 1)).toBe(1);\n  });\n\n  it('returns n if n is between min and max', function() {\n    expect(clamp(0.5, 0, 1)).toBe(0.5);\n  });\n});\n"
  },
  {
    "path": "lib/clamp.js",
    "content": "'use strict';\n\n/**\n * Clamp a number between a minimum and maximum value.\n * @param {Number} number\n * @param {Number} min\n * @param {Number} max\n * @return {Number}\n*/\nmodule.exports = function (number, min, max) {\n  return Math.min(Math.max(number, min), max);\n};\n"
  },
  {
    "path": "lib/createComponent.js",
    "content": "'use strict';\n\n// Adapted from ReactART:\n// https://github.com/reactjs/react-art\n\nvar RenderLayer = require('./RenderLayer');\n\nfunction createComponent (name) {\n  var ReactCanvasComponent = function (element) {\n    this.node = null;\n    this.subscriptions = null;\n    this.listeners = null;\n    this.node = new RenderLayer();\n    this._mountImage = null;\n    this._currentElement = element;\n    this._renderedChildren = null;\n    this._mostRecentlyPlacedChild = null;\n  };\n  ReactCanvasComponent.displayName = name;\n  for (var i = 1, l = arguments.length; i < l; i++) {\n    Object.assign(ReactCanvasComponent.prototype, arguments[i]);\n  }\n\n  return ReactCanvasComponent;\n}\n\nmodule.exports = createComponent;\n"
  },
  {
    "path": "lib/hitTest.js",
    "content": "'use strict';\n\nvar FrameUtils = require('./FrameUtils');\nvar EventTypes = require('./EventTypes');\n\n/**\n * RenderLayer hit testing\n *\n * @param {Event} e\n * @param {RenderLayer} rootLayer\n * @param {?HTMLElement} rootNode\n * @return {RenderLayer}\n */\nfunction hitTest (e, rootLayer, rootNode) {\n  var touch = e.touches ? e.touches[0] : e;\n  var touchX = touch.pageX;\n  var touchY = touch.pageY;\n  var rootNodeBox;\n  if (rootNode) {\n    rootNodeBox = rootNode.getBoundingClientRect();\n    touchX -= rootNodeBox.left;\n    touchY -= rootNodeBox.top;\n  }\n\n  touchY = touchY - window.pageYOffset;\n  touchX = touchX - window.pageXOffset;\n  return getLayerAtPoint(\n    rootLayer,\n    e.type,\n    FrameUtils.make(touchX, touchY, 1, 1),\n    rootLayer.translateX || 0,\n    rootLayer.translateY || 0\n  );\n}\n\n/**\n * @private\n */\nfunction sortByZIndexDescending (layer, otherLayer) {\n  return (otherLayer.zIndex || 0) - (layer.zIndex || 0);\n}\n\n/**\n * @private\n */\nfunction getHitHandle (type) {\n  var hitHandle;\n  for (var tryHandle in EventTypes) {\n    if (EventTypes[tryHandle] === type) {\n      hitHandle = tryHandle;\n      break;\n    }\n  }\n  return hitHandle;\n}\n\n/**\n * @private\n */\nfunction getLayerAtPoint (root, type, point, tx, ty) {\n  var layer = null;\n  var hitHandle = getHitHandle(type);\n  var sortedChildren;\n  var hitFrame = FrameUtils.clone(root.frame);\n\n  // Early bail for non-visible layers\n  if (typeof root.alpha === 'number' && root.alpha < 0.01) {\n    return null;\n  }\n\n  // Child-first search\n  if (root.children) {\n    sortedChildren = root.children.slice().reverse().sort(sortByZIndexDescending);\n    for (var i=0, len=sortedChildren.length; i < len; i++) {\n      layer = getLayerAtPoint(\n        sortedChildren[i],\n        type,\n        point,\n        tx + (root.translateX || 0),\n        ty + (root.translateY || 0)\n      );\n      if (layer) {\n        break;\n      }\n    }\n  }\n\n  // Check for hit outsets\n  if (root.hitOutsets) {\n    hitFrame = FrameUtils.inset(FrameUtils.clone(hitFrame),\n      -root.hitOutsets[0], -root.hitOutsets[1],\n      -root.hitOutsets[2], -root.hitOutsets[3]\n    );\n  }\n\n  // Check for x/y translation\n  if (tx) {\n    hitFrame.x += tx;\n  }\n\n  if (ty) {\n    hitFrame.y += ty;\n  }\n\n  // No child layer at the given point. Try the parent layer.\n  if (!layer && root[hitHandle] && FrameUtils.intersects(hitFrame, point)) {\n    layer = root;\n  }\n\n  return layer;\n}\n\nmodule.exports = hitTest;\nmodule.exports.getHitHandle = getHitHandle;\n\n"
  },
  {
    "path": "lib/layoutNode.js",
    "content": "'use strict';\n\nvar computeLayout = require('./Layout');\n\n/**\n * This computes the CSS layout for a RenderLayer tree and mutates the frame\n * objects at each node.\n *\n * @param {Renderlayer} root\n * @return {Object}\n */\nfunction layoutNode (root) {\n  var rootNode = createNode(root);\n  computeLayout(rootNode);\n  walkNode(rootNode);\n  return rootNode;\n}\n\nfunction createNode (layer) {\n  return {\n    layer: layer,\n    layout: {\n      width: undefined, // computeLayout will mutate\n      height: undefined, // computeLayout will mutate\n      top: 0,\n      left: 0,\n    },\n    style: layer._originalStyle || {},\n    children: (layer.children || []).map(createNode)\n  };\n}\n\nfunction walkNode (node, parentLeft, parentTop) {\n  node.layer.frame.x = node.layout.left + (parentLeft || 0);\n  node.layer.frame.y = node.layout.top + (parentTop || 0);\n  node.layer.frame.width = node.layout.width;\n  node.layer.frame.height = node.layout.height;\n  if (node.children && node.children.length > 0) {\n    node.children.forEach(function (child) {\n      walkNode(child, node.layer.frame.x, node.layer.frame.y);\n    });\n  }\n}\n\nmodule.exports = layoutNode;\n"
  },
  {
    "path": "lib/measureText.js",
    "content": "'use strict';\n\nvar FontFace = require('./FontFace');\nvar FontUtils = require('./FontUtils');\nvar LineBreaker = require('linebreak');\n\nvar canvas = document.createElement('canvas');\nvar ctx = canvas.getContext('2d');\n\nvar _cache = {};\nvar _zeroMetrics = {\n  width: 0,\n  height: 0,\n  lines: []\n};\n\nfunction getCacheKey (text, width, fontFace, fontSize, lineHeight) {\n  return text + width + fontFace.id + fontSize + lineHeight;\n}\n\n/**\n * Given a string of text, available width, and font return the measured width\n * and height.\n * @param {String} text The input string\n * @param {Number} width The available width\n * @param {FontFace} fontFace The FontFace to use\n * @param {Number} fontSize The font size in CSS pixels\n * @param {Number} lineHeight The line height in CSS pixels\n * @return {Object} Measured text size with `width` and `height` members.\n */\nmodule.exports = function measureText (text, width, fontFace, fontSize, lineHeight) {\n  var cacheKey = getCacheKey(text, width, fontFace, fontSize, lineHeight);\n  var cached = _cache[cacheKey];\n  if (cached) {\n    return cached;\n  }\n\n  // Bail and return zero unless we're sure the font is ready.\n  if (!FontUtils.isFontLoaded(fontFace)) {\n    return _zeroMetrics;\n  }\n\n  var measuredSize = {};\n  var textMetrics;\n  var lastMeasuredWidth;\n  var words;\n  var tryLine;\n  var currentLine;\n  var breaker;\n  var bk;\n  var lastBreak;\n\n  ctx.font = fontFace.attributes.style + ' ' + fontFace.attributes.weight + ' ' + fontSize + 'px ' + fontFace.family;\n  textMetrics = ctx.measureText(text);\n\n  measuredSize.width = textMetrics.width;\n  measuredSize.height = lineHeight;\n  measuredSize.lines = [];\n\n  if (measuredSize.width <= width) {\n    // The entire text string fits.\n    measuredSize.lines.push({width: measuredSize.width, text: text});\n  } else {\n    // Break into multiple lines.\n    measuredSize.width = width;\n    currentLine = '';\n    breaker = new LineBreaker(text);\n    \n    while (bk = breaker.nextBreak()) {\n      var word = text.slice(lastBreak ? lastBreak.position : 0, bk.position);\n      \n      tryLine = currentLine + word;\n      textMetrics = ctx.measureText(tryLine);\n      if (textMetrics.width > width || (lastBreak && lastBreak.required)) {\n        measuredSize.height += lineHeight;\n        measuredSize.lines.push({width: lastMeasuredWidth, text: currentLine.trim()});\n        currentLine = word;\n        lastMeasuredWidth = ctx.measureText(currentLine.trim()).width;\n      } else {\n        currentLine = tryLine;\n        lastMeasuredWidth = textMetrics.width;\n      }\n      \n      lastBreak = bk;\n    }\n    \n    currentLine = currentLine.trim();\n    if (currentLine.length > 0) {\n      textMetrics = ctx.measureText(currentLine);\n      measuredSize.lines.push({width: textMetrics, text: currentLine});\n    }\n  }\n\n  _cache[cacheKey] = measuredSize;\n\n  return measuredSize;\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-canvas\",\n  \"version\": \"1.3.0\",\n  \"description\": \"High performance <canvas> rendering for React components\",\n  \"main\": \"lib/ReactCanvas.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/Flipboard/react-canvas.git\"\n  },\n  \"scripts\": {\n    \"start\": \"./node_modules/.bin/gulp\",\n    \"test\": \"./node_modules/.bin/jest\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"canvas\"\n  ],\n  \"author\": \"Michael Johnston <mj@flipboard.com>\",\n  \"license\": \"BSD-3-Clause\",\n  \"homepage\": \"https://github.com/Flipboard/react-canvas\",\n  \"bugs\": {\n    \"url\": \"https://github.com/Flipboard/react-canvas/issues\"\n  },\n  \"devDependencies\": {\n    \"babel-core\": \"^6.22.1\",\n    \"babel-loader\": \"^6.2.10\",\n    \"babel-preset-react\": \"^6.22.0\",\n    \"brfs\": \"^1.4.3\",\n    \"del\": \"^2.2.2\",\n    \"envify\": \"^4.0.0\",\n    \"gulp\": \"^3.9.1\",\n    \"gulp-connect\": \"^5.0.0\",\n    \"jest\": \"^18.1.0\",\n    \"react\": \"^15.0.0\",\n    \"react-dom\": \"^15.0.0\",\n    \"transform-loader\": \"^0.2.3\",\n    \"webpack\": \"^1.14.0\",\n    \"webpack-stream\": \"^3.2.0\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^15.0.0\"\n  },\n  \"dependencies\": {\n    \"fbjs\": \"^0.8.8\",\n    \"linebreak\": \"^0.3.0\",\n    \"scroller\": \"git://github.com/mjohnston/scroller\"\n  }\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "module.exports = {\n  cache: true,\n\n  watch: true,\n\n  entry: {\n    'listview': ['./examples/listview/app.js'],\n    'timeline': ['./examples/timeline/app.js'],\n    'gradient': ['./examples/gradient/app.js'],\n    'css-layout': ['./examples/css-layout/app.js']\n  },\n\n  output: {\n    filename: '[name].js'\n  },\n\n  module: {\n    loaders: [\n      { test: /\\.js$/, loader: 'babel-loader!transform/cacheable?envify' },\n    ],\n    postLoaders: [\n      { loader: \"transform?brfs\" }\n    ]\n  },\n  devtool: ['source-map'],\n  resolve: {\n    root: __dirname,\n    alias: {\n      'react-canvas': 'lib/ReactCanvas.js'\n    }\n  }\n};\n"
  }
]