* Editor does not live inside a full-blown Brackets UI layout. The Editor *will* be

* reported as the "active editor" by EditorManager, however.

* @return {!{doc:!Document, editor:!Editor}}

function createMockEditor(initialContent, languageId, visibleRange) {

/*
 * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint vars: true, plusplus: true, devel: true, browser: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, describe, beforeEach, afterEach, it, runs, waitsFor, waitsForDone, expect, spyOn, jasmine */

define(function (require, exports, module) {
    'use strict';

    // Load dependent modules
    var SpecRunnerUtils     = require("spec/SpecRunnerUtils");

    describe("FileIndexManager", function () {

        describe("unit tests", function () {
            var ProjectManager = require("project/ProjectManager"),
                FileIndexManager = require("project/FileIndexManager"),
                curProjectRoot;

            beforeEach(function () {
                curProjectRoot = null;
                spyOn(ProjectManager, "shouldShow").andCallFake(function () {
                    return true;
                });
                spyOn(ProjectManager, "getProjectRoot").andCallFake(function () {
                    return curProjectRoot;
                });
            });

            function makeFakeProjectDirectory(fakePath, fakeReadEntries) {
                var readEntriesSpy = jasmine.createSpy().andCallFake(fakeReadEntries);
                return {
                    fullPath: fakePath,
                    createReader: function () {
                        return {
                            readEntries: readEntriesSpy
                        };
                    },
                    readEntriesSpy: readEntriesSpy
                };
            }

            it("should abort a running scan and start a new one if another scan is requested while dirty", function () {
                var firstCB, secondCB, firstFileInfoResult, secondFileInfoResult,
                    firstProject = makeFakeProjectDirectory("fakeProject1", function (cb) {
                        firstCB = cb;
                    }),
                    secondProject = makeFakeProjectDirectory("fakeProject2", function (cb) {
                        secondCB = cb;
                    });

                curProjectRoot = firstProject;

                // Ensure it's dirty to begin with
                FileIndexManager.markDirty();

                // Request file infos for the first project. This should start a scan.
                FileIndexManager.getFileInfoList("all").done(function (infos) {
                    firstFileInfoResult = infos;
                });

                // "Switch" to a new project
                curProjectRoot = secondProject;

                // Mark it dirty again and start a second file info request before the first one has "read" its folder
                FileIndexManager.markDirty();
                FileIndexManager.getFileInfoList("all").done(function (infos) {
                    secondFileInfoResult = infos;
                });

                // "Complete" the first scan's read request
                firstCB([{ isFile: true, name: "test1FirstProjectFile.js", fullPath: "test1FirstProjectFile.js" }]);

                // Since the first scan was aborted, we shouldn't have gotten any result for it.
                expect(firstFileInfoResult).toBeUndefined();

                // "Complete" the second scan's read request
                secondCB([{ isFile: true, name: "test1SecondProjectFile.js", fullPath: "test1SecondProjectFile.js" }]);

                // Now both callers should have received the info from the second project.
                expect(firstFileInfoResult.length).toBe(1);
                expect(firstFileInfoResult[0].name).toEqual("test1SecondProjectFile.js");
                expect(secondFileInfoResult.length).toBe(1);
                expect(secondFileInfoResult[0].name).toEqual("test1SecondProjectFile.js");

                // Each readEntries should only have been called once.
                expect(firstProject.readEntriesSpy.callCount).toBe(1);
                expect(secondProject.readEntriesSpy.callCount).toBe(1);
            });

            it("should abort a running scan and start a new one when marked dirty (even before another scan is requested)", function () {
                var firstCB, secondCB, firstFileInfoResult,
                    firstProject = makeFakeProjectDirectory("fakeProject1", function (cb) {
                        firstCB = cb;
                    }),
                    secondProject = makeFakeProjectDirectory("fakeProject2", function (cb) {
                        secondCB = cb;
                    });

                curProjectRoot = firstProject;

                // Ensure it's dirty to begin with
                FileIndexManager.markDirty();

                // Request file infos for the first project. This should start a scan.
                FileIndexManager.getFileInfoList("all").done(function (infos) {
                    firstFileInfoResult = infos;
                });

                // "Switch" to a new project
                curProjectRoot = secondProject;

                // Mark it dirty again without making a new request. This should still cause the scan to restart.
                FileIndexManager.markDirty();

                // "Complete" the first scan's read request
                firstCB([{ isFile: true, name: "test2FirstProjectFile.js", fullPath: "test2FirstProjectFile.js" }]);

                // Since the first scan was aborted, we shouldn't have gotten any result for it.
                expect(firstFileInfoResult).toBeUndefined();

                // "Complete" the second scan's read request
                secondCB([{ isFile: true, name: "test2SecondProjectFile.js", fullPath: "test2SecondProjectFile.js" }]);

                // Now the initial caller should have received the info from the second project.
                expect(firstFileInfoResult.length).toBe(1);
                expect(firstFileInfoResult[0].name).toEqual("test2SecondProjectFile.js");

                // Each readEntries should only have been called once.
                expect(firstProject.readEntriesSpy.callCount).toBe(1);
                expect(secondProject.readEntriesSpy.callCount).toBe(1);
            });

            it("should not start a new scan if another scan is requested while not dirty", function () {
                var firstCB, secondCB, firstFileInfoResult, secondFileInfoResult;
                curProjectRoot = makeFakeProjectDirectory("fakeProject1", function (cb) {
                    firstCB = cb;
                });

                // Ensure it's dirty to begin with
                FileIndexManager.markDirty();

                // Start the first file info request
                FileIndexManager.getFileInfoList("all").done(function (infos) {
                    firstFileInfoResult = infos;
                });

                // The first scan should have requested the project root
                expect(ProjectManager.getProjectRoot).toHaveBeenCalled();
                expect(ProjectManager.getProjectRoot.callCount).toBe(1);

                // Start a second file info request without marking dirty or changing the project root
                FileIndexManager.getFileInfoList("all").done(function (infos) {
                    secondFileInfoResult = infos;
                });

                // We shouldn't have started a second scan, so the project root should not have been requested again.
                expect(ProjectManager.getProjectRoot.callCount).toBe(1);
                expect(curProjectRoot.readEntriesSpy.callCount).toBe(1);

                // "Complete" the scan's read request
                firstCB([{ isFile: true, name: "test3ProjectFile.js", fullPath: "test3ProjectFile.js" }]);

                // Both callers should have received the info from the first project.
                expect(firstFileInfoResult.length).toBe(1);
                expect(firstFileInfoResult[0].name).toEqual("test3ProjectFile.js");
                expect(secondFileInfoResult.length).toBe(1);
                expect(secondFileInfoResult[0].name).toEqual("test3ProjectFile.js");
            });
        });

        describe("integration tests", function () {

            this.category = "integration";

            var testPath = SpecRunnerUtils.getTestPath("/spec/FileIndexManager-test-files");
            var FileIndexManager,
                ProjectManager,
                brackets;

            beforeEach(function () {

                runs(function () {
                    SpecRunnerUtils.createTestWindowAndRun(this, function (testWindow) {
                        brackets = testWindow.brackets;

                        // Load module instances from brackets.test
                        FileIndexManager  = testWindow.brackets.test.FileIndexManager;
                        ProjectManager = testWindow.brackets.test.ProjectManager;
                    });
                });
            });

            afterEach(function () {
                brackets          = null;
                FileIndexManager  = null;
                ProjectManager    = null;
                SpecRunnerUtils.closeTestWindow();
            });

            it("should index files in directory", function () {
                // Open a directory
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var allFiles, cssFiles;
                runs(function () {
                    FileIndexManager.getFileInfoList("all")
                        .done(function (result) {
                            allFiles = result;
                        });
                });
                waitsFor(function () { return allFiles; }, "FileIndexManager.getFileInfoList() timeout", 1000);

                runs(function () {
                    FileIndexManager.getFileInfoList("css")
                        .done(function (result) {
                            cssFiles = result;
                        });
                });
                waitsFor(function () { return cssFiles; }, "FileIndexManager.getFileInfoList() timeout", 1000);

                runs(function () {
                    expect(allFiles.length).toEqual(8);
                    expect(cssFiles.length).toEqual(3);
                });

            });

            it("should handle identical simultaneous requests without doing extra work", function () {  // #330
                // Open a directory
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var projectRoot;
                var promise1, promise2;
                var allFiles1, allFiles2;
                runs(function () {
                    projectRoot = ProjectManager.getProjectRoot();
                    spyOn(projectRoot, "createReader").andCallThrough();

                    // Kick off two index requests in parallel
                    promise1 = FileIndexManager.getFileInfoList("all")
                        .done(function (result) {
                            allFiles1 = result;
                        });
                    promise2 = FileIndexManager.getFileInfoList("all") // same request again
                        .done(function (result) {
                            allFiles2 = result;
                        });

                    waitsForDone(promise1, "First FileIndexManager.getFileInfoList()");
                    waitsForDone(promise2, "Second FileIndexManager.getFileInfoList()");
                });

                runs(function () {
                    // Correct response to both promises
                    expect(allFiles1.length).toEqual(8);
                    expect(allFiles2.length).toEqual(8);

                    // Didn't scan project tree twice
                    expect(projectRoot.createReader.callCount).toBe(1);
                });
            });

            it("should handle differing simultaneous requests without doing extra work", function () {  // #330
                // Open a directory
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var projectRoot;
                var promise1, promise2;
                var allFiles1, allFiles2;
                runs(function () {
                    projectRoot = ProjectManager.getProjectRoot();
                    spyOn(projectRoot, "createReader").andCallThrough();

                    // Kick off two index requests in parallel
                    promise1 = FileIndexManager.getFileInfoList("all")
                        .done(function (result) {
                            allFiles1 = result;
                        });
                    promise2 = FileIndexManager.getFileInfoList("css") // different request in parallel
                        .done(function (result) {
                            allFiles2 = result;
                        });

                    waitsForDone(promise1, "First FileIndexManager.getFileInfoList()");
                    waitsForDone(promise2, "Second FileIndexManager.getFileInfoList()");
                });

                runs(function () {
                    // Correct response to both promises
                    expect(allFiles1.length).toEqual(8);
                    expect(allFiles2.length).toEqual(3);

                    // Didn't scan project tree twice
                    expect(projectRoot.createReader.callCount).toBe(1);
                });
            });

            it("should match a specific filename and return the correct FileInfo", function () {
                // Open a directory
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var fileList;

                runs(function () {
                    FileIndexManager.getFilenameMatches("all", "file_four.css")
                        .done(function (results) {
                            fileList = results;
                        });
                });

                waitsFor(function () { return fileList; }, 1000);

                runs(function () {
                    expect(fileList.length).toEqual(1);
                    expect(fileList[0].name).toEqual("file_four.css");
                    expect(fileList[0].fullPath).toEqual(testPath + "/file_four.css");
                });
            });

            it("should update the indicies on project change", function () {
                // Load spec/FileIndexManager-test-files
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var allFiles;
                runs(function () {
                    FileIndexManager.getFileInfoList("all")
                        .done(function (result) {
                            allFiles = result;
                        });
                });

                waitsFor(function () { return allFiles; }, "FileIndexManager.getFileInfoList() timeout", 1000);

                runs(function () {
                    expect(allFiles.length).toEqual(8);
                });

                // load a subfolder in the test project
                // spec/FileIndexManager-test-files/dir1/dir2
                SpecRunnerUtils.loadProjectInTestWindow(testPath + "/dir1/dir2/");

                var dir2Files;
                runs(function () {
                    FileIndexManager.getFileInfoList("all")
                        .done(function (result) {
                            dir2Files = result;
                        });
                });

                waitsFor(function () { return dir2Files; }, "FileIndexManager.getFileInfoList() timeout", 1000);

                runs(function () {
                    expect(dir2Files.length).toEqual(2);
                    expect(dir2Files[0].name).toEqual("file_eight.css");
                    expect(dir2Files[1].name).toEqual("file_seven.js");
                });
            });

            it("should update the indicies after being marked dirty", function () {
                // Load spec/FileIndexManager-test-files
                SpecRunnerUtils.loadProjectInTestWindow(testPath);

                var allFiles; // set by checkAllFileCount

                // helper function to validate base state of 8 files
                function checkAllFileCount(fileCount) {
                    var files;
                    runs(function () {
                        FileIndexManager.getFileInfoList("all")
                            .done(function (result) {
                                files = result;
                            });
                    });

                    waitsFor(function () { return files; }, "FileIndexManager.getFileInfoList() timeout", 1000);

                    runs(function () {
                        allFiles = files;
                        expect(files.length).toEqual(fileCount);
                    });
                }

                // verify 8 files in base state
                checkAllFileCount(8);

                // add a temporary file to the folder
                var entry;

                // create a 9th file
                runs(function () {
                    var root = ProjectManager.getProjectRoot();
                    root.getFile("new-file.txt",
                                 { create: true, exclusive: true },
                                 function (fileEntry) { entry = fileEntry; });
                });

                waitsFor(function () { return entry; }, "getFile() timeout", 1000);

                runs(function () {
                    // mark FileIndexManager dirty after new file was created
                    FileIndexManager.markDirty();
                });

                // verify 9 files
                checkAllFileCount(9);

                var cleanupComplete = false;

                // verify the new file was added to the "all" index
                runs(function () {
                    var filtered = allFiles.filter(function (value) {
                        return (value.name === "new-file.txt");
                    });
                    expect(filtered.length).toEqual(1);

                    // remove the 9th file
                    brackets.fs.unlink(entry.fullPath, function (err) {
                        cleanupComplete = (err === brackets.fs.NO_ERROR);
                    });
                });

                // wait for the file to be deleted
                waitsFor(function () { return cleanupComplete; }, 1000);

                runs(function () {
                    // mark FileIndexManager dirty after new file was deleted
                    FileIndexManager.markDirty();
                });

                // verify that we're back to 8 files
                checkAllFileCount(8);

                // make sure the 9th file was removed from the index
                runs(function () {
                    var filtered = allFiles.filter(function (value) {
                        return (value.name === "new-file.txt");
                    });
                    expect(filtered.length).toEqual(0);
                });
            });
        });
    });
});


* Must be cleaned up by calling destroyMockEditor(document) later.

* @param {{startLine: number, endLine: number}=} visibleRange

# mkdirp

Like `mkdir -p`, but in node.js!

[![build status](https://secure.travis-ci.org/substack/node-mkdirp.png)](http://travis-ci.org/substack/node-mkdirp)

# example

## pow.js

```js
var mkdirp = require('mkdirp');

mkdirp('/tmp/foo/bar/baz', function (err) {
    if (err) console.error(err)
    else console.log('pow!')
});
```

Output

```
pow!
```

And now /tmp/foo/bar/baz exists, huzzah!

# methods

```js
var mkdirp = require('mkdirp');
```

## mkdirp(dir, mode, cb)

Create a new directory and any necessary subdirectories at `dir` with octal
permission string `mode`.

If `mode` isn't specified, it defaults to `0777 & (~process.umask())`.

`cb(err, made)` fires with the error or the first directory `made`
that had to be created, if any.

## mkdirp.sync(dir, mode)

Synchronously create a new directory and any necessary subdirectories at `dir`
with octal permission string `mode`.

If `mode` isn't specified, it defaults to `0777 & (~process.umask())`.

Returns the first directory that had to be created, if any.

# install

With [npm](http://npmjs.org) do:

```
npm install mkdirp
```

# license

MIT


// Don't run code in ES5 strict mode.
// In case this module was in strict mode, all other modules called by this would also be strict.
// But when testing if the strict mode is preserved, we must ensure that this module is NOT strict.

var path = require("path"),
    expect = require("expect.js"),
    rewire = require("rewire");

function checkForTypeError(err) {
    expect(err.constructor).to.be(TypeError);
}

describe("rewire " + (typeof testEnv === "undefined"? "(node)": "(" + testEnv + ")"), function () {
    it("should work like require()", function () {
        rewire("./moduleA.js").getFilename();
        require("./moduleA.js").getFilename();
        expect(rewire("./moduleA.js").getFilename()).to.eql(require("./moduleA.js").getFilename());
        expect(rewire("../testModules/someOtherModule.js").filename).to.eql(require("../testModules/someOtherModule.js").filename);
    });
    it("should return a fresh instance of the module", function () {
        var someOtherModule = require("./someOtherModule.js"),
            rewiredSomeOtherModule;

        someOtherModule.fs = "This has been modified";
        rewiredSomeOtherModule = rewire("./someOtherModule.js");
        expect(rewiredSomeOtherModule.fs).not.to.be("This has been modified");
    });
    it("should not cache the rewired module", function () {
        var rewired,
            someOtherModule = require("./someOtherModule.js");

        someOtherModule.fs = "This has been changed";

        rewired = rewire("./someOtherModule.js");
        expect(someOtherModule).not.to.be(rewired);
        expect(require("./moduleA.js").someOtherModule).not.to.be(rewired);
        expect(require("./moduleA.js").someOtherModule).to.be(someOtherModule);
        expect(require("./moduleA.js").someOtherModule.fs).to.be("This has been changed");
    });
    it("should modify the module so it provides a __set__ - function", function () {
        expect(rewire("./moduleA.js").__set__).to.be.a(Function);
        expect(rewire("./moduleB.js").__set__).to.be.a(Function);
    });
    it("should modify the module so it provides a __get__ - function", function () {
        expect(rewire("./moduleA.js").__get__).to.be.a(Function);
        expect(rewire("./moduleB.js").__get__).to.be.a(Function);
    });
    it("should not influence other modules", function () {
        var rewiredModuleA = rewire("./moduleA.js");

        expect(require("./someOtherModule.js").__set__).to.be(undefined);
        expect(require("./someOtherModule.js").__get__).to.be(undefined);
    });
    it("should not override/influence global objects by default", function () {
        // This should throw no exception
        rewire("./moduleA.js").checkSomeGlobals();
        rewire("./moduleB.js").checkSomeGlobals();
    });
    it("should provide the ability to set private vars", function () {
        var rewiredModuleA = rewire("./moduleA.js"),
            newObj = {};

        expect(rewiredModuleA.getMyNumber()).to.be(0);
        rewiredModuleA.__set__("myNumber", 2);
        expect(rewiredModuleA.getMyNumber()).to.be(2);
        rewiredModuleA.__set__("myObj", newObj);
        expect(rewiredModuleA.getMyObj()).to.be(newObj);
        rewiredModuleA.__set__("env", "ENVENV");
    });
    it("should provide the ability to get private vars", function () {
        var rewiredModuleA = rewire("./moduleA.js");

        expect(rewiredModuleA.__get__("myNumber")).to.be(rewiredModuleA.getMyNumber());
        expect(rewiredModuleA.__get__("myObj")).to.be(rewiredModuleA.getMyObj());
    });
    it("should provide the ability to inject mocks", function (done) {
        var rewiredModuleA = rewire("./moduleA.js"),
            mockedFs = {
                readFileSync: function (file) {
                    expect(file).to.be("bla.txt");
                    done();
                }
            };

        rewiredModuleA.__set__("fs", mockedFs);
        rewiredModuleA.readFileSync();
    });
    it("should not influence other modules when injecting mocks", function () {
        var rewiredModuleA = rewire("./moduleA.js"),
            someOtherModule,
            mockedFs = {};

        rewiredModuleA.__set__("fs", mockedFs);
        someOtherModule = require("./someOtherModule.js");
        expect(someOtherModule.fs).not.to.be(mockedFs);
    });
    it("should provide the ability to mock global objects just within the module", function () {
        var rewiredModuleA = rewire("./moduleA.js"),
            rewiredModuleB = rewire("./moduleB.js"),
            consoleMock = {},
            bufferMock = {},
            documentMock = {},
            newFilename = "myFile.js";

        rewiredModuleA.__set__({
            console: consoleMock,
            __filename: newFilename
        });
        expect(rewiredModuleA.getConsole()).to.be(consoleMock);
        expect(rewiredModuleB.getConsole()).not.to.be(consoleMock);
        expect(console).not.to.be(consoleMock);
        expect(rewiredModuleA.getFilename()).to.be(newFilename);
        expect(rewiredModuleB.getFilename()).not.to.be(newFilename);
        expect(console).not.to.be(newFilename);
        if (typeof window === "undefined") {
            rewiredModuleA.__set__("Buffer", bufferMock);
            expect(rewiredModuleA.getBuffer()).to.be(bufferMock);
            expect(rewiredModuleB.getBuffer()).not.to.be(bufferMock);
            expect(Buffer).not.to.be(bufferMock);
        } else {
            rewiredModuleA.__set__("document", documentMock);
            expect(rewiredModuleA.getDocument()).to.be(documentMock);
            expect(rewiredModuleB.getDocument() === documentMock).to.be(false);
            expect(document === documentMock).to.be(false);
        }
    });
    it("should be possible to mock global objects that are added on runtime", function () {
        var rewiredModule;

        if (typeof window === "undefined") {
            global.someGlobalVar = "test";
            rewiredModule = rewire("./moduleA.js");
            rewiredModule.__set__("someGlobalVar", "other value");
            expect(global.someGlobalVar).to.be("test");
            expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
            delete global.someGlobalVar;
        } else {
            window.someGlobalVar = "test";
            rewiredModule = rewire("./moduleA.js");
            rewiredModule.__set__("someGlobalVar", "other value");
            expect(window.someGlobalVar).to.be("test");
            expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
            if (typeof navigator !== "undefined" && /MSIE [6-8]\.[0-9]/g.test(navigator.userAgent) === false) {
                delete window.someGlobalVar;
            }
        }
    });
    it("should not be a problem to have a comment on file end", function () {
        var rewired = rewire("./emptyModule.js");

        rewired.__set__("someVar", "hello");
        expect(rewired.__get__("someVar")).to.be("hello");
    });
    it("should not influence the original require if nothing has been required within the rewired module", function () {
        rewire("./emptyModule.js"); // nothing happens here because emptyModule doesn't require anything
        expect(require("./moduleA.js").__set__).to.be(undefined); // if restoring the original node require didn't worked, the module would have a setter
    });
    it("subsequent calls of rewire should always return a new instance", function () {
        expect(rewire("./moduleA.js")).not.to.be(rewire("./moduleA.js"));
    });
    it("should preserve the strict mode", function () {
        var strictModule = rewire("./strictModule.js");

        expect(function () {
            strictModule.doSomethingUnstrict();
        }).to.throwException(checkForTypeError);
    });
    it("should not modify line numbers in stack traces", function () {
        var throwError = rewire("./throwError.js");

        try {
            throwError();
        } catch (err) {
            if (err.stack) {
                expect(err.stack.split("\n")[1]).to.match(/:2:11/);
            }
        }
    });
});


/*!
 * Connect - utils
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var http = require('http')
  , crypto = require('crypto')
  , parse = require('url').parse
  , signature = require('cookie-signature');

/**
 * Return `true` if the request has a body, otherwise return `false`.
 *
 * @param  {IncomingMessage} req
 * @return {Boolean}
 * @api private
 */

exports.hasBody = function(req) {
  return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};

/**
 * Extract the mime type from the given request's
 * _Content-Type_ header.
 *
 * @param  {IncomingMessage} req
 * @return {String}
 * @api private
 */

exports.mime = function(req) {
  var str = req.headers['content-type'] || '';
  return str.split(';')[0];
};

/**
 * Generate an `Error` from the given status `code`
 * and optional `msg`.
 *
 * @param {Number} code
 * @param {String} msg
 * @return {Error}
 * @api private
 */

exports.error = function(code, msg){
  var err = new Error(msg || http.STATUS_CODES[code]);
  err.status = code;
  return err;
};

/**
 * Return md5 hash of the given string and optional encoding,
 * defaulting to hex.
 *
 *     utils.md5('wahoo');
 *     // => "e493298061761236c96b02ea6aa8a2ad"
 *
 * @param {String} str
 * @param {String} encoding
 * @return {String}
 * @api private
 */

exports.md5 = function(str, encoding){
  return crypto
    .createHash('md5')
    .update(str)
    .digest(encoding || 'hex');
};

/**
 * Merge object b with object a.
 *
 *     var a = { foo: 'bar' }
 *       , b = { bar: 'baz' };
 *
 *     utils.merge(a, b);
 *     // => { foo: 'bar', bar: 'baz' }
 *
 * @param {Object} a
 * @param {Object} b
 * @return {Object}
 * @api private
 */

exports.merge = function(a, b){
  if (a && b) {
    for (var key in b) {
      a[key] = b[key];
    }
  }
  return a;
};

/**
 * Escape the given string of `html`.
 *
 * @param {String} html
 * @return {String}
 * @api private
 */

exports.escape = function(html){
  return String(html)
    .replace(/&(?!\w+;)/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"');
};

/**
 * Return a unique identifier with the given `len`.
 *
 *     utils.uid(10);
 *     // => "FDaS435D2z"
 *
 * @param {Number} len
 * @return {String}
 * @api private
 */

exports.uid = function(len) {
  return crypto.randomBytes(Math.ceil(len * 3 / 4))
    .toString('base64')
    .slice(0, len)
    .replace(/\//g, '-')
    .replace(/\+/g, '_');
};

/**
 * Sign the given `val` with `secret`.
 *
 * @param {String} val
 * @param {String} secret
 * @return {String}
 * @api private
 */

exports.sign = function(val, secret){
  console.warn('do not use utils.sign(), use https://github.com/visionmedia/node-cookie-signature')
  return val + '.' + crypto
    .createHmac('sha256', secret)
    .update(val)
    .digest('base64')
    .replace(/=+$/, '');
};

/**
 * Unsign and decode the given `val` with `secret`,
 * returning `false` if the signature is invalid.
 *
 * @param {String} val
 * @param {String} secret
 * @return {String|Boolean}
 * @api private
 */

exports.unsign = function(val, secret){
  console.warn('do not use utils.unsign(), use https://github.com/visionmedia/node-cookie-signature')
  var str = val.slice(0, val.lastIndexOf('.'));
  return exports.sign(str, secret) == val
    ? str
    : false;
};

/**
 * Parse signed cookies, returning an object
 * containing the decoded key/value pairs,
 * while removing the signed key from `obj`.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

exports.parseSignedCookies = function(obj, secret){
  var ret = {};
  Object.keys(obj).forEach(function(key){
    var val = obj[key];
    if (0 == val.indexOf('s:')) {
      val = signature.unsign(val.slice(2), secret);
      if (val) {
        ret[key] = val;
        delete obj[key];
      }
    }
  });
  return ret;
};

/**
 * Parse a signed cookie string, return the decoded value
 *
 * @param {String} str signed cookie string
 * @param {String} secret
 * @return {String} decoded value
 * @api private
 */

exports.parseSignedCookie = function(str, secret){
  return 0 == str.indexOf('s:')
    ? signature.unsign(str.slice(2), secret)
    : str;
};

/**
 * Parse JSON cookies.
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

exports.parseJSONCookies = function(obj){
  Object.keys(obj).forEach(function(key){
    var val = obj[key];
    var res = exports.parseJSONCookie(val);
    if (res) obj[key] = res;
  });
  return obj;
};

/**
 * Parse JSON cookie string
 *
 * @param {String} str
 * @return {Object} Parsed object or null if not json cookie
 * @api private
 */

exports.parseJSONCookie = function(str) {
  if (0 == str.indexOf('j:')) {
    try {
      return JSON.parse(str.slice(2));
    } catch (err) {
      // no op
    }
  }
};

/**
 * Pause `data` and `end` events on the given `obj`.
 * Middleware performing async tasks _should_ utilize
 * this utility (or similar), to re-emit data once
 * the async operation has completed, otherwise these
 * events may be lost.
 *
 *      var pause = utils.pause(req);
 *      fs.readFile(path, function(){
 *         next();
 *         pause.resume();
 *      });
 *
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

exports.pause = require('pause');

/**
 * Strip `Content-*` headers from `res`.
 *
 * @param {ServerResponse} res
 * @api private
 */

exports.removeContentHeaders = function(res){
  Object.keys(res._headers).forEach(function(field){
    if (0 == field.indexOf('content')) {
      res.removeHeader(field);
    }
  });
};

/**
 * Check if `req` is a conditional GET request.
 *
 * @param {IncomingMessage} req
 * @return {Boolean}
 * @api private
 */

exports.conditionalGET = function(req) {
  return req.headers['if-modified-since']
    || req.headers['if-none-match'];
};

/**
 * Respond with 401 "Unauthorized".
 *
 * @param {ServerResponse} res
 * @param {String} realm
 * @api private
 */

exports.unauthorized = function(res, realm) {
  res.statusCode = 401;
  res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
  res.end('Unauthorized');
};

/**
 * Respond with 304 "Not Modified".
 *
 * @param {ServerResponse} res
 * @param {Object} headers
 * @api private
 */

exports.notModified = function(res) {
  exports.removeContentHeaders(res);
  res.statusCode = 304;
  res.end();
};

/**
 * Return an ETag in the form of `"<size>-<mtime>"`
 * from the given `stat`.
 *
 * @param {Object} stat
 * @return {String}
 * @api private
 */

exports.etag = function(stat) {
  return '"' + stat.size + '-' + Number(stat.mtime) + '"';
};

/**
 * Parse the given Cache-Control `str`.
 *
 * @param {String} str
 * @return {Object}
 * @api private
 */

exports.parseCacheControl = function(str){
  var directives = str.split(',')
    , obj = {};

  for(var i = 0, len = directives.length; i < len; i++) {
    var parts = directives[i].split('=')
      , key = parts.shift().trim()
      , val = parseInt(parts.shift(), 10);

    obj[key] = isNaN(val) ? true : val;
  }

  return obj;
};

/**
 * Parse the `req` url with memoization.
 *
 * @param {ServerRequest} req
 * @return {Object}
 * @api private
 */

exports.parseUrl = function(req){
  var parsed = req._parsedUrl;
  if (parsed && parsed.href == req.url) {
    return parsed;
  } else {
    return req._parsedUrl = parse(req.url);
  }
};

/**
 * Parse byte `size` string.
 *
 * @param {String} size
 * @return {Number}
 * @api private
 */

exports.parseBytes = require('bytes');


import { extend, isFunction } from './util';

import { createLinkedState } from './linked-state';

import { triggerComponentRender, setComponentProps } from './vdom/component';

import { extend, clone, isFunction } from './util';

import { extend, isFunction, isString } from '../util';
import { isFunctionalComponent } from './functional-component';

/** Check if two nodes are equivalent.
 *	@param {Element} node
 *	@param {VNode} vnode
 *	@private
 */
export function isSameNodeType(node, vnode) {
	if (node.nodeType===3) return isString(vnode);
	if (isFunctionalComponent(vnode)) return true;
	let nodeName = vnode.nodeName;
	if (isFunction(nodeName)) return node._componentConstructor===nodeName;
	return node.nodeName.toLowerCase()===nodeName;
}

/** Reconstruct Component-style `props` from a VNode
 *	@todo: determine if it would be acceptible to drop the extend() clone here for speed
 *	@private
 */
export function getNodeProps(vnode) {
	let props = extend({}, vnode.attributes);
	if (vnode.children) {
		props.children = vnode.children;
	}
	return props;
}


import { h, render, Component } from '../../src/preact';
/** @jsx h */

describe('context', () => {
	let scratch;

	before( () => {
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);
	});

	beforeEach( () => {
		scratch.innerHTML = '';
	});

	after( () => {
		scratch.parentNode.removeChild(scratch);
		scratch = null;
	});

	it('should pass context to grandchildren', () => {
		const CONTEXT = { a:'a' };
		const PROPS = { b:'b' };

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return
<Inner {...props} />
;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {});
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {});
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);
	});

	it('should pass context to direct children', () => {
		const CONTEXT = { a:'a' };
		const PROPS = { b:'b' };

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return <Inner {...props} />;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {});
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {});
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);

		// make sure render() could make use of context.a
		expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] }));
	});
});


