Viewing File: /usr/local/cpanel/base/frontend/jupiter/email_accounts/index.cmb.js

/*
# email_accounts/services/emailAccountsService.js                                     Copyright 2022 cPanel, L.L.C.
#                                                                                            All rights reserved.
# copyright@cpanel.net                                                                          http://cpanel.net
# This code is subject to the cPanel license.                                  Unauthorized copying is prohibited
*/

/* global define: false, PAGE: false */

define(
    'app/services/emailAccountsService',[
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/util/parse",
        "cjt/util/query",
        "cjt/io/uapi-request",
        "cjt/io/batch-request",
        "cjt/io/uapi",
        "cjt/io/api",
        "cjt/services/APIService",
        "cjt/services/alertService",
    ],
    function(angular, _, LOCALE, PARSE, QUERY, APIREQUEST, BATCH) {
        "use strict";

        var HTML_INFINITY = "∞";
        var DEFAULT_ACCOUNT_LOGIN = "Main Account";

        var app = angular.module("cpanel.emailAccounts.service", [
            "cjt2.services.api",
            "cjt2.services.alert",
        ]);
        app.value("PAGE", PAGE);

        /**
         * Service wrapper for email accounts
         *
         * @module domains
         *
         * @param  {Object} $q angular $q object
         * @param  {Object} APIService cjt2 api service
         * @param  {Object} $timeout
         * @param  {Object} PAGE window.PAGE object
         */
        app.factory("emailAccountsService", [
            "$q",
            "APIService",
            "alertService",
            "$timeout",
            "PAGE",
            function($q, APIService, alertService, $timeout, PAGE) {

                // This class provides conveniences that allow an
                // alert with an automatically-created ID to be removed
                // or replaced easily. It mandates that each alert be
                // NON-replace.
                function AlertObject(args) {
                    this._init(args);
                }

                _.assign(
                    AlertObject.prototype,
                    {
                        _init: function(args) {
                            if (args.replace) {
                                throw "bad replace";
                            }

                            var localArgs = _.assign( {}, args );
                            localArgs.replace = false;

                            alertService.add(localArgs);

                            var alerts = alertService.getAlerts( args.group );
                            var theAlert = alerts.slice(-1)[0];
                            var alertID = theAlert.id;

                            this.id = alertID;
                            this.group = args.group;
                        },

                        remove: function remove() {
                            alertService.removeById( this.id, this.group );
                        },
                    }
                );

                // --------------------------------------------------

                // Set the default success transform on the prototype
                var servicePrototype = new APIService({
                    transformAPISuccess: function(response) {
                        return response.data;
                    },
                });

                var EmailAccountsService = function() {};
                EmailAccountsService.prototype = servicePrototype;

                var emailStats;

                /**
                 * Helps with the classes on the quota progress bar
                 *
                 * @method quotaProgressType
                 * @param {number} displayPercentage
                 * @returns {string} class for the quota progress bar
                 */
                function quotaProgressType(displayPercentage) {
                    if (displayPercentage >= 80) {
                        return "danger";
                    } else if (displayPercentage >= 60) {
                        return "warning";
                    } else if (displayPercentage < 60) {
                        return "success";
                    } else {
                        return "success";
                    }
                }

                /**
                 * Restructures Email account
                 *
                 * @method decorateEmailObj
                 * @param {Object} obj Email account object returned from API
                 * @returns {Object} returns modified email account
                 */
                function decorateEmailObj(obj) {
                    obj.diskused = parseInt(obj.diskused, 10);
                    obj.humandiskused = LOCALE.format_bytes( obj._diskused );
                    obj.id = obj.email;

                    if (obj._diskquota === 0 || obj.diskquota === 0 || obj.diskquota === "unlimited") {
                        obj.diskquota = 0;
                        obj.humandiskquota = HTML_INFINITY;
                        obj.diskusedpercent = 0;
                        obj.quotaType = "unlimited";
                        obj.quota = "";
                        obj.displayProgressbar = false;
                    } else {
                        obj.diskquota = parseInt(obj._diskquota, 10) / 1024 / 1024;
                        obj.humandiskquota = LOCALE.format_bytes(obj._diskquota);
                        obj.diskusedpercent = ((obj._diskused / obj._diskquota) * 100).toFixed(2);
                        obj.quotaProgressType = quotaProgressType(obj.diskusedpercent);
                        obj.quotaType = "userdefined";
                        obj.quota = obj.diskquota;
                        obj.quotaUnit = "MB";
                        obj.displayProgressbar = true;
                    }

                    obj.humandiskusedpercent = LOCALE.numf(obj.diskusedpercent) + "%";
                    obj.suspended_login = PARSE.parsePerlBoolean(obj.suspended_login);
                    obj.suspended_incoming = PARSE.parsePerlBoolean(obj.suspended_incoming);
                    obj.suspended_outgoing = PARSE.parsePerlBoolean(obj.suspended_outgoing);
                    obj.hold_outgoing = PARSE.parsePerlBoolean(obj.hold_outgoing);
                    obj.has_suspended = PARSE.parsePerlBoolean(obj.has_suspended);

                    obj.isDefault = obj.login === DEFAULT_ACCOUNT_LOGIN;

                    var webmailQuery = QUERY.make_query_string( {
                        user: obj.email,
                        return_request_uri: location.pathname,
                    } );

                    obj.webmailLink = "../mail/webmailform.html?" + webmailQuery;

                    return obj;
                }

                function _isNonClobberableUser(user) {

                    // See https://go.cpanel.net/UserManager-list_users for what “special” means.
                    if (user.special) {
                        return true;
                    }

                    return _.values(user.services).some( function(s) {
                        return PARSE.parsePerlBoolean(s.enabled);
                    } );
                }

                angular.extend(EmailAccountsService.prototype, {

                    /**
                     * Gets local storage values
                     * @method getStoredValue
                     * @param  {string}  key
                     * @return {string} returns the stored value
                     */
                    getStoredValue: function(key) {
                        var storageValue = localStorage.getItem(key);
                        if ( storageValue && (0 === storageValue.indexOf(PAGE.securityToken + ":")) ) {
                            storageValue = storageValue.substr( 1 + PAGE.securityToken.length );
                        } else {
                            storageValue = "";
                        }
                        return storageValue;
                    },

                    /**
                     * dataWrapper
                     * @method _dataWrapper
                     * @return {Promise} Returns a promise
                     */
                    _dataWrapper: function(apiCall) {
                        return this.deferred(apiCall).promise;
                    },

                    /**
                     * Gets upgrade url
                     * @method getUpgradeUrl
                     * @return {string} Returns upgarde url
                     */
                    getUpgradeUrl: function() {
                        return PAGE.upgradeUrl;
                    },

                    /**
                     * Gets email resource usage
                     * @method getEmailStats
                     * @return {Promise} Returns a promise
                     */
                    getEmailStats: function() {
                        if (emailStats) {
                            return $q.resolve(emailStats);
                        }

                        var apiCall = new APIREQUEST.Class();

                        apiCall.initialize("ResourceUsage", "get_usages");
                        apiCall.addFilter("id", "contains", "email_accounts");

                        return this.deferred(apiCall).promise
                            .then(function(response) {
                                emailStats = {};
                                var stats;
                                if (response && response.length) {
                                    stats = response[0];
                                    emailStats.maximum = parseInt(stats.maximum, 10);
                                    emailStats.used = parseInt(stats.usage, 10);
                                    if (!isNaN(emailStats.maximum) && emailStats.maximum) {
                                        emailStats.available = emailStats.maximum - emailStats.used;
                                    } else {
                                        emailStats.available = -1;
                                    }

                                } else {
                                    return $q.reject();
                                }
                                return emailStats;
                            });

                    },

                    /**
                     * Gets domains
                     * @method getMailDomains
                     * @return {Promise} Returns a promise
                     */
                    getMailDomains: function() {
                        var apiCall = new APIREQUEST.Class();

                        apiCall.initialize("Email", "list_mail_domains");

                        return this.deferred(apiCall).promise
                            .then(function(response) {
                                var domains = _.map(response, _.property("domain"));
                                return domains;
                            });
                    },

                    // Promise resolves truthy if the User Manager user
                    // is either nonexistent or has no services.
                    _isClobberableUserPromise: function _isClobberableUserPromise(emailAddressStr) {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("UserManager", "list_users");
                        apiCall.addFilter("full_username", "eq", emailAddressStr);

                        return this._dataWrapper(apiCall).then( function(users) {
                            return !users.some(_isNonClobberableUser) && users[0];
                        } );
                    },

                    /**
                     * Creates email account
                     *
                     * XXX: getEmailStats() must be called first.
                     *
                     * @method createEmail
                     * @param {Object} accountDetails Email account details
                     * @return {Promise} Returns a promise
                     */
                    createEmail: function(accountDetails) {
                        if (!emailStats) {
                            throw "Call getEmailStats() first.";
                        }

                        var apiCall = new APIREQUEST.Class();

                        var apiArgs, apiOverrides;

                        if (!accountDetails.setPassword) {

                            apiArgs = {
                                "username": accountDetails.userName,
                                "domain": accountDetails.domain,
                                "alternate_email": accountDetails.recoveryEmail,
                                "send_invite": 1,
                                "services.email.enabled": 1,
                                "services.email.quota": accountDetails.quota,
                                "services.email.send_welcome_email": accountDetails.sendWelcomeEmail ? 1 : 0,
                            };

                            // need to email reset link
                            apiCall.initialize("UserManager", "create_user", apiArgs);

                            // If this API call fails we want to examine the
                            // full response to see if there’s a typed error.
                            apiOverrides = {
                                transformAPIFailure: Object,
                            };
                        } else {
                            apiArgs = {
                                email: accountDetails.userName,
                                domain: accountDetails.domain,
                                password: accountDetails.password,
                                quota: accountDetails.quota,
                                send_welcome_email: accountDetails.sendWelcomeEmail ? 1 : 0,
                            };
                            apiCall.initialize("Email", "add_pop", apiArgs);
                        }

                        var self = this;

                        var realFailureXformer = this.presetDefaultHandlers.transformAPIFailure;

                        var emailAddressStr = accountDetails.userName + "@" + accountDetails.domain;

                        var ALERTGROUP = "emailAccounts";

                        var alertObj = new AlertObject( {
                            group: ALERTGROUP,
                            type: "info",
                            message: LOCALE.maketext("Creating email account …"),
                        } );

                        var apiPromise = this.deferred(apiCall, apiOverrides).promise;

                        return apiPromise
                            .then(function(response) {
                                emailStats.used = emailStats.used + 1;

                                if (emailStats.available !== -1) {
                                    emailStats.available = emailStats.available - 1;
                                }

                                // Plus Addressing to be sent to the Inbox
                                if (accountDetails.autoCreateSubaddressFolders !== true) {
                                    var subAddressingAPI = new APIREQUEST.Class();
                                    subAddressingAPI.initialize("Email", "disable_mailbox_autocreate", {
                                        email: emailAddressStr,
                                    });
                                    return self.deferred(subAddressingAPI).promise.then(function() {
                                        return response;
                                    });
                                }

                                return response;
                            }).catch( function(createResp) {

                                // If the call failed, check for
                                // “AlreadyExists”, which means a User
                                // Manager entry already exists for that
                                // email address.
                                if (createResp.data && createResp.data.type === "AlreadyExists") {
                                    var isLeftover = !_isNonClobberableUser(createResp.data && createResp.data.detail.entry);

                                    if (isLeftover) {
                                        var clobberableAcct = createResp.data.detail;

                                        var phrases = [
                                            LOCALE.maketext("“[_1]” already exists in the [asis,User Manager].", _.escape(emailAddressStr) ),
                                            LOCALE.maketext("This account appears to be left over from a previous subaccount.", _.escape(emailAddressStr)),
                                            LOCALE.maketext("Deleting …"),
                                        ];

                                        new AlertObject( {
                                            group: ALERTGROUP,
                                            type: "info",
                                            message: phrases.join(" "),
                                        } );

                                        var deleteArgs = {
                                            username: accountDetails.userName,
                                            domain: accountDetails.domain,
                                        };

                                        // We confirmed that User Manager’s
                                        // conflicting user is a “leftover”,
                                        // so let’s delete that user.
                                        var apiCall = new APIREQUEST.Class();
                                        apiCall.initialize("UserManager", "delete_user", deleteArgs);
                                        return self._dataWrapper(apiCall).then( function() {
                                            var msgHtml;

                                            if (clobberableAcct.alternate_email) {
                                                msgHtml = LOCALE.maketext("The system deleted [asis,User Manager]’s old leftover “[_1]” account (alternate email address: [_2]).", _.escape(emailAddressStr), _.escape(clobberableAcct.alternate_email));
                                            } else {
                                                msgHtml = LOCALE.maketext("The system deleted [asis,User Manager]’s old leftover “[_1]” account.", _.escape(emailAddressStr));
                                            }

                                            alertService.add( {
                                                group: ALERTGROUP,
                                                type: "info",
                                                message: msgHtml,
                                                replace: false,
                                            } );

                                            // Now that we’ve deleted the
                                            // empty User Manager account,
                                            // retry the original creation.
                                            return self.createEmail(accountDetails);
                                        } );
                                    }
                                }

                                return $q.reject(typeof createResp === "string" ? createResp : realFailureXformer(createResp));
                            }).finally( function() {
                                alertObj.remove();
                            });
                    },

                    /**
                     * Gets default account usage
                     * @method getDefaultAccountUsage
                     * @return {Promise} Returns a promise
                     */
                    getDefaultAccountUsage: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Email", "get_main_account_disk_usage");
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Gets whether or not the shared address book is enabled
                     * @method isSharedAddressBookEnabled
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the shared AB is enabled
                     */
                    isSharedAddressBookEnabled: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("DAV", "has_shared_global_addressbook");
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Enables the shared address book
                     * @method enableSharedAddressBook
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the shared AB is enabled
                     */
                    enableSharedAddressBook: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("DAV", "enable_shared_global_addressbook");
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Disables the shared address book
                     * @method disableSharedAddressBook
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the shared AB is enabled
                     */
                    disableSharedAddressBook: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("DAV", "disable_shared_global_addressbook");
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Gets whether or not UTF-8 Mailboxes names are enabled.
                     * @method isUTF8MailboxNamesEnabled
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the call was successful.
                     */
                    isUTF8MailboxNamesEnabled: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Mailboxes", "has_utf8_mailbox_names");
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Enables UTF-8 mailbox support
                     * @method enableUTF8MailboxNames
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the call was successful.
                     */
                    enableUTF8MailboxNames: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Mailboxes", "set_utf8_mailbox_names");
                        apiCall.addArgument("enabled", 1);
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Disables UTF-8 mailbox support
                     * @method disableUTF8MailboxNames
                     * @return {Promise} Returns a promise that resolves to a boolean indicating whether or not the call was successful.
                     */
                    disableUTF8MailboxNames: function() {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Mailboxes", "set_utf8_mailbox_names");
                        apiCall.addArgument("enabled", 0);
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Gets email account details
                     * @method getEmailAccountDetails
                     * @param {string} email email account
                     * @return {Promise} Returns a promise
                     */
                    getEmailAccountDetails: function(email) {
                        if (email) {

                            var apiCall = new APIREQUEST.Class();
                            apiCall.initialize("Email", "list_pops_with_disk");

                            apiCall.addFilter("email", "eq", email);
                            apiCall.addArgument("no_human_readable_keys", 1);
                            apiCall.addArgument("get_restrictions", 1);

                            var autoCreateApiCall = new APIREQUEST.Class();
                            autoCreateApiCall.initialize("Email", "get_mailbox_autocreate", {
                                "email": email,
                            });
                            var batchApiCall = new BATCH.Class([apiCall, autoCreateApiCall]);

                            var self = this;
                            return self.deferred(batchApiCall).promise
                                .then(function(responses) {

                                    responses = responses.map(function(response) {
                                        return response.data;
                                    });

                                    // Extract auto create setting
                                    var detailsResponse = responses[0];
                                    detailsResponse[0].autoCreateSubaddressFolders = responses[1];
                                    return detailsResponse;
                                })
                                .then(function(response) {
                                    if (_.isArray(response) && response.length === 0) {
                                        return $q.reject(LOCALE.maketext("You do not have an email account named “[_1]”.", _.escape(email)));
                                    }
                                    return decorateEmailObj(response[0]);
                                }, function(error) {
                                    return error;
                                });
                        }
                    },

                    /**
                     * Determine if Plus Address Folder Generation is Enabled
                     * @method isPlusAddressFolderCreationEnabled
                     * @param {string} email email account
                     * @return {Promise} Returns a promise
                     */
                    isPlusAddressFolderCreationEnabled: function(email) {
                        if (email) {
                            var apiCall = new APIREQUEST.Class();
                            apiCall.initialize("Email", "get_mailbox_autocreate", {
                                "email": email,
                            });

                            return this.deferred(apiCall).promise.then(function(response) {
                                return response;
                            });
                        }
                    },

                    /**
                     * Disable Plus Address Folder Generation
                     * @method disablePlusAddressFolderCreation
                     * @param {string} email email account
                     * @return {Promise} Returns a promise
                     */
                    disablePlusAddressFolderCreation: function(email) {
                        return this._createAPICall(
                            "disable_mailbox_autocreate",
                            {
                                email: email,
                                fullEmail: email,
                            },
                            {
                                success: LOCALE.translatable("You disabled automatic folder creation for “[_1]”."),
                                error: LOCALE.translatable("The system could not disable automatic folder creation for “[_1]”."),
                            }
                        );
                    },

                    /**
                     * Enable Plus Address Folder Generation
                     * @method enablePlusAddressFolderCreation
                     * @param {string} email email account
                     * @return {Promise} Returns a promise
                     */
                    enablePlusAddressFolderCreation: function(email) {
                        return this._createAPICall(
                            "enable_mailbox_autocreate",
                            {
                                email: email,
                                fullEmail: email,
                            },
                            {
                                success: LOCALE.translatable("You enabled automatic folder creation for “[_1]”."),
                                error: LOCALE.translatable("The system could not enable automatic folder creation for “[_1]”."),
                            }
                        );
                    },

                    /**
                     * Delete email account
                     * @method deleteEmail
                     * @param {string} email email account
                     * @return {Promise} Returns a promise
                     */
                    deleteEmail: function(email) {
                        if (email) {
                            var apiCall = new APIREQUEST.Class();
                            apiCall.initialize("Email", "delete_pop", { email: email });
                            return this.deferred(apiCall).promise
                                .then(function(response) {
                                    emailStats.used = emailStats.used - 1;

                                    if (emailStats.available !== -1) {
                                        emailStats.available = emailStats.available + 1;
                                    }

                                    return response;
                                });
                        }
                    },

                    /**
                     * Delete multiple emails
                     * @method deleteEmails
                     * @param {string[]} emails email accounts
                     * @return {Promise} Returns a promise
                     */
                    deleteEmails: function(emails) {
                        if (emails && emails.length > 0) {
                            var batchCalls = [];
                            for (var i = 0, len = emails.length; i < len; i++) {
                                var apiCall = new APIREQUEST.Class();
                                apiCall.initialize("Email", "delete_pop", { email: emails[i].email });
                                batchCalls.push(apiCall);
                            }

                            var batch = new BATCH.Class(batchCalls);

                            return this.deferred(batch).promise
                                .then(function(response) {
                                    emailStats.used = emailStats.used - emails.length;

                                    if (emailStats.available !== -1) {
                                        emailStats.available = emailStats.available + emails.length;
                                    }

                                    return response;
                                });
                        }
                    },

                    _createAPICall: function(method, attributes, messages) {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Email", method, attributes);
                        return this._dataWrapper(apiCall).then(
                            function() {
                                return messages.success ? { method: method, type: "success", message: LOCALE.makevar(messages.success, _.escape(attributes.fullEmail)), autoClose: 10000 } : { method: method, type: "success" };
                            },
                            function(error) {
                                return messages.error ? { method: method, type: "danger", message: LOCALE.makevar(messages.error, _.escape(attributes.fullEmail), error) } : { method: method, type: "danger" };
                            }
                        );
                    },

                    /**
                     * Gets the number of currently held messages in the mail queue for the specified email account
                     * @method getHeldMessageCount
                     * @param {Object} emailAccount The email account to get the held message count for
                     * @return {Promise} Returns a promise that resolves to an integer count of the number of held messages
                     */
                    getHeldMessageCount: function(email) {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Email", "get_held_message_count", {
                            email: email,
                        });
                        return this._dataWrapper(apiCall);
                    },

                    /**
                     * Deletes any held messages in the mail queue for the specified email account
                     * @method deleteHeldMessages
                     * @param {Object} emailAccount The email account to delete the held messages for
                     * @return {Promise} Returns a promise that resolves to an integer count of the number of deleted messages
                     */
                    deleteHeldMessages: function(email, releaseAfterDelete) {
                        var apiCall = new APIREQUEST.Class();
                        apiCall.initialize("Email", "delete_held_messages", {
                            email: email,
                            release_after_delete: (releaseAfterDelete) ? 1 : 0,
                        });
                        return this._dataWrapper(apiCall);
                    },

                    _handleSuspensions: function(newSettings, currentSuspendedState, suspendOptions) {
                        var calls = [];
                        if (suspendOptions.incoming !== currentSuspendedState.incoming) {
                            if (!suspendOptions.incoming) {
                                calls.push(this._createAPICall(
                                    "unsuspend_incoming",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You unsuspended incoming mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t unsuspend incoming mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            } else {
                                calls.push(this._createAPICall(
                                    "suspend_incoming",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You suspended incoming mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t suspend incoming mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }
                        }

                        if (suspendOptions.login !== currentSuspendedState.login) {
                            if (!suspendOptions.login) {
                                calls.push(this._createAPICall(
                                    "unsuspend_login",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You unsuspended logins for “[_1]”."),
                                        error: LOCALE.translatable("We can’t unsuspend logins for “[_1]”:“[_2]”"),
                                    }
                                ));
                            } else {
                                calls.push(this._createAPICall(
                                    "suspend_login",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You suspended logins for “[_1]”."),
                                        error: LOCALE.translatable("We can’t suspend logins for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }
                        }

                        if (suspendOptions.outgoing !== currentSuspendedState.outgoing) {
                            if (suspendOptions.outgoing === "hold") {
                                calls.push(this._createAPICall(
                                    "hold_outgoing",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("We’re holding outgoing mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t hold mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            } else if (suspendOptions.outgoing === "suspend") {
                                calls.push(this._createAPICall(
                                    "suspend_outgoing",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You suspended outgoing mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t suspend outgoing mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }

                            if (currentSuspendedState.outgoing === "suspend") {
                                calls.push(this._createAPICall(
                                    "unsuspend_outgoing",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You unsuspended outgoing mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t unsuspend outgoing mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }

                            if (currentSuspendedState.outgoing === "hold") {
                                calls.push(this._createAPICall(
                                    "release_outgoing",
                                    {
                                        email: newSettings.email,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You unsuspended outgoing mail for “[_1]”."),
                                        error: LOCALE.translatable("We can’t unsuspend outgoing mail for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }
                        }

                        return calls;
                    },

                    /**
                     * Updates email account details
                     * @method updateEmail
                     * @param {Object} oldSettings old email account values
                     * @param {Object} newSettings new email account values
                     * @param {Object} currentSuspendedState current suspended state
                     * @param {Object} suspendOptions suspend options
                     * @return {Promise} Returns a promise
                     */
                    updateEmail: function(oldSettings, newSettings, currentSuspendedState, suspendOptions) {
                        var calls = [];

                        if (oldSettings && newSettings) {

                            if (oldSettings.autoCreateSubaddressFolders !== newSettings.autoCreateSubaddressFolders) {
                                if (newSettings.autoCreateSubaddressFolders) {
                                    calls.push(this.enablePlusAddressFolderCreation(newSettings.email));
                                } else {
                                    calls.push(this.disablePlusAddressFolderCreation(newSettings.email));
                                }
                            }

                            if (typeof newSettings.password !== "undefined" && newSettings.password) {
                                calls.push(this._createAPICall(
                                    "passwd_pop",
                                    {
                                        email: newSettings.email,
                                        password: newSettings.password,
                                        domain: newSettings.domain,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You updated the password for “[_1]”."),
                                        error: LOCALE.translatable("We can’t update the password for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }

                            if (Number(oldSettings.diskquota).toFixed(2) !== newSettings.quota.toFixed(2)) {
                                calls.push(this._createAPICall(
                                    "edit_pop_quota",
                                    {
                                        email: newSettings.user,
                                        domain: newSettings.domain,
                                        quota: newSettings.quota,
                                        fullEmail: newSettings.email,
                                    },
                                    {
                                        success: LOCALE.translatable("You updated the storage space for “[_1]”."),
                                        error: LOCALE.translatable("We can’t update the storage space for “[_1]”:“[_2]”"),
                                    }
                                ));
                            }

                            if (suspendOptions && currentSuspendedState) {

                                // Handle deletion of queued email before processing the account suspensions
                                if (suspendOptions.deleteHeldMessages) {
                                    var releaseAfterDelete = currentSuspendedState.outgoing === "hold" && suspendOptions.outgoing === "allow";
                                    var self = this;
                                    return this.deleteHeldMessages(newSettings.email, releaseAfterDelete).then(
                                        function(deletedCount) {
                                            if (releaseAfterDelete) {
                                                suspendOptions.outgoing = "hold";
                                            }
                                            var additionalCalls = self._handleSuspensions(newSettings, currentSuspendedState, suspendOptions);
                                            var allCalls = calls.concat(additionalCalls);
                                            if ( allCalls.length > 0 ) {
                                                return $q.all(allCalls);
                                            } else {
                                                return $timeout( function() {
                                                    return $q.resolve(
                                                        [{
                                                            type: "success",
                                                            method: "delete_held_messages",
                                                            message: LOCALE.maketext("[quant,_1,message has,messages have] been queued for deletion from the outgoing mail queue.", deletedCount),
                                                        }]
                                                    );
                                                }, 0);
                                            }

                                        },
                                        function(error) {
                                            return $timeout( function() {
                                                return $q.resolve(
                                                    [{ type: "danger", message: error }]
                                                );
                                            }, 0);
                                        }
                                    );

                                } else {
                                    var additionalCalls = this._handleSuspensions(newSettings, currentSuspendedState, suspendOptions);
                                    calls = calls.concat(additionalCalls);
                                }
                            }

                            if ( calls.length > 0 ) {
                                return $q.all(calls);
                            } else {
                                return $timeout( function() {
                                    return $q.resolve(
                                        [{ type: "success", message: LOCALE.maketext("Email account “[_1]” is up to date.", newSettings.email), autoClose: 10000 }]
                                    );
                                }, 0);
                            }

                        }
                    },

                    /**
                     * Gets the list of email accounts
                     * @method getEmailAccounts
                     * @param  {Object}  apiParams An object providing the UAPI filter, paginate, and sort properties
                     * @return {Promise}           Returns a promise that resolves to the list of email accounts
                     */
                    getEmailAccounts: function(apiParams) {

                        if ( this.currentGetRequest && this.currentGetRequest.jqXHR ) {
                            this.currentGetRequest.jqXHR.abort();
                        }

                        var apiCall = new APIREQUEST.Class();

                        // We always format the data on the frontend so avoid doing it on the backend for non-displayed data
                        if (!apiParams) {
                            apiParams = {};
                        }
                        apiParams.no_human_readable_keys = 1;
                        apiParams.get_restrictions = 1;
                        apiParams.include_main = 1;
                        apiCall.initialize("Email", "list_pops_with_disk", apiParams);

                        var deferred = $q.defer();
                        var service = this;

                        // We want to be able to access the underlying jQuery XHR object here so that we can
                        // .abort() any in flight calls to list_pops_with_disk when a new one is submitted.
                        this.currentGetRequest = new APIService.AngularAPICall(apiCall, {
                            done: function(response) {

                                service.currentGetRequest = undefined;

                                if ( response.parsedResponse.error ) {
                                    deferred.reject(response.parsedResponse.error);
                                } else {

                                    var result = response.parsedResponse;

                                    var rdata = result.data;
                                    var rdatalen = rdata.length;
                                    for (var rd = 0; rd < rdatalen; rd++) {
                                        decorateEmailObj(rdata[rd]);
                                    }

                                    deferred.resolve(result);
                                }

                            },
                            fail: function() {
                                service.currentGetRequest = undefined;
                            },
                        });

                        return deferred.promise;
                    },

                });

                return new EmailAccountsService();
            },
        ]);
    }
);

(function(root) {
define("jquery-chosen", ["jquery"], function() {
  return (function() {
/*!
Chosen, a Select Box Enhancer for jQuery and Prototype
by Patrick Filler for Harvest, http://getharvest.com

Version 1.5.1
Full source at https://github.com/harvesthq/chosen
Copyright (c) 2011-2016 Harvest http://getharvest.com

MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
This file is generated by `grunt build`, do not edit it by hand.
*/

(function() {
  var $, AbstractChosen, Chosen, SelectParser, _ref,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  SelectParser = (function() {
    function SelectParser() {
      this.options_index = 0;
      this.parsed = [];
    }

    SelectParser.prototype.add_node = function(child) {
      if (child.nodeName.toUpperCase() === "OPTGROUP") {
        return this.add_group(child);
      } else {
        return this.add_option(child);
      }
    };

    SelectParser.prototype.add_group = function(group) {
      var group_position, option, _i, _len, _ref, _results;
      group_position = this.parsed.length;
      this.parsed.push({
        array_index: group_position,
        group: true,
        label: this.escapeExpression(group.label),
        title: group.title ? group.title : void 0,
        children: 0,
        disabled: group.disabled,
        classes: group.className
      });
      _ref = group.childNodes;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        option = _ref[_i];
        _results.push(this.add_option(option, group_position, group.disabled));
      }
      return _results;
    };

    SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
      if (option.nodeName.toUpperCase() === "OPTION") {
        if (option.text !== "") {
          if (group_position != null) {
            this.parsed[group_position].children += 1;
          }
          this.parsed.push({
            array_index: this.parsed.length,
            options_index: this.options_index,
            value: option.value,
            text: option.text,
            html: option.innerHTML,
            title: option.title ? option.title : void 0,
            selected: option.selected,
            disabled: group_disabled === true ? group_disabled : option.disabled,
            group_array_index: group_position,
            group_label: group_position != null ? this.parsed[group_position].label : null,
            classes: option.className,
            style: option.style.cssText
          });
        } else {
          this.parsed.push({
            array_index: this.parsed.length,
            options_index: this.options_index,
            empty: true
          });
        }
        return this.options_index += 1;
      }
    };

    SelectParser.prototype.escapeExpression = function(text) {
      var map, unsafe_chars;
      if ((text == null) || text === false) {
        return "";
      }
      if (!/[\&\<\>\"\'\`]/.test(text)) {
        return text;
      }
      map = {
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&#x27;",
        "`": "&#x60;"
      };
      unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g;
      return text.replace(unsafe_chars, function(chr) {
        return map[chr] || "&amp;";
      });
    };

    return SelectParser;

  })();

  SelectParser.select_to_array = function(select) {
    var child, parser, _i, _len, _ref;
    parser = new SelectParser();
    _ref = select.childNodes;
    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
      child = _ref[_i];
      parser.add_node(child);
    }
    return parser.parsed;
  };

  AbstractChosen = (function() {
    function AbstractChosen(form_field, options) {
      this.form_field = form_field;
      this.options = options != null ? options : {};
      if (!AbstractChosen.browser_is_supported()) {
        return;
      }
      this.is_multiple = this.form_field.multiple;
      this.set_default_text();
      this.set_default_values();
      this.setup();
      this.set_up_html();
      this.register_observers();
      this.on_ready();
    }

    AbstractChosen.prototype.set_default_values = function() {
      var _this = this;
      this.click_test_action = function(evt) {
        return _this.test_active_click(evt);
      };
      this.activate_action = function(evt) {
        return _this.activate_field(evt);
      };
      this.active_field = false;
      this.mouse_on_container = false;
      this.results_showing = false;
      this.result_highlighted = null;
      this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
      this.disable_search_threshold = this.options.disable_search_threshold || 0;
      this.disable_search = this.options.disable_search || false;
      this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
      this.group_search = this.options.group_search != null ? this.options.group_search : true;
      this.search_contains = this.options.search_contains || false;
      this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true;
      this.max_selected_options = this.options.max_selected_options || Infinity;
      this.inherit_select_classes = this.options.inherit_select_classes || false;
      this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true;
      this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true;
      this.include_group_label_in_selected = this.options.include_group_label_in_selected || false;
      return this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY;
    };

    AbstractChosen.prototype.set_default_text = function() {
      if (this.form_field.getAttribute("data-placeholder")) {
        this.default_text = this.form_field.getAttribute("data-placeholder");
      } else if (this.is_multiple) {
        this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
      } else {
        this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
      }
      return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
    };

    AbstractChosen.prototype.choice_label = function(item) {
      if (this.include_group_label_in_selected && (item.group_label != null)) {
        return "<b class='group-name'>" + item.group_label + "</b>" + item.html;
      } else {
        return item.html;
      }
    };

    AbstractChosen.prototype.mouse_enter = function() {
      return this.mouse_on_container = true;
    };

    AbstractChosen.prototype.mouse_leave = function() {
      return this.mouse_on_container = false;
    };

    AbstractChosen.prototype.input_focus = function(evt) {
      var _this = this;
      if (this.is_multiple) {
        if (!this.active_field) {
          return setTimeout((function() {
            return _this.container_mousedown();
          }), 50);
        }
      } else {
        if (!this.active_field) {
          return this.activate_field();
        }
      }
    };

    AbstractChosen.prototype.input_blur = function(evt) {
      var _this = this;
      if (!this.mouse_on_container) {
        this.active_field = false;
        return setTimeout((function() {
          return _this.blur_test();
        }), 100);
      }
    };

    AbstractChosen.prototype.results_option_build = function(options) {
      var content, data, data_content, shown_results, _i, _len, _ref;
      content = '';
      shown_results = 0;
      _ref = this.results_data;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        data = _ref[_i];
        data_content = '';
        if (data.group) {
          data_content = this.result_add_group(data);
        } else {
          data_content = this.result_add_option(data);
        }
        if (data_content !== '') {
          shown_results++;
          content += data_content;
        }
        if (options != null ? options.first : void 0) {
          if (data.selected && this.is_multiple) {
            this.choice_build(data);
          } else if (data.selected && !this.is_multiple) {
            this.single_set_selected_text(this.choice_label(data));
          }
        }
        if (shown_results >= this.max_shown_results) {
          break;
        }
      }
      return content;
    };

    AbstractChosen.prototype.result_add_option = function(option) {
      var classes, option_el;
      if (!option.search_match) {
        return '';
      }
      if (!this.include_option_in_results(option)) {
        return '';
      }
      classes = [];
      if (!option.disabled && !(option.selected && this.is_multiple)) {
        classes.push("active-result");
      }
      if (option.disabled && !(option.selected && this.is_multiple)) {
        classes.push("disabled-result");
      }
      if (option.selected) {
        classes.push("result-selected");
      }
      if (option.group_array_index != null) {
        classes.push("group-option");
      }
      if (option.classes !== "") {
        classes.push(option.classes);
      }
      option_el = document.createElement("li");
      option_el.className = classes.join(" ");
      option_el.style.cssText = option.style;
      option_el.setAttribute("data-option-array-index", option.array_index);
      option_el.innerHTML = option.search_text;
      if (option.title) {
        option_el.title = option.title;
      }
      return this.outerHTML(option_el);
    };

    AbstractChosen.prototype.result_add_group = function(group) {
      var classes, group_el;
      if (!(group.search_match || group.group_match)) {
        return '';
      }
      if (!(group.active_options > 0)) {
        return '';
      }
      classes = [];
      classes.push("group-result");
      if (group.classes) {
        classes.push(group.classes);
      }
      group_el = document.createElement("li");
      group_el.className = classes.join(" ");
      group_el.innerHTML = group.search_text;
      if (group.title) {
        group_el.title = group.title;
      }
      return this.outerHTML(group_el);
    };

    AbstractChosen.prototype.results_update_field = function() {
      this.set_default_text();
      if (!this.is_multiple) {
        this.results_reset_cleanup();
      }
      this.result_clear_highlight();
      this.results_build();
      if (this.results_showing) {
        return this.winnow_results();
      }
    };

    AbstractChosen.prototype.reset_single_select_options = function() {
      var result, _i, _len, _ref, _results;
      _ref = this.results_data;
      _results = [];
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        result = _ref[_i];
        if (result.selected) {
          _results.push(result.selected = false);
        } else {
          _results.push(void 0);
        }
      }
      return _results;
    };

    AbstractChosen.prototype.results_toggle = function() {
      if (this.results_showing) {
        return this.results_hide();
      } else {
        return this.results_show();
      }
    };

    AbstractChosen.prototype.results_search = function(evt) {
      if (this.results_showing) {
        return this.winnow_results();
      } else {
        return this.results_show();
      }
    };

    AbstractChosen.prototype.winnow_results = function() {
      var escapedSearchText, option, regex, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref;
      this.no_results_clear();
      results = 0;
      searchText = this.get_search_text();
      escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
      zregex = new RegExp(escapedSearchText, 'i');
      regex = this.get_search_regex(escapedSearchText);
      _ref = this.results_data;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        option = _ref[_i];
        option.search_match = false;
        results_group = null;
        if (this.include_option_in_results(option)) {
          if (option.group) {
            option.group_match = false;
            option.active_options = 0;
          }
          if ((option.group_array_index != null) && this.results_data[option.group_array_index]) {
            results_group = this.results_data[option.group_array_index];
            if (results_group.active_options === 0 && results_group.search_match) {
              results += 1;
            }
            results_group.active_options += 1;
          }
          option.search_text = option.group ? option.label : option.html;
          if (!(option.group && !this.group_search)) {
            option.search_match = this.search_string_match(option.search_text, regex);
            if (option.search_match && !option.group) {
              results += 1;
            }
            if (option.search_match) {
              if (searchText.length) {
                startpos = option.search_text.search(zregex);
                text = option.search_text.substr(0, startpos + searchText.length) + '</em>' + option.search_text.substr(startpos + searchText.length);
                option.search_text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
              }
              if (results_group != null) {
                results_group.group_match = true;
              }
            } else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) {
              option.search_match = true;
            }
          }
        }
      }
      this.result_clear_highlight();
      if (results < 1 && searchText.length) {
        this.update_results_content("");
        return this.no_results(searchText);
      } else {
        this.update_results_content(this.results_option_build());
        return this.winnow_results_set_highlight();
      }
    };

    AbstractChosen.prototype.get_search_regex = function(escaped_search_string) {
      var regex_anchor;
      regex_anchor = this.search_contains ? "" : "^";
      return new RegExp(regex_anchor + escaped_search_string, 'i');
    };

    AbstractChosen.prototype.search_string_match = function(search_string, regex) {
      var part, parts, _i, _len;
      if (regex.test(search_string)) {
        return true;
      } else if (this.enable_split_word_search && (search_string.indexOf(" ") >= 0 || search_string.indexOf("[") === 0)) {
        parts = search_string.replace(/\[|\]/g, "").split(" ");
        if (parts.length) {
          for (_i = 0, _len = parts.length; _i < _len; _i++) {
            part = parts[_i];
            if (regex.test(part)) {
              return true;
            }
          }
        }
      }
    };

    AbstractChosen.prototype.choices_count = function() {
      var option, _i, _len, _ref;
      if (this.selected_option_count != null) {
        return this.selected_option_count;
      }
      this.selected_option_count = 0;
      _ref = this.form_field.options;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        option = _ref[_i];
        if (option.selected) {
          this.selected_option_count += 1;
        }
      }
      return this.selected_option_count;
    };

    AbstractChosen.prototype.choices_click = function(evt) {
      evt.preventDefault();
      if (!(this.results_showing || this.is_disabled)) {
        return this.results_show();
      }
    };

    AbstractChosen.prototype.keyup_checker = function(evt) {
      var stroke, _ref;
      stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
      this.search_field_scale();
      switch (stroke) {
        case 8:
          if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) {
            return this.keydown_backstroke();
          } else if (!this.pending_backstroke) {
            this.result_clear_highlight();
            return this.results_search();
          }
          break;
        case 13:
          evt.preventDefault();
          if (this.results_showing) {
            return this.result_select(evt);
          }
          break;
        case 27:
          if (this.results_showing) {
            this.results_hide();
          }
          return true;
        case 9:
        case 38:
        case 40:
        case 16:
        case 91:
        case 17:
        case 18:
          break;
        default:
          return this.results_search();
      }
    };

    AbstractChosen.prototype.clipboard_event_checker = function(evt) {
      var _this = this;
      return setTimeout((function() {
        return _this.results_search();
      }), 50);
    };

    AbstractChosen.prototype.container_width = function() {
      if (this.options.width != null) {
        return this.options.width;
      } else {
        return "" + this.form_field.offsetWidth + "px";
      }
    };

    AbstractChosen.prototype.include_option_in_results = function(option) {
      if (this.is_multiple && (!this.display_selected_options && option.selected)) {
        return false;
      }
      if (!this.display_disabled_options && option.disabled) {
        return false;
      }
      if (option.empty) {
        return false;
      }
      return true;
    };

    AbstractChosen.prototype.search_results_touchstart = function(evt) {
      this.touch_started = true;
      return this.search_results_mouseover(evt);
    };

    AbstractChosen.prototype.search_results_touchmove = function(evt) {
      this.touch_started = false;
      return this.search_results_mouseout(evt);
    };

    AbstractChosen.prototype.search_results_touchend = function(evt) {
      if (this.touch_started) {
        return this.search_results_mouseup(evt);
      }
    };

    AbstractChosen.prototype.outerHTML = function(element) {
      var tmp;
      if (element.outerHTML) {
        return element.outerHTML;
      }
      tmp = document.createElement("div");
      tmp.appendChild(element);
      return tmp.innerHTML;
    };

    AbstractChosen.browser_is_supported = function() {
      if (/iP(od|hone)/i.test(window.navigator.userAgent)) {
        return false;
      }
      if (/Android/i.test(window.navigator.userAgent)) {
        if (/Mobile/i.test(window.navigator.userAgent)) {
          return false;
        }
      }
      if (/IEMobile/i.test(window.navigator.userAgent)) {
        return false;
      }
      if (/Windows Phone/i.test(window.navigator.userAgent)) {
        return false;
      }
      if (/BlackBerry/i.test(window.navigator.userAgent)) {
        return false;
      }
      if (/BB10/i.test(window.navigator.userAgent)) {
        return false;
      }
      if (window.navigator.appName === "Microsoft Internet Explorer") {
        return document.documentMode >= 8;
      }
      return true;
    };

    AbstractChosen.default_multiple_text = "Select Some Options";

    AbstractChosen.default_single_text = "Select an Option";

    AbstractChosen.default_no_result_text = "No results match";

    return AbstractChosen;

  })();

  $ = jQuery;

  $.fn.extend({
    chosen: function(options) {
      if (!AbstractChosen.browser_is_supported()) {
        return this;
      }
      return this.each(function(input_field) {
        var $this, chosen;
        $this = $(this);
        chosen = $this.data('chosen');
        if (options === 'destroy') {
          if (chosen instanceof Chosen) {
            chosen.destroy();
          }
          return;
        }
        if (!(chosen instanceof Chosen)) {
          $this.data('chosen', new Chosen(this, options));
        }
      });
    }
  });

  Chosen = (function(_super) {
    __extends(Chosen, _super);

    function Chosen() {
      _ref = Chosen.__super__.constructor.apply(this, arguments);
      return _ref;
    }

    Chosen.prototype.setup = function() {
      this.form_field_jq = $(this.form_field);
      this.current_selectedIndex = this.form_field.selectedIndex;
      return this.is_rtl = this.form_field_jq.hasClass("chosen-rtl");
    };

    Chosen.prototype.set_up_html = function() {
      var container_classes, container_props;
      container_classes = ["chosen-container"];
      container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single"));
      if (this.inherit_select_classes && this.form_field.className) {
        container_classes.push(this.form_field.className);
      }
      if (this.is_rtl) {
        container_classes.push("chosen-rtl");
      }
      container_props = {
        'class': container_classes.join(' '),
        'style': "width: " + (this.container_width()) + ";",
        'title': this.form_field.title
      };
      if (this.form_field.id.length) {
        container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen";
      }
      this.container = $("<div />", container_props);
      if (this.is_multiple) {
        this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>');
      } else {
        this.container.html('<a class="chosen-single chosen-default"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>');
      }
      this.form_field_jq.hide().after(this.container);
      this.dropdown = this.container.find('div.chosen-drop').first();
      this.search_field = this.container.find('input').first();
      this.search_results = this.container.find('ul.chosen-results').first();
      this.search_field_scale();
      this.search_no_results = this.container.find('li.no-results').first();
      if (this.is_multiple) {
        this.search_choices = this.container.find('ul.chosen-choices').first();
        this.search_container = this.container.find('li.search-field').first();
      } else {
        this.search_container = this.container.find('div.chosen-search').first();
        this.selected_item = this.container.find('.chosen-single').first();
      }
      this.results_build();
      this.set_tab_index();
      return this.set_label_behavior();
    };

    Chosen.prototype.on_ready = function() {
      return this.form_field_jq.trigger("chosen:ready", {
        chosen: this
      });
    };

    Chosen.prototype.register_observers = function() {
      var _this = this;
      this.container.bind('touchstart.chosen', function(evt) {
        _this.container_mousedown(evt);
        return evt.preventDefault();
      });
      this.container.bind('touchend.chosen', function(evt) {
        _this.container_mouseup(evt);
        return evt.preventDefault();
      });
      this.container.bind('mousedown.chosen', function(evt) {
        _this.container_mousedown(evt);
      });
      this.container.bind('mouseup.chosen', function(evt) {
        _this.container_mouseup(evt);
      });
      this.container.bind('mouseenter.chosen', function(evt) {
        _this.mouse_enter(evt);
      });
      this.container.bind('mouseleave.chosen', function(evt) {
        _this.mouse_leave(evt);
      });
      this.search_results.bind('mouseup.chosen', function(evt) {
        _this.search_results_mouseup(evt);
      });
      this.search_results.bind('mouseover.chosen', function(evt) {
        _this.search_results_mouseover(evt);
      });
      this.search_results.bind('mouseout.chosen', function(evt) {
        _this.search_results_mouseout(evt);
      });
      this.search_results.bind('mousewheel.chosen DOMMouseScroll.chosen', function(evt) {
        _this.search_results_mousewheel(evt);
      });
      this.search_results.bind('touchstart.chosen', function(evt) {
        _this.search_results_touchstart(evt);
      });
      this.search_results.bind('touchmove.chosen', function(evt) {
        _this.search_results_touchmove(evt);
      });
      this.search_results.bind('touchend.chosen', function(evt) {
        _this.search_results_touchend(evt);
      });
      this.form_field_jq.bind("chosen:updated.chosen", function(evt) {
        _this.results_update_field(evt);
      });
      this.form_field_jq.bind("chosen:activate.chosen", function(evt) {
        _this.activate_field(evt);
      });
      this.form_field_jq.bind("chosen:open.chosen", function(evt) {
        _this.container_mousedown(evt);
      });
      this.form_field_jq.bind("chosen:close.chosen", function(evt) {
        _this.input_blur(evt);
      });
      this.search_field.bind('blur.chosen', function(evt) {
        _this.input_blur(evt);
      });
      this.search_field.bind('keyup.chosen', function(evt) {
        _this.keyup_checker(evt);
      });
      this.search_field.bind('keydown.chosen', function(evt) {
        _this.keydown_checker(evt);
      });
      this.search_field.bind('focus.chosen', function(evt) {
        _this.input_focus(evt);
      });
      this.search_field.bind('cut.chosen', function(evt) {
        _this.clipboard_event_checker(evt);
      });
      this.search_field.bind('paste.chosen', function(evt) {
        _this.clipboard_event_checker(evt);
      });
      if (this.is_multiple) {
        return this.search_choices.bind('click.chosen', function(evt) {
          _this.choices_click(evt);
        });
      } else {
        return this.container.bind('click.chosen', function(evt) {
          evt.preventDefault();
        });
      }
    };

    Chosen.prototype.destroy = function() {
      $(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
      if (this.search_field[0].tabIndex) {
        this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex;
      }
      this.container.remove();
      this.form_field_jq.removeData('chosen');
      return this.form_field_jq.show();
    };

    Chosen.prototype.search_field_disabled = function() {
      this.is_disabled = this.form_field_jq[0].disabled;
      if (this.is_disabled) {
        this.container.addClass('chosen-disabled');
        this.search_field[0].disabled = true;
        if (!this.is_multiple) {
          this.selected_item.unbind("focus.chosen", this.activate_action);
        }
        return this.close_field();
      } else {
        this.container.removeClass('chosen-disabled');
        this.search_field[0].disabled = false;
        if (!this.is_multiple) {
          return this.selected_item.bind("focus.chosen", this.activate_action);
        }
      }
    };

    Chosen.prototype.container_mousedown = function(evt) {
      if (!this.is_disabled) {
        if (evt && evt.type === "mousedown" && !this.results_showing) {
          evt.preventDefault();
        }
        if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
          if (!this.active_field) {
            if (this.is_multiple) {
              this.search_field.val("");
            }
            $(this.container[0].ownerDocument).bind('click.chosen', this.click_test_action);
            this.results_show();
          } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) {
            evt.preventDefault();
            this.results_toggle();
          }
          return this.activate_field();
        }
      }
    };

    Chosen.prototype.container_mouseup = function(evt) {
      if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
        return this.results_reset(evt);
      }
    };

    Chosen.prototype.search_results_mousewheel = function(evt) {
      var delta;
      if (evt.originalEvent) {
        delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail;
      }
      if (delta != null) {
        evt.preventDefault();
        if (evt.type === 'DOMMouseScroll') {
          delta = delta * 40;
        }
        return this.search_results.scrollTop(delta + this.search_results.scrollTop());
      }
    };

    Chosen.prototype.blur_test = function(evt) {
      if (!this.active_field && this.container.hasClass("chosen-container-active")) {
        return this.close_field();
      }
    };

    Chosen.prototype.close_field = function() {
      $(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
      this.active_field = false;
      this.results_hide();
      this.container.removeClass("chosen-container-active");
      this.clear_backstroke();
      this.show_search_field_default();
      return this.search_field_scale();
    };

    Chosen.prototype.activate_field = function() {
      this.container.addClass("chosen-container-active");
      this.active_field = true;
      this.search_field.val(this.search_field.val());
      return this.search_field.focus();
    };

    Chosen.prototype.test_active_click = function(evt) {
      var active_container;
      active_container = $(evt.target).closest('.chosen-container');
      if (active_container.length && this.container[0] === active_container[0]) {
        return this.active_field = true;
      } else {
        return this.close_field();
      }
    };

    Chosen.prototype.results_build = function() {
      this.parsing = true;
      this.selected_option_count = null;
      this.results_data = SelectParser.select_to_array(this.form_field);
      if (this.is_multiple) {
        this.search_choices.find("li.search-choice").remove();
      } else if (!this.is_multiple) {
        this.single_set_selected_text();
        if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
          this.search_field[0].readOnly = true;
          this.container.addClass("chosen-container-single-nosearch");
        } else {
          this.search_field[0].readOnly = false;
          this.container.removeClass("chosen-container-single-nosearch");
        }
      }
      this.update_results_content(this.results_option_build({
        first: true
      }));
      this.search_field_disabled();
      this.show_search_field_default();
      this.search_field_scale();
      return this.parsing = false;
    };

    Chosen.prototype.result_do_highlight = function(el) {
      var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
      if (el.length) {
        this.result_clear_highlight();
        this.result_highlight = el;
        this.result_highlight.addClass("highlighted");
        maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
        visible_top = this.search_results.scrollTop();
        visible_bottom = maxHeight + visible_top;
        high_top = this.result_highlight.position().top + this.search_results.scrollTop();
        high_bottom = high_top + this.result_highlight.outerHeight();
        if (high_bottom >= visible_bottom) {
          return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
        } else if (high_top < visible_top) {
          return this.search_results.scrollTop(high_top);
        }
      }
    };

    Chosen.prototype.result_clear_highlight = function() {
      if (this.result_highlight) {
        this.result_highlight.removeClass("highlighted");
      }
      return this.result_highlight = null;
    };

    Chosen.prototype.results_show = function() {
      if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
        this.form_field_jq.trigger("chosen:maxselected", {
          chosen: this
        });
        return false;
      }
      this.container.addClass("chosen-with-drop");
      this.results_showing = true;
      this.search_field.focus();
      this.search_field.val(this.search_field.val());
      this.winnow_results();
      return this.form_field_jq.trigger("chosen:showing_dropdown", {
        chosen: this
      });
    };

    Chosen.prototype.update_results_content = function(content) {
      return this.search_results.html(content);
    };

    Chosen.prototype.results_hide = function() {
      if (this.results_showing) {
        this.result_clear_highlight();
        this.container.removeClass("chosen-with-drop");
        this.form_field_jq.trigger("chosen:hiding_dropdown", {
          chosen: this
        });
      }
      return this.results_showing = false;
    };

    Chosen.prototype.set_tab_index = function(el) {
      var ti;
      if (this.form_field.tabIndex) {
        ti = this.form_field.tabIndex;
        this.form_field.tabIndex = -1;
        return this.search_field[0].tabIndex = ti;
      }
    };

    Chosen.prototype.set_label_behavior = function() {
      var _this = this;
      this.form_field_label = this.form_field_jq.parents("label");
      if (!this.form_field_label.length && this.form_field.id.length) {
        this.form_field_label = $("label[for='" + this.form_field.id + "']");
      }
      if (this.form_field_label.length > 0) {
        return this.form_field_label.bind('click.chosen', function(evt) {
          if (_this.is_multiple) {
            return _this.container_mousedown(evt);
          } else {
            return _this.activate_field();
          }
        });
      }
    };

    Chosen.prototype.show_search_field_default = function() {
      if (this.is_multiple && this.choices_count() < 1 && !this.active_field) {
        this.search_field.val(this.default_text);
        return this.search_field.addClass("default");
      } else {
        this.search_field.val("");
        return this.search_field.removeClass("default");
      }
    };

    Chosen.prototype.search_results_mouseup = function(evt) {
      var target;
      target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
      if (target.length) {
        this.result_highlight = target;
        this.result_select(evt);
        return this.search_field.focus();
      }
    };

    Chosen.prototype.search_results_mouseover = function(evt) {
      var target;
      target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
      if (target) {
        return this.result_do_highlight(target);
      }
    };

    Chosen.prototype.search_results_mouseout = function(evt) {
      if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
        return this.result_clear_highlight();
      }
    };

    Chosen.prototype.choice_build = function(item) {
      var choice, close_link,
        _this = this;
      choice = $('<li />', {
        "class": "search-choice"
      }).html("<span>" + (this.choice_label(item)) + "</span>");
      if (item.disabled) {
        choice.addClass('search-choice-disabled');
      } else {
        close_link = $('<a />', {
          "class": 'search-choice-close',
          'data-option-array-index': item.array_index
        });
        close_link.bind('click.chosen', function(evt) {
          return _this.choice_destroy_link_click(evt);
        });
        choice.append(close_link);
      }
      return this.search_container.before(choice);
    };

    Chosen.prototype.choice_destroy_link_click = function(evt) {
      evt.preventDefault();
      evt.stopPropagation();
      if (!this.is_disabled) {
        return this.choice_destroy($(evt.target));
      }
    };

    Chosen.prototype.choice_destroy = function(link) {
      if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) {
        this.show_search_field_default();
        if (this.is_multiple && this.choices_count() > 0 && this.search_field.val().length < 1) {
          this.results_hide();
        }
        link.parents('li').first().remove();
        return this.search_field_scale();
      }
    };

    Chosen.prototype.results_reset = function() {
      this.reset_single_select_options();
      this.form_field.options[0].selected = true;
      this.single_set_selected_text();
      this.show_search_field_default();
      this.results_reset_cleanup();
      this.form_field_jq.trigger("change");
      if (this.active_field) {
        return this.results_hide();
      }
    };

    Chosen.prototype.results_reset_cleanup = function() {
      this.current_selectedIndex = this.form_field.selectedIndex;
      return this.selected_item.find("abbr").remove();
    };

    Chosen.prototype.result_select = function(evt) {
      var high, item;
      if (this.result_highlight) {
        high = this.result_highlight;
        this.result_clear_highlight();
        if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
          this.form_field_jq.trigger("chosen:maxselected", {
            chosen: this
          });
          return false;
        }
        if (this.is_multiple) {
          high.removeClass("active-result");
        } else {
          this.reset_single_select_options();
        }
        high.addClass("result-selected");
        item = this.results_data[high[0].getAttribute("data-option-array-index")];
        item.selected = true;
        this.form_field.options[item.options_index].selected = true;
        this.selected_option_count = null;
        if (this.is_multiple) {
          this.choice_build(item);
        } else {
          this.single_set_selected_text(this.choice_label(item));
        }
        if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) {
          this.results_hide();
        }
        this.show_search_field_default();
        if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
          this.form_field_jq.trigger("change", {
            'selected': this.form_field.options[item.options_index].value
          });
        }
        this.current_selectedIndex = this.form_field.selectedIndex;
        evt.preventDefault();
        return this.search_field_scale();
      }
    };

    Chosen.prototype.single_set_selected_text = function(text) {
      if (text == null) {
        text = this.default_text;
      }
      if (text === this.default_text) {
        this.selected_item.addClass("chosen-default");
      } else {
        this.single_deselect_control_build();
        this.selected_item.removeClass("chosen-default");
      }
      return this.selected_item.find("span").html(text);
    };

    Chosen.prototype.result_deselect = function(pos) {
      var result_data;
      result_data = this.results_data[pos];
      if (!this.form_field.options[result_data.options_index].disabled) {
        result_data.selected = false;
        this.form_field.options[result_data.options_index].selected = false;
        this.selected_option_count = null;
        this.result_clear_highlight();
        if (this.results_showing) {
          this.winnow_results();
        }
        this.form_field_jq.trigger("change", {
          deselected: this.form_field.options[result_data.options_index].value
        });
        this.search_field_scale();
        return true;
      } else {
        return false;
      }
    };

    Chosen.prototype.single_deselect_control_build = function() {
      if (!this.allow_single_deselect) {
        return;
      }
      if (!this.selected_item.find("abbr").length) {
        this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
      }
      return this.selected_item.addClass("chosen-single-with-deselect");
    };

    Chosen.prototype.get_search_text = function() {
      return $('<div/>').text($.trim(this.search_field.val())).html();
    };

    Chosen.prototype.winnow_results_set_highlight = function() {
      var do_high, selected_results;
      selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
      do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
      if (do_high != null) {
        return this.result_do_highlight(do_high);
      }
    };

    Chosen.prototype.no_results = function(terms) {
      var no_results_html;
      no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
      no_results_html.find("span").first().html(terms);
      this.search_results.append(no_results_html);
      return this.form_field_jq.trigger("chosen:no_results", {
        chosen: this
      });
    };

    Chosen.prototype.no_results_clear = function() {
      return this.search_results.find(".no-results").remove();
    };

    Chosen.prototype.keydown_arrow = function() {
      var next_sib;
      if (this.results_showing && this.result_highlight) {
        next_sib = this.result_highlight.nextAll("li.active-result").first();
        if (next_sib) {
          return this.result_do_highlight(next_sib);
        }
      } else {
        return this.results_show();
      }
    };

    Chosen.prototype.keyup_arrow = function() {
      var prev_sibs;
      if (!this.results_showing && !this.is_multiple) {
        return this.results_show();
      } else if (this.result_highlight) {
        prev_sibs = this.result_highlight.prevAll("li.active-result");
        if (prev_sibs.length) {
          return this.result_do_highlight(prev_sibs.first());
        } else {
          if (this.choices_count() > 0) {
            this.results_hide();
          }
          return this.result_clear_highlight();
        }
      }
    };

    Chosen.prototype.keydown_backstroke = function() {
      var next_available_destroy;
      if (this.pending_backstroke) {
        this.choice_destroy(this.pending_backstroke.find("a").first());
        return this.clear_backstroke();
      } else {
        next_available_destroy = this.search_container.siblings("li.search-choice").last();
        if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
          this.pending_backstroke = next_available_destroy;
          if (this.single_backstroke_delete) {
            return this.keydown_backstroke();
          } else {
            return this.pending_backstroke.addClass("search-choice-focus");
          }
        }
      }
    };

    Chosen.prototype.clear_backstroke = function() {
      if (this.pending_backstroke) {
        this.pending_backstroke.removeClass("search-choice-focus");
      }
      return this.pending_backstroke = null;
    };

    Chosen.prototype.keydown_checker = function(evt) {
      var stroke, _ref1;
      stroke = (_ref1 = evt.which) != null ? _ref1 : evt.keyCode;
      this.search_field_scale();
      if (stroke !== 8 && this.pending_backstroke) {
        this.clear_backstroke();
      }
      switch (stroke) {
        case 8:
          this.backstroke_length = this.search_field.val().length;
          break;
        case 9:
          if (this.results_showing && !this.is_multiple) {
            this.result_select(evt);
          }
          this.mouse_on_container = false;
          break;
        case 13:
          if (this.results_showing) {
            evt.preventDefault();
          }
          break;
        case 32:
          if (this.disable_search) {
            evt.preventDefault();
          }
          break;
        case 38:
          evt.preventDefault();
          this.keyup_arrow();
          break;
        case 40:
          evt.preventDefault();
          this.keydown_arrow();
          break;
      }
    };

    Chosen.prototype.search_field_scale = function() {
      var div, f_width, h, style, style_block, styles, w, _i, _len;
      if (this.is_multiple) {
        h = 0;
        w = 0;
        style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
        styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
        for (_i = 0, _len = styles.length; _i < _len; _i++) {
          style = styles[_i];
          style_block += style + ":" + this.search_field.css(style) + ";";
        }
        div = $('<div />', {
          'style': style_block
        });
        div.text(this.search_field.val());
        $('body').append(div);
        w = div.width() + 25;
        div.remove();
        f_width = this.container.outerWidth();
        if (w > f_width - 10) {
          w = f_width - 10;
        }
        return this.search_field.css({
          'width': w + 'px'
        });
      }
    };

    return Chosen;

  })(AbstractChosen);

}).call(this);


  }).apply(root, arguments);
});
}(this));

(function(root) {
define("angular-chosen", ["angular","jquery-chosen"], function() {
  return (function() {
/**
 * angular-chosen-localytics - Angular Chosen directive is an AngularJS Directive that brings the Chosen jQuery in a Angular way
 * @version v1.3.0
 * @link http://github.com/leocaseiro/angular-chosen
 * @license MIT
 */
(function() {
  var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  angular.module('localytics.directives', []);

  angular.module('localytics.directives').directive('chosen', [
    '$timeout', function($timeout) {
      var CHOSEN_OPTION_WHITELIST, NG_OPTIONS_REGEXP, isEmpty, snakeCase;
      NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
      CHOSEN_OPTION_WHITELIST = ['persistentCreateOption', 'createOptionText', 'createOption', 'skipNoResults', 'noResultsText', 'allowSingleDeselect', 'disableSearchThreshold', 'disableSearch', 'enableSplitWordSearch', 'inheritSelectClasses', 'maxSelectedOptions', 'placeholderTextMultiple', 'placeholderTextSingle', 'searchContains', 'singleBackstrokeDelete', 'displayDisabledOptions', 'displaySelectedOptions', 'width', 'includeGroupLabelInSelected', 'maxShownResults'];
      snakeCase = function(input) {
        return input.replace(/[A-Z]/g, function($1) {
          return "_" + ($1.toLowerCase());
        });
      };
      isEmpty = function(value) {
        var key;
        if (angular.isArray(value)) {
          return value.length === 0;
        } else if (angular.isObject(value)) {
          for (key in value) {
            if (value.hasOwnProperty(key)) {
              return false;
            }
          }
        }
        return true;
      };
      return {
        restrict: 'A',
        require: '?ngModel',
        priority: 1,
        link: function(scope, element, attr, ngModel) {
          var chosen, empty, initOrUpdate, match, options, origRender, startLoading, stopLoading, updateMessage, valuesExpr, viewWatch;
          scope.disabledValuesHistory = scope.disabledValuesHistory ? scope.disabledValuesHistory : [];
          element = $(element);
          element.addClass('localytics-chosen');
          options = scope.$eval(attr.chosen) || {};
          angular.forEach(attr, function(value, key) {
            if (indexOf.call(CHOSEN_OPTION_WHITELIST, key) >= 0) {
              return attr.$observe(key, function(value) {
                options[snakeCase(key)] = String(element.attr(attr.$attr[key])).slice(0, 2) === '{{' ? value : scope.$eval(value);
                return updateMessage();
              });
            }
          });
          startLoading = function() {
            return element.addClass('loading').attr('disabled', true).trigger('chosen:updated');
          };
          stopLoading = function() {
            element.removeClass('loading');
            if (angular.isDefined(attr.disabled)) {
              element.attr('disabled', attr.disabled);
            } else {
              element.attr('disabled', false);
            }
            return element.trigger('chosen:updated');
          };
          chosen = null;
          empty = false;
          initOrUpdate = function() {
            var defaultText;
            if (chosen) {
              return element.trigger('chosen:updated');
            } else {
              $timeout(function() {
                chosen = element.chosen(options).data('chosen');
              });
              if (angular.isObject(chosen)) {
                return defaultText = chosen.default_text;
              }
            }
          };
          updateMessage = function() {
            if (empty) {
              element.attr('data-placeholder', chosen.results_none_found).attr('disabled', true);
            } else {
              element.removeAttr('data-placeholder');
            }
            return element.trigger('chosen:updated');
          };
          if (ngModel) {
            origRender = ngModel.$render;
            ngModel.$render = function() {
              origRender();
              return initOrUpdate();
            };
            element.on('chosen:hiding_dropdown', function() {
              return scope.$apply(function() {
                return ngModel.$setTouched();
              });
            });
            if (attr.multiple) {
              viewWatch = function() {
                return ngModel.$viewValue;
              };
              scope.$watch(viewWatch, ngModel.$render, true);
            }
          } else {
            initOrUpdate();
          }
          attr.$observe('disabled', function() {
            return element.trigger('chosen:updated');
          });
          if (attr.ngOptions && ngModel) {
            match = attr.ngOptions.match(NG_OPTIONS_REGEXP);
            valuesExpr = match[7];
            scope.$watchCollection(valuesExpr, function(newVal, oldVal) {
              var timer;
              return timer = $timeout(function() {
                if (angular.isUndefined(newVal)) {
                  return startLoading();
                } else {
                  empty = isEmpty(newVal);
                  stopLoading();
                  return updateMessage();
                }
              });
            });
            return scope.$on('$destroy', function(event) {
              if (typeof timer !== "undefined" && timer !== null) {
                return $timeout.cancel(timer);
              }
            });
          }
        }
      };
    }
  ]);

}).call(this);


  }).apply(root, arguments);
});
}(this));

/*
# email_accounts/filters/encodeURIComponent.js                        Copyright(c) 2020 cPanel, L.L.C.
#                                                                               All rights reserved.
# copyright@cpanel.net                                                             http://cpanel.net
# This code is subject to the cPanel license.                     Unauthorized copying is prohibited
*/

/* global define: false */

define(
    'app/filters/encodeURIComponent',[
        "angular",
    ],
    function(angular) {

        "use strict";

        /**
         * A filter to provide a wrapper around window.encodeURIComponent for use in Angular markup, mostly useful for building links
         * @param  {string} value The value to provide to encodeURIComponent
         * @returns {string}       Returns the value filtered through window.encodeURIComponent
         *
         * @example
         * <a href="../some/page.html#/{{ someValue | encodeURIComponent }}" target="_blank">
         */

        var module;
        try {
            module = angular.module("cpanel.emailAccounts");
        } catch (e) {
            module = angular.module("cpanel.emailAccounts", []);
        }

        module.filter("encodeURIComponent", function() {
            return window.encodeURIComponent;
        });

    }
);

/*
# cjt/decorators/paginationDecorator.js             Copyright(c) 2020 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global define: false */

define(
    'app/decorators/paginationDecorator',[
        "angular",
        "cjt/core",
        "cjt/util/locale",
        "uiBootstrap"
    ],
    function(angular, CJT, LOCALE) {

        "use strict";

        var module;
        var MODULE_NAMESPACE = "cpanel.emailAccounts";
        var TEMPLATE_PATH = "decorators/pagination.phtml";
        var RELATIVE_PATH = "email_accounts/" + TEMPLATE_PATH;

        try {
            module = angular.module(MODULE_NAMESPACE);
        } catch (e) {
            module = angular.module(MODULE_NAMESPACE, ["ui.bootstrap.pagination"]);
        }

        module.config(["$provide", function($provide) {

            // Extend the ngModelDirective to interpolate its name attribute
            $provide.decorator("uibPaginationDirective", ["$delegate", function($delegate) {

                var uiPaginationDirective = $delegate[0];

                /**
                 * Update the ids in the page collection
                 *
                 * @method updateIds
                 * @param  {Array} pages
                 * @param  {string} id    Id of the directive, used as a prefix
                 */
                var updateIds = function(pages, id) {
                    if (!pages) {
                        return;
                    }

                    pages.forEach(function(page) {
                        page.id = id + "_" + page.text;
                    });
                };

                /**
                 * Update aria labels page collection
                 *
                 * @method updateIds
                 * @param  {Array} pages
                 */
                var updateAriaLabel = function(pages) {
                    if (!pages) {
                        return;
                    }

                    pages.forEach(function(page) {
                        page.ariaLabel = LOCALE.maketext("Go to page “[_1]”.", page.text);
                    });
                };

                /**
                 * Update current selected text
                 *
                 * @method updateCurrentSelectedText
                 * @param  {string} page - Current page number
                 * @param  {string} totalPages - Total pages
                 * @returns {string} Text to display
                 */
                var updateCurrentSelectedText = function(page, totalPages) {
                    return LOCALE.maketext("Page [numf,_1] of [numf,_2]", page, totalPages);
                };

                // Use a local template
                uiPaginationDirective.templateUrl = CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH;

                // Extend the page model with the id field.
                var linkFn = uiPaginationDirective.link;

                /**
                 * Compile function for uiPagination Directive
                 *
                 * @method uiPaginationDirective.compile
                 */
                uiPaginationDirective.compile = function() {
                    return function(scope, element, attrs, ctrls) {
                        var paginationCtrl = ctrls[0];

                        linkFn.apply(this, arguments);

                        scope.parentId = attrs.id;
                        scope.ariaLabels = {
                            title: LOCALE.maketext("Pagination"),
                            firstPage: LOCALE.maketext("Go to first page."),
                            previousPage: LOCALE.maketext("Go to previous page."),
                            nextPage: LOCALE.maketext("Go to next page."),
                            lastPage: LOCALE.maketext("Go to last page."),
                        };

                        scope.updateCurrentSelectedText = updateCurrentSelectedText;

                        var render = paginationCtrl.render;
                        paginationCtrl.render = function() {
                            render.apply(paginationCtrl);
                            updateIds(scope.pages, scope.parentId);
                            updateAriaLabel(scope.pages);
                        };

                    };
                };

                return $delegate;
            }]);
        }]);

        return {
            namespace: MODULE_NAMESPACE,
            template: TEMPLATE_PATH
        };
    }
);

/*
# email_accounts/views/list.js                       Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global define, PAGE */

define(
    'app/views/list',[
        "lodash",
        "angular",
        "cjt/util/locale",
        "uiBootstrap",
        "cjt/services/alertService",
        "cjt/directives/alert",
        "cjt/directives/alertList",
        "cjt/directives/actionButtonDirective",
        "cjt/directives/toggleSortDirective",
        "cjt/directives/searchDirective",
        "cjt/services/cpanel/componentSettingSaverService",
        "cjt/directives/pageSizeButtonDirective",
        "app/services/emailAccountsService",
        "cjt/directives/statsDirective",
        "cjt/directives/indeterminateState",
        "app/decorators/paginationDecorator",
        "cjt/directives/disableAnimations",
    ],
    function(_, angular, LOCALE) {
        "use strict";

        var app;
        try {
            app = angular.module("cpanel.emailAccounts");
        } catch (e) {
            app = angular.module("cpanel.emailAccounts", []);
        }

        app.value("PAGE", PAGE);

        /**
         * List Controller for Email
         *
         * @module ListController
         */
        var controller = app.controller(
            "ListController",
            [
                "$scope",
                "$location",
                "emailAccountsService",
                "PAGE",
                "alertService",
                "$timeout",
                "$routeParams",
                "$window",
                "componentSettingSaverService",
                "ONE_MEBIBYTE",
                function(
                    $scope,
                    $location,
                    emailAccountsService,
                    PAGE,
                    alertService,
                    $timeout,
                    $routeParams,
                    $window,
                    componentSettingSaverService,
                    ONE_MEBIBYTE
                ) {

                    var emailAccounts = this;

                    emailAccounts.isRTL = PAGE.isRTL;
                    emailAccounts.statsCssClass = "hide-stats";
                    emailAccountsService.getEmailStats().then(function(response) {
                        $scope.accountStats = response;
                        emailAccounts.statsCssClass = "animate-stats";
                    });

                    $scope.upgradeLink = emailAccountsService.getUpgradeUrl();

                    /**
                     * Redirects to create view
                     * @method createEmail
                     */
                    emailAccounts.createEmail = function() {
                        $location.path("/create");
                    };

                    var COMPONENT_NAME = "EmailAccountsTable";

                    var storageKeys = {
                        filter: "EmailAccountsListFilter",
                        currentPage: "EmailAccountsListCurrentPage",
                        quickFilter: "EmailAccountsListQuickFilter",
                    };

                    if (app.firstLoad) {

                        // clear local storage
                        _.forOwn(storageKeys, function(value) {
                            localStorage.removeItem(value);
                        } );
                    }

                    var MAX_NAME_WIDTH = 200;
                    var DEFAULT_TIMEOUT = 250;

                    emailAccounts.webmailEnabled = PAGE.webmailEnabled;
                    emailAccounts.dprefix = PAGE.dprefix;

                    emailAccounts.loadingEmailAccounts = false;
                    emailAccounts.filterTermPending = true;

                    emailAccounts.storageKeys = storageKeys;

                    emailAccounts.meta = {

                        // sort settings
                        sortReverse: false,
                        sortBy: "user",
                        sortDirection: "asc",
                        sortFields: ["user", "domain", "has_suspended", "_diskused", "_diskquota", "diskusedpercent_float"],

                        // pager settings
                        showPager: false,
                        maxPages: 5,
                        totalItems: 0,
                        currentPage: 1,
                        pageSizes: [20, 50, 100, 500],
                        pageSize: 20,
                        start: 0,
                        limit: 10,
                    };

                    emailAccounts.multiDeleteSelected = false;
                    emailAccounts.checkedCount = 0;
                    emailAccounts.selectAllState = false;
                    var selectedItems = {};

                    var storageFilterValue = localStorage.getItem(emailAccounts.storageKeys.filter);
                    if ( storageFilterValue && (0 === storageFilterValue.indexOf(PAGE.securityToken + ":")) ) {
                        emailAccounts.meta.filterValue = storageFilterValue.substr( 1 + PAGE.securityToken.length );
                    } else {
                        emailAccounts.meta.filterValue = "";
                    }


                    var storageQuickFilterValue = localStorage.getItem(emailAccounts.storageKeys.quickFilter);
                    if ( storageQuickFilterValue && (0 === storageQuickFilterValue.indexOf(PAGE.securityToken + ":")) ) {
                        emailAccounts.quickFilter = storageQuickFilterValue.substr( 1 + PAGE.securityToken.length );
                    } else {
                        emailAccounts.quickFilter = "all";
                    }

                    var storageCurrentPageValue = localStorage.getItem(emailAccounts.storageKeys.currentPage);
                    if (storageCurrentPageValue && (0 === storageCurrentPageValue.indexOf(PAGE.securityToken + ":")) ) {
                        emailAccounts.meta.currentPage = storageCurrentPageValue.substr( 1 + PAGE.securityToken.length );
                    } else {
                        emailAccounts.meta.currentPage = 1;
                    }

                    if ( $routeParams.account && $routeParams.account !== emailAccounts.meta.filterValue ) {
                        emailAccounts.meta.filterValue = $routeParams.account;
                        emailAccounts.meta.accounts = undefined;
                    }

                    /**
                     * Clears selected state
                     *
                     * @method clearSelectedState
                     */
                    function clearSelectedState() {
                        emailAccounts.selectAllState = false;
                        emailAccounts.checkedCount = 0;
                        emailAccounts.multiDeleteSelected = false;
                    }

                    /**
                     * Helps decide to show or hide email in the expanded view based on email account length
                     * @method showInnerEmail
                     * @param {string} email
                     * @returns {Boolean}
                     */
                    function showInnerEmail(email) {
                        var el = document.getElementById("account-name_" + email);
                        if (el) {
                            var width = $window.getComputedStyle(el).width;

                            if (width) {
                                width = width.slice(0, -2);
                                return width >= MAX_NAME_WIDTH;
                            }
                        }

                        return true;
                    }

                    /**
                     * Updates locale storage
                     *
                     * @method updateLocalStorage
                     * @param {string} key
                     * @param {string} value
                     */
                    function updateLocalStorage(key, value) {
                        localStorage.setItem(
                            emailAccounts.storageKeys[key],
                            PAGE.securityToken + ":" + value
                        );
                    }

                    /**
                     * Sets the sort, paginiation, and page size settings based on the provided object
                     * @method setMetaFromComponentSettings
                     * @param  {Object} settings An object containing the settings
                     *  {
                     *      sortBy:        A string indicating which field to sort by, must be one of the values in meta.sortFields
                     *      sortDirection: A string indicating the sort direction, must be one of "asc" or "desc"
                     *      pageSize:      A string or integer indicating the page size, must be one of the values in meta.pageSizes
                     *  }
                     */
                    emailAccounts.setMetaFromComponentSettings = function(settings) {

                        if ( settings.hasOwnProperty("sortBy") && settings.sortBy && _.find(emailAccounts.meta.sortFields, function(f) {
                            return f === settings.sortBy;
                        }) ) {
                            emailAccounts.meta.sortBy = settings.sortBy;
                        }

                        if ( settings.hasOwnProperty("sortDirection") && settings.sortDirection && (settings.sortDirection === "asc" || settings.sortDirection === "desc" ) ) {
                            emailAccounts.meta.sortDirection = settings.sortDirection;
                        }

                        if ( settings.hasOwnProperty("pageSize") && settings.pageSize && _.find(emailAccounts.meta.pageSizes, function(s) {
                            return s === parseInt(settings.pageSize);
                        }) ) {
                            emailAccounts.meta.pageSize = parseInt(settings.pageSize);
                        }

                    };

                    /**
                     * Stores the current values of sortBy, sortDirection, pageSize
                     * @method saveMetaToComponentSettings
                     */
                    emailAccounts.saveMetaToComponentSettings = function() {
                        componentSettingSaverService.set(COMPONENT_NAME, {
                            sortBy: emailAccounts.meta.sortBy,
                            sortDirection: emailAccounts.meta.sortDirection,
                            pageSize: emailAccounts.meta.pageSize,
                        });
                    };

                    /**
                     * Clears status
                     * @method clearStatus
                     * @param {Object} emailAccount
                     */
                    emailAccounts.clearStatus = function(emailAccount) {

                        // Account was deleted, remove it from the list
                        if ( emailAccount && emailAccount.deleted ) {

                            var index = emailAccounts.meta.accounts.indexOf(emailAccount);

                            // Don't remove anything if the item isn't found in the list
                            if ( index > -1 ) {
                                emailAccounts.meta.accounts.splice(index, 1);
                                emailAccounts.meta.totalItems--;

                                emailAccounts.meta.mobileItemCountText = LOCALE.maketext("[_1] - [_2] of [_3]",
                                    emailAccounts.meta.start, emailAccounts.meta.limit, emailAccounts.meta.totalItems
                                );
                            }

                            emailAccount.isExpanded = false;

                            // If we've removed all the items on the page, but there are more items in the result set, fetch them
                            if ( emailAccounts.meta.accounts.length === 0 && emailAccounts.meta.totalItems > emailAccounts.meta.pageSize ) {

                                // If we deleted the last item on the last page, go back by 1 page
                                if ( emailAccounts.meta.currentPage === emailAccounts.meta.totalPages ) {
                                    emailAccounts.meta.currentPage--;
                                }

                                return emailAccounts.fetch();
                            }

                        }
                    };

                    /**
                     * Callback for clicking on one of the table headers to sort by column
                     * @method sortList
                     */
                    emailAccounts.sortList = function() {

                        if ( emailAccounts.currentFetchTimeout ) {
                            $timeout.cancel(emailAccounts.currentFetchTimeout);
                        }

                        emailAccounts.currentFetchTimeout = $timeout(function() {
                            emailAccounts.saveMetaToComponentSettings();
                            clearSelectedState();
                            emailAccounts.meta.currentPage = 1;
                            updateLocalStorage("currentPage", emailAccounts.meta.currentPage);
                            return emailAccounts.fetch();
                        }, DEFAULT_TIMEOUT);
                    };

                    /**
                     * Callback for clicking on one of the pagination nav links to move between pages
                     * @method selectPage
                     */
                    emailAccounts.selectPage = function() {
                        if (emailAccounts.loadingEmailAccounts) {
                            return;
                        }

                        if ( emailAccounts.currentFetchTimeout ) {
                            $timeout.cancel(emailAccounts.currentFetchTimeout);
                        }

                        return emailAccounts.currentFetchTimeout = $timeout(function() {
                            clearSelectedState();
                            updateLocalStorage("currentPage", emailAccounts.meta.currentPage);
                            return emailAccounts.fetch();
                        }, DEFAULT_TIMEOUT);
                    };

                    /**
                     * Callback for selecting a page size from the pagination
                     * @method selectPageSize
                     */
                    emailAccounts.selectPageSize = function() {

                        if ( emailAccounts.currentFetchTimeout ) {
                            $timeout.cancel(emailAccounts.currentFetchTimeout);
                        }

                        emailAccounts.currentFetchTimeout = $timeout(function() {
                            emailAccounts.saveMetaToComponentSettings();
                            clearSelectedState();
                            emailAccounts.meta.currentPage = 1;
                            return emailAccounts.fetch();
                        }, DEFAULT_TIMEOUT);
                    };

                    /**
                     * Callback for entering filter input into the search bar
                     * @method searchList
                     */
                    emailAccounts.searchList = function() {
                        emailAccounts.filterTermPending = true;
                        emailAccounts.meta.currentPage = 1;
                        clearSelectedState();
                        updateLocalStorage("filter", emailAccounts.meta.filterValue);
                        updateLocalStorage("currentPage", emailAccounts.meta.currentPage);
                        return emailAccounts.fetch();
                    };

                    /**
                     * Gets selected state
                     * @method getSelectedState
                     * @returns {Boolean} returns if the checkbox is selected
                     */
                    emailAccounts.getSelectedState = function() {
                        return emailAccounts.checkedCount > 0 || emailAccounts.selectAllState;
                    };

                    /**
		     * Gets boolean for selected accounts
		     * @method hasSelectedAccounts
		     * @returns {Boolean} returns if checkedCount is > 0
		     */
                    emailAccounts.hasSelectedAccounts = function() {
                        return emailAccounts.checkedCount > 0;
                    };

                    /**
                     * Gets indeterminate state
                     * @method getIndeterminateState
                     * @returns {Boolean} returns indeterminate state
                     */
                    emailAccounts.getIndeterminateState = function() {
                        return emailAccounts.checkedCount > 0 && !emailAccounts.selectAllState;
                    };

                    /**
                     * Toggles selection
                     *
                     * @method toggleSelection
                     */
                    emailAccounts.toggleSelection = function(account) {
                        if (account.selected) {
                            emailAccounts.checkedCount++;
                            selectedItems[account.email] = true;
                        } else {
                            emailAccounts.checkedCount--;
                            delete selectedItems[account.email];
                        }
                        if (emailAccounts.checkedCount === 0) {
                            emailAccounts.multiDeleteSelected = false;
                        }
                        emailAccounts.selectAllState = emailAccounts.checkedCount === emailAccounts.meta.accounts.length;
                    };

                    /**
                     * Toggles select all
                     * @method toggleSelectAll
                     */
                    emailAccounts.toggleSelectAll = function() {
                        if (emailAccounts.meta.accounts.length === 0) {
                            return;
                        }

                        // if we are in an indeterminate state, make sure that select all unselects
                        var accountsCountSystemExcluded = emailAccounts.meta.accounts.length - 1;
                        var indeterminateState = emailAccounts.checkedCount > 0 && emailAccounts.checkedCount !== accountsCountSystemExcluded;
                        if (indeterminateState) {
                            emailAccounts.selectAllState = !emailAccounts.selectAllState;
                        }

                        for (var i = 0, listLength = emailAccounts.meta.accounts.length; i < listLength; i++) {
                            var account = emailAccounts.meta.accounts[i];
                            if (account.isDefault) {
                                continue;
                            }
                            account.selected = emailAccounts.selectAllState;
                            if (emailAccounts.selectAllState) {
                                selectedItems[account.email] = emailAccounts.selectAllState;
                            } else {
                                delete selectedItems[account.email];
                            }
                        }
                        if (emailAccounts.selectAllState) {
                            emailAccounts.checkedCount = accountsCountSystemExcluded;
                        } else {
                            emailAccounts.checkedCount = 0;
                            emailAccounts.multiDeleteSelected = false;
                        }
                    };

                    /**
                     * Get delete multiple messages
                     *
                     * @method getDeleteMultipleMsg
                     */
                    emailAccounts.getDeleteMultipleMsg = function() {
                        var selected;

                        if (emailAccounts.checkedCount > 1) {
                            return LOCALE.maketext("Delete the selected [quant,_1,email account,email accounts]?", emailAccounts.checkedCount);
                        } else if (emailAccounts.checkedCount === 1) {
                            for (var i = 0, listLength = emailAccounts.meta.accounts.length; i < listLength; i++) {
                                if (emailAccounts.meta.accounts[i].selected) {
                                    selected = emailAccounts.meta.accounts[i].email;
                                    break;
                                }
                            }
                            return LOCALE.maketext("Delete “[_1]”?", selected);
                        }
                    };

                    /**
                     * Gets text for multi delete
                     *
                     * @method getMultiDeleteButtonTxt
                     * @returns {string} delete text
                     */
                    emailAccounts.getMultiDeleteButtonTxt = function() {
                        return LOCALE.maketext("Delete ([numf,_1])", emailAccounts.checkedCount);
                    };

                    /**
                     * Callback for deleting an email account from the list
                     * @method delete
                     */
                    emailAccounts.delete = function(account) {

                        account.delete_requested = true;
                        account.removing = true;

                        // If we're removing the last item in the list, clear the green on the search box
                        if ( emailAccounts.meta.accounts.length === 1 && emailAccounts.meta.filterValue ) {
                            emailAccounts.filterTermPending = true;
                        }

                        return emailAccountsService.deleteEmail(account.email)
                            .then(function() {
                                emailAccounts.filterTermPending = false;

                                alertService.add({
                                    message: LOCALE.maketext("Account “[_1]” deleted.", account.user + "@" + account.domain),
                                    type: "success",
                                    closeable: true,
                                    autoClose: 10000,
                                    group: "emailAccounts",
                                });

                                account.delete_requested = false;
                                account.removing = false;
                                account.deleted = true;

                                delete selectedItems[account.email];
                                emailAccounts.checkedCount--;

                                // Special handling for when the last record is removed from a filtered list
                                // Allows the search to go red while the deleted status stays displayed for 10s
                                if ( emailAccounts.meta.filterValue && emailAccounts.meta.accounts.length === 1 && emailAccounts.meta.totalItems <= emailAccounts.meta.pageSize ) {
                                    emailAccounts.meta.accounts = [];
                                }

                                emailAccounts.clearStatus(account);
                            },
                            function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    group: "emailAccounts",
                                });
                                account.delete_requested = false;
                                account.removing = false;
                                account.deleted = false;
                            }
                            );
                    };

                    /**
                     * Deletes multiple emails
                     *
                     * @method deleteMultiple
                     */
                    emailAccounts.deleteMultiple = function() {
                        emailAccounts.removingMultiple = true;

                        var accounts = [];
                        for (var i = 0, listLength = emailAccounts.meta.accounts.length; i < listLength; i++) {
                            if (emailAccounts.meta.accounts[i].selected && !emailAccounts.meta.accounts[i].isDefault) {
                                accounts.push(emailAccounts.meta.accounts[i]);
                            }
                        }

                        // If we're removing the last item in the list, clear the green on the search box
                        if ( emailAccounts.meta.accounts.length === 1 && emailAccounts.meta.filterValue ) {
                            emailAccounts.filterTermPending = true;
                        }

                        return emailAccountsService.deleteEmails(accounts)
                            .then(function() {
                                var message = "";
                                if (accounts.length > 1) {
                                    message = LOCALE.maketext("Deleted [numf,_1] email accounts.", accounts.length);
                                } else if (accounts.length === 1) {
                                    message = LOCALE.maketext("Email account “[_1]” deleted.", accounts[0].user + "@" + accounts[0].domain);
                                }

                                alertService.add({
                                    message: message,
                                    type: "success",
                                    closeable: true,
                                    autoClose: 10000,
                                    group: "emailAccounts",
                                });

                                clearSelectedState();
                                emailAccounts.removingMultiple = false;

                                for (var i = 0, len = accounts.length; i < len; i++) {
                                    delete selectedItems[accounts[0]];
                                }

                                return emailAccounts.fetch();
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    group: "emailAccounts",
                                });
                                emailAccounts.removingMultiple = false;
                                emailAccounts.multiDeleteSelected = false;
                            });
                    };

                    /**
                     * Sets API params
                     * @method setApiParams
                     * @param {Object} emailAccounts
                     * @param {string} sortMethod
                     */
                    function setApiParams(emailAccounts, sortMethod) {
                        var apiParams = {
                            "api.sort": 1,
                            "api.sort_column_0": emailAccounts.meta.sortBy,
                            "api.sort_method_0": sortMethod,
                            "api.sort_reverse_0": emailAccounts.meta.sortDirection === "asc" ? 0 : 1,
                            "api.paginate": 1,
                            "api.paginate_start": ((emailAccounts.meta.currentPage - 1) * emailAccounts.meta.pageSize) + 1,
                            "api.paginate_size": emailAccounts.meta.pageSize,
                            "api.paginate_page": emailAccounts.meta.currentPage,
                        };

                        if ( emailAccounts.meta.sortBy !== "user" ) {
                            apiParams["api.sort_column_1"] = "user";
                            apiParams["api.sort_method_1"] = "lexicographic";
                        }

                        switch (emailAccounts.quickFilter) {
                            case "restricted":
                                apiParams["api.filter_term_1"] = 1;
                                apiParams["api.filter_type_1"] = "eq";
                                apiParams["api.filter_column_1"] = "has_suspended";
                                break;
                            case "default":
                                apiParams["api.filter_term_1"] = "Main Account";
                                apiParams["api.filter_type_1"] = "eq";
                                apiParams["api.filter_column_1"] = "login";

                                // clearing search value on default quick filter
                                emailAccounts.meta.filterValue = "";
                                delete apiParams["api.paginate"];
                                delete apiParams["api.paginate_start"];
                                delete apiParams["api.paginate_size"];
                                delete apiParams["api.paginate_page"];
                                emailAccounts.meta.currentPage = 1;
                                break;
                            case "overUsed":
                                apiParams["api.filter_term_1"] = 100;
                                apiParams["api.filter_type_1"] = "gt";
                                apiParams["api.filter_column_1"] = "diskusedpercent";
                                break;
                        }

                        if ( emailAccounts.meta.filterValue && emailAccounts.meta.filterValue !== "" ) {
                            apiParams["api.filter"] = 1;
                            apiParams["api.filter_term_0"] = emailAccounts.meta.filterValue;
                            apiParams["api.filter_column_0"] = "login";
                        }
                        return apiParams;
                    }

                    /**
                     * Controls show/hide available accounts warings
                     * @method showNoAvailableAccountsWarning
                     */
                    emailAccounts.showNoAvailableAccountsWarning = function() {
                        $scope.showNoAvailableAccounts = !$scope.showNoAvailableAccounts;
                    };

                    /**
                     * Close callout
                     * @method closeCallout
                     */
                    emailAccounts.closeCallout = function() {
                        $scope.showNoAvailableAccounts = false;
                    };

                    /**
                     * Calls emailAccountsService.getEmailAccounts to load the email accounts for the current page
                     * @method fetch
                     */
                    emailAccounts.fetch = function() {

                        emailAccounts.loadingEmailAccounts = true;

                        var sortMethod = "lexicographic";

                        if ( emailAccounts.meta.sortBy === "_diskused" || emailAccounts.meta.sortBy === "diskusedpercent_float" || emailAccounts.meta.sortBy === "has_suspended" ) {
                            sortMethod = "numeric";
                        } else if ( emailAccounts.meta.sortBy === "_diskquota" ) {
                            sortMethod = "numeric_zero_as_max";
                        }

                        var apiParams = setApiParams(emailAccounts, sortMethod);

                        emailAccounts.meta.accounts = [];

                        // Setting min-height to the current height to prevent the page jumping
                        // around when the list is fetching
                        var container = angular.element("#popsAccountList");

                        if ( container && container[0] ) {
                            container.css({ minHeight: $window.getComputedStyle(container[0]).height });
                        }

                        var apiPromise = emailAccountsService.getEmailAccounts(apiParams);
                        emailAccounts.fetchPromise = apiPromise;

                        return apiPromise.then(
                            function(response) {

                                // We only want to actually process the response if it's the last request we sent
                                if ( emailAccounts.fetchPromise !== apiPromise ) {
                                    return;
                                }

                                clearSelectedState();

                                var data = response.data;
                                var metadata = response.meta;

                                emailAccounts.meta.totalItems = metadata.paginate.total_records;
                                emailAccounts.meta.totalPages = metadata.paginate.total_pages;
                                emailAccounts.meta.currentPage = metadata.paginate.current_page;

                                if (emailAccounts.meta.totalItems > _.min(emailAccounts.meta.pageSizes)) {
                                    emailAccounts.meta.showPager = true;
                                    var start = (emailAccounts.meta.currentPage - 1) * emailAccounts.meta.pageSize;
                                    emailAccounts.meta.start = start + 1;
                                    emailAccounts.meta.limit = start + data.length;

                                } else {

                                    // hide pager and pagination
                                    emailAccounts.meta.showPager = false;

                                    if (data.length === 0) {
                                        emailAccounts.meta.start = 0;
                                    } else {
                                        emailAccounts.meta.start = 1;
                                    }

                                    emailAccounts.meta.limit = data.length;
                                }

                                emailAccounts.meta.mobileItemCountText = LOCALE.maketext("[_1] - [_2] of [_3]",
                                    emailAccounts.meta.start, emailAccounts.meta.limit, emailAccounts.meta.totalItems
                                );

                                angular.element("#popsAccountList").css({ minHeight: "" });

                                for (var i = 0, len = data.length; i < len; i++) {
                                    if (selectedItems[data[i].email] && !data[i].isDefault) {
                                        data[i].selected = true;
                                        emailAccounts.checkedCount++;
                                    }
                                }

                                if (data.length > 0) {
                                    emailAccounts.selectAllState = emailAccounts.checkedCount === data.length;
                                } else {
                                    emailAccounts.selectAllState = false;
                                }

                                // This is done to improve performance of loading 100+ records at a time in Firefox
                                if (data.length > 100) {
                                    var tempEmailAccounts = [];
                                    var chunkSize = 50;

                                    while (data.length) {
                                        tempEmailAccounts.push(data.splice(0, chunkSize));
                                    }
                                    emailAccounts.meta.accounts = tempEmailAccounts.shift();

                                    bindEmailsToList(tempEmailAccounts);

                                } else {
                                    emailAccounts.meta.accounts = data;
                                }

                                emailAccounts.loadingEmailAccounts = false;
                                emailAccounts.filterTermPending = false;
                            },
                            function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    group: "emailAccounts",
                                });
                                emailAccounts.loadingEmailAccounts = false;
                                emailAccounts.filterTermPending = false;
                            }
                        );
                    };

                    function bindEmailsToList(items) {

                        // push them in chunks
                        var emails = items.shift();

                        if (typeof emails !== "undefined" && emails !== null) {
                            $timeout(function() {
                                emailAccounts.meta.accounts = emailAccounts.meta.accounts.concat(emails);
                                bindEmailsToList(items);
                            }, 0);
                        }
                    }

                    /**
                     * Filters email
                     * @method filterEmails
                     */
                    emailAccounts.filterEmails = function() {

                        if (emailAccounts.quickFilter) {
                            emailAccounts.meta.currentPage = 1;
                            updateLocalStorage("quickFilter", emailAccounts.quickFilter);
                            updateLocalStorage("currentPage", emailAccounts.meta.currentPage);
                            return emailAccounts.fetch();
                        }
                    };

                    /**
                     * Gets css class that should be assinged to search field
                     * @method getSearchClass
                     * @returns {string} css class to be assigned to search field
                     */
                    emailAccounts.getSearchClass = function() {
                        if ( !emailAccounts.filterTermPending && !emailAccounts.loadingEmailAccounts && emailAccounts.meta.filterValue ) {
                            return emailAccounts.meta.accounts && emailAccounts.meta.accounts.length > 0 ? "success" : "danger";
                        } else {
                            return "";
                        }
                    };

                    /**
                     * Gets email accuont details
                     * @method getDetails
                     * @param {Object} account email account
                     * @param {Object} isExpanded expanded state
                     */
                    emailAccounts.getDetails = function(account, isExpanded) {
                        if (isExpanded) {
                            account.displayInnerEmail = showInnerEmail(account.email);
                        }
                        account.isExpanded = isExpanded;
                    };

                    /**
                     * Redirects to connect Devices interface
                     *
                     * @param {Object} emailAccount email account
                     */
                    emailAccounts.connectDevices = function(emailAccount) {
                        var path = emailAccounts.dprefix + "mail/clientconf.html?acct=" + encodeURIComponent(emailAccount.email);

                        window.location.href = path;
                    };

                    /**
                     * Redirects to manage account
                     * @method manageAccount
                     * @param {Object} emailAccount
                     * @param {string} scrollTo
                     */
                    emailAccounts.manageAccount = function(emailAccount, scrollTo) {
                        if (emailAccount && emailAccount.isDefault) {
                            $location.path("/manageDefault/");
                        } else {

                            var path = "/manage/" + encodeURIComponent(emailAccount.email);

                            if (typeof scrollTo !== "undefined" && scrollTo) {
                                path = path + "/" + scrollTo;
                            }

                            $location.path(path);
                        }
                    };

                    $scope.$on("$destroy", function() {
                        componentSettingSaverService.unregister(COMPONENT_NAME);
                    });

                    /**
                     * initialze
                     * @method init
                     */
                    function init() {

                        if ( app.firstLoad && PAGE.nvdata && PAGE.nvdata.hasOwnProperty(COMPONENT_NAME) ) {
                            emailAccounts.setMetaFromComponentSettings(PAGE.nvdata[COMPONENT_NAME]);
                        }

                        app.firstLoad = false;

                        emailAccounts.loadingEmailAccounts = true;
                        return componentSettingSaverService.register(COMPONENT_NAME)
                            .then(
                                function(result) {
                                    if ( result ) {

                                        if ( $routeParams.account && $routeParams.account !== result.filterValue ) {
                                            result.filterValue = $routeParams.account;
                                            emailAccounts.meta.currentPage = 1;
                                            emailAccounts.meta.accounts = undefined;
                                            emailAccounts.saveMetaToComponentSettings();
                                        }

                                        emailAccounts.setMetaFromComponentSettings(result);
                                    }

                                    if ( !emailAccounts.meta.accounts ) {
                                        return emailAccounts.fetch();
                                    }

                                },
                                function() {
                                    if ( !emailAccounts.meta.accounts ) {
                                        return emailAccounts.fetch();
                                    }
                                }
                            );
                    }

                    init();
                },
            ]
        );

        return controller;
    }
);

