<commit_before>/*
 * 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, Mustache, document */
/*unittests: Install Extension Dialog*/

define(function (require, exports, module) {
    "use strict";
    
    var Dialogs                = require("widgets/Dialogs"),
        StringUtils            = require("utils/StringUtils"),
        Strings                = require("strings"),
        Commands               = require("command/Commands"),
        CommandManager         = require("command/CommandManager"),
        FileSystem             = require("filesystem/FileSystem"),
        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_OVERWRITE_CONFIRMED = 10,
        STATE_NEEDS_UPDATE        = 11;
    
    /** 
     * @constructor
     * Creates a new extension installer dialog.
     * @param {{install: function(url), cancel: function()}} installer The installer backend to use.
     */
    function InstallExtensionDialog(installer, _isUpdate) {
        this._installer = installer;
        this._state = STATE_CLOSED;
        this._installResult = null;
        this._isUpdate = _isUpdate;

        // 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);
                    }
                })<commit_msg>Adding margin before "Install extension" spinner<commit_after>/*
 * 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, Mustache, document */
/*unittests: Install Extension Dialog*/

define(function (require, exports, module) {
    "use strict";
    
    var Dialogs                = require("widgets/Dialogs"),
        StringUtils            = require("utils/StringUtils"),
        Strings                = require("strings"),
        Commands               = require("command/Commands"),
        CommandManager         = require("command/CommandManager"),
        FileSystem             = require("filesystem/FileSystem"),
        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_OVERWRITE_CONFIRMED = 10,
        STATE_NEEDS_UPDATE        = 11;
    
    /** 
     * @constructor
     * Creates a new extension installer dialog.
     * @param {{install: function(url), cancel: function()}} installer The installer backend to use.
     */
    function InstallExtensionDialog(installer, _isUpdate) {
        this._installer = installer;
        this._state = STATE_CLOSED;
        this._installResult = null;
        this._isUpdate = _isUpdate;

        // 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' style='margin-left: 10px'/>");
            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);
                    }
                })
{'fraction_non_alphanumeric': 0.09145599083291556, 'fraction_numerical': 0.004010599441380792, 'mean_word_length': 2.219737145492276, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2129659', 'n_tokens_mistral': 3657, 'n_tokens_neox': 3415, 'n_words': 1093}