/** Update component state by copying properties from `state` to `this.state`.

*	@param {object} state		A hash of state properties to update with new values

if (!this.prevState) this.prevState = extend({}, s);

extend(s, isFunction(state) ? state(s, this.props) : state);

keep keep keep keep keep replace keep keep keep keep

if (!this.prevState) this.prevState = clone(s);

import { SYNC_RENDER, DOM_RENDER, NO_RENDER, EMPTY, EMPTY_BASE } from '../constants';
import options from '../options';
import { extend, isFunction } from '../util';
import { hook, deepHook } from '../hooks';
import { enqueueRender } from '../render-queue';
import { getNodeProps } from '.';
import build from './build';
import { createComponent, collectComponent } from './component-recycler';
import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';

/** Mark component as dirty and queue up a render.
 *	@param {Component} component
 *	@private
 */
export function triggerComponentRender(component) {
	if (!component._dirty) {
		component._dirty = true;
		enqueueRender(component);
	}
}

/** Set a component's `props` (generally derived from JSX attributes).
 *	@param {Object} props
 *	@param {Object} [opts]
 *	@param {boolean} [opts.renderSync=false]	If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
 *	@param {boolean} [opts.render=true]			If `false`, no render will be triggered.
 */
export function setComponentProps(component, props, opts, context) {
	let d = component._disableRendering;
	component._disableRendering = true;

	opts = opts || EMPTY;

	if (context) {
		if (!component.prevContext) component.prevContext = extend({}, component.context);
		component.context = context;
	}

	hook(component, 'componentWillReceiveProps', props, component.context);

	if (!component.prevProps) component.prevProps = extend({}, component.props);
	component.props = props;

	component._disableRendering = d;

	if (opts.render!==false) {
		if (opts.renderSync || options.syncComponentUpdates) {
			renderComponent(component);
		}
		else {
			triggerComponentRender(component);
		}
	}
}