/*
# email_accounts/validators/emailAccountFullLength.js                           Copyright 2022 cPanel, L.L.C.
#                                                                                      All rights reserved.
# copyright@cpanel.net                                                                    http://cpanel.net
# This code is subject to the cPanel license.                            Unauthorized copying is prohibited
*/

/* global define: false */

define(
    'app/validators/emailAccountFullLength',[
        "angular"
    ],
    function(angular) {

        "use strict";

        var module;

        /**
         * Directive that checks the full length of two form fields + 1 (for the @) to make sure an email address is a valid length
         * @attribute {Integer} emailFullLength The maximum allowed length for the two fields + 1
         * @attribute {String}  emailOtherValue The other form field to compare against
         *
         * @example
         * <form name="formName">
         *     <input type="text" name="account" email-full-length="254" email-other-value="formName.domain">
         *     <input type="text" name="domain">
         * </form>
         */

        try {
            module = angular.module("cpanel.emailAccount");
        } catch (e) {
            module = angular.module("cpanel.emailAccount", []);
        }

        module.directive("emailFulllength", function() {
            return {
                require: "ngModel",
                restrict: "A",
                link: function( scope, element, attrs, ngModel ) { // eslint-disable-line no-unused-vars

                    ngModel.$validators.emailFulllength = function(model, view) { // eslint-disable-line no-unused-vars

                        var ngOtherModel = getNgOtherModel();
                        if (!ngOtherModel) {
                            var value = view || "";
                            return (value.length + 1) <= attrs.emailFulllength;
                        }

                        var thisIsEmpty = ngModel.$isEmpty(view);
                        var otherIsEmpty = ngOtherModel.$isEmpty(ngOtherModel.$viewValue);
                        if (thisIsEmpty && otherIsEmpty) {

                            // Don't validate this condition if both values are empty
                            return true;
                        } else {

                            var thisValue = view || "";
                            var otherValue = (ngOtherModel.$pending || ngOtherModel.$invalid) ? ngOtherModel.$viewValue : ngOtherModel.$modelValue;

                            return (thisValue.length + otherValue.length + 1) <= attrs.emailFulllength;
                        }

                    };

                    scope.$watchGroup([
                        function() {
                            var ngOtherModel = getNgOtherModel();
                            return ngOtherModel && ngOtherModel.$viewValue;
                        },
                        function() {
                            var ngOtherModel = getNgOtherModel();
                            return ngOtherModel && ngOtherModel.$modelValue;
                        }
                    ], function() {
                        ngModel.$validate();
                    });

                    var _ngOtherModel;
                    function getNgOtherModel() {
                        if (!_ngOtherModel) {
                            _ngOtherModel = scope.$eval(attrs.emailOtherValue);
                        }
                        return _ngOtherModel;
                    }
                }
            };
        });
    }
);

