} else if (newState === STATE_NEEDS_UPDATE) {

/*
 * Copyright (c) 2013 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, it, xit, expect, beforeEach, afterEach, waits, waitsFor, runs, $, waitsForDone, spyOn, jasmine */
/*unittests: Install Extension Dialog*/

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

    var SpecRunnerUtils = require("spec/SpecRunnerUtils"),
        KeyEvent        = require("utils/KeyEvent"),
        NativeApp       = require("utils/NativeApp"),
        Strings         = require("strings");

    describe("Install Extension Dialog", function () {
        var testWindow, dialog, fields, goodInstaller, badInstaller, InstallExtensionDialog, closed,
            url = "http://brackets.io/extensions/myextension.zip";

        beforeEach(function () {
            if (!testWindow) {
                SpecRunnerUtils.createTestWindowAndRun(this, function (w) {
                    testWindow = w;
                });
            }
        });

        afterEach(function () {
            runs(function () {
                if (dialog) {
                    dialog._close();
                } else {
                    closed = true;
                }
            });
            waitsFor(function () { return closed; }, "dialog closing");
            runs(function () {
                fields = null;
                closed = false;
                dialog = null;
            });
        });

        var successfulResult = {
            installationStatus: "INSTALLED"
        };

        function makeInstaller(succeed, deferred) {
            var installer = {
                install: function () {
                    if (!deferred) {
                        deferred = new $.Deferred();
                        if (succeed) {
                            deferred.resolve(successfulResult);
                        } else {
                            deferred.reject();
                        }
                    }
                    return deferred.promise();
                },
                cancel: function () {
                }
            };
            spyOn(installer, "install").andCallThrough();
            spyOn(installer, "cancel");
            dialog._installer = installer;
            return installer;
        }

        describe("when user-initiated", function () {

            beforeEach(function () {
                closed = false;
                dialog = new testWindow.brackets.test.InstallExtensionDialog._Dialog();
                dialog.show()
                    .always(function () {
                        closed = true;
                    });
                fields = {
                    $dlg: dialog.$dlg,
                    $okButton: dialog.$okButton,
                    $cancelButton: dialog.$cancelButton,
                    $url: dialog.$url,
                    $browseExtensionsButton: dialog.$browseExtensionsButton
                };
            });

            function setUrl() {
                fields.$url
                    .val(url)
                    .trigger("input");
            }

            it("should open the dialog", function () {
                expect(fields.$dlg[0]).not.toBeNull();
            });

            it("should have the install button disabled when dialog is first open due to empty url field", function () {
                expect(fields.$okButton.prop("disabled")).toBe(true);
            });

            it("should not start install if enter hit while url field is empty", function () {
                var installer = makeInstaller(true);
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(installer.install).not.toHaveBeenCalled();
            });

            it("should close the dialog when Cancel button clicked before entering url", function () {
                fields.$cancelButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            // bootstrap-modal handles Esc on keyup
            it("should close the dialog when Esc pressed before entering url", function () {
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should enable the install button when the url field is nonempty", function () {
                setUrl();
                expect(fields.$okButton.prop("disabled")).toBe(false);
            });

            it("should re-disable the install button if the url field becomes nonempty and then empty", function () {
                setUrl();
                fields.$url
                    .val("")
                    .trigger("input");
                expect(fields.$okButton.prop("disabled")).toBe(true);
            });

            it("should close the dialog when Cancel button clicked after entering url", function () {
                setUrl();
                fields.$cancelButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog when Esc pressed after entering url", function () {
                setUrl();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should start the installer with the given URL when Install button clicked", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                expect(installer.install).toHaveBeenCalledWith(url);
            });

            it("should start the installer with the given URL when Enter is pressed", function () {
                var installer = makeInstaller(true);
                setUrl();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(installer.install).toHaveBeenCalledWith(url);
            });

            it("should disable the ok button while installing", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                expect(fields.$okButton.prop("disabled")).toBe(true);
                deferred.resolve(successfulResult);
            });

            it("should hide the url field and 'browse extensions' button while installing", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                expect(fields.$url.is(":visible")).toBe(false);
                expect(fields.$browseExtensionsButton.is(":visible")).toBe(false);
                deferred.resolve(successfulResult);
            });

            it("should do nothing if Enter pressed while installing", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                installer.install.reset();

                var modSpy = jasmine.createSpy("modSpy");
                fields.$dlg.on("DOMSubtreeModified", modSpy);
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(modSpy).not.toHaveBeenCalled();
                expect(installer.install).not.toHaveBeenCalled();
                expect(installer.cancel).not.toHaveBeenCalled();

                deferred.resolve(successfulResult);
            });

            it("should cancel installation if cancel clicked while installing", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                expect(installer.cancel).toHaveBeenCalled();
                deferred.resolve(successfulResult);
            });

            it("should cancel installation if Esc pressed while installing", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(installer.cancel).toHaveBeenCalled();
                deferred.resolve(successfulResult);
            });

            it("should disable the cancel button while cancellation is processed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                expect(fields.$cancelButton.prop("disabled")).toBe(true);
                deferred.reject("CANCELED");
            });

            it("should ignore the Esc key while cancellation is processed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(true);
                deferred.reject("CANCELED");
            });

            it("should ignore the Enter key while cancellation is processed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(true);
                deferred.reject("CANCELED");
            });

            it("should re-enable the ok button and hide cancel button after install succeeds", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve(successfulResult);
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.is(":visible")).toBe(false);
            });

            it("should re-enable the ok button and hide cancel button after install succeeds synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.is(":visible")).toBe(false);
            });

            it("should re-enable the ok button and hide cancel button after install fails", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.reject();
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.is(":visible")).toBe(false);
            });

            it("should re-enable the ok button and hide cancel button after install fails synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.is(":visible")).toBe(false);
            });

            it("should re-enable the ok button and hide cancel button after install finishes canceling", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                deferred.reject("CANCELED");
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.is(":visible")).toBe(false);
            });

            it("should close the dialog if ok button clicked after install succeeds", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve(successfulResult);
                fields.$okButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if ok button clicked after install succeeds synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                fields.$okButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Enter pressed after install succeeds", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve(successfulResult);
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Enter pressed after install succeeds synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Esc pressed after install succeeds", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve(successfulResult);
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Esc pressed after install succeeds synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if ok button clicked after install fails", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.reject();
                fields.$okButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if ok button clicked after install fails synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                fields.$okButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Enter pressed after install fails", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.reject();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Enter pressed after install fails synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Esc pressed after install fails", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.reject();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Esc pressed after install fails synchronously", function () {
                var installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if ok button clicked after install finishes canceling", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                deferred.reject("CANCELED");
                fields.$okButton.click();
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Enter pressed after install finishes canceling", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                deferred.reject("CANCELED");
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should close the dialog if Esc pressed after install finishes canceling", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(true);
                setUrl();
                fields.$okButton.click();
                fields.$cancelButton.click();
                deferred.reject("CANCELED");
                SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should time out and re-enable close button if cancel doesn't complete quickly", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    expect(fields.$okButton.prop("disabled")).toBe(false);
                    deferred.reject("CANCELED");
                });
            });

            it("should close when clicking Close button after timing out if cancel doesn't complete quickly", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    fields.$okButton.click();
                    expect(fields.$dlg.is(":visible")).toBe(false);
                    deferred.reject("CANCELED");
                });
            });

            it("should close when hitting Enter after timing out if cancel doesn't complete quickly", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    fields.$okButton.click();
                    SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_RETURN, "keydown", fields.$dlg[0]);
                    deferred.reject("CANCELED");
                });
            });

            it("should close when hitting Esc after timing out if cancel doesn't complete quickly", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    fields.$okButton.click();
                    SpecRunnerUtils.simulateKeyEvent(KeyEvent.DOM_VK_ESCAPE, "keyup", fields.$dlg[0]);
                    deferred.reject("CANCELED");
                });
            });

            it("should keep close button enabled and not throw an exception if install succeeds after cancelation", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    deferred.resolve(successfulResult);
                    expect(fields.$okButton.prop("disabled")).toBe(false);
                });
            });

            it("should keep close button enabled and not throw an exception if install fails after cancelation", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    deferred.reject();
                    expect(fields.$okButton.prop("disabled")).toBe(false);
                });
            });

            it("should keep close button enabled and not throw an exception if install cancelation completes after cancelation", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                runs(function () {
                    setUrl();
                    dialog._cancelTimeout = 50;
                    fields.$okButton.click();
                    fields.$cancelButton.click();
                });
                waits(100);
                runs(function () {
                    deferred.reject("CANCELED");
                    expect(fields.$okButton.prop("disabled")).toBe(false);
                });
            });

            it("should open the extension list wiki page when the user clicks on the Browse Extensions button", function () {
                var NativeApp = testWindow.brackets.getModule("utils/NativeApp");
                spyOn(NativeApp, "openURLInDefaultBrowser");
                fields.$browseExtensionsButton.click();
                expect(NativeApp.openURLInDefaultBrowser).toHaveBeenCalledWith("https://github.com/adobe/brackets/wiki/Brackets-Extensions");
            });

            it("should display a warning message if the extension is already installed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve({
                    installationStatus: "ALREADY_INSTALLED"
                });
                expect(fields.$okButton.text()).toEqual(Strings.OVERWRITE);
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.text()).toEqual(Strings.CANCEL);
                expect(fields.$cancelButton.prop("disabled")).toBe(false);
                expect(fields.$browseExtensionsButton.is(":visible")).toBe(false);
                expect(fields.$dlg.find(".message").text()).toBe(Strings.EXTENSION_ALREADY_INSTALLED);
            });

            it("should display a warning message if the same version is already installed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve({
                    installationStatus: "SAME_VERSION"
                });
                expect(fields.$okButton.text()).toEqual(Strings.OVERWRITE);
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.text()).toEqual(Strings.CANCEL);
                expect(fields.$cancelButton.prop("disabled")).toBe(false);
                expect(fields.$browseExtensionsButton.is(":visible")).toBe(false);
                expect(fields.$dlg.find(".message").text()).toBe(Strings.EXTENSION_SAME_VERSION);
            });

            it("should display a warning message if an older version is already installed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                deferred.resolve({
                    installationStatus: "OLDER_VERSION",
                    installedVersion: "1.0.0",
                    metadata: {
                        version: "0.9.0"
                    }
                });
                expect(fields.$okButton.text()).toEqual(Strings.OVERWRITE);
                expect(fields.$okButton.prop("disabled")).toBe(false);
                expect(fields.$cancelButton.text()).toEqual(Strings.CANCEL);
                expect(fields.$cancelButton.prop("disabled")).toBe(false);
                expect(fields.$browseExtensionsButton.is(":visible")).toBe(false);
                expect(fields.$dlg.find(".message").text().indexOf("0.9.0")).not.toEqual(-1);
            });

            it("should delete the downloaded file if the warning message is cancelled", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                var packageFilename = "/path/to/downloaded/package.zip";
                deferred.resolve({
                    installationStatus: "ALREADY_INSTALLED",
                    localPath: packageFilename
                });
                spyOn(testWindow.brackets.fs, "unlink");
                fields.$cancelButton.click();
                expect(testWindow.brackets.fs.unlink).toHaveBeenCalledWith(packageFilename, jasmine.any(Function));
                expect(fields.$dlg.is(":visible")).toBe(false);
            });

            it("should pass the installation result if the warning message is confirmed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                var packageFilename = "/path/to/downloaded/package.zip";
                deferred.resolve({
                    installationStatus: "ALREADY_INSTALLED",
                    localPath: packageFilename
                });
                spyOn(testWindow.brackets.fs, "unlink");
                fields.$okButton.click();
                expect(testWindow.brackets.fs.unlink).not.toHaveBeenCalled();
                expect(fields.$dlg.is(":visible")).toBe(false);
                var dialogDone = false;
                dialog._dialogDeferred.done(function (result) {
                    dialogDone = true;
                    expect(result.installationStatus).toBe("ALREADY_INSTALLED");
                });
                expect(dialogDone).toBe(true);
            });

            it("should pass installation result when update is needed", function () {
                var deferred = new $.Deferred(),
                    installer = makeInstaller(null, deferred);
                setUrl();
                fields.$okButton.click();
                var packageFilename = "/path/to/downloaded/package.zip";
                deferred.resolve({
                    installationStatus: "NEEDS_UPDATE",
                    localPath: packageFilename
                });
                expect(fields.$dlg.is(":visible")).toBe(false);
                var dialogDone = false;
                dialog._dialogDeferred.done(function (result) {
                    dialogDone = true;
                    expect(result.installationStatus).toBe("NEEDS_UPDATE");
                });
                expect(dialogDone).toBe(true);
            });
        });

        describe("when initiated from Extension Manager", function () {
            var url = "https://my.repository.io/my-extension.zip", installer;
            beforeEach(function () {
                closed = false;
            });

            it("should immediately start installing from the given url, and in the correct UI state", function () {
                var deferred = new $.Deferred();
                dialog = new testWindow.brackets.test.InstallExtensionDialog._Dialog();
                installer = makeInstaller(null, deferred);
                dialog.show(url)
                    .always(function () {
                        closed = true;
                    });
                fields = {
                    $dlg: dialog.$dlg,
                    $okButton: dialog.$okButton,
                    $cancelButton: dialog.$cancelButton,
                    $url: dialog.$url
                };
                expect(installer.install).toHaveBeenCalledWith(url);
                expect(fields.$okButton.prop("disabled")).toBe(true);
                expect(fields.$url.is(":visible")).toBe(false);
                deferred.resolve(successfulResult);
            });
        });

        // THIS MUST BE THE LAST SPEC IN THE SUITE
        it("should close the test window", function () {
            SpecRunnerUtils.closeTestWindow();
        });
    });
});


} else if (newState === STATE_INSTALL_FAILED) {

/*
 * 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, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
/*global define, $, XMLHttpRequest, window */

/**
 * RemoteAgent defines and provides an interface for custom remote functions
 * loaded from RemoteFunctions. Remote commands are executed via
 * `call(name, varargs)`.
 *
 * Remote events are dispatched as events on this object.
 */
define(function RemoteAgent(require, exports, module) {
    "use strict";

    var $exports = $(exports);

    var LiveDevelopment = require("LiveDevelopment/LiveDevelopment"),
        Inspector       = require("LiveDevelopment/Inspector/Inspector"),
        RemoteFunctions = require("text!LiveDevelopment/Agents/RemoteFunctions.js");

    var _load; // deferred load
    var _objectId; // the object id of the remote object
    var _intervalId; // interval used to send keepAlive events

    // WebInspector Event: DOM.attributeModified
    function _onAttributeModified(event, res) {
        // res = {nodeId, name, value}
        var matches = /^data-ld-(.*)/.exec(res.name);
        if (matches) {
            $exports.triggerHandler(matches[1], res);
        }
    }

    /** Call a remote function
     * The parameters are passed on to the remote functions. Nodes are resolved
     * and sent as objectIds.
     * @param {string} function name
     */
    function call(method, varargs) {
        console.assert(_objectId, "Attempted to call remote method without objectId set.");
        var args = Array.prototype.slice.call(arguments, 1);

        // if the last argument is a function it is the callback function
        var callback;
        if (typeof args[args.length - 1] === "function") {
            callback = args.pop();
        }

        // Resolve node parameters
        var i;
        for (i in args) {
            if (args[i].nodeId) {
                args[i] = args[i].resolve();
            }
        }
        $.when.apply(undefined, args).done(function onResolvedAllNodes() {
            var i, arg, params = [];
            for (i in arguments) {
                arg = args[i];
                if (arg.objectId) {
                    params.push({objectId: arg.objectId});
                } else {
                    params.push({value: arg});
                }
            }
            Inspector.Runtime.callFunctionOn(_objectId, "_LD." + method, params, undefined, callback);
        });
    }

    function _stopKeepAliveInterval() {
        if (_intervalId) {
            window.clearInterval(_intervalId);
            _intervalId = null;
        }
    }

    function _startKeepAliveInterval() {
        _stopKeepAliveInterval();

        _intervalId = window.setInterval(function () {
            call("keepAlive");
        }, 1000);
    }

    /**
     * @private
     * Cancel the keepAlive interval if the page reloads
     */
    function _onFrameStartedLoading(event, res) {
        _stopKeepAliveInterval();
    }

    // WebInspector Event: Page.loadEventFired
    function _onLoadEventFired(event, res) {
        // res = {timestamp}
        var command = "window._LD=" + RemoteFunctions + "(" + LiveDevelopment.config.experimental + ")";

        Inspector.Runtime.evaluate(command, function onEvaluate(response) {
            if (response.error || response.wasThrown) {
                _load.reject(null, response.error);
            } else {
                _objectId = response.result.objectId;
                _load.resolve();

                _startKeepAliveInterval();
            }
        });
    }

    /** Initialize the agent */
    function load() {
        _load = new $.Deferred();
        $(Inspector.Page).on("loadEventFired.RemoteAgent", _onLoadEventFired);
        $(Inspector.Page).on("frameStartedLoading.RemoteAgent", _onFrameStartedLoading);
        $(Inspector.DOM).on("attributeModified.RemoteAgent", _onAttributeModified);

        return _load.promise();
    }

    /** Clean up */
    function unload() {
        $(Inspector.Page).off(".RemoteAgent");
        $(Inspector.DOM).off(".RemoteAgent");
        _stopKeepAliveInterval();
    }

    // Export public functions
    exports.call = call;
    exports.load = load;
    exports.unload = unload;
});


/*
 * 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, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
/*global define, $ */

/**
 * HTMLDocument manages a single HTML source document
 *
 * # EDITING
 *
 * Editing the document will cause the corresponding node to be updated
 * by calling `applyChanges` on the DOMAgent. This will only work for
 * altering text nodes and will break when attempting to change DOM elements
 * or inserting or deleting nodes.
 *
 * # HIGHLIGHTING
 *
 * HTMLDocument supports highlighting nodes from the HighlightAgent and
 * highlighting the DOMNode corresponding to the cursor position in the
 * editor.
 */
define(function HTMLDocumentModule(require, exports, module) {
    "use strict";

    var DocumentManager     = require("document/DocumentManager"),
        DOMAgent            = require("LiveDevelopment/Agents/DOMAgent"),
        HighlightAgent      = require("LiveDevelopment/Agents/HighlightAgent"),
        HTMLInstrumentation = require("language/HTMLInstrumentation"),
        Inspector           = require("LiveDevelopment/Inspector/Inspector"),
        LiveDevelopment     = require("LiveDevelopment/LiveDevelopment");

    /** Constructor
     *
     * @param Document the source document from Brackets
     */
    var HTMLDocument = function HTMLDocument(doc, editor) {
        this.doc = doc;
        if (!editor) {
            return;
        }
        this.editor = editor;
        this._instrumentationEnabled = false;

        this.onCursorActivity = this.onCursorActivity.bind(this);
        this.onDocumentSaved = this.onDocumentSaved.bind(this);

        $(this.editor).on("cursorActivity", this.onCursorActivity);
        $(DocumentManager).on("documentSaved", this.onDocumentSaved);

        // Experimental code
        if (LiveDevelopment.config.experimental) {
            // Used by highlight agent to highlight editor text as selected in browser
            this.onHighlight = this.onHighlight.bind(this);
            $(HighlightAgent).on("highlight", this.onHighlight);

            this.onChange = this.onChange.bind(this);
            $(this.editor).on("change", this.onChange);
        }
    };

    /**
     * Enable instrumented HTML
     * @param enabled {boolean}
     */
    HTMLDocument.prototype.setInstrumentationEnabled = function setInstrumentationEnabled(enabled) {
        if (enabled && !this._instrumentationEnabled) {
            HTMLInstrumentation.scanDocument(this.doc);
            HTMLInstrumentation._markText(this.editor);
        }

        this._instrumentationEnabled = enabled;
    };

    /**
     * Returns a JSON object with HTTP response overrides
     * @returns {{body: string}}
     */
    HTMLDocument.prototype.getResponseData = function getResponseData(enabled) {
        var body = (this._instrumentationEnabled) ?
                HTMLInstrumentation.generateInstrumentedHTML(this.doc) : this.doc.getText();

        return {
            body: body
        };
    };

    /** Close the document */
    HTMLDocument.prototype.close = function close() {
        if (!this.editor) {
            return;
        }

        $(this.editor).off("cursorActivity", this.onCursorActivity);
        $(DocumentManager).off("documentSaved", this.onDocumentSaved);

        // Experimental code
        if (LiveDevelopment.config.experimental) {
            $(HighlightAgent).off("highlight", this.onHighlight);
            this.onHighlight();

            $(this.editor).off("change", this.onChange);
        }
    };

    /** Event Handlers *******************************************************/

    /** Triggered on cursor activity by the editor */
    HTMLDocument.prototype.onCursorActivity = function onCursorActivity(event, editor) {
        if (!this.editor) {
            return;
        }
        var codeMirror = this.editor._codeMirror;
        if (Inspector.config.highlight) {
            var tagID = HTMLInstrumentation._getTagIDAtDocumentPos(
                this.editor,
                codeMirror.getCursor()
            );

            if (tagID === -1) {
                HighlightAgent.hide();
            } else {
                HighlightAgent.domElement(tagID);
            }
        }
    };

    /** Triggered on change by the editor */
    HTMLDocument.prototype.onChange = function onChange(event, editor, change) {
        if (!this.editor) {
            return;
        }
        var codeMirror = this.editor._codeMirror;
        while (change) {
            var from = codeMirror.indexFromPos(change.from);
            var to = codeMirror.indexFromPos(change.to);
            var text = change.text.join("\n");
            DOMAgent.applyChange(from, to, text);
            change = change.next;
        }
    };

    /** Triggered by the HighlightAgent to highlight a node in the editor */
    HTMLDocument.prototype.onHighlight = function onHighlight(event, node) {
        if (!node || !node.location || !this.editor) {
            if (this._highlight) {
                this._highlight.clear();
                delete this._highlight;
            }
            return;
        }
        var codeMirror = this.editor._codeMirror;
        var to, from = codeMirror.posFromIndex(node.location);
        if (node.closeLocation) {
            to = node.closeLocation + node.closeLength;
        } else {
            to = node.location + node.length;
        }
        to = codeMirror.posFromIndex(to);
        if (this._highlight) {
            this._highlight.clear();
        }
        this._highlight = codeMirror.markText(from, to, { className: "highlight" });
    };

    /** Triggered when a document is saved */
    HTMLDocument.prototype.onDocumentSaved = function onDocumentSaved(event, doc) {
        if (doc === this.doc) {
            HTMLInstrumentation.scanDocument(this.doc);
            HTMLInstrumentation._markText(this.editor);
        }
    };

    // Export the class
    module.exports = HTMLDocument;
});


/**
 * Module dependencies.
 */

var crypto = require('crypto');

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

exports.sign = function(val, secret){
  if ('string' != typeof val) throw new TypeError('cookie required');
  if ('string' != typeof secret) throw new TypeError('secret required');
  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){
  if ('string' != typeof val) throw new TypeError('cookie required');
  if ('string' != typeof secret) throw new TypeError('secret required');
  var str = val.slice(0, val.lastIndexOf('.'));
  return exports.sign(str, secret) == val ? str : false;
};


keep keep keep replace replace replace replace keep

/*
 * 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, nomen: true, indent: 4, maxerr: 50 */
/*global define, window, $, brackets, PathUtils, Mustache, document */
/*unittests: Install Extension Dialog*/

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

    require("thirdparty/path-utils/path-utils.min");

    var Dialogs                = require("widgets/Dialogs"),
        StringUtils            = require("utils/StringUtils"),
        Strings                = require("strings"),
        Commands               = require("command/Commands"),
        CommandManager         = require("command/CommandManager"),
        KeyEvent               = require("utils/KeyEvent"),
        Package                = require("extensibility/Package"),
        NativeApp              = require("utils/NativeApp"),
        InstallDialogTemplate  = require("text!htmlContent/install-extension-dialog.html");

    var STATE_CLOSED            = 0,
        STATE_START             = 1,
        STATE_VALID_URL         = 2,
        STATE_INSTALLING        = 3,
        STATE_INSTALLED         = 4,
        STATE_INSTALL_FAILED    = 5,
        STATE_CANCELING_INSTALL = 6,
        STATE_CANCELING_HUNG    = 7,
        STATE_INSTALL_CANCELED  = 8,
        STATE_ALREADY_INSTALLED = 9,
        STATE_NEEDS_UPDATE      = 10;

    /**
     * @constructor
     * Creates a new extension installer dialog.
     * @param {{install: function(url), cancel: function()}} installer The installer backend to use.
     */
    function InstallExtensionDialog(installer) {
        this._installer = installer;
        this._state = STATE_CLOSED;
        this._installResult = null;

        // Timeout before we allow user to leave STATE_INSTALL_CANCELING without waiting for a resolution
        // (per-instance so we can poke it for unit testing)
        this._cancelTimeout = 10 * 1000;
    }

    /** @type {jQuery} The dialog root. */
    InstallExtensionDialog.prototype.$dlg = null;

    /** @type {jQuery} The url input field. */
    InstallExtensionDialog.prototype.$url = null;

    /** @type {jQuery} The ok button. */
    InstallExtensionDialog.prototype.$okButton = null;

    /** @type {jQuery} The cancel button. */
    InstallExtensionDialog.prototype.$cancelButton = null;

    /** @type {jQuery} The area containing the url input label and field. */
    InstallExtensionDialog.prototype.$inputArea = null;

    /** @type {jQuery} The area containing the installation message and spinner. */
    InstallExtensionDialog.prototype.$msgArea = null;

    /** @type {jQuery} The span containing the installation message. */
    InstallExtensionDialog.prototype.$msg = null;

    /** @type {jQuery} The "Browse Extensions" button. */
    InstallExtensionDialog.prototype.$browseExtensionsButton = null;

    /** @type {$.Deferred} A deferred that's resolved/rejected when the dialog is closed and
        something has/hasn't been installed successfully. */
    InstallExtensionDialog.prototype._dialogDeferred = null;

    /** @type {{install: function(url), cancel: function()}} installer The installer backend for this dialog. */
    InstallExtensionDialog.prototype._installer = null;

    /** @type {number} The current state of the dialog; one of the STATE_* constants above. */
    InstallExtensionDialog.prototype._state = null;

    /**
     * @private
     * Transitions the dialog into a new state as the installation proceeds.
     * @param {number} newState The state to transition into; one of the STATE_* variables.
     */
    InstallExtensionDialog.prototype._enterState = function (newState) {
        var url,
            msg,
            self = this,
            prevState = this._state;

        // Store the new state up front in case some of the processing below ends up changing
        // the state again immediately.
        this._state = newState;

        switch (newState) {
        case STATE_START:
            // This should match the default appearance of the dialog when it first opens.
            this.$msg.find(".spinner").remove();
            this.$msgArea.hide();
            this.$inputArea.show();
            this.$okButton
                .prop("disabled", true)
                .text(Strings.INSTALL);
            break;

        case STATE_VALID_URL:
            this.$okButton.prop("disabled", false);
            break;

        case STATE_INSTALLING:
            url = this.$url.val();
            this.$inputArea.hide();
            this.$browseExtensionsButton.hide();
            this.$msg.text(StringUtils.format(Strings.INSTALLING_FROM, url))
                .append("<span class='spinner spin'/>");
            this.$msgArea.show();
            this.$okButton.prop("disabled", true);
            this._installer.install(url)
                .done(function (result) {
                    self._installResult = result;
                    if (result.installationStatus === Package.InstallationStatuses.ALREADY_INSTALLED ||
                            result.installationStatus === Package.InstallationStatuses.OLDER_VERSION ||
                            result.installationStatus === Package.InstallationStatuses.SAME_VERSION) {
                        self._enterState(STATE_ALREADY_INSTALLED);
                    } else if (result.installationStatus === Package.InstallationStatuses.NEEDS_UPDATE) {
                        self._enterState(STATE_NEEDS_UPDATE);
                    } else {
                        self._enterState(STATE_INSTALLED);
                    }
                })
                .fail(function (err) {
                    // If the "failure" is actually a user-requested cancel, don't show an error UI
                    if (err === "CANCELED") {
                        console.assert(self._state === STATE_CANCELING_INSTALL || self._state === STATE_CANCELING_HUNG);
                        self._enterState(STATE_INSTALL_CANCELED);
                    } else {
                        self._errorMessage = Package.formatError(err);
                        self._enterState(STATE_INSTALL_FAILED);
                    }
                });
            break;

        case STATE_CANCELING_INSTALL:
            // This should call back the STATE_INSTALLING fail() handler above, unless it's too late to cancel
            // in which case we'll still jump to STATE_INSTALLED after this
            this.$cancelButton.prop("disabled", true);
            this.$msg.text(Strings.CANCELING_INSTALL);
            this._installer.cancel();
            window.setTimeout(function () {
                if (self._state === STATE_CANCELING_INSTALL) {
                    self._enterState(STATE_CANCELING_HUNG);
                }
            }, this._cancelTimeout);
            break;

        case STATE_CANCELING_HUNG:
            this.$msg.text(Strings.CANCELING_HUNG);
            this.$okButton
                .removeAttr("disabled")
                .text(Strings.CLOSE);
            break;

        case STATE_INSTALLED:
        case STATE_INSTALL_FAILED:
        case STATE_INSTALL_CANCELED:
            if (newState === STATE_INSTALLED) {
                msg = Strings.INSTALL_SUCCEEDED;
            } else if (newState === STATE_INSTALL_FAILED) {
                msg = Strings.INSTALL_FAILED;
            } else {
                msg = Strings.INSTALL_CANCELED;
            }
            this.$msg.html($("<strong/>").text(msg));
            if (this._errorMessage) {
                this.$msg.append($("<p/>").text(this._errorMessage));
            }
            this.$okButton
                .removeAttr("disabled")
                .text(Strings.CLOSE);
            this.$cancelButton.hide();
            break;

        case STATE_ALREADY_INSTALLED:
            var installResult = this._installResult;
            var status = installResult.installationStatus;
            var msgText = Strings["EXTENSION_" + status];
            if (status === Package.InstallationStatuses.OLDER_VERSION) {
                msgText = StringUtils.format(msgText, installResult.metadata.version, installResult.installedVersion);
            }
            this.$msg.text(msgText);
            this.$okButton
                .prop("disabled", false)
                .text(Strings.OVERWRITE);
            break;

        // When we identify that an update needs to be installed (something that
        // happens at quitting time), we just dismiss the dialog.
        case STATE_NEEDS_UPDATE:
            self._enterState(STATE_CLOSED);
            break;

        case STATE_CLOSED:
            $(document.body).off(".installDialog");

           // Only resolve as successful if we actually installed something.
            Dialogs.cancelModalDialogIfOpen("install-extension-dialog");
            if (prevState === STATE_INSTALLED || prevState === STATE_NEEDS_UPDATE) {
                this._dialogDeferred.resolve(this._installResult);
            } else {
                this._dialogDeferred.reject();
            }
            break;
        }
    };

    /**
     * @private
     * Handle a click on the Cancel button, which either cancels an ongoing installation (leaving
     * the dialog open), or closes the dialog if no installation is in progress.
     */
    InstallExtensionDialog.prototype._handleCancel = function () {
        if (this._state === STATE_INSTALLING) {
            this._enterState(STATE_CANCELING_INSTALL);
        } else if (this._state === STATE_ALREADY_INSTALLED) {
            // If we were prompting the user about overwriting a previous installation,
            // and the user cancels, we can delete the downloaded file.
            if (this._installResult && this._installResult.localPath) {
                var filename = this._installResult.localPath;
                brackets.fs.unlink(filename, function () {
                    // ignore the result
                });
            }
            this._enterState(STATE_CLOSED);
        } else if (this._state !== STATE_CANCELING_INSTALL) {
            this._enterState(STATE_CLOSED);
        }
    };

    /**
     * @private
     * Handle a click on the default button, which is "Install" while we're waiting for the
     * user to enter a URL, and "Close" once we've successfully finished installation.
     */
    InstallExtensionDialog.prototype._handleOk = function () {
        if (this._state === STATE_INSTALLED ||
                this._state === STATE_INSTALL_FAILED ||
                this._state === STATE_INSTALL_CANCELED ||
                this._state === STATE_CANCELING_HUNG) {
            // In these end states, this is a "Close" button: just close the dialog and indicate
            // success.
            this._enterState(STATE_CLOSED);
        } else if (this._state === STATE_VALID_URL) {
            this._enterState(STATE_INSTALLING);
        } else if (this._state === STATE_ALREADY_INSTALLED) {
            this._enterState(STATE_NEEDS_UPDATE);
        }
    };

    /**
     * @private
     * Handle key up events on the document. We use this to detect the Esc key.
     */
    InstallExtensionDialog.prototype._handleKeyUp = function (e) {
        if (e.keyCode === KeyEvent.DOM_VK_ESCAPE) {
            this._handleCancel();
        }
    };

    /**
     * @private
     * Handle typing in the URL field.
     */
    InstallExtensionDialog.prototype._handleUrlInput = function () {
        var url = this.$url.val(),
            valid = (url !== "");
        if (!valid && this._state === STATE_VALID_URL) {
            this._enterState(STATE_START);
        } else if (valid && this._state === STATE_START) {
            this._enterState(STATE_VALID_URL);
        }
    };

    /**
     * @private
     * Closes the dialog if it's not already closed. For unit testing only.
     */
    InstallExtensionDialog.prototype._close = function () {
        if (this._state !== STATE_CLOSED) {
            this._enterState(STATE_CLOSED);
        }
    };

    /**
     * Initialize and show the dialog.
     * @param {string=} urlToInstall If specified, immediately starts installing the given file as if the user had
     *     specified it.
     * @return {$.Promise} A promise object that will be resolved when the selected extension
     *     has finished installing, or rejected if the dialog is cancelled.
     */
    InstallExtensionDialog.prototype.show = function (urlToInstall) {
        if (this._state !== STATE_CLOSED) {
            // Somehow the dialog got invoked twice. Just ignore this.
            return this._dialogDeferred.promise();
        }

        // We ignore the promise returned by showModalDialogUsingTemplate, since we're managing the
        // lifecycle of the dialog ourselves.
        Dialogs.showModalDialogUsingTemplate(Mustache.render(InstallDialogTemplate, Strings), false);

        this.$dlg          = $(".install-extension-dialog.instance");
        this.$url          = this.$dlg.find(".url").focus();
        this.$okButton     = this.$dlg.find(".dialog-button[data-button-id='ok']");
        this.$cancelButton = this.$dlg.find(".dialog-button[data-button-id='cancel']");
        this.$inputArea    = this.$dlg.find(".input-field");
        this.$msgArea      = this.$dlg.find(".message-field");
        this.$msg          = this.$msgArea.find(".message");
        this.$browseExtensionsButton = this.$dlg.find(".browse-extensions");

        this.$okButton.on("click", this._handleOk.bind(this));
        this.$cancelButton.on("click", this._handleCancel.bind(this));
        this.$url.on("input", this._handleUrlInput.bind(this));
        this.$browseExtensionsButton.on("click", function () {
            NativeApp.openURLInDefaultBrowser(brackets.config.extension_wiki_url);
        });
        $(document.body).on("keyup.installDialog", this._handleKeyUp.bind(this));

        this._enterState(STATE_START);
        if (urlToInstall) {
            // Act as if the user had manually entered the URL.
            this.$url.val(urlToInstall);
            this._enterState(STATE_VALID_URL);
            this._enterState(STATE_INSTALLING);
        }

        this._dialogDeferred = new $.Deferred();
        return this._dialogDeferred.promise();
    };

    /** Mediates between this module and the Package extension-installation utils. Mockable for unit-testing. */
    function InstallerFacade() { }
    InstallerFacade.prototype.install = function (url) {
        if (this.pendingInstall) {
            console.error("Extension installation already pending");
            return new $.Deferred().reject("DOWNLOAD_ID_IN_USE").promise();
        }
        this.pendingInstall = Package.installFromURL(url);

        // Store now since we'll null pendingInstall immediately if the promise was resolved synchronously
        var promise = this.pendingInstall.promise;

        var self = this;
        this.pendingInstall.promise.always(function () {
            self.pendingInstall = null;
        });

        return promise;
    };
    InstallerFacade.prototype.cancel = function () {
        this.pendingInstall.cancel();
    };

    /**
     * @private
     * Show a dialog that allows the user to enter the URL of an extension ZIP file to install.
     * @return {$.Promise} A promise object that will be resolved when the selected extension
     *     has finished installing, or rejected if the dialog is cancelled.
     */
    function showDialog() {
        var dlg = new InstallExtensionDialog(new InstallerFacade());
        return dlg.show();
    }

    /**
     * @private
     * Show the installation dialog and automatically begin installing the given URL.
     * @param {string=} urlToInstall If specified, immediately starts installing the given file as if the user had
     *     specified it.
     * @return {$.Promise} A promise object that will be resolved when the selected extension
     *     has finished installing, or rejected if the dialog is cancelled.
     */
    function installUsingDialog(urlToInstall) {
        var dlg = new InstallExtensionDialog(new InstallerFacade());
        return dlg.show(urlToInstall);
    }

    exports.showDialog = showDialog;
    exports.installUsingDialog = installUsingDialog;

    // Exposed for unit testing only
    exports._Dialog = InstallExtensionDialog;
});