/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
 *	@param {Component} component
 *	@param {Object} [opts]
 *	@param {boolean} [opts.build=false]		If `true`, component will build and store a DOM node if not already associated with one.
 *	@private
 */
export function renderComponent(component, opts) {
	if (component._disableRendering) return;

	let skip, rendered,
		props = component.props,
		state = component.state,
		context = component.context,
		previousProps = component.prevProps || props,
		previousState = component.prevState || state,
		previousContext = component.prevContext || context,
		isUpdate = component.base;

	if (isUpdate) {
		component.props = previousProps;
		component.state = previousState;
		component.context = previousContext;
		if (hook(component, 'shouldComponentUpdate', props, state, context)===false) {
			skip = true;
		}
		else {
			hook(component, 'componentWillUpdate', props, state, context);
		}
		component.props = props;
		component.state = state;
		component.context = context;
	}

	component.prevProps = component.prevState = component.prevContext = null;
	component._dirty = false;

	if (!skip) {
		rendered = hook(component, 'render', props, state, context);

		let childComponent = rendered && rendered.nodeName,
			childContext = component.getChildContext ? component.getChildContext() : context,
			base;

		if (isFunction(childComponent) && childComponent.prototype.render) {
			// set up high order component link

			let inst = component._component;
			if (inst && inst.constructor!==childComponent) {
				unmountComponent(inst.base, inst, false);
				inst = null;
			}

			let childProps = getNodeProps(rendered);

			if (inst) {
				setComponentProps(inst, childProps, SYNC_RENDER, childContext);
			}
			else {
				inst = createComponent(childComponent, childProps, childContext);
				inst._parentComponent = component;
				component._component = inst;
				if (component.base) deepHook(inst, 'componentWillMount');
				setComponentProps(inst, childProps, NO_RENDER, childContext);
				renderComponent(inst, DOM_RENDER);
				if (component.base) deepHook(inst, 'componentDidMount');
			}

			base = inst.base;
		}
		else {
			// destroy high order component link
			if (component._component) {
				unmountComponent(component.base, component._component);
			}
			component._component = null;

			if (component.base || (opts && opts.build)) {
				base = build(component.base, rendered || EMPTY_BASE, childContext);
			}
		}

		if (component.base && base!==component.base) {
			let p = component.base.parentNode;
			if (p) p.replaceChild(base, component.base);
		}

		component.base = base;
		if (base) {
			base._component = component;
			base._componentConstructor = component.constructor;
		}

		if (isUpdate) {
			hook(component, 'componentDidUpdate', previousProps, previousState, previousContext);
		}
	}

	let cb = component._renderCallbacks;
	if (cb) {
		for (let i=cb.length; i--; ) cb[i]();
		cb.length = 0;
	}

	return rendered;
}