/*
# email_accounts/views/create.js                     Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global define, PAGE */

define(
    'app/views/create',[
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/util/parse",
        "uiBootstrap",
        "cjt/validator/email-validator",
        "app/services/emailAccountsService",
        "app/validators/emailAccountFullLength",
        "cjt/directives/statsDirective",
        "cjt/directives/passwordFieldDirective",
        "cjt/services/alertService",
        "cjt/directives/alert",
        "cjt/directives/alertList",
        "cjt/directives/actionButtonDirective",
        "cjt/directives/toggleLabelInfoDirective",
        "cjt/directives/labelSuffixDirective",
        "cjt/directives/validationContainerDirective",
        "cjt/directives/validationItemDirective",
        "cjt/directives/bytesInput",
        "cjt/services/cpanel/componentSettingSaverService"
    ],
    function(angular, _, LOCALE, PARSE) {
        "use strict";

        var app;

        try {
            app = angular.module("cpanel.emailAccounts");
        } catch (e) {
            app = angular.module("cpanel.emailAccounts", []);
        }

        app.value("PAGE", PAGE);

        /**
         * Create Controller for Email
         *
         * @module CreateController
         */
        var controller = app.controller(
            "CreateController",
            [
                "$scope",
                "$location",
                "$anchorScroll",
                "$timeout",
                "emailAccountsService",
                "PAGE",
                "alertService",
                "$routeParams",
                "componentSettingSaverService",
                "$q",
                function(
                    $scope,
                    $location,
                    $anchorScroll,
                    $timeout,
                    emailAccountsService,
                    PAGE,
                    alertService,
                    $routeParams,
                    componentSettingSaverService,
                    $q
                ) {

                    var emailAccount = this;
                    var redirectTimer;
                    var COMPONENT_NAME = "EmailAccountsCreate";
                    var domain = decodeURIComponent($routeParams.domain);
                    var storageKeys = {
                        stayOnPage: "EmailAccountsCreateStayOnPage",
                    };

                    $anchorScroll.yOffset = 70;

                    emailAccount.isRTL = PAGE.isRTL;
                    emailAccount.isLoading = true;
                    emailAccount.statsCssClass = "hide-stats";
                    emailAccount.details = {};
                    $scope.showAllHelp = false;
                    $scope.upgradeURL = emailAccountsService.getUpgradeUrl();

                    $scope.requiredPasswordStrength = PAGE.requiredPasswordStrength;
                    $scope.isInviteSubEnabled = !!PAGE.isInviteSubEnabled;
                    $scope.defaultQuota = PAGE.userDefinedQuotaDefaultValue ? PAGE.userDefinedQuotaDefaultValue : undefined;
                    $scope.maxQuota = PAGE.maxEmailQuota;
                    $scope.canSetUnlimited = PAGE.canSetUnlimited;
                    $scope.maxQuotaHelpText = LOCALE.maketext("Quotas cannot exceed [format_bytes,_1].", PAGE.maxEmailQuota * 1024 * 1024);
                    $scope.maxEmailQuotaText = PAGE.canSetUnlimited ? LOCALE.maketext("Unlimited") : LOCALE.maketext("[format_bytes,_1]", PAGE.maxEmailQuota * 1024 * 1024);
                    $scope.mailDomains = [];

                    function setDefaultsForOptionalSettings() {
                        $scope.showOptionalSettings = false;
                        $scope.OptionalSettingsButtonText = LOCALE.maketext("Edit Settings");
                        $scope.OptionalSettingsIconClass = "fas fa-pencil-alt";
                    }

                    setDefaultsForOptionalSettings();

                    emailAccount.toggleOptionalSettingsButtonState = function() {
                        $scope.showOptionalSettings = !$scope.showOptionalSettings;

                        if ($scope.showOptionalSettings === false) {

                            // we will discard all the changes made in optional settings and set them back to defaults
                            emailAccount.details.quota = $scope.defaultQuota;
                            emailAccount.details.quotaType = PAGE.defaultQuotaSelected;
                            emailAccount.details.sendWelcomeEmail = true;

                            emailAccount.details.autoCreateSubaddressFolders = true;
                            emailAccount.details.assignMaxDiskspace = false;
                            emailAccount.details.quotaUnit = "MB";

                            setDefaultsForOptionalSettings();

                        } else {
                            $scope.OptionalSettingsButtonText = LOCALE.maketext("Reset Settings");
                            $scope.OptionalSettingsIconClass = "fas fa-trash";
                        }
                    };

                    /**
                     * Called on first load
                     *
                     * @method firstLoad
                     */
                    function firstLoad() {
                        return $q.all([
                            componentSettingSaverService.register(COMPONENT_NAME),
                            emailAccountsService.getMailDomains(),
                            emailAccountsService.getEmailStats()
                        ]).then(
                            function(responses) {
                                $scope.mailDomains = responses[1];
                                $scope.accountStats = responses[2];
                                emailAccount.statsCssClass = "animate-stats";
                                return initialize()
                                    .then(function(result) {
                                        emailAccount.details = result;
                                    });
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                            }
                        ).finally(function() {
                            emailAccount.isLoading = false;
                        });
                    }

                    /**
                     * Initialization
                     *
                     * @method initialize
                     * @param {Boolean} keepDomain
                     */
                    function initialize(keepDomain) {
                        return componentSettingSaverService.get(COMPONENT_NAME)
                            .then(function(response) {
                                if (typeof response !== "undefined" && response) {
                                    $scope.showAllHelp = response.showAllHelp;
                                }

                                if ($scope.accountStats.available) {
                                    var accountDetails = {};
                                    accountDetails.userName = null;
                                    accountDetails.domain = null;

                                    if (keepDomain) {
                                        accountDetails.domain = emailAccount.details.domain;
                                    } else if ($scope.mailDomains.length > 0) {
                                        if (typeof domain !== "undefined" && domain) {
                                            if ($scope.mailDomains.indexOf(domain) !== -1) {
                                                accountDetails.domain = domain;
                                            } else {
                                                accountDetails.domain = PAGE.primaryDomain;
                                            }
                                        } else {
                                            accountDetails.domain = PAGE.primaryDomain;
                                        }
                                    }

                                    accountDetails.quota = $scope.defaultQuota;
                                    accountDetails.password = null;
                                    accountDetails.quotaType = PAGE.defaultQuotaSelected;
                                    accountDetails.sendWelcomeEmail = true;
                                    accountDetails.setPassword = true;
                                    accountDetails.recoveryEmail = null;
                                    accountDetails.autoCreateSubaddressFolders = true;
                                    accountDetails.assignMaxDiskspace = false;
                                    accountDetails.quotaUnit = "MB";
                                    accountDetails.stayOnView = PARSE.parseBoolean(emailAccountsService.getStoredValue(storageKeys.stayOnPage));

                                    return accountDetails;
                                } else {

                                    // 10 seconds delay
                                    redirectTimer = $timeout( function() {
                                        $location.path("/list");
                                    }, 10000 );
                                }

                            });
                    }

                    firstLoad();

                    /**
                     * Stores the current values in the component settings
                     * @method saveToComponentSettings
                     */
                    emailAccount.saveToComponentSettings = function() {
                        return componentSettingSaverService.set(COMPONENT_NAME, {
                            showAllHelp: $scope.showAllHelp
                        });
                    };

                    /**
                     * Called when stayOnPage checkbox is changed
                     *
                     * @method stayOnPageChanged
                     */
                    emailAccount.stayOnPageChanged = function() {

                        if (emailAccount.details.stayOnView) {
                            localStorage.setItem(
                                storageKeys.stayOnPage,
                                PAGE.securityToken + ":" + "true"
                            );
                        } else {

                            // clear local storage
                            localStorage.removeItem(storageKeys.stayOnPage);
                        }
                    };

                    /**
                     * Scrolls to missing domains section
                     *
                     * @method scrollToMissingDomains
                     */
                    emailAccount.scrollToMissingDomains = function() {
                        $anchorScroll("missingDomainSection");
                        var missingDomainsSec = angular.element( document.querySelector( "#missingDomainSection" ) );

                        missingDomainsSec.addClass("section-highlight");
                        $timeout(function() {
                            missingDomainsSec.removeClass("section-highlight");
                        }, 11000);
                    };

                    /**
                     * Called when create button is clicked
                     *
                     * @method create
                     * @param {Object} details
                     */
                    emailAccount.create = function(details) {
                        emailAccount.frmCreateEmail.$submitted = true;

                        if (!emailAccount.frmCreateEmail.$valid || emailAccount.frmCreateEmail.$pending) {
                            return;
                        }

                        if (typeof details !== "undefined" && details) {

                            if (details.quotaType === "unlimited") {
                                if (!$scope.canSetUnlimited) {
                                    details.quota = $scope.maxQuota;
                                } else {
                                    details.quota = 0;
                                }
                            }

                            return emailAccountsService.createEmail(details)
                                .then(function(response) {

                                    alertService.add({
                                        type: "success",
                                        message: LOCALE.maketext("You created “[_1]” ([output,url,_2,View]).",
                                            details.userName + "@" + details.domain,
                                            "#/list/" + details.userName + "@" + details.domain
                                        ),
                                        closeable: true,
                                        replace: false,
                                        autoClose: 10000,
                                        group: "emailAccounts"
                                    });

                                    // we use the data from this call to possibly redirect the
                                    // user to the list, so we need to wait until this finishes
                                    // before proceeding
                                    return emailAccountsService.getEmailStats()
                                        .then(function(response) {
                                            $scope.accountStats = response;
                                            if (!emailAccount.details.stayOnView) {
                                                emailAccount.backToListView();
                                            } else {
                                                return initialize(true)
                                                    .then(function(result) {
                                                        emailAccount.details = result;
                                                        emailAccount.frmCreateEmail.$setPristine();
                                                    });
                                            }
                                        });

                                }, function(error) {
                                    alertService.add({
                                        type: "danger",
                                        message: error,
                                        closeable: true,
                                        replace: false,
                                        group: "emailAccounts"
                                    });
                                });
                        }
                    };

                    /**
                     * Back to List View
                     * @method backToListView
                     */
                    emailAccount.backToListView = function() {
                        $location.path("/list");
                    };

                    /**
                     * Toggles help
                     *
                     * @method toggleHelp
                     */
                    emailAccount.toggleHelp = function() {
                        $scope.showAllHelp = !$scope.showAllHelp;
                        emailAccount.saveToComponentSettings();
                        $scope.$broadcast("showHideAllChange", $scope.showAllHelp);
                    };

                    $scope.$on("$destroy", function() {
                        localStorage.removeItem(storageKeys.stayOnPage);
                        componentSettingSaverService.unregister(COMPONENT_NAME);
                        $timeout.cancel( redirectTimer );
                    });

                    function _updateExamplePlusAddress() {
                        var domain = emailAccount.details.domain || "example.com";
                        var user = emailAccount.details.userName || "user";
                        emailAccount.examplePlusAddress = _.escape(user + "+plusaddress@" + domain);
                    }

                    $scope.$watch("emailAccount.details.domain", _updateExamplePlusAddress);
                    $scope.$watch("emailAccount.details.userName", _updateExamplePlusAddress);
                }
            ]
        );

        return controller;
    }
);