<!doctype html>

	<meta charset="utf-8">
	<title>jQuery UI Tooltip Test Suite</title>

	<link rel="stylesheet" href="../../../external/qunit.css">
	<link rel="stylesheet" href="../qunit-composite.css">

jQuery UI Tooltip Test Suite


Welcome to Brackets! [![Build Status](https://travis-ci.org/adobe/brackets.png?branch=master)](https://travis-ci.org/adobe/brackets)
-------------------

Installers for the latest build can be [downloaded here](http://download.brackets.io/).

This is an early version of Brackets, a code editor for HTML, CSS
and JavaScript that's *built* in HTML, CSS and JavaScript.

What makes Brackets different from other web code editors?

* **Tools shouldn't get in your way.** Instead of cluttering up your coding
environment with lots of panels and icons, the Quick Edit UI in Brackets puts
context-specific code and tools inline.
* **Brackets is in sync with your browser.** With Live Development, Brackets
works directly with your browser to push code edits instantly and jump
back and forth between your real source code and the browser view.
* **Do it yourself.** Because Brackets is open source, and built with HTML, CSS
and JavaScript, you can help build the best code editor for the web.

You can see some
[screenshots of Brackets](https://github.com/adobe/brackets/wiki/Brackets-Screenshots)
on the wiki and [intro videos](http://www.youtube.com/user/CodeBrackets) on the Brackets YouTube channel.

Brackets is fairly early in development, so some of the features you would
expect in a code editor are missing, and some existing features might be
incomplete or not as useful as you'd want. But if you like the direction
it's going, _there are lots of ways you can help._ See [CONTRIBUTING.md](https://github.com/adobe/brackets/blob/master/CONTRIBUTING.md)
for more info. Please contribute!

The text editor inside Brackets is based on
[CodeMirror](http://github.com/marijnh/CodeMirror)—thanks to Marijn for
taking our pull requests, implementing feature requests and fixing bugs! See
[Notes on CodeMirror](https://github.com/adobe/brackets/wiki/Notes-on-CodeMirror)
for info on how we're using CodeMirror.

How to run Brackets
-------------------

**Brackets isn't ready for general use yet.** It's still missing a lot of basic
editor features. That said, what's there is reasonably stable—the
Brackets team uses Brackets to develop Brackets full time. So feel free to
give it a spin and let us know what's missing!

Although Brackets is built in HTML/CSS/JS, it currently runs as a desktop
application in a thin native shell, so that it can access your local files.
(If you just try to open the index.html file in a browser, it won't work yet.)
The native shell for Brackets lives in a separate repo,
[adobe/brackets-shell](https://github.com/adobe/brackets-shell/).

The Brackets native shell currently runs on Mac and Windows.
The community has started working on a Linux port, and is making great progress;
if you're interested, check out the
[Linux Version](https://github.com/adobe/brackets/wiki/Linux-Version) wiki page.

You can download stable builds of Brackets from
[download.brackets.io](http://download.brackets.io). If you want to pull the repo
directly via git, see [How to Hack on Brackets](https://github.com/adobe/brackets/wiki/How-to-Hack-on-Brackets)
for instructions on how to get everything.

By default, Brackets opens a folder containing some simple "Getting Started" content.
You can choose a different folder to edit using *File > Open Folder*. (Might we
suggest editing the Brackets source code and submitting some pull requests?)

Most of Brackets should be pretty self-explanatory, but for information on how
to use its unique features, like Quick Edit and Live Development, please read
[How to Use Brackets](http://github.com/adobe/brackets/wiki/How-to-Use-Brackets).
The [extensions wiki page](https://github.com/adobe/brackets/wiki/Brackets-Extensions)
has a list of extensions that have been contributed.
Also, see the [release notes](http://github.com/adobe/brackets/wiki/Release-Notes)
for a list of new features and known issues in each build.

I found a bug/missing feature!
------------------------------

Issues starting Brackets the first time? Please review [Troubleshooting](https://github.com/adobe/brackets/wiki/Troubleshooting).

**For bugs** be sure to [search existing issues](https://github.com/adobe/brackets/issues) first.
Include steps to consistently reproduce the problem, actual vs. expected results, and your OS and
Brackets version number. Disable all extensions to verify the issue is a core Brackets bug.
[Read more guidelines for filing good bugs...](https://github.com/adobe/brackets/wiki/How-to-Report-an-Issue)

**For feature requests** please first check our [feature backlog](http://bit.ly/BracketsBacklog) to
see if it's already there; you can upvote it if so. If not, feel free to file it as an issue; we'll
move it to the feature backlog for you.

I want to help!
---------------

Awesome! Please read the [Guide to Contributing](https://github.com/adobe/brackets/blob/master/CONTRIBUTING.md)
and learn [How to Hack on Brackets](https://github.com/adobe/brackets/wiki/How-to-Hack-on-Brackets).

I want to keep track of how Brackets is doing!
----------------------------------------------

Not sure you needed the exclamation point there, but we like your enthusiasm.

**What's Brackets working on next?**

* In our [feature backlog](http://bit.ly/BracketsBacklog), the columns labeled "Sprint N"
  are features already in progress and should ship within 2 weeks. Features at the top of
  the "Product Backlog" list will come next.
* Watch our [GitHub activity stream](https://github.com/adobe/brackets/pulse).

**Contact info:**

* **Twitter:** [@brackets](http://twitter.com/#!/brackets)
* **Blog:** http://blog.brackets.io/
* **IRC:** [#brackets on freenode](http://webchat.freenode.net/?channels=brackets)
* **Developers mailing list:** http://groups.google.com/group/brackets-dev


var path = require('path');
var fs = require('fs');

module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;

function mkdirP (p, mode, f, made) {
    if (typeof mode === 'function' || mode === undefined) {
        f = mode;
        mode = 0777 & (~process.umask());
    }
    if (!made) made = null;

    var cb = f || function () {};
    if (typeof mode === 'string') mode = parseInt(mode, 8);
    p = path.resolve(p);

    fs.mkdir(p, mode, function (er) {
        if (!er) {
            made = made || p;
            return cb(null, made);
        }
        switch (er.code) {
            case 'ENOENT':
                mkdirP(path.dirname(p), mode, function (er, made) {
                    if (er) cb(er, made);
                    else mkdirP(p, mode, cb, made);
                });
                break;

            // In the case of any other error, just see if there's a dir
            // there already.  If so, then hooray!  If not, then something
            // is borked.
            default:
                fs.stat(p, function (er2, stat) {
                    // if the stat fails, then that's super weird.
                    // let the original error be the failure reason.
                    if (er2 || !stat.isDirectory()) cb(er, made)
                    else cb(null, made);
                });
                break;
        }
    });
}

mkdirP.sync = function sync (p, mode, made) {
    if (mode === undefined) {
        mode = 0777 & (~process.umask());
    }
    if (!made) made = null;

    if (typeof mode === 'string') mode = parseInt(mode, 8);
    p = path.resolve(p);

    try {
        fs.mkdirSync(p, mode);
        made = made || p;
    }
    catch (err0) {
        switch (err0.code) {
            case 'ENOENT' :
                made = sync(path.dirname(p), mode, made);
                sync(p, mode, made);
                break;

            // In the case of any other error, just see if there's a dir
            // there already.  If so, then hooray!  If not, then something
            // is borked.
            default:
                var stat;
                try {
                    stat = fs.statSync(p);
                }
                catch (err1) {
                    throw err0;
                }
                if (!stat.isDirectory()) throw err0;
                break;
        }
    }

    return made;
};


module( "progressbar: methods" );

test( "destroy", function() {
	expect( 1 );
	domEqual( "#progressbar", function() {
		$( "#progressbar" ).progressbar().progressbar( "destroy" );
	});
});

test( "value", function() {
	expect( 3 );

	var element = $( "<div>" ).progressbar({ value: 20 });
	equal( element.progressbar( "value" ), 20, "correct value as getter" );
	equal( element.progressbar( "value", 30), element, "chainable as setter" );
	equal( element.progressbar( "option", "value" ), 30, "correct value after setter" );
});


<!doctype html>

	<meta charset="utf-8">
	<title>jQuery UI Spinner - Overflow</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

	<link rel="stylesheet" href="../demos.css">

	Select a value:
	<input id="spinner" name="value" />

Overflowing spinner restricted to a range of -10 to 10.
For anything above 10, it'll overflow to -10, and the other way round.


// Only resolve as successful if we actually installed something.

if (prevState === STATE_INSTALLED || prevState === STATE_NEEDS_UPDATE) {

keep keep keep keep keep replace keep keep keep keep keep

if (prevState === STATE_INSTALLED || prevState === STATE_NEEDS_UPDATE ||

prevState === STATE_OVERWRITE_CONFIRMED) {


/* =============================================================
 * bootstrap-scrollspy.js v2.3.1
 * http://twitter.github.com/bootstrap/javascript.html#scrollspy
 * =============================================================
 * Copyright 2012 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================== */

!function ($) {

  "use strict"; // jshint ;_;

 /* SCROLLSPY CLASS DEFINITION
  * ========================== */

  function ScrollSpy(element, options) {
    var process = $.proxy(this.process, this)
      , $element = $(element).is('body') ? $(window) : $(element)
      , href
    this.options = $.extend({}, $.fn.scrollspy.defaults, options)
    this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
    this.selector = (this.options.target
      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
      || '') + ' .nav li > a'
    this.$body = $('body')
    this.refresh()
    this.process()
  }

  ScrollSpy.prototype = {

      constructor: ScrollSpy

    , refresh: function () {
        var self = this
          , $targets

        this.offsets = $([])
        this.targets = $([])

        $targets = this.$body
          .find(this.selector)
          .map(function () {
            var $el = $(this)
              , href = $el.data('target') || $el.attr('href')
              , $href = /^#\w/.test(href) && $(href)
            return ( $href
              && $href.length
              && [[ $href.position().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]] ) || null
          })
          .sort(function (a, b) { return a[0] - b[0] })
          .each(function () {
            self.offsets.push(this[0])
            self.targets.push(this[1])
          })
      }

    , process: function () {
        var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
          , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
          , maxScroll = scrollHeight - this.$scrollElement.height()
          , offsets = this.offsets
          , targets = this.targets
          , activeTarget = this.activeTarget
          , i

        if (scrollTop >= maxScroll) {
          return activeTarget != (i = targets.last()[0])
            && this.activate ( i )
        }

        for (i = offsets.length; i--;) {
          activeTarget != targets[i]
            && scrollTop >= offsets[i]
            && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
            && this.activate( targets[i] )
        }
      }

    , activate: function (target) {
        var active
          , selector

        this.activeTarget = target

        $(this.selector)
          .parent('.active')
          .removeClass('active')

        selector = this.selector
          + '[data-target="' + target + '"],'
          + this.selector + '[href="' + target + '"]'

        active = $(selector)
          .parent('li')
          .addClass('active')

        if (active.parent('.dropdown-menu').length)  {
          active = active.closest('li.dropdown').addClass('active')
        }

        active.trigger('activate')
      }

  }

 /* SCROLLSPY PLUGIN DEFINITION
  * =========================== */

  var old = $.fn.scrollspy

  $.fn.scrollspy = function (option) {
    return this.each(function () {
      var $this = $(this)
        , data = $this.data('scrollspy')
        , options = typeof option == 'object' && option
      if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  $.fn.scrollspy.Constructor = ScrollSpy

  $.fn.scrollspy.defaults = {
    offset: 10
  }

 /* SCROLLSPY NO CONFLICT
  * ===================== */

  $.fn.scrollspy.noConflict = function () {
    $.fn.scrollspy = old
    return this
  }

 /* SCROLLSPY DATA-API
  * ================== */

  $(window).on('load', function () {
    $('[data-spy="scroll"]').each(function () {
      var $spy = $(this)
      $spy.scrollspy($spy.data())
    })
  })

}(window.jQuery);


# Formidable

[![Build Status](https://secure.travis-ci.org/felixge/node-formidable.png?branch=master)](http://travis-ci.org/felixge/node-formidable)

## Purpose

A node.js module for parsing form data, especially file uploads.

## Current status

This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading
and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from
a large variety of clients and is considered production-ready.

## Features

* Fast (~500mb/sec), non-buffering multipart parser
* Automatically writing file uploads to disk
* Low memory footprint
* Graceful error handling
* Very high test coverage

## Changelog

### v1.0.9

* Emit progress when content length header parsed (Tim Koschützki)
* Fix Readme syntax due to GitHub changes (goob)
* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)

### v1.0.8

* Strip potentially unsafe characters when using `keepExtensions: true`.
* Switch to utest / urun for testing
* Add travis build

### v1.0.7

* Remove file from package that was causing problems when installing on windows. (#102)
* Fix typos in Readme (Jason Davies).

### v1.0.6

* Do not default to the default to the field name for file uploads where
  filename="".

### v1.0.5

* Support filename="" in multipart parts
* Explain unexpected end() errors in parser better

**Note:** Starting with this version, formidable emits 'file' events for empty
file input fields. Previously those were incorrectly emitted as regular file
input fields with value = "".

### v1.0.4

* Detect a good default tmp directory regardless of platform. (#88)

### v1.0.3

* Fix problems with utf8 characters (#84) / semicolons in filenames (#58)
* Small performance improvements
* New test suite and fixture system

### v1.0.2

* Exclude node\_modules folder from git
* Implement new `'aborted'` event
* Fix files in example folder to work with recent node versions
* Make gently a devDependency

[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2)

### v1.0.1

* Fix package.json to refer to proper main directory. (#68, Dean Landolt)

[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1)

### v1.0.0

* Add support for multipart boundaries that are quoted strings. (Jeff Craig)

This marks the beginning of development on version 2.0 which will include
several architectural improvements.

[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0)

### v0.9.11

* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki)
* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class

**Important:** The old property names of the File class will be removed in a
future release.

[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11)

### Older releases

These releases were done before starting to maintain the above Changelog:

* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10)
* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9)
* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8)
* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7)
* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6)
* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5)
* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4)
* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3)
* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2)
* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0)

## Installation

Via [npm](http://github.com/isaacs/npm):

    npm install formidable@latest

Manually:

    git clone git://github.com/felixge/node-formidable.git formidable
    vim my.js
    # var formidable = require('./formidable');

Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.

## Example

Parse an incoming file upload.

    var formidable = require('formidable'),
        http = require('http'),

        util = require('util');

    http.createServer(function(req, res) {
      if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
        // parse a file upload
        var form = new formidable.IncomingForm();
        form.parse(req, function(err, fields, files) {
          res.writeHead(200, {'content-type': 'text/plain'});
          res.write('received upload:\n\n');
          res.end(util.inspect({fields: fields, files: files}));
        });
        return;
      }

      // show a file upload form
      res.writeHead(200, {'content-type': 'text/html'});
      res.end(
        '<form action="/upload" enctype="multipart/form-data" method="post">'+
        '<input type="text" name="title">
'+
        '<input type="file" name="upload" multiple="multiple">
'+
        '<input type="submit" value="Upload">'+
        '</form>'
      );
    }).listen(80);

## API

### formidable.IncomingForm

__new formidable.IncomingForm()__

Creates a new incoming form.

__incomingForm.encoding = 'utf-8'__

The encoding to use for incoming form fields.

__incomingForm.uploadDir = process.env.TMP || '/tmp' || process.cwd()__

The directory for placing file uploads in. You can move them later on using
`fs.rename()`. The default directory is picked at module load time depending on
the first existing directory from those listed above.

__incomingForm.keepExtensions = false__

If you want the files written to `incomingForm.uploadDir` to include the extensions of the original files, set this property to `true`.

__incomingForm.type__

Either 'multipart' or 'urlencoded' depending on the incoming request.

__incomingForm.maxFieldsSize = 2 * 1024 * 1024__

Limits the amount of memory a field (not file) can allocate in bytes.
If this value is exceeded, an `'error'` event is emitted. The default
size is 2MB.

__incomingForm.hash = false__

If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.

__incomingForm.bytesReceived__

The amount of bytes received for this form so far.

__incomingForm.bytesExpected__

The expected number of bytes in this form.

__incomingForm.parse(request, [cb])__

Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback:

    incomingForm.parse(req, function(err, fields, files) {
      // ...
    });

__incomingForm.onPart(part)__

You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events  processing which would occur otherwise, making you fully responsible for handling the processing.

    incomingForm.onPart = function(part) {
      part.addListener('data', function() {
        // ...
      });
    }

If you want to use formidable to only handle certain parts for you, you can do so:

    incomingForm.onPart = function(part) {
      if (!part.filename) {
        // let formidable handle all non-file parts
        incomingForm.handlePart(part);
      }
    }

Check the code in this method for further inspiration.

__Event: 'progress' (bytesReceived, bytesExpected)__

Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.

__Event: 'field' (name, value)__

Emitted whenever a field / value pair has been received.

__Event: 'fileBegin' (name, file)__

Emitted whenever a new file is detected in the upload stream. Use this even if
you want to stream the file to somewhere else while buffering the upload on
the file system.

__Event: 'file' (name, file)__

Emitted whenever a field / file pair has been received. `file` is an instance of `File`.

__Event: 'error' (err)__

Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.

__Event: 'aborted'__

Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core).

__Event: 'end' ()__

Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.

### formidable.File

__file.size = 0__

The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.

__file.path = null__

The path this file is being written to. You can modify this in the `'fileBegin'` event in
case you are unhappy with the way formidable generates a temporary path for your files.

__file.name = null__

The name this file had according to the uploading client.

__file.type = null__

The mime type of this file, according to the uploading client.

__file.lastModifiedDate = null__

A date object (or `null`) containing the time this file was last written to. Mostly
here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).

__file.hash = null__

If hash calculation was set, you can read the hex digest out of this var.

## License

Formidable is licensed under the MIT license.

## Ports

* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable

## Credits

* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js


var getRewireRegExp = require("./getRewireRegExp.js");

/**
 * Searches for rewire(); statements and returns all strings that are between the brackets.
 *
 * @param {!String} src
 * @return {Array}
 */
function getRewireRequires(src) {
    var result = [],
        regExp = getRewireRegExp(),
        match;

    src = " " + src;    // ensure that rewire() is not at index 0 otherwise the regexp won't work in this case
    match = regExp.exec(src);
    while (match !== null) {
        result.push(match[2]);
        match = regExp.exec(src);
    }

    return result;
}

module.exports = getRewireRequires;


InstallExtensionDialog.prototype._handleOk = function () {

this._state === STATE_INSTALL_CANCELED ||

// In these end states, this is a "Close" button: just close the dialog and indicate

} else if (this._state === STATE_VALID_URL) {


mime

A comprehensive library for mime-type mapping

# mime

Comprehensive MIME type mapping API. Includes all 600+ types and 800+ extensions defined by the Apache project, plus additional types submitted by the node.js community.

## Install

Install with [npm](http://github.com/isaacs/npm):

    npm install mime

## API - Queries

### mime.lookup(path)
Get the mime type associated with a file. Performs a case-insensitive lookup using the extension in `path` (the substring after the last '/' or '.').  E.g.

    var mime = require('mime');

    mime.lookup('/path/to/file.txt');         // => 'text/plain'
    mime.lookup('file.txt');                  // => 'text/plain'
    mime.lookup('.TXT');                      // => 'text/plain'
    mime.lookup('htm');                       // => 'text/html'

### mime.extension(type)
Get the default extension for `type`

    mime.extension('text/html');                 // => 'html'
    mime.extension('application/octet-stream');  // => 'bin'

### mime.charsets.lookup()

Map mime-type to charset

    mime.charsets.lookup('text/plain');        // => 'UTF-8'

(The logic for charset lookups is pretty rudimentary.  Feel free to suggest improvements.)

## API - Defining Custom Types

The following APIs allow you to add your own type mappings within your project.  If you feel a type should be included as part of node-mime, see [requesting new types](https://github.com/bentomas/node-mime/wiki/Requesting-New-Types).

### mime.define()

Add custom mime/extension mappings

    mime.define({
        'text/x-some-format': ['x-sf', 'x-sft', 'x-sfml'],
        'application/x-my-type': ['x-mt', 'x-mtt'],
        // etc ...
    });

    mime.lookup('x-sft');                 // => 'text/x-some-format'

The first entry in the extensions array is returned by `mime.extension()`. E.g.

    mime.extension('text/x-some-format'); // => 'x-sf'

### mime.load(filepath)

Load mappings from an Apache ".types" format file

    mime.load('./my_project.types');

The .types file format is simple -  See the `types` dir for examples.


function eofFunc() {
    // this is a fake brace: {
    var foo;
    if (foo) {
        alert("brace: }");
    }
}