/** Apply the Component referenced by a VNode to the DOM.
 *	@param {Element} dom	The DOM node to mutate
 *	@param {VNode} vnode	A Component-referencing VNode
 *	@returns {Element} dom	The created/mutated element
 *	@private
 */
export function buildComponentFromVNode(dom, vnode, context) {
	let c = dom && dom._component;

	if (isFunctionalComponent(vnode)) {
		let p = build(dom, buildFunctionalComponent(vnode, context), context);
		p._componentConstructor = vnode.nodeName;
		return p;
	}

	let isOwner = c && dom._componentConstructor===vnode.nodeName;
	while (c && !isOwner && (c=c._parentComponent)) {
		isOwner = c.constructor===vnode.nodeName;
	}

	if (isOwner) {
		setComponentProps(c, getNodeProps(vnode), SYNC_RENDER, context);
	}
	else {
		if (c) {
			unmountComponent(dom, c);
			dom = null;
		}
		dom = createComponentFromVNode(vnode, dom, context);
	}

	return dom;
}

/** Instantiate and render a Component, given a VNode whose nodeName is a constructor.
 *	@param {VNode} vnode
 *	@private
 */
function createComponentFromVNode(vnode, dom, context) {
	let props = getNodeProps(vnode);
	let component = createComponent(vnode.nodeName, props, context);
	if (dom) component.base = dom;
	setComponentProps(component, props, NO_RENDER, context);
	renderComponent(component, DOM_RENDER);

	// let node = component.base;
	//if (!node._component) {
	//	node._component = component;
	//	node._componentConstructor = vnode.nodeName;
	//}

	return component.base;
}

/** Remove a component from the DOM and recycle it.
 *	@param {Element} dom			A DOM node from which to unmount the given Component
 *	@param {Component} component	The Component instance to unmount
 *	@private
 */
function unmountComponent(dom, component, remove) {
	// console.warn('unmounting mismatched component', component);

	hook(component, 'componentWillUnmount');
	if (remove!==false) {
		if (dom._component===component) {
			delete dom._component;
			delete dom._componentConstructor;
		}
		let base = component.base;
		if (base && base.parentNode) {
			base.parentNode.removeChild(base);
		}
	}
	component._parentComponent = null;
	hook(component, 'componentDidUnmount');
	collectComponent(component);
}


import npm from 'rollup-plugin-npm';
import babel from 'rollup-plugin-babel';

export default {
	plugins: [
		npm({
			// for index.js:
			main: true
		}),
		babel({
			sourceMap: true,
			exclude: 'node_modules/**'
		})
	]
};


.babelrc
.eslintrc
.travis.yml
karma.conf.js
rollup.config.js
test/


import options from './options';
import VNode from './vnode';
import { hook } from './hooks';
import { empty } from './util';

const SHARED_TEMP_ARRAY = [];

/** JSX/hyperscript reviver
 *	@see http://jasonformat.com/wtf-is-jsx
 *	@public
 *  @example
 *  /** @jsx h *\/
 *  import { render, h } from 'preact';
 *  render(foo, document.body);
 */
export default function h(nodeName, attributes) {
	let len = arguments.length,
		children, arr, lastSimple;

	if (len>2) {
		children = [];
		for (let i=2; i<len; i++) {
			let p = arguments[i];
			if (empty(p)) continue;
			if (p.join) {
				arr = p;
			}
			else {
				arr = SHARED_TEMP_ARRAY;
				arr[0] = p;
			}
			for (let j=0; j<arr.length; j++) {
				let child = arr[j],
					simple = !empty(child) && !(child instanceof VNode);
				if (simple) child = String(child);
				if (simple && lastSimple) {
					children[children.length-1] += child;
				}
				else if (!empty(child)) {
					children.push(child);
				}
				lastSimple = simple;
			}
		}
	}

	if (attributes && attributes.children) {
		delete attributes.children;
	}

	let p = new VNode(nodeName, attributes || undefined, children || undefined);
	hook(options, 'vnode', p);
	return p;
}


/** Fast clone. Note: does not filter out non-own properties. */

import { NON_DIMENSION_PROPS } from './constants';

/** Copy own-properties from `props` onto `obj`.
 *	@returns obj
 *	@private
 */
export function extend(obj, props) {
	for (let i in props) if (hasOwnProperty.call(props, i)) {
		obj[i] = props[i];
	}
	return obj;
}

/** Create a caching wrapper for the given function.
 *	@private
 */
export function memoize(fn, mem) {
	mem = mem || {};
	return k => hasOwnProperty.call(mem, k) ? mem[k] : (mem[k] = fn(k));
}

/** Get a deep property value from the given object, expressed in dot-notation.
 *	@private
 */
export function delve(obj, key) {
	for (let p=key.split('.'), i=0; i<p.length && obj; i++) {
		obj = obj[p];
	}
	return obj;
}

/** Convert an Array-like object to an Array
 *	@private
 */
export function toArray(obj) {
	let arr = [],
		i = obj.length;
	while (i--) arr[i] = obj[i];
	return arr;
}

/** @private is the given object a Function? */
export const isFunction = obj => 'function'===typeof obj;

/** @private is the given object a String? */
export const isString = obj => 'string'===typeof obj;

/** @private Safe reference to builtin hasOwnProperty */
export const hasOwnProperty = Object.prototype.hasOwnProperty;

/** Check if a value is `null` or `undefined`.
 *	@private
 */
export const empty = x => (x===null || x===undefined);

/** Convert a hashmap of styles to CSSText
 *	@private
 */