/*
# email_accounts/views/manage.js                     Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global define, PAGE */

define(
    'app/views/manage',[
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/util/parse",
        "uiBootstrap",
        "app/services/emailAccountsService",
        "app/filters/encodeURIComponent",
        "cjt/services/alertService",
        "cjt/directives/alert",
        "cjt/directives/alertList",
        "cjt/directives/actionButtonDirective",
        "cjt/directives/toggleSwitchDirective",
        "cjt/directives/toggleLabelInfoDirective",
        "cjt/directives/validationContainerDirective",
        "cjt/directives/validationItemDirective",
        "cjt/services/cpanel/componentSettingSaverService"
    ],
    function(angular, _, LOCALE, parse) {
        "use strict";

        var app = angular.module("cpanel.emailAccounts");
        app.value("PAGE", PAGE);

        /**
         * Manage Controller for Email
         *
         * @module ManageController
         */
        var controller = app.controller(
            "ManageController",
            ["$scope", "$location", "$anchorScroll", "$timeout", "emailAccountsService", "PAGE", "alertService", "$routeParams", "componentSettingSaverService",
                function($scope, $location, $anchorScroll, $timeout, emailAccountsService, PAGE, alertService, $routeParams, componentSettingSaverService) {

                    var emailAccount = this;
                    emailAccount.isLoading = true;
                    emailAccount.isRTL = PAGE.isRTL;

                    $anchorScroll.yOffset = 70;

                    var storageKeys = {
                        stayOnPage: "EmailAccountsManageStayOnPage",
                    };

                    $scope.showAllHelp = false;
                    var COMPONENT_NAME = "EmailAccountsManage";
                    componentSettingSaverService.register(COMPONENT_NAME);

                    emailAccount.dprefix = PAGE.dprefix;

                    emailAccount.webmailEnabled = parse.parseBoolean(PAGE.webmailEnabled);
                    emailAccount.requiredPasswordStrength = PAGE.requiredPasswordStrength;
                    emailAccount.externalAuthConfig = PAGE.externalAuthModulesConfigured;
                    emailAccount.showCalAndContacts = PAGE.showCalendarAndContactItems;
                    emailAccount.emailDiskUsageEnabled = PAGE.emailDiskUsageEnabled;
                    emailAccount.emailFiltersEnabled = PAGE.emailFiltersEnabled;
                    emailAccount.autoResponderEnabled = PAGE.autoResponderEnabled;

                    $scope.defaultQuota = PAGE.userDefinedQuotaDefaultValue ? PAGE.userDefinedQuotaDefaultValue : undefined;
                    $scope.maxQuota = PAGE.maxEmailQuota;
                    $scope.canSetUnlimited = PAGE.canSetUnlimited;
                    $scope.maxQuotaHelpText = LOCALE.maketext("Quotas cannot exceed [format_bytes,_1].", PAGE.maxEmailQuota * 1024 * 1024);
                    $scope.maxEmailQuotaText = PAGE.canSetUnlimited ? LOCALE.maketext("Unlimited") : LOCALE.maketext("[format_bytes,_1]", PAGE.maxEmailQuota * 1024 * 1024);

                    emailAccount.stayOnView = parse.parseBoolean(emailAccountsService.getStoredValue(storageKeys.stayOnPage));

                    $scope.showAllHelp = false;

                    // Get the variables from the URL
                    var email = decodeURIComponent($routeParams.emailAccount);

                    emailAccount.examplePlusAddress = _.escape(email.split("@").join("+plusaddress@"));

                    var scrollTo = $routeParams.scrollTo;

                    var currentAccountState = {};
                    emailAccount.currentSuspendedState = {};
                    emailAccount.suspendOptions = {};

                    /**
                     * Resets manage view
                     * @method resetData
                     */
                    function resetData() {
                        currentAccountState = {};
                        emailAccount.currentSuspendedState = {};
                        emailAccount.suspendOptions = {};
                        emailAccount.details = {};
                    }

                    /**
                     * Stores the current values in the component settings
                     * @method saveToComponentSettings
                     */
                    emailAccount.saveToComponentSettings = function() {
                        componentSettingSaverService.set(COMPONENT_NAME, {
                            showAllHelp: $scope.showAllHelp
                        });
                    };

                    /**
                     * scrolls to specified area
                     * @method _scrollToArea
                     */
                    function _scrollToArea() {
                        return $timeout( function() {
                            if (typeof scrollTo !== "undefined" && scrollTo) {
                                $anchorScroll(scrollTo);
                                var section = angular.element( document.querySelector("#" + scrollTo) );

                                section.addClass("restriction-section-highlight");
                                return $timeout(function() {
                                    section.removeClass("restriction-section-highlight");
                                }, 11000);
                            }

                        }, 1000 );
                    }

                    /**
                     * Initialize
                     * @method initialize
                     */
                    function initialize() {

                        componentSettingSaverService.get(COMPONENT_NAME).then(function(response) {
                            if (typeof response !== "undefined" && response) {
                                $scope.showAllHelp = response.showAllHelp;
                            }
                        });

                        return emailAccountsService.getEmailAccountDetails(email)
                            .then(function(response) {

                                // this is referenced, so changing one changes the other without the copy
                                currentAccountState = angular.copy(response);
                                emailAccount.details = response;

                                emailAccount.details.autoCreateSubaddressFolders = parse.parseBoolean(emailAccount.details.autoCreateSubaddressFolders);

                                emailAccount.suspendOptions.login = emailAccount.currentSuspendedState.login = response.suspended_login;
                                emailAccount.suspendOptions.incoming = emailAccount.currentSuspendedState.incoming = response.suspended_incoming;
                                emailAccount.suspendOptions.suspended_outgoing = response.suspended_outgoing;
                                emailAccount.suspendOptions.hold_outgoing = response.hold_outgoing;
                                emailAccount.suspendOptions.has_suspended = response.has_suspended;
                                emailAccount.suspendOptions.outgoing = emailAccount.currentSuspendedState.outgoing = emailAccount.suspendOptions.suspended_outgoing === true ? "suspend" : emailAccount.suspendOptions.hold_outgoing === true ? "hold" : "allow";

                                if (emailAccount.suspendOptions.outgoing === "hold") {
                                    return emailAccountsService.getHeldMessageCount(email)
                                        .then(function(messageCount) {
                                            emailAccount.suspendOptions.currentlyHeld = messageCount;
                                            emailAccount.isLoading = false;
                                            return _scrollToArea();
                                        }, function(error) {
                                            alertService.add({
                                                type: "danger",
                                                message: error,
                                                closeable: true,
                                                replace: false,
                                                group: "emailAccounts"
                                            });
                                            emailAccount.isLoading = false;
                                        });
                                } else {
                                    emailAccount.isLoading = false;
                                    return _scrollToArea();
                                }
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                                emailAccount.isLoading = false;
                                emailAccount.backToListView();
                            });

                    }

                    /**
                     * Called when stay on page checkbox changed
                     * @method stayOnPageChanged
                     */
                    emailAccount.stayOnPageChanged = function() {

                        if (emailAccount.stayOnView) {
                            localStorage.setItem(
                                storageKeys.stayOnPage,
                                PAGE.securityToken + ":" + "true"
                            );
                        } else {

                            // clear local storage
                            localStorage.removeItem(storageKeys.stayOnPage);
                        }
                    };

                    /**
                     * Toggles help text on view
                     * @method toggleHelp
                     */
                    emailAccount.toggleHelp = function() {
                        $scope.showAllHelp = !$scope.showAllHelp;
                        emailAccount.saveToComponentSettings();
                        $scope.$broadcast("showHideAllChange", $scope.showAllHelp);
                    };

                    /**
                     * Returns the correct text for how many messages are held.
                     * This is done here so that the JS logic for quant will work properly. If
                     * this was done in the template, the template toolkit logic only works on initial load
                     * and would not change if the value passed in changes.
                     *
                     * @param {number} number - the number of messages that are being held
                     * @return {string} a localized string
                     *
                     */
                    emailAccount.currentlyHeldMessageText = function(number) {
                        return LOCALE.maketext("Delete [quant,_1,message,messages] from the mail queue.", number);
                    };

                    /**
                     * Called when update is clicked
                     * @method update
                     * @param {Object} emailDetails email account details
                     */
                    emailAccount.update = function(emailDetails) {

                        emailAccount.frmManageAccount.$submitted = true;

                        if (!emailAccount.frmManageAccount.$valid || emailAccount.frmManageAccount.$pending) {
                            return;
                        }
                        if ( emailDetails.quotaType === "unlimited" && !$scope.canSetUnlimited ) {
                            emailDetails.quota = $scope.maxQuota;
                        } else if (emailDetails.quotaType === "unlimited") {
                            emailDetails.quota = 0;
                        }

                        return emailAccountsService.updateEmail(currentAccountState, emailDetails, emailAccount.currentSuspendedState, emailAccount.suspendOptions)
                            .then(function(data) {
                                var successCount = 0;
                                var successObj = [];

                                _.forEach(data, function(obj) {
                                    if (obj.type === "danger") {
                                        alertService.add({
                                            type: obj.type,
                                            message: obj.message,
                                            closeable: true,
                                            replace: false,
                                            autoClose: obj.autoClose,
                                            group: "emailAccounts"
                                        });
                                    } else if (obj.type === "success") {
                                        successObj.push(obj);
                                        successCount = successCount + 1;
                                    }
                                });

                                if (successCount) {
                                    if (successCount > 1) {
                                        alertService.add({
                                            type: "success",
                                            message: LOCALE.maketext("All of your changes to “[_1]” are saved.", emailDetails.email),
                                            closeable: true,
                                            replace: false,
                                            autoClose: 10000,
                                            group: "emailAccounts"
                                        });
                                    } else {
                                        alertService.add({
                                            type: "success",
                                            message: successObj[0].message,
                                            closeable: true,
                                            replace: false,
                                            autoClose: 10000,
                                            group: "emailAccounts"
                                        });
                                    }


                                    if (emailAccount.stayOnView) {
                                        emailAccount.isLoading = true;
                                        emailAccount.frmManageAccount.$setPristine();
                                        resetData();
                                        initialize();
                                    } else {
                                        emailAccount.backToListView();
                                    }

                                }
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error.message,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                            });
                    };

                    /**
                     * Delete email account
                     * @method delete
                     * @param {string} email
                     */
                    emailAccount.delete = function(email) {
                        return emailAccountsService.deleteEmail(email)
                            .then(function() {
                                alertService.add({
                                    type: "success",
                                    message: LOCALE.maketext("You deleted “[_1]”.", email),
                                    closeable: true,
                                    replace: false,
                                    autoClose: 10000,
                                    group: "emailAccounts"
                                });

                                emailAccount.backToListView();

                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                            });
                    };

                    /**
                     * Back to List View
                     * @method backToListView
                     */
                    emailAccount.backToListView = function() {
                        $location.path("/list");
                    };

                    $scope.$on("$destroy", function() {
                        componentSettingSaverService.unregister(COMPONENT_NAME);
                        localStorage.removeItem(storageKeys.stayOnPage);
                    });

                    initialize();

                }
            ]
        );

        return controller;
    }
);