export function styleObjToCss(s) {
	let str = '';
	for (let prop in s) {
		if (hasOwnProperty.call(s, prop)) {
			let val = s[prop];
			str += jsToCss(prop);
			str += ': ';
			str += val;
			if (typeof val==='number' && !NON_DIMENSION_PROPS[prop]) {
				str += 'px';
			}
			str += '; ';
		}
	}
	return str;
}

/** Convert a hashmap of CSS classes to a space-delimited className string
 *	@private
 */
export function hashToClassName(c) {
	let str = '';
	for (let prop in c) {
		if (c[prop]) {
			if (str) str += ' ';
			str += prop;
		}
	}
	return str;
}

/** Convert a JavaScript camel-case CSS property name to a CSS property name
 *	@private
 *	@function
 */
export const jsToCss = memoize( s => s.replace(/([A-Z])/,'-$1').toLowerCase() );


root = true

[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[{package.json,.*rc,*.yml}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false


import { ATTR_KEY, EMPTY } from '../constants';
import { hasOwnProperty, memoize } from '../util';
import options from '../options';
import { hook } from '../hooks';

export function ensureNodeData(node) {
	return node[ATTR_KEY] || (node[ATTR_KEY] = {});
}

/** Append multiple children to a Node.
 *	Uses a Document Fragment to batch when appending 2 or more children
 *	@private
 */
export function appendChildren(parent, children) {
	let len = children.length;
	if (len<=2) {
		parent.appendChild(children[0]);
		if (len===2) parent.appendChild(children[1]);
		return;
	}

	let frag = document.createDocumentFragment();
	for (let i=0; i<len; i++) frag.appendChild(children[i]);
	parent.appendChild(frag);
}

/** Retrieve the value of a rendered attribute
 *	@private
 */
export function getAccessor(node, name, value, cache) {
	if (name!=='type' && name in node) return node[name];
	if (name==='class') return node.className;
	if (name==='style') return node.style.cssText;
	let attrs = node[ATTR_KEY];
	if (cache!==false && attrs && hasOwnProperty.call(attrs, name)) return attrs[name];
	return value;
}

/** Set a named attribute on the given Node, with special behavior for some names and event handlers.
 *	If `value` is `null`, the attribute/handler will be removed.
 *	@param {Element} node	An element to mutate
 *	@param {string} name	The name/key to set, such as an event or attribute name
 *	@param {any} value		An attribute value, such as a function to be used as an event handler
 *	@param {any} previousValue	The last value that was set for this name/node pair
 *	@private
 */
export function setAccessor(node, name, value) {
	if (name==='class') {
		node.className = value;
	}
	else if (name==='style') {
		node.style.cssText = value;
	}
	else if (name==='dangerouslySetInnerHTML') {
		node.innerHTML = value.__html;
	}
	else if (name in node && name!=='type') {
		node[name] = value;
	}
	else {
		setComplexAccessor(node, name, value);
	}

	ensureNodeData(node)[name] = getAccessor(node, name, value, false);
}

/** For props without explicit behavior, apply to a Node as event handlers or attributes.
 *	@private
 */
function setComplexAccessor(node, name, value) {
	if (name.substring(0,2)==='on') {
		let type = normalizeEventName(name),
			l = node._listeners || (node._listeners = {}),
			fn = !l[type] ? 'add' : !value ? 'remove' : null;
		if (fn) node[fn+'EventListener'](type, eventProxy);
		l[type] = value;
		return;
	}

	let type = typeof value;
	if (value===null) {
		node.removeAttribute(name);
	}
	else if (type!=='function' && type!=='object') {
		node.setAttribute(name, value);
	}
}

/** Proxy an event to hooked event handlers
 *	@private
 */
function eventProxy(e) {
	let fn = this._listeners[normalizeEventName(e.type)];
	if (fn) return fn.call(this, hook(options, 'event', e) || e);
}

/** Convert an Event name/type to lowercase and strip any "on*" prefix.
 *	@function
 *	@private
 */
let normalizeEventName = memoize(t => t.replace(/^on/i,'').toLowerCase());

/** Get a hashmap of node properties, preferring preact's cached property values over the DOM's
 *	@private
 */
export function getNodeAttributes(node) {
	return node[ATTR_KEY] || getRawNodeAttributes(node) || EMPTY;
	// let list = getRawNodeAttributes(node),
	// 	l = node[ATTR_KEY];
	// return l && list ? extend(list, l) : (l || list || EMPTY);
}

/** Get a node's attributes as a hashmap, regardless of type.
 *	@private
 */
function getRawNodeAttributes(node) {
	let list = node.attributes;
	if (!list || !list.getNamedItem) return list;
	if (list.length) return getAttributesAsObject(list);
}

/** Convert a DOM `.attributes` NamedNodeMap to a hashmap.
 *	@private
 */
function getAttributesAsObject(list) {
	let attrs;
	for (let i=list.length; i--; ) {
		let item = list[i];
		if (!attrs) attrs = {};
		attrs[item.name] = item.value;
	}
	return attrs;
}


export const jsToCss = memoize( s => s.replace(/([A-Z])/,'-$1').toLowerCase() );

/** Just a memoized String.prototype.toLowerCase */

export const toLowerCase = memoize( s => s.toLowerCase() );


import h from './h';
import Component from './component';
import render from './render';
import { rerender } from './render-queue';
import options from './options';

export default {
	h,
	Component,
	render,
	rerender,
	options,
	hooks: options
};


import preact, { h, render, Component, hooks, options } from '../../src/preact';
import { expect } from 'chai';

describe('preact', () => {
	it('should be available as a default export', () => {
		expect(preact).to.be.an('object');
		expect(preact).to.have.property('h', h);
		expect(preact).to.have.property('render', render);
		expect(preact).to.have.property('Component', Component);
		expect(preact).to.have.property('hooks', hooks);
		expect(preact).to.have.property('options', options);
	});

	it('should be available as named exports', () => {
		expect(h).to.be.a('function');
		expect(render).to.be.a('function');
		expect(Component).to.be.a('function');
		expect(hooks).to.exist.and.be.an('object');
		expect(options).to.exist.and.be.an('object');
	});
});


import { SYNC_RENDER, DOM_RENDER, NO_RENDER, EMPTY, EMPTY_BASE } from '../constants';

import { extend, isFunction } from '../util';

import { hook, deepHook } from '../hooks';

import { enqueueRender } from '../render-queue';

import { clone, isFunction } from '../util';


import { h } from '../../src/preact';
import VNode from '../../src/vnode';
import { expect } from 'chai';

/*eslint-env browser, mocha */

/** @jsx h */

let flatten = obj => JSON.parse(JSON.stringify(obj));

describe('h(jsx)', () => {
	it('should return a VNode', () => {
		let r;
		expect( () => r = h('foo') ).not.to.throw();
		expect(r).to.be.an('object');
		expect(r).to.be.an.instanceof(VNode);
		expect(r).to.have.property('nodeName', 'foo');
		expect(r).to.have.property('attributes', undefined);
		expect(r).to.have.property('children', undefined);
	});

	it('should perserve raw attributes', () => {
		let attrs = { foo:'bar', baz:10, func:()=>{} },
			r = h('foo', attrs);
		expect(r).to.be.an('object')
			.with.property('attributes')
			.that.deep.equals(attrs);
	});

	it('should support element children', () => {
		let r = h(
			'foo',
			null,
			h('bar'),
			h('baz')
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				new VNode('bar'),
				new VNode('baz')
			]);
	});

	it('should support element children', () => {
		let r = h(
			'foo',
			null,
			h('bar'),
			h('baz', null, h('test'))
		);

		r = flatten(r);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				{ nodeName:'bar' },
				{ nodeName:'baz', children:[
					{ nodeName:'test' }
				]}
			]);
	});

	it('should support text children', () => {
		let r = h(
			'foo',
			null,
			'textstuff'
		);

		expect(r).to.be.an('object')
			.with.property('children')
			.with.length(1)
			.with.property('0')
			.that.equals('textstuff');
	});

	it('should merge adjacent text children', () => {
		let r = h(
			'foo',
			null,
			'one',
			'two',
			h('bar'),
			'three',
			h('baz'),
			h('baz'),
			'four',
			'five',
			'six'
		);

		r = flatten(r);

		expect(r).to.be.an('object')
			.with.property('children')
			.that.deep.equals([
				'onetwo',
				{ nodeName:'bar' },
				'three',
				{ nodeName:'baz' },
				{ nodeName:'baz' },
				'fourfivesix'
			]);
	});
});


import { EMPTY_BASE } from '../constants';
import { getNodeProps } from '.';
import { isFunction } from '../util';

/** Check if a VNode is a reference to a stateless functional component.
 *	A function component is represented as a VNode whose `nodeName` property is a reference to a function.
 *	If that function is not a Component (ie, has no `.render()` method on a prototype), it is considered a stateless functional component.
 *	@param {VNode} vnode	A VNode
 *	@private
 */
export function isFunctionalComponent({ nodeName }) {
	return isFunction(nodeName) && !nodeName.prototype.render;
}

/** Construct a resultant VNode from a VNode referencing a stateless functional component.
 *	@param {VNode} vnode	A VNode with a `nodeName` property that is a reference to a function.
 *	@private
 */
export function buildFunctionalComponent(vnode, context) {
	return vnode.nodeName(getNodeProps(vnode), context) || EMPTY_BASE;
}


# Preact