/*
# email_accounts/views/mnageDefault.js               Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global define, PAGE */

define(
    'app/views/manageDefault',[
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/util/parse",
        "uiBootstrap",
        "app/services/emailAccountsService",
        "app/filters/encodeURIComponent",
        "cjt/services/alertService",
        "cjt/directives/alert",
        "cjt/directives/alertList",
        "cjt/directives/actionButtonDirective",
        "cjt/directives/toggleSwitchDirective",
        "cjt/directives/toggleLabelInfoDirective",
        "cjt/directives/validationContainerDirective",
        "cjt/directives/validationItemDirective"
    ],
    function(angular, _, LOCALE, parse) {
        "use strict";

        var app = angular.module("cpanel.emailAccounts");
        app.value("PAGE", PAGE);

        /**
         * Manage default account controller for Email
         *
         * @module ManageController
         */
        var controller = app.controller(
            "ManageDefaultController",
            ["$scope", "$location", "emailAccountsService", "PAGE", "alertService", "$q",
                function($scope, $location, emailAccountsService, PAGE, alertService, $q) {

                    var emailAccount = this;
                    emailAccount.isLoading = true;
                    emailAccount.isRTL = PAGE.isRTL;

                    $scope.showAllHelp = false;

                    emailAccount.email = PAGE.mainEmailAccount;
                    emailAccount.webmailEnabled = parse.parseBoolean(PAGE.webmailEnabled);
                    emailAccount.emailDiskUsageEnabled = PAGE.emailDiskUsageEnabled;
                    emailAccount.defaultAddressEnabled = PAGE.defaultAddressEnabled;
                    emailAccount.dprefix = PAGE.dprefix;
                    emailAccount.examplePlusAddress = _.escape(emailAccount.email) + "+plusaddress@" + PAGE.hostname;

                    /**
                     * Initialize
                     * @method initialize
                     */
                    function initialize() {

                        var getDefaultAccountusage = emailAccountsService.getDefaultAccountUsage()
                            .then(function(data) {
                                emailAccount.diskUsage = data;
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                                emailAccount.backToListView();
                            });

                        var isSharedAddressBookEnabled = false;

                        var isUTF8MailboxNamesEnabled = emailAccountsService.isUTF8MailboxNamesEnabled()
                            .then(function(data) {
                                emailAccount.UTF8Mailbox = parse.parsePerlBoolean(data.enabled);
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                                emailAccount.backToListView();
                            });

                        var isAutoFolderCreationEnabled = emailAccountsService.isPlusAddressFolderCreationEnabled(emailAccount.email)
                            .then(function(data) {
                                emailAccount.autoCreateSubaddressFolders = parse.parsePerlBoolean(data);
                            }, function(error) {
                                alertService.add({
                                    type: "danger",
                                    message: error,
                                    closeable: true,
                                    replace: false,
                                    group: "emailAccounts"
                                });
                                emailAccount.backToListView();
                            });

                        $q.all([getDefaultAccountusage, isSharedAddressBookEnabled, isUTF8MailboxNamesEnabled, isAutoFolderCreationEnabled]).then(function() {
                            emailAccount.isLoading = false;
                        });
                    }

                    /**
                     * Show/hide help text
                     * @method toggleHelp
                     */
                    emailAccount.toggleHelp = function() {
                        $scope.showAllHelp = !$scope.showAllHelp;
                        $scope.$broadcast("showHideAllChange", $scope.showAllHelp);
                    };

                    /**
                     * Toggle shared address book state
                     * @method toggleSharedAddressBookStatus
                     */
                    emailAccount.toggleSharedAddressBookStatus = function() {
                        emailAccount.shareAddressBook = !emailAccount.shareAddressBook;

                        if (emailAccount.shareAddressBook) {
                            return emailAccountsService.enableSharedAddressBook()
                                .then(function(response) {
                                    alertService.add({
                                        type: "success",
                                        message: LOCALE.maketext("All of your email accounts can access the system-managed [output,em,Shared Address Book]."),
                                        closeable: true,
                                        replace: false,
                                        autoClose: 10000,
                                        group: "emailAccounts"
                                    });
                                }, function(error) {
                                    alertService.add({
                                        type: "danger",
                                        message: error,
                                        closeable: true,
                                        replace: false,
                                        group: "emailAccounts"
                                    });
                                    emailAccount.backToListView();
                                });
                        } else {
                            return emailAccountsService.disableSharedAddressBook()
                                .then(function(response) {
                                    alertService.add({
                                        type: "success",
                                        message: LOCALE.maketext("Only your default email address can access the system-managed [output,em,Shared Address Book]."),
                                        closeable: true,
                                        replace: false,
                                        autoClose: 10000,
                                        group: "emailAccounts"
                                    });
                                }, function(error) {
                                    alertService.add({
                                        type: "danger",
                                        message: error,
                                        closeable: true,
                                        replace: false,
                                        group: "emailAccounts"
                                    });
                                    emailAccount.backToListView();
                                });
                        }

                    };

                    /*
                     * Toggle UTF-8 mailbox support
                     * @method toggleUTF8MailboxNames
                     */
                    emailAccount.toggleUTF8MailboxNames = function() {
                        emailAccount.UTF8Mailbox = !emailAccount.UTF8Mailbox;

                        if (emailAccount.UTF8Mailbox) {
                            return emailAccountsService.enableUTF8MailboxNames()
                                .then(function(response) {
                                    alertService.add({
                                        type: "success",
                                        message: LOCALE.maketext("Mailbox names will now save as [asis,UTF-8]."),
                                        closeable: true,
                                        replace: false,
                                        autoClose: 10000,
                                        group: "emailAccounts"
                                    });
                                }, function(error) {
                                    alertService.add({
                                        type: "danger",
                                        message: error,
                                        closeable: true,
                                        replace: false,
                                        group: "emailAccounts"
                                    });
                                    emailAccount.backToListView();
                                });
                        } else {
                            return emailAccountsService.disableUTF8MailboxNames()
                                .then(function(response) {
                                    alertService.add({
                                        type: "success",
                                        message: LOCALE.maketext("Mailbox names will no longer save as [asis,UTF-8]."),
                                        closeable: true,
                                        replace: false,
                                        autoClose: 10000,
                                        group: "emailAccounts"
                                    });
                                }, function(error) {
                                    alertService.add({
                                        type: "danger",
                                        message: error,
                                        closeable: true,
                                        replace: false,
                                        group: "emailAccounts"
                                    });
                                    emailAccount.backToListView();
                                });
                        }

                    };

                    /*
                     * Toggle Plus Address Folder Auto Creation
                     * @method toggleAutoFolderCreation
                     */
                    emailAccount.toggleAutoFolderCreation = function() {
                        emailAccount.autoCreateSubaddressFolders = !emailAccount.autoCreateSubaddressFolders;
                        function _onFailure(error) {
                            alertService.add({
                                type: "danger",
                                message: error,
                                closeable: true,
                                replace: false,
                                group: "emailAccounts"
                            });
                            emailAccount.backToListView();
                        }
                        function _onSuccess(data) {
                            alertService.add({
                                type: "success",
                                message: data.message,
                                closeable: true,
                                replace: false,
                                autoClose: 10000,
                                group: "emailAccounts"
                            });
                        }

                        // Because this utilizes the service enable / disable,
                        // the success and failure messages are located there.
                        if (emailAccount.autoCreateSubaddressFolders) {
                            return emailAccountsService.enablePlusAddressFolderCreation(emailAccount.email).then(_onSuccess, _onFailure);
                        } else {
                            return emailAccountsService.disablePlusAddressFolderCreation(emailAccount.email).then(_onSuccess, _onFailure);
                        }
                    };

                    /**
                     * Back to List View
                     * @method backToListView
                     */
                    emailAccount.backToListView = function() {
                        $location.path("/list");
                    };

                    initialize();

                }
            ]
        );

        return controller;
    }
);