[![npm](https://img.shields.io/npm/v/preact.svg)](http://npm.im/preact)
[![travis](https://travis-ci.org/developit/preact.svg?branch=master)](https://travis-ci.org/developit/preact)
[![gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/developit/preact)

Preact is an attempt to recreate the core value proposition of React _(or similar libraries like Mithril)_ using as little code as possible, with first-class support for ES2015.  Currently the library is around **3kb** (minified & gzipped).

It retains a large amount of compatibility with React, but only the [stateless functional components](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html#stateless-functional-components) and [ES6 Classes] interface.  As one would expect coming from React, Components are simple building blocks for composing a User Interface.

>  :information_desk_person: **Documentation is available in the [Preact Wiki](https://github.com/developit/preact/wiki).**

## Demos

- [**ESBench**](http://esbench.com) is built using Preact.
- [**Nectarine.rocks**](http://nectarine.rocks) _([Github Project](https://github.com/developit/nectarine))_ :peach:
- [**Documentation Viewer**](https://documentation-viewer.firebaseapp.com) _([Github Project](https://github.com/developit/documentation-viewer))_
- [**TodoMVC**](http://developit.github.io/preact-todomvc/) _([Github Project](https://github.com/developit/preact-todomvc))_
- [**Hacker News Minimal**](https://developit.github.io/hn_minimal/) _([Github Project](https://github.com/developit/hn_minimal))_
- [**Preact Boilerplate**](https://preact-boilerplate.surge.sh) _([Github Project](https://github.com/developit/preact-boilerplate))_ :zap:
- [**Preact Redux Example**](https://github.com/developit/preact-redux-example) :star:
- [**Flickr Browser**](http://codepen.io/developit/full/VvMZwK/) (@ CodePen)
- [**Animating Text**](http://codepen.io/developit/full/LpNOdm/) (@ CodePen)
- [**60FPS Rainbow Spiral**](http://codepen.io/developit/full/xGoagz/) (@ CodePen)
- [**Simple Clock**](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/) (@ JSFiddle)
- [**3D + ThreeJS**](http://codepen.io/developit/pen/PPMNjd?editors=0010) (@ CodePen)
- [**Create your Own!**](https://jsfiddle.net/developit/rs6zrh5f/embedded/result/) (@ JSFiddle)

## Libraries & Add-ons

- :earth_americas: [**preact-router**](https://git.io/preact-router): URL routing for your components.
- :tulip: [**preact-svg**](https://git.io/preact-svg): Use inline `<svg>` in Preact.
- :page_facing_up: [**preact-render-to-string**](https://git.io/preact-render-to-string): Universal rendering.
- :raised_hands: [**preact-compat**](https://git.io/preact-compat): use any React library with Preact. *([full example](http://git.io/preact-compat-example))*
- :rocket: [**preact-photon**](https://git.io/preact-photon): build beautiful desktop UI with [photon](http://photonkit.com).
- :microscope: [**preact-jsx-chai**](https://git.io/preact-jsx-chai): JSX assertion testing _(no DOM or phantomjs!)_
- :bookmark_tabs: [**preact-markup**](https://git.io/preact-markup): Render HTML & Custom Elements as JSX & Components
- :pencil: [**preact-richtextarea**](https://git.io/preact-richtextarea): Simple HTML editor component
- :repeat: [**preact-cycle**](https://git.io/preact-cycle): Functional-reactive paradigm for Preact.

## Getting Started

> :information_desk_person: You [don't _have_ to use ES2015 to use Preact](https://github.com/developit/preact-without-babel)... but you should.

The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc.  If you don't, start with [preact-boilerplate] or a [CodePen Template](http://codepen.io/developit/pen/pgaROe?editors=0010).

### Import what you need

The `preact` module provides both named and default exports, so you can either import everything under a namespace of your choosing, or just what you need as locals:

##### Named:

```js
import { h, render, Component } from 'preact';

// Tell Babel to transform JSX into h() calls:
/** @jsx h */
```

##### Default:

```js
import preact from 'preact';

// Tell Babel to transform JSX into preact.h() calls:
/** @jsx preact.h */
```

> Named imports work well for highly structured applications, whereas the default import is quick and never needs to be updated when using different parts of the library.
>
> Instead of declaring the `@jsx` pragma in your code, it's best to configure it globally in a `.babelrc`:
>
> ```js
> { "jsxPragma": "h" }
> ```

### Rendering JSX

Out of the box, Preact provides an `h()` function that turns your JSX into Virtual DOM elements _([here's how](http://jasonformat.com/wtf-is-jsx))_. It also provides a `render()` function that creates a DOM tree from that Virtual DOM.

To render some JSX, just import those two functions and use them like so:

```js
import { h, render } from 'preact';

render((
	<div id="foo">
		<span>Hello, world!</span>
		<button onClick={ e => alert("hi!"); }>Click Me</button>
	</div>
), document.body);
```

This should seem pretty straightforward if you've used [hyperscript] or one of its many friends.

Rendering hyperscript with a virtual DOM is pointless, though. We want to render components and have them updated when data changes - that's where the power of virtual DOM diffing shines. :star2:

### Components

Preact exports a generic `Component` class, which can be extended to build encapsulated, self-updating pieces of a User Interface.  Components support all of the standard React [lifecycle methods], like `shouldComponentUpdate()` and `componentWillReceiveProps()`.  Providing specific implementations of these methods is the preferred mechanism for controlling _when_ and _how_ components update.

Components also have a `render()` method, but unlike React this method is passed `(props, state)` as arguments. This provides an ergonomic means to destructure `props` and `state` into local variables to be referenced from JSX.

Let's take a look at a very simple `Clock` component, which shows the current time.

```js
import { h, render, Component } from 'preact';

class Clock extends Component {
	render() {
		let time = new Date().toLocaleTimeString();
		return <span>{ time }</span>;
	}
}

// render an instance of Clock into <body>:
render(<Clock />, document.body);
```

That's great. Running this produces the following HTML DOM structure:

```html
<span>10:28:57 PM</span>
```

In order to have the clock's time update every second, we need to know when `<Clock>` gets mounted to the DOM. _If you've used HTML5 Custom Elements, this is similar to the `attachedCallback` and `detachedCallback` lifecycle methods._ Preact invokes the following lifecycle methods if they are defined for a Component:

| Lifecycle method            | When it gets called                              |
|-----------------------------|--------------------------------------------------|
| `componentWillMount`        | before the component gets mounted to the DOM     |
| `componentDidMount`         | after the component gets mounted to the DOM      |
| `componentWillUnmount`      | prior to removal from the DOM                    |
| `componentDidUnmount`       | after removal from the DOM                       |
| `componentWillReceiveProps` | before new props get accepted                    |
| `shouldComponentUpdate`     | before `render()`. Return `false` to skip render |
| `componentWillUpdate`       | before `render()`                                |
| `componentDidUpdate`        | after `render()`                                 |

So, we want to have a 1-second timer start once the Component gets added to the DOM, and stop if it is removed. We'll create the timer and store a reference to it in `componentDidMount`, and stop the timer in `componentWillUnmount`. On each timer tick, we'll update the component's `state` object with a new time value. Doing this will automatically re-render the component.

```js
import { h, render, Component } from 'preact';

class Clock extends Component {
	constructor() {
		super();
		// set initial time:
		this.state.time = Date.now();
	}

	componentDidMount() {
		// update time every second
		this.timer = setInterval(() => {
			this.setState({ time: Date.now() });
		}, 1000);
	}

	componentWillUnmount() {
		// stop when not renderable
		clearInterval(this.timer);
	}

	render(props, state) {
		let time = new Date(state.time).toLocaleTimeString();
		return <span>{ time }</span>;
	}
}

// render an instance of Clock into <body>:
render(<Clock />, document.body);
```

Now we have [a ticking clock](http://jsfiddle.net/developit/u9m5x0L7/embedded/result,js/)!

### Props & State

The concept (and nomenclature) for `props` and `state` is the same as in React. `props` are passed to a component by defining attributes in JSX, `state` is internal state. Changing either triggers a re-render, though by default Preact re-renders Components asynchronously for `state` changes and synchronously for `props` changes.  You can tell Preact to render `prop` changes asynchronously by setting `options.syncComponentUpdates` to `false`.

---

## Linked State

One area Preact takes a little further than React is in optimizing state changes. A common pattern in ES2015 React code is to use Arrow functions within a `render()` method in order to update state in response to events.  Creating functions enclosed in a scope on every render is inefficient and forces the garbage collector to do more work than is necessary.

One solution to this is to bind component methods declaratively.
Here is an example using [decko](http://git.io/decko):

```js
class Foo extends Component {
	@bind
	updateText(e) {
		this.setState({ text: e.target.value });
	}
	render({ }, { text }) {
		return <input value={text} onInput={this.updateText} />;
	}
}
```

While this achieves much better runtime performance, it's still a lot of unnecessary code to wire up state to UI.

Fortunately there is a solution, in the form of `linkState()`. Calling `component.linkState('text')` returns a function that accepts an Event and uses it's associated value to update the given property in your component's state. Calls to linkState() with the same state property are cached, so there is no performance penalty.  Here is the previous example rewritten using _Linked State_:

```js
class Foo extends Component {
	render({ }, { text }) {
		return <input value={text} onInput={this.linkState('text')} />;
	}
}
```

Simple and effective. It handles linking state from any input type, or an optional second parameter can be used to explicitly provide a keypath to the new state value.

## Examples

Here is a somewhat verbose Preact `<Link>` component:

```js
class Link extends Component {
	render(props, state) {
		return <a href={ props.href }>{ props.children }</a>;
	}
}
```

Since this is ES6/ES2015, we can further simplify:

```js
class Link extends Component {
	render({ href, children }) {
		return <a {...{ href, children }} />;
	}
}

// or, for wide-open props support:
class Link extends Component {
	render(props) {
		return <a {...props} />;
	}
}

// or, as a stateless functional component:
const Link = ({ children, ...props }) => (
	<a {...props}>{ children }</a>
);
```

## Extensions

It is likely that some projects based on Preact would wish to extend Component with great new functionality.

Perhaps automatic connection to stores for a Flux-like architecture, or mixed-in context bindings to make it feel more like `React.createClass()`.  Just use ES2015 inheritance:

```js
class BoundComponent extends Component {
	constructor(props) {
		super(props);
		this.bind();
	}
	bind() {
		this.binds = {};
		for (let i in this) {
			this.binds[i] = this[i].bind(this);
		}
	}
}

// example usage
class Link extends BoundComponent {
	click() {
		open(this.href);
	}
	render() {
		let { click } = this.binds;
		return <span onclick={ click }>{ children }</span>;
	}
}
```

The possibilities are pretty endless here. You could even add support for rudimentary mixins:

```js
class MixedComponent extends Component {
	constructor() {
		super();
		(this.mixins || []).forEach( m => Object.assign(this, m) );
	}
}
```

## License

MIT

[ES6 Classes]: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
[hyperscript]: https://github.com/dominictarr/hyperscript
[preact-boilerplate]: https://github.com/developit/preact-boilerplate
[lifecycle methods]: https://facebook.github.io/react/docs/component-specs.html


if (!component.prevContext) component.prevContext = extend({}, component.context);

hook(component, 'componentWillReceiveProps', props, component.context);

keep keep keep keep replace keep keep keep keep

if (!component.prevContext) component.prevContext = clone(component.context);


import { empty, isString, isFunction, delve } from './util';

/** Create an Event handler function that sets a given state property.
 *	@param {Component} component	The component whose state should be updated
 *	@param {string} key				A dot-notated key path to update in the component's state
 *	@param {string} eventPath		A dot-notated key path to the value that should be retrieved from the Event or component
 *	@returns {function} linkedStateHandler
 *	@private
 */
export function createLinkedState(component, key, eventPath) {
	let path = key.split('.'),
		p0 = path[0],
		len = path.length;
	return function(e) {
		let t = this,
			s = component.state,
			obj = s,
			v, i;
		if (isString(eventPath)) {
			v = delve(e, eventPath);
			if (empty(v) && (t=t._component)) {
				v = delve(t, eventPath);
			}
		}
		else {
			v = (t.nodeName+t.type).match(/^input(checkbox|radio)$/i) ? t.checked : t.value;
		}
		if (isFunction(v)) v = v.call(t);
		if (len>1) {
			for (i=0; i<len-1; i++) {
				obj = obj[path[i]] || (obj[path[i]] = {});
			}
			obj[path[i]] = v;
			v = s[p0];
		}
		component.setState({ [p0]: v });
	};
}


import { isFunction, isString, styleObjToCss, hashToClassName } from './util';

/** Global options
 *	@public
 *	@namespace options {Object}
 */
export default {

	/** If `true`, `prop` changes trigger synchronous component updates.
	 *	@boolean
	 */
	syncComponentUpdates: true,

	/** Processes all created VNodes.
	 *	@param {VNode} vnode	A newly-created VNode to normalize/process
	 *	@protected
	 */
	vnode(n) {
		let attrs = n.attributes;
		if (!attrs || isFunction(n.nodeName)) return;

		// normalize className to class.
		let p = attrs.className;
		if (p) attrs['class'] = p;
		delete attrs.className;

		normalize(attrs, 'class', hashToClassName);
		normalize(attrs, 'style', styleObjToCss);
	}
};

function normalize(obj, prop, fn) {
	let v = obj[prop];
	if (v && !isString(v)) {
		obj[prop] = fn(v);
	}
}


/** Virtual DOM Node */
export default function VNode(nodeName, attributes, children) {
	/** @type {string|function} */
	this.nodeName = nodeName;

	/** @type {object<string>|undefined} */
	this.attributes = attributes;

	/** @type {array<VNode>|undefined} */
	this.children = children;
}


if (!component.prevProps) component.prevProps = extend({}, component.props);

keep keep keep replace keep keep keep keep keep

if (!component.prevProps) component.prevProps = clone(component.props);

import { extend, isFunction, isString } from '../util';
import { isFunctionalComponent } from './functional-component';

/** Check if two nodes are equivalent.
 *	@param {Element} node
 *	@param {VNode} vnode
 *	@private
 */
export function isSameNodeType(node, vnode) {
	if (node.nodeType===3) return isString(vnode);
	if (isFunctionalComponent(vnode)) return true;
	let nodeName = vnode.nodeName;
	if (isFunction(nodeName)) return node._componentConstructor===nodeName;
	return node.nodeName.toLowerCase()===nodeName;
}

/** Reconstruct Component-style `props` from a VNode
 *	@todo: determine if it would be acceptible to drop the extend() clone here for speed
 *	@private
 */
export function getNodeProps(vnode) {
	let props = extend({}, vnode.attributes);
	if (vnode.children) {
		props.children = vnode.children;
	}
	return props;
}


import { isFunction } from './util';

/** Invoke a "hook" method with arguments if it exists.
 *	@private
 */
export function hook(obj, name, ...args) {
	let fn = obj[name];
	if (fn && isFunction(fn)) return fn.apply(obj, args);
}

/** Invoke hook() on a component and child components (recursively)
 *	@private
 */
export function deepHook(obj, ...args) {
	do {
		hook(obj, ...args);
	} while ((obj=obj._component));
}


import build from './vdom/build';
import { deepHook } from './hooks';

/** Render JSX into a `parent` Element.
 *	@param {VNode} vnode		A (JSX) VNode to render
 *	@param {Element} parent		DOM element to render into
 *	@param {Element} [merge]	Attempt to re-use an existing DOM tree rooted at `merge`
 *	@public
 *
 *	@example
 *	// render a div into :
 *	render(
hello!
, document.body);
 *
 *	@example
 *	// render a "Thing" component into #foo:
 *	const Thing = ({ name }) => { name };
 *	render(<Thing name="one" />, document.querySelector('#foo'));
 */
export default function render(vnode, parent, merge) {
	let existing = merge && merge._component && merge._componentConstructor===vnode.nodeName,
		built = build(merge, vnode),
		c = !existing && built._component;

	if (c) deepHook(c, 'componentWillMount');

	if (built.parentNode!==parent) {
		parent.appendChild(built);
	}

	if (c) deepHook(c, 'componentDidMount');

	return built;
}


import { extend, isFunction, isString } from '../util';

import { isFunctionalComponent } from './functional-component';

import { clone, toLowerCase, isFunction, isString } from '../util';


import { h, render, Component } from '../../src/preact';
/** @jsx h */

describe('context', () => {
	let scratch;

	before( () => {
		scratch = document.createElement('div');
		(document.body || document.documentElement).appendChild(scratch);
	});

	beforeEach( () => {
		scratch.innerHTML = '';
	});

	after( () => {
		scratch.parentNode.removeChild(scratch);
		scratch = null;
	});

	it('should pass context to grandchildren', () => {
		const CONTEXT = { a:'a' };
		const PROPS = { b:'b' };

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return
<Inner {...props} />
;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {});
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {});
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);
	});

	it('should pass context to direct children', () => {
		const CONTEXT = { a:'a' };
		const PROPS = { b:'b' };

		class Outer extends Component {
			getChildContext() {
				return CONTEXT;
			}
			render(props) {
				return <Inner {...props} />;
			}
		}
		sinon.spy(Outer.prototype, 'getChildContext');

		class Inner extends Component {
			shouldComponentUpdate() { return true; }
			componentWillReceiveProps() {}
			componentWillUpdate() {}
			componentDidUpdate() {}
			render(props, state, context) {
				return
{ context && context.a }
;
			}
		}
		sinon.spy(Inner.prototype, 'shouldComponentUpdate');
		sinon.spy(Inner.prototype, 'componentWillReceiveProps');
		sinon.spy(Inner.prototype, 'componentWillUpdate');
		sinon.spy(Inner.prototype, 'componentDidUpdate');
		sinon.spy(Inner.prototype, 'render');

		render(<Outer />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledOnce;

		// initial render does not invoke anything but render():
		expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);

		CONTEXT.foo = 'bar';
		render(<Outer {...PROPS} />, scratch, scratch.lastChild);

		expect(Outer.prototype.getChildContext).to.have.been.calledTwice;

		expect(Inner.prototype.shouldComponentUpdate).to.have.been.calledOnce.and.calledWith(PROPS, {}, CONTEXT);
		expect(Inner.prototype.componentWillReceiveProps).to.have.been.calledWith(PROPS, CONTEXT);
		expect(Inner.prototype.componentWillUpdate).to.have.been.calledWith(PROPS, {});
		expect(Inner.prototype.componentDidUpdate).to.have.been.calledWith({}, {});
		expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);

		// make sure render() could make use of context.a
		expect(Inner.prototype.render).to.have.returned(sinon.match({ children:['a'] }));
	});
});


import { ATTR_KEY } from '../constants';
import { hasOwnProperty, memoize } from '../util';
import { setAccessor, ensureNodeData } from '.';

/** DOM node pool, keyed on nodeName. */

let nodes = {};

let normalizeName = memoize(name => name.toUpperCase());

export function collectNode(node) {
	cleanNode(node);
	let name = normalizeName(node.nodeName),
		list = nodes[name];
	if (list) list.push(node);
	else nodes[name] = [node];
}

export function createNode(nodeName) {
	let name = normalizeName(nodeName),
		list = nodes[name],
		node = list && list.pop() || document.createElement(nodeName);
	ensureNodeData(node);
	return node;
}

function cleanNode(node) {
	if (node.parentNode) node.parentNode.removeChild(node);

	if (node.nodeType===3) return;

	let attrs = node[ATTR_KEY];
	for (let i in attrs) {
		if (hasOwnProperty.call(attrs, i)) {
			setAccessor(node, i, null, attrs[i]);
		}
	}

	node[ATTR_KEY] = node._component = node._componentConstructor = null;

	// if (node.childNodes.length>0) {
	// 	console.warn(`Warning: Recycler collecting <${node.nodeName}> with ${node.childNodes.length} children.`);
	// 	toArray(node.childNodes).forEach(recycler.collect);
	// }
}


if (node.nodeType===3) return isString(vnode);

if (isFunctionalComponent(vnode)) return true;

if (isFunction(nodeName)) return node._componentConstructor===nodeName;

keep keep keep keep replace keep keep keep

import { SYNC_RENDER, DOM_RENDER, NO_RENDER, EMPTY, EMPTY_BASE } from '../constants';
import options from '../options';
import { extend, isFunction } from '../util';
import { hook, deepHook } from '../hooks';
import { enqueueRender } from '../render-queue';
import { getNodeProps } from '.';
import build from './build';
import { createComponent, collectComponent } from './component-recycler';
import { isFunctionalComponent, buildFunctionalComponent } from './functional-component';

/** Mark component as dirty and queue up a render.
 *	@param {Component} component
 *	@private
 */
export function triggerComponentRender(component) {
	if (!component._dirty) {
		component._dirty = true;
		enqueueRender(component);
	}
}

/** Set a component's `props` (generally derived from JSX attributes).
 *	@param {Object} props
 *	@param {Object} [opts]
 *	@param {boolean} [opts.renderSync=false]	If `true` and {@link options.syncComponentUpdates} is `true`, triggers synchronous rendering.
 *	@param {boolean} [opts.render=true]			If `false`, no render will be triggered.
 */
export function setComponentProps(component, props, opts, context) {
	let d = component._disableRendering;
	component._disableRendering = true;

	opts = opts || EMPTY;

	if (context) {
		if (!component.prevContext) component.prevContext = extend({}, component.context);
		component.context = context;
	}

	hook(component, 'componentWillReceiveProps', props, component.context);

	if (!component.prevProps) component.prevProps = extend({}, component.props);
	component.props = props;

	component._disableRendering = d;

	if (opts.render!==false) {
		if (opts.renderSync || options.syncComponentUpdates) {
			renderComponent(component);
		}
		else {
			triggerComponentRender(component);
		}
	}
}

/** Render a Component, triggering necessary lifecycle events and taking High-Order Components into account.
 *	@param {Component} component
 *	@param {Object} [opts]
 *	@param {boolean} [opts.build=false]		If `true`, component will build and store a DOM node if not already associated with one.
 *	@private
 */
export function renderComponent(component, opts) {
	if (component._disableRendering) return;

	let skip, rendered,
		props = component.props,
		state = component.state,
		context = component.context,
		previousProps = component.prevProps || props,
		previousState = component.prevState || state,
		previousContext = component.prevContext || context,
		isUpdate = component.base;

	if (isUpdate) {
		component.props = previousProps;
		component.state = previousState;
		component.context = previousContext;
		if (hook(component, 'shouldComponentUpdate', props, state, context)===false) {
			skip = true;
		}
		else {
			hook(component, 'componentWillUpdate', props, state, context);
		}
		component.props = props;
		component.state = state;
		component.context = context;
	}

	component.prevProps = component.prevState = component.prevContext = null;
	component._dirty = false;

	if (!skip) {
		rendered = hook(component, 'render', props, state, context);

		let childComponent = rendered && rendered.nodeName,
			childContext = component.getChildContext ? component.getChildContext() : context,
			base;

		if (isFunction(childComponent) && childComponent.prototype.render) {
			// set up high order component link

			let inst = component._component;
			if (inst && inst.constructor!==childComponent) {
				unmountComponent(inst.base, inst, false);
				inst = null;
			}

			let childProps = getNodeProps(rendered);

			if (inst) {
				setComponentProps(inst, childProps, SYNC_RENDER, childContext);
			}
			else {
				inst = createComponent(childComponent, childProps, childContext);
				inst._parentComponent = component;
				component._component = inst;
				if (component.base) deepHook(inst, 'componentWillMount');
				setComponentProps(inst, childProps, NO_RENDER, childContext);
				renderComponent(inst, DOM_RENDER);
				if (component.base) deepHook(inst, 'componentDidMount');
			}

			base = inst.base;
		}
		else {
			// destroy high order component link
			if (component._component) {
				unmountComponent(component.base, component._component);
			}
			component._component = null;

			if (component.base || (opts && opts.build)) {
				base = build(component.base, rendered || EMPTY_BASE, childContext);
			}
		}

		if (component.base && base!==component.base) {
			let p = component.base.parentNode;
			if (p) p.replaceChild(base, component.base);
		}

		component.base = base;
		if (base) {
			base._component = component;
			base._componentConstructor = component.constructor;
		}

		if (isUpdate) {
			hook(component, 'componentDidUpdate', previousProps, previousState, previousContext);
		}
	}

	let cb = component._renderCallbacks;
	if (cb) {
		for (let i=cb.length; i--; ) cb[i]();
		cb.length = 0;
	}

	return rendered;
}

/** Apply the Component referenced by a VNode to the DOM.
 *	@param {Element} dom	The DOM node to mutate
 *	@param {VNode} vnode	A Component-referencing VNode
 *	@returns {Element} dom	The created/mutated element
 *	@private
 */
export function buildComponentFromVNode(dom, vnode, context) {
	let c = dom && dom._component;

	if (isFunctionalComponent(vnode)) {
		let p = build(dom, buildFunctionalComponent(vnode, context), context);
		p._componentConstructor = vnode.nodeName;
		return p;
	}

	let isOwner = c && dom._componentConstructor===vnode.nodeName;
	while (c && !isOwner && (c=c._parentComponent)) {
		isOwner = c.constructor===vnode.nodeName;
	}

	if (isOwner) {
		setComponentProps(c, getNodeProps(vnode), SYNC_RENDER, context);
	}
	else {
		if (c) {
			unmountComponent(dom, c);
			dom = null;
		}
		dom = createComponentFromVNode(vnode, dom, context);
	}

	return dom;
}

/** Instantiate and render a Component, given a VNode whose nodeName is a constructor.
 *	@param {VNode} vnode
 *	@private
 */
function createComponentFromVNode(vnode, dom, context) {
	let props = getNodeProps(vnode);
	let component = createComponent(vnode.nodeName, props, context);
	if (dom) component.base = dom;
	setComponentProps(component, props, NO_RENDER, context);
	renderComponent(component, DOM_RENDER);

	// let node = component.base;
	//if (!node._component) {
	//	node._component = component;
	//	node._componentConstructor = vnode.nodeName;
	//}

	return component.base;
}

/** Remove a component from the DOM and recycle it.
 *	@param {Element} dom			A DOM node from which to unmount the given Component
 *	@param {Component} component	The Component instance to unmount
 *	@private
 */
function unmountComponent(dom, component, remove) {
	// console.warn('unmounting mismatched component', component);

	hook(component, 'componentWillUnmount');
	if (remove!==false) {
		if (dom._component===component) {
			delete dom._component;
			delete dom._componentConstructor;
		}
		let base = component.base;
		if (base && base.parentNode) {
			base.parentNode.removeChild(base);
		}
	}
	component._parentComponent = null;
	hook(component, 'componentDidUnmount');
	collectComponent(component);
}


/node_modules
/npm-debug.log
.DS_Store
/dist
/_dev


let props = extend({}, vnode.attributes);


/*eslint no-var:0, object-shorthand:0 */

var coverage = String(process.env.COVERAGE) === 'true',
	ci = String(process.env.CI).match(/^(1|true)$/gi),
	pullRequest = !String(process.env.TRAVIS_PULL_REQUEST).match(
		/^(0|false|undefined)$/gi
	),
	masterBranch = String(process.env.TRAVIS_BRANCH).match(/^master$/gi),
	sauceLabs = ci && !pullRequest && masterBranch,
	performance = !coverage && String(process.env.PERFORMANCE) !== 'false',
	webpack = require('webpack'),
	path = require('path');

var sauceLabsLaunchers = {
	sl_chrome: {
		base: 'SauceLabs',
		browserName: 'chrome',
		platform: 'Windows 10'
	},
	sl_firefox: {
		base: 'SauceLabs',
		browserName: 'firefox',
		platform: 'Windows 10'
	},
	// TODO: Safari always fails and disconnects before any tests are executed.
	// This seems to be an issue with Saucelabs and they're actively investigating
	// that (see: https://mobile.twitter.com/bromann/status/1136323458328084482).
	// We'll disable Safari for now until that's resolved.
	// sl_safari: {
	// 	base: 'SauceLabs',
	// 	browserName: 'Safari',
	// 	version: '11',
	// 	platform: 'OS X 10.13'
	// },
	sl_edge: {
		base: 'SauceLabs',
		browserName: 'MicrosoftEdge',
		platform: 'Windows 10'
	},
	sl_ie_11: {
		base: 'SauceLabs',
		browserName: 'internet explorer',
		version: '11.0',
		platform: 'Windows 7'
	}
};

var localLaunchers = {
	ChromeNoSandboxHeadless: {
		base: 'Chrome',
		flags: [
			'--no-sandbox',
			'--disable-setuid-sandbox',
			// See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
			'--headless',
			'--disable-gpu',
			'--no-gpu',
			// Without a remote debugging port, Google Chrome exits immediately.
			'--remote-debugging-port=9333'
		]
	}
};

module.exports = function(config) {
	const watchMode = config.singleRun;

	config.set({
		browsers: sauceLabs
			? Object.keys(sauceLabsLaunchers)
			: Object.keys(localLaunchers),

		frameworks: ['source-map-support', 'mocha', 'chai-sinon'],

		reporters: ['mocha'].concat(
			coverage ? 'coverage' : [],
			sauceLabs ? 'saucelabs' : []
		),

		coverageReporter: {
			dir: path.join(__dirname, 'coverage'),
			reporters: [
				{ type: 'text-summary' },
				{ type: 'html' },
				{ type: 'lcovonly', subdir: '.', file: 'lcov.info' }
			]
		},

		mochaReporter: {
			showDiff: true
		},

		browserLogOptions: { terminal: true },
		browserConsoleLogOptions: { terminal: true },

		browserNoActivityTimeout: 5 * 60 * 1000,

		// Use only two browsers concurrently, works better with open source Sauce Labs remote testing
		concurrency: 2,

		captureTimeout: 0,

		sauceLabs: {
			build: `CI #${process.env.TRAVIS_BUILD_NUMBER} (${process.env.TRAVIS_BUILD_ID})`,
			tunnelIdentifier:
				process.env.TRAVIS_JOB_NUMBER ||
				`local${require('./package.json').version}`,
			connectLocationForSERelay: 'localhost',
			connectPortForSERelay: 4445,
			startConnect: false
		},

		customLaunchers: sauceLabs ? sauceLabsLaunchers : localLaunchers,

		files: [
			{ pattern: 'test/polyfills.js', watched: false },
			{
				pattern:
					config.grep ||
					'{debug,hooks,compat,test-utils,}/test/{browser,shared}/**/*.test.js',
				watched: false
			}
		],

		preprocessors: {
			'{debug,hooks,compat,test-utils,}/test/**/*': ['webpack', 'sourcemap']
		},

		webpack: {
			output: {
				filename: '[name].js'
			},
			mode: 'development',
			devtool: 'inline-source-map',
			module: {
				noParse: [/benchmark\.js$/],

				/* Transpile source and test files */
				rules: [
					// Special case for sinon.js which ships ES2015+ code in their
					// esm bundle
					{
						test: /node_modules\/sinon\/.*\.jsx?$/,
						loader: 'babel-loader'
					},

					{
						test: /\.jsx?$/,
						exclude: /node_modules/,
						loader: 'babel-loader',
						options: {
							plugins: [
								coverage && ['istanbul', { include: '**/dist/**/*.js' }]
							].filter(Boolean)
						}
					}
				]
			},
			resolve: {
				// The React DevTools integration requires preact as a module
				// rather than referencing source files inside the module
				// directly
				alias: {
					'preact/debug': path.join(__dirname, './debug/'),
					'preact/devtools': path.join(__dirname, './devtools/'),
					'preact/compat': path.join(__dirname, './compat/'),
					'preact/hooks': path.join(__dirname, './hooks/'),
					'preact/test-utils': path.join(__dirname, './test-utils/'),
					preact: path.join(__dirname, '')
				}
			},
			plugins: [
				new webpack.DefinePlugin({
					coverage: coverage,
					NODE_ENV: JSON.stringify(process.env.NODE_ENV || ''),
					ENABLE_PERFORMANCE: performance
				})
			],
			performance: {
				hints: false
			}
		},

		webpackMiddleware: {
			noInfo: true,
			stats: 'errors-only'
		}
	});
};


sudo: false

notifications:
  email: false

language: node_js

node_js:
  - "10"

cache:
  directories:
    - node_modules

# Make chrome browser available for testing
services:
  - xvfb

install:
  - npm ci

addons:
  sauce_connect: true

script:
  - COVERAGE=true FLAKEY=false npm run test
  - ./node_modules/coveralls/bin/coveralls.js < ./coverage/lcov.info;

after_success: sizereport --config