/*
# email_accounts/index.js                            Copyright 2022 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global require: false, define: false, PAGE: false */

define(
    'app/index',[
        "angular",
        "cjt/core",
        "cjt/modules",
        "cjt/util/locale",
        "ngRoute",
        "uiBootstrap",
        "cjt/services/alertService",
        "cjt/directives/alert",
        "cjt/directives/alertList",
        "cjt/directives/callout",
        "app/services/emailAccountsService",
        "jquery-chosen",
        "angular-chosen"
    ],
    function(angular, LOCALE) {

        "use strict";

        return function() {

            // First create the application
            angular.module("cpanel.emailAccounts", [
                "ngRoute",
                "ui.bootstrap",
                "cjt2.cpanel",
                "cjt2.services.api",
                "cpanel.emailAccounts.service",
                "localytics.directives",
                "cjt2.directives.bytesInput"
            ]);

            // Then load the application dependencies
            var app = require(
                [
                    "cjt/bootstrap",
                    "cjt/util/locale",
                    "cjt/directives/breadcrumbs",
                    "app/services/emailAccountsService",
                    "app/filters/encodeURIComponent",
                    "app/views/list",
                    "app/views/create",
                    "app/views/manage",
                    "app/views/manageDefault"
                ], function(BOOTSTRAP, LOCALE) {

                    var app = angular.module("cpanel.emailAccounts");
                    app.value("PAGE", PAGE);
                    app.constant("ONE_MEBIBYTE", 1048576);

                    app.firstLoad = true;

                    app.config([
                        "$routeProvider",
                        function($routeProvider) {
                            $routeProvider.when("/list/:account?", {
                                controller: "ListController",
                                controllerAs: "emailAccounts",
                                templateUrl: "views/list.ptt",
                                breadcrumb: {
                                    id: "list",
                                    name: LOCALE.maketext("List Email Accounts"),
                                    path: "/list"
                                }
                            });

                            $routeProvider.when("/create/:domain?", {
                                controller: "CreateController",
                                controllerAs: "emailAccount",
                                templateUrl: "views/create.ptt",
                                breadcrumb: {
                                    id: "create",
                                    name: LOCALE.maketext("Create an Email Account"),
                                    path: "/create/",
                                    parentID: "list"
                                }
                            });

                            $routeProvider.when("/manage/:emailAccount/:scrollTo?", {
                                controller: "ManageController",
                                controllerAs: "emailAccount",
                                templateUrl: "views/manage.ptt",
                                breadcrumb: {
                                    id: "manage",
                                    name: LOCALE.maketext("Manage an Email Account"),
                                    path: "/manage/",
                                    parentID: "list"
                                }
                            });

                            $routeProvider.when("/manageDefault/", {
                                controller: "ManageDefaultController",
                                controllerAs: "emailAccount",
                                templateUrl: "views/manageDefault.ptt",
                                breadcrumb: {
                                    id: "manageDefault",
                                    name: LOCALE.maketext("Manage Default Email Account"),
                                    path: "/manageDefault/",
                                    parentID: "list"
                                }
                            });

                            $routeProvider.otherwise({
                                "redirectTo": "/list"
                            });
                        }
                    ]);

                    BOOTSTRAP("#content", "cpanel.emailAccounts");

                });

            return app;
        };
    }
);

Back to Directory File Manager