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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
/*global setInterval, clearInterval */

(function (global) {
    "use strict";

    var ProtocolManager = global._Brackets_LiveDev_ProtocolManager;

    var _document = null;
    var _transport;

    /**
     * Retrieves related documents (external CSS and JS files)
     *
     * @return {{scripts: object, stylesheets: object}} Related scripts and stylesheets
     */
    function related() {

        var rel = {
            scripts: {},
            stylesheets: {}
        };
        var i;
        // iterate on document scripts (HTMLCollection doesn't provide forEach iterator).
        for (i = 0; i < _document.scripts.length; i++) {
            // add only external scripts
            if (_document.scripts[i].src) {
                rel.scripts[_document.scripts[i].src] = true;
            }
        }

        var s, j;
        //traverse @import rules
        var traverseRules = function _traverseRules(sheet, base) {
            var i,
                href = sheet.href,
                cssRules;

            // Deal with Firefox's SecurityError when accessing sheets
            // from other domains. Chrome will safely return `undefined`.
            try {
                cssRules = sheet.cssRules;
            } catch (e) {
                if (e.name !== "SecurityError") {
                    throw e;
                }
            }

            if (href && cssRules) {
                if (rel.stylesheets[href] === undefined) {
                    rel.stylesheets[href] = [];
                }
                rel.stylesheets[href].push(base);

                for (i = 0; i < cssRules.length; i++) {
                    if (cssRules[i].href) {
                        traverseRules(cssRules[i].styleSheet, base);
                    }
                }
            }
        };
        //iterate on document.stylesheets (StyleSheetList doesn't provide forEach iterator).
        for (j = 0; j < document.styleSheets.length; j++) {
            s = document.styleSheets[j];
            traverseRules(s, s.href);
        }
        return rel;
    }

    /**
     * Common functions.
     */
    var Utils = {

        isExternalStylesheet: function (node) {
            return (node.nodeName.toUpperCase() === "LINK" && node.rel === "stylesheet" && node.href);
        },
        isExternalScript: function (node) {
            return (node.nodeName.toUpperCase() === "SCRIPT" && node.src);
        }
    };

    /**
     * CSS related commands and notifications
     */
    var CSS = {

            /**
            * Maintains a map of stylesheets loaded thorugh @import rules and their parents.
            * Populated by extractImports, consumed by notifyImportsAdded / notifyImportsRemoved.
            * @type {
            */
            stylesheets : {},

            /**
             * Check the stylesheet that was just added be really loaded
             * to be able to extract potential import-ed stylesheets.
             * It invokes notifyStylesheetAdded once the sheet is loaded.
             * @param  {string} href Absolute URL of the stylesheet.
             */
            checkForStylesheetLoaded : function (href) {
                var self = this;

                // Inspect CSSRules for @imports:
                // styleSheet obejct is required to scan CSSImportRules but
                // browsers differ on the implementation of MutationObserver interface.
                // Webkit triggers notifications before stylesheets are loaded,
                // Firefox does it after loading.
                // There are also differences on when 'load' event is triggered for
                // the 'link' nodes. Webkit triggers it before stylesheet is loaded.
                // Some references to check:
                //      http://www.phpied.com/when-is-a-stylesheet-really-loaded/
                //      http://stackoverflow.com/questions/17747616/webkit-dynamically-created-stylesheet-when-does-it-really-load
                //        http://stackoverflow.com/questions/11425209/are-dom-mutation-observers-slower-than-dom-mutation-events
                //
                // TODO: This is just a temporary 'cross-browser' solution, it needs optimization.
                var loadInterval = setInterval(function () {
                    var i;
                    for (i = 0; i < document.styleSheets.length; i++) {
                        if (document.styleSheets[i].href === href) {
                            //clear interval
                            clearInterval(loadInterval);
                            // notify stylesheets added
                            self.notifyStylesheetAdded(href);
                            break;
                        }
                    }
                }, 50);
            },

            onStylesheetRemoved : function (url) {
                // get style node created when setting new text for stylesheet.
                var s = document.getElementById(url);
                // remove
                if (s && s.parentNode && s.parentNode.removeChild) {
                    s.parentNode.removeChild(s);
                }
            },

            /**
             * Send a notification for the stylesheet added and
             * its import-ed styleshets based on document.stylesheets diff
             * from previous status. It also updates stylesheets status.
             */
            notifyStylesheetAdded : function () {
                var added = {},
                    current,
                    newStatus;

                current = this.stylesheets;
                newStatus = related().stylesheets;

                Object.keys(newStatus).forEach(function (v, i) {
                    if (!current[v]) {
                        added[v] = newStatus[v];
                    }
                });

                Object.keys(added).forEach(function (v, i) {
                    _transport.send(JSON.stringify({
                        method: "StylesheetAdded",
                        href: v,
                        roots: [added[v]]
                    }));
                });

                this.stylesheets = newStatus;
            },

            /**
             * Send a notification for the removed stylesheet and
             * its import-ed styleshets based on document.stylesheets diff
             * from previous status. It also updates stylesheets status.
             */
            notifyStylesheetRemoved : function () {

                var self = this;
                var removed = {},
                    newStatus,
                    current;

                current = self.stylesheets;
                newStatus = related().stylesheets;

                Object.keys(current).forEach(function (v, i) {
                    if (!newStatus[v]) {
                        removed[v] = current[v];
                        // remove node created by setStylesheetText if any
                        self.onStylesheetRemoved(current[v]);
                    }
                });

                Object.keys(removed).forEach(function (v, i) {
                    _transport.send(JSON.stringify({
                        method: "StylesheetRemoved",
                        href: v,
                        roots: [removed[v]]
                    }));
                });

                self.stylesheets = newStatus;
            }
        };

    /* process related docs added */
    function _onNodesAdded(nodes) {
        var i;
        for (i = 0; i < nodes.length; i++) {
            //check for Javascript files
            if (Utils.isExternalScript(nodes[i])) {
                _transport.send(JSON.stringify({
                    method: 'ScriptAdded',
                    src: nodes[i].src
                }));
            }
            //check for stylesheets
            if (Utils.isExternalStylesheet(nodes[i])) {
                CSS.checkForStylesheetLoaded(nodes[i].href);
            }
        }
    }
    /* process related docs removed */
    function _onNodesRemoved(nodes) {
        var i;
        //iterate on removed nodes
        for (i = 0; i < nodes.length; i++) {

            // check for external JS files
            if (Utils.isExternalScript(nodes[i])) {
                _transport.send(JSON.stringify({
                    method: 'ScriptRemoved',
                    src: nodes[i].src
                }));
            }
            //check for external StyleSheets
            if (Utils.isExternalStylesheet(nodes[i])) {
                CSS.notifyStylesheetRemoved(nodes[i].href);
            }
        }
    }

    function _enableListeners() {
        // enable MutationOberver if it's supported
        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        if (MutationObserver) {
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    if (mutation.addedNodes.length > 0) {
                        _onNodesAdded(mutation.addedNodes);
                    }
                    if (mutation.removedNodes.length > 0) {
                        _onNodesRemoved(mutation.removedNodes);
                    }
                });
            });
            observer.observe(_document, {
                childList: true,
                subtree: true
            });
        } else {
            // use MutationEvents as fallback
            document.addEventListener('DOMNodeInserted', function niLstnr(e) {
                _onNodesAdded([e.target]);
            });
            document.addEventListener('DOMNodeRemoved', function nrLstnr(e) {
                _onNodesRemoved([e.target]);
            });
        }
    }

    /**
     * Start listening for events and send initial related documents message.
     *
     * @param {HTMLDocument} document
     * @param {object} transport Live development transport connection
     */
    function start(document, transport) {
        _transport = transport;
        _document = document;
        // start listening to node changes
        _enableListeners();

        var rel = related();

        // send the current status of related docs.
        _transport.send(JSON.stringify({
            method: "DocumentRelated",
            related: rel
        }));
        // initialize stylesheets with current status for further notifications.
        CSS.stylesheets = rel.stylesheets;
    }

    /**
     * Stop listening.
     * TODO currently a no-op.
     */
    function stop() {

    }

    var DocumentObserver = {
        start: start,
        stop: stop,
        related: related
    };

    ProtocolManager.setDocumentObserver(DocumentObserver);

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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */
/*global setInterval, clearInterval */

(function (global) {
    "use strict";

    var ProtocolManager = global._Brackets_LiveDev_ProtocolManager;

    var _document = null;
    var _transport;

    /**
     * Retrieves related documents (external CSS and JS files)
     *
     * @return {{scripts: object, stylesheets: object}} Related scripts and stylesheets
     */
    function related() {

        var rel = {
            scripts: {},
            stylesheets: {}
        };
        var i;
        // iterate on document scripts (HTMLCollection doesn't provide forEach iterator).
        for (i = 0; i < _document.scripts.length; i++) {
            // add only external scripts
            if (_document.scripts[i].src) {
                rel.scripts[_document.scripts[i].src] = true;
            }
        }

        var s, j;
        //traverse @import rules
        var traverseRules = function _traverseRules(sheet, base) {
            var i,
                href = sheet ? sheet.href : null,
                cssRules;

            // Deal with Firefox's SecurityError when accessing sheets
            // from other domains. Chrome will safely return `undefined`.
            try {
                cssRules = sheet.cssRules;
            } catch (e) {
                if (e.name !== "SecurityError") {
                    throw e;
                }
            }

            if (href && cssRules) {
                if (rel.stylesheets[href] === undefined) {
                    rel.stylesheets[href] = [];
                }
                rel.stylesheets[href].push(base);

                for (i = 0; i < cssRules.length; i++) {
                    if (cssRules[i].href) {
                        traverseRules(cssRules[i].styleSheet, base);
                    }
                }
            }
        };
        //iterate on document.stylesheets (StyleSheetList doesn't provide forEach iterator).
        for (j = 0; j < document.styleSheets.length; j++) {
            s = document.styleSheets[j];
            traverseRules(s, s.href);
        }
        return rel;
    }

    /**
     * Common functions.
     */
    var Utils = {

        isExternalStylesheet: function (node) {
            return (node.nodeName.toUpperCase() === "LINK" && node.rel === "stylesheet" && node.href);
        },
        isExternalScript: function (node) {
            return (node.nodeName.toUpperCase() === "SCRIPT" && node.src);
        }
    };

    /**
     * CSS related commands and notifications
     */
    var CSS = {

            /**
            * Maintains a map of stylesheets loaded thorugh @import rules and their parents.
            * Populated by extractImports, consumed by notifyImportsAdded / notifyImportsRemoved.
            * @type {
            */
            stylesheets : {},

            /**
             * Check the stylesheet that was just added be really loaded
             * to be able to extract potential import-ed stylesheets.
             * It invokes notifyStylesheetAdded once the sheet is loaded.
             * @param  {string} href Absolute URL of the stylesheet.
             */
            checkForStylesheetLoaded : function (href) {
                var self = this;

                // Inspect CSSRules for @imports:
                // styleSheet obejct is required to scan CSSImportRules but
                // browsers differ on the implementation of MutationObserver interface.
                // Webkit triggers notifications before stylesheets are loaded,
                // Firefox does it after loading.
                // There are also differences on when 'load' event is triggered for
                // the 'link' nodes. Webkit triggers it before stylesheet is loaded.
                // Some references to check:
                //      http://www.phpied.com/when-is-a-stylesheet-really-loaded/
                //      http://stackoverflow.com/questions/17747616/webkit-dynamically-created-stylesheet-when-does-it-really-load
                //        http://stackoverflow.com/questions/11425209/are-dom-mutation-observers-slower-than-dom-mutation-events
                //
                // TODO: This is just a temporary 'cross-browser' solution, it needs optimization.
                var loadInterval = setInterval(function () {
                    var i;
                    for (i = 0; i < document.styleSheets.length; i++) {
                        if (document.styleSheets[i].href === href) {
                            //clear interval
                            clearInterval(loadInterval);
                            // notify stylesheets added
                            self.notifyStylesheetAdded(href);
                            break;
                        }
                    }
                }, 50);
            },

            onStylesheetRemoved : function (url) {
                // get style node created when setting new text for stylesheet.
                var s = document.getElementById(url);
                // remove
                if (s && s.parentNode && s.parentNode.removeChild) {
                    s.parentNode.removeChild(s);
                }
            },

            /**
             * Send a notification for the stylesheet added and
             * its import-ed styleshets based on document.stylesheets diff
             * from previous status. It also updates stylesheets status.
             */
            notifyStylesheetAdded : function () {
                var added = {},
                    current,
                    newStatus;

                current = this.stylesheets;
                newStatus = related().stylesheets;

                Object.keys(newStatus).forEach(function (v, i) {
                    if (!current[v]) {
                        added[v] = newStatus[v];
                    }
                });

                Object.keys(added).forEach(function (v, i) {
                    _transport.send(JSON.stringify({
                        method: "StylesheetAdded",
                        href: v,
                        roots: [added[v]]
                    }));
                });

                this.stylesheets = newStatus;
            },

            /**
             * Send a notification for the removed stylesheet and
             * its import-ed styleshets based on document.stylesheets diff
             * from previous status. It also updates stylesheets status.
             */
            notifyStylesheetRemoved : function () {

                var self = this;
                var removed = {},
                    newStatus,
                    current;

                current = self.stylesheets;
                newStatus = related().stylesheets;

                Object.keys(current).forEach(function (v, i) {
                    if (!newStatus[v]) {
                        removed[v] = current[v];
                        // remove node created by setStylesheetText if any
                        self.onStylesheetRemoved(current[v]);
                    }
                });

                Object.keys(removed).forEach(function (v, i) {
                    _transport.send(JSON.stringify({
                        method: "StylesheetRemoved",
                        href: v,
                        roots: [removed[v]]
                    }));
                });

                self.stylesheets = newStatus;
            }
        };

    /* process related docs added */
    function _onNodesAdded(nodes) {
        var i;
        for (i = 0; i < nodes.length; i++) {
            //check for Javascript files
            if (Utils.isExternalScript(nodes[i])) {
                _transport.send(JSON.stringify({
                    method: 'ScriptAdded',
                    src: nodes[i].src
                }));
            }
            //check for stylesheets
            if (Utils.isExternalStylesheet(nodes[i])) {
                CSS.checkForStylesheetLoaded(nodes[i].href);
            }
        }
    }
    /* process related docs removed */
    function _onNodesRemoved(nodes) {
        var i;
        //iterate on removed nodes
        for (i = 0; i < nodes.length; i++) {

            // check for external JS files
            if (Utils.isExternalScript(nodes[i])) {
                _transport.send(JSON.stringify({
                    method: 'ScriptRemoved',
                    src: nodes[i].src
                }));
            }
            //check for external StyleSheets
            if (Utils.isExternalStylesheet(nodes[i])) {
                CSS.notifyStylesheetRemoved(nodes[i].href);
            }
        }
    }

    function _enableListeners() {
        // enable MutationOberver if it's supported
        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        if (MutationObserver) {
            var observer = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    if (mutation.addedNodes.length > 0) {
                        _onNodesAdded(mutation.addedNodes);
                    }
                    if (mutation.removedNodes.length > 0) {
                        _onNodesRemoved(mutation.removedNodes);
                    }
                });
            });
            observer.observe(_document, {
                childList: true,
                subtree: true
            });
        } else {
            // use MutationEvents as fallback
            document.addEventListener('DOMNodeInserted', function niLstnr(e) {
                _onNodesAdded([e.target]);
            });
            document.addEventListener('DOMNodeRemoved', function nrLstnr(e) {
                _onNodesRemoved([e.target]);
            });
        }
    }

    /**
     * Start listening for events and send initial related documents message.
     *
     * @param {HTMLDocument} document
     * @param {object} transport Live development transport connection
     */
    function start(document, transport) {
        _transport = transport;
        _document = document;
        // start listening to node changes
        _enableListeners();

        var rel = related();

        // send the current status of related docs.
        _transport.send(JSON.stringify({
            method: "DocumentRelated",
            related: rel
        }));
        // initialize stylesheets with current status for further notifications.
        CSS.stylesheets = rel.stylesheets;
    }

    /**
     * Stop listening.
     * TODO currently a no-op.
     */
    function stop() {

    }

    var DocumentObserver = {
        start: start,
        stop: stop,
        related: related
    };

    ProtocolManager.setDocumentObserver(DocumentObserver);

}(this));<|endoftext|>


<commit_before>/**
 * @ngdoc controller
 * @name Umbraco.Editors.DataType.EditController
 * @function
 *
 * @description
 * The controller for the content editor
 */
function DataTypeEditController($scope, $routeParams, $location, dataTypeResource, notificationsService, navigationService, angularHelper, serverValidationManager, contentEditingHelper, formHelper) {

    //method used to configure the pre-values when we retreive them from the server
    function createPreValueProps(preVals) {
        $scope.preValues = [];
        for (var i = 0; i < preVals.length; i++) {
            $scope.preValues.push({
                hideLabel: preVals[i].hideLabel,
                alias: preVals[i].key,
                description: preVals[i].description,
                label: preVals[i].label,
                view: preVals[i].view,
                value: preVals[i].value
            });
        }
    }

    //set up the standard data type props
    $scope.properties = {
        selectedEditor: {
            alias: "selectedEditor",
            description: "Select a property editor",
            label: "Property editor"
        },
        selectedEditorId: {
            alias: "selectedEditorId",
            label: "Property editor alias"
        }
    };

    //setup the pre-values as props
    $scope.preValues = [];

    if ($routeParams.create) {
        //we are creating so get an empty content item
        dataTypeResource.getScaffold($routeParams.id, $routeParams.doctype)
            .then(function(data) {
                $scope.loaded = true;
                $scope.preValuesLoaded = true;
                $scope.content = data;
            });
    }
    else {
        //we are editing so get the content item from the server
        dataTypeResource.getById($routeParams.id)
            .then(function(data) {
                $scope.loaded = true;
                $scope.preValuesLoaded = true;
                $scope.content = data;

                navigationService.setActiveTreeType("datatype");
                navigationService.syncPath([String(data.id)]);

                createPreValueProps($scope.content.preValues);

                //in one particular special case, after we've created a new item we redirect back to the edit
                // route but there might be server validation errors in the collection which we need to display
                // after the redirect, so we will bind all subscriptions which will show the server validation errors
                // if there are any and then clear them so the collection no longer persists them.
                serverValidationManager.executeAndClearAllSubscriptions();
            });
    }

    $scope.$watch("content.selectedEditor", function (newVal, oldVal) {
        //when the value changes, we need to dynamically load in the new editor
        if (newVal !== null && newVal !== undefined && newVal != oldVal) {
            //we are editing so get the content item from the server
            var currDataTypeId = $routeParams.create ? undefined : $routeParams.id;
            dataTypeResource.getPreValues(newVal, currDataTypeId)
                .then(function (data) {
                    $scope.preValuesLoaded = true;
                    $scope.content.preValues = data;
                    createPreValueProps($scope.content.preValues);
                });
        }
    });

    $scope.save = function() {

        if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) {

            dataTypeResource.save($scope.content, $scope.preValues, $routeParams.create)
                .then(function(data) {

                    formHelper.resetForm({ scope: $scope, notifications: data.notifications });

                    contentEditingHelper.handleSuccessfulSave({
                        scope: $scope,
                        newContent: data,
                        rebindCallback: function() {
                            createPreValueProps(data.preValues);
                        }
                    });

                }, function(err) {

                    //NOTE: in the case of data type values we are setting the orig/new props
                    // to be the same thing since that only really matters for content/media.
                    contentEditingHelper.handleSaveError({
                        err: err,
                        allNewProps: $scope.preValues,
                        allOrigProps: $scope.preValues
                    });
                });
        }

    };

}

angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);<commit_msg>Fixes: U4-2690 data type editor, makes 2 requests for the initial load<commit_after>/**
 * @ngdoc controller
 * @name Umbraco.Editors.DataType.EditController
 * @function
 *
 * @description
 * The controller for the content editor
 */
function DataTypeEditController($scope, $routeParams, $location, dataTypeResource, notificationsService, navigationService, angularHelper, serverValidationManager, contentEditingHelper, formHelper) {

    //method used to configure the pre-values when we retreive them from the server
    function createPreValueProps(preVals) {
        $scope.preValues = [];
        for (var i = 0; i < preVals.length; i++) {
            $scope.preValues.push({
                hideLabel: preVals[i].hideLabel,
                alias: preVals[i].key,
                description: preVals[i].description,
                label: preVals[i].label,
                view: preVals[i].view,
                value: preVals[i].value
            });
        }
    }

    //set up the standard data type props
    $scope.properties = {
        selectedEditor: {
            alias: "selectedEditor",
            description: "Select a property editor",
            label: "Property editor"
        },
        selectedEditorId: {
            alias: "selectedEditorId",
            label: "Property editor alias"
        }
    };

    //setup the pre-values as props
    $scope.preValues = [];

    if ($routeParams.create) {
        //we are creating so get an empty content item
        dataTypeResource.getScaffold($routeParams.id, $routeParams.doctype)
            .then(function(data) {
                $scope.loaded = true;
                $scope.preValuesLoaded = true;
                $scope.content = data;
            });
    }
    else {
        //we are editing so get the content item from the server
        dataTypeResource.getById($routeParams.id)
            .then(function(data) {
                $scope.loaded = true;
                $scope.preValuesLoaded = true;
                $scope.content = data;

                navigationService.setActiveTreeType("datatype");
                navigationService.syncPath([String(data.id)]);

                createPreValueProps($scope.content.preValues);

                //in one particular special case, after we've created a new item we redirect back to the edit
                // route but there might be server validation errors in the collection which we need to display
                // after the redirect, so we will bind all subscriptions which will show the server validation errors
                // if there are any and then clear them so the collection no longer persists them.
                serverValidationManager.executeAndClearAllSubscriptions();
            });
    }

    $scope.$watch("content.selectedEditor", function (newVal, oldVal) {

        //when the value changes, we need to dynamically load in the new editor
        if (oldVal !== null && newVal !== null && newVal !== undefined && newVal != oldVal) {
            //we are editing so get the content item from the server
            var currDataTypeId = $routeParams.create ? undefined : $routeParams.id;
            dataTypeResource.getPreValues(newVal, currDataTypeId)
                .then(function (data) {
                    $scope.preValuesLoaded = true;
                    $scope.content.preValues = data;
                    createPreValueProps($scope.content.preValues);
                });
        }
    });

    $scope.save = function() {

        if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) {

            dataTypeResource.save($scope.content, $scope.preValues, $routeParams.create)
                .then(function(data) {

                    formHelper.resetForm({ scope: $scope, notifications: data.notifications });

                    contentEditingHelper.handleSuccessfulSave({
                        scope: $scope,
                        newContent: data,
                        rebindCallback: function() {
                            createPreValueProps(data.preValues);
                        }
                    });

                }, function(err) {

                    //NOTE: in the case of data type values we are setting the orig/new props
                    // to be the same thing since that only really matters for content/media.
                    contentEditingHelper.handleSaveError({
                        err: err,
                        allNewProps: $scope.preValues,
                        allOrigProps: $scope.preValues
                    });
                });
        }

    };

}

angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);<|endoftext|>


<commit_before>import * as autosuggest from 'platform/forms-system/src/js/definitions/autosuggest';
import disabilityLabels from '../content/disabilityLabels';
import {
  descriptionInfo,
  autoSuggestTitle,
  newOnlyAlert,
  increaseAndNewAlert,
} from '../content/addDisabilities';
import NewDisability from '../components/NewDisability';
import ArrayField from '../components/ArrayField';
import { validateDisabilityName, requireDisability } from '../validations';
import {
  newConditionsOnly,
  newAndIncrease,
  hasClaimedConditions,
} from '../utils';

import fullSchema from 'vets-json-schema/dist/21-526EZ-ALLCLAIMS-schema.json';

const { condition } = fullSchema.definitions.newDisabilities.items.properties;

export const uiSchema = {
  'ui:description': 'Please tell us the new conditions you want to claim.',
  newDisabilities: {
    'ui:title': 'New condition',
    'ui:field': ArrayField,
    'ui:options': {
      viewField: NewDisability,
      reviewTitle: 'New Disabilities',
      itemName: 'Disability',
    },
    // Ideally, this would show the validation on the array itself (or the name field in an array
    //  item), but that's not working.
    'ui:validations': [requireDisability],
    items: {
      condition: autosuggest.uiSchema(
        autoSuggestTitle,
        () =>
          Promise.resolve(
            Object.entries(disabilityLabels).map(([key, value]) => ({
              id: key,
              label: value,
            })),
          ),
        {
          'ui:options': {
            freeInput: true,
            inputTransformers: [
              // Replace a bunch of things that aren't valid with valid equivalents
              input => input.replace(/["”']/g, '’'),
              input => input.replace(/[;–]/g, ' -- '),
              input => input.replace(/[&]/g, ' and '),
              input => input.replace(/[\\]/g, '/'),
              // Strip out everything that's not valid and doesn't need to be replaced
              input => input.replace(/([^a-zA-Z0-9\-’.,/() ]+)/g, ''),
              // Get rid of extra whitespace characters
              input => input.trim(),
              input => input.replace(/\s{2,}/, ' '),
            ],
          },
          // autoSuggest schema doesn't have any default validations as long as { `freeInput: true` }
          'ui:validations': [validateDisabilityName],
        },
      ),
      'view:descriptionInfo': {
        'ui:description': descriptionInfo,
      },
    },
  },
  // This object only shows up when the user tries to continue without claiming either a rated or new condition
  'view:newDisabilityErrors': {
    'view:newOnlyAlert': {
      'ui:description': newOnlyAlert,
      'ui:options': {
        hideIf: formData =>
          !newConditionsOnly(formData) || hasClaimedConditions(formData),
      },
    },
    'view:increaseAndNewAlert': {
      'ui:description': increaseAndNewAlert,
      'ui:options': {
        hideIf: formData =>
          // Only show this alert if the veteran is claiming both rated and new conditions
          !newAndIncrease(formData) || hasClaimedConditions(formData),
      },
    },
  },
};

export const schema = {
  type: 'object',
  properties: {
    newDisabilities: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        properties: {
          condition,
          'view:descriptionInfo': { type: 'object', properties: {} },
        },
      },
    },
    'view:newDisabilityErrors': {
      type: 'object',
      properties: {
        'view:newOnlyAlert': { type: 'object', properties: {} },
        'view:increaseAndNewAlert': { type: 'object', properties: {} },
      },
    },
  },
};
<commit_msg>Update disabilities name regex (#9746)<commit_after>import * as autosuggest from 'platform/forms-system/src/js/definitions/autosuggest';
import disabilityLabels from '../content/disabilityLabels';
import {
  descriptionInfo,
  autoSuggestTitle,
  newOnlyAlert,
  increaseAndNewAlert,
} from '../content/addDisabilities';
import NewDisability from '../components/NewDisability';
import ArrayField from '../components/ArrayField';
import { validateDisabilityName, requireDisability } from '../validations';
import {
  newConditionsOnly,
  newAndIncrease,
  hasClaimedConditions,
} from '../utils';

import fullSchema from 'vets-json-schema/dist/21-526EZ-ALLCLAIMS-schema.json';

const { condition } = fullSchema.definitions.newDisabilities.items.properties;

export const uiSchema = {
  'ui:description': 'Please tell us the new conditions you want to claim.',
  newDisabilities: {
    'ui:title': 'New condition',
    'ui:field': ArrayField,
    'ui:options': {
      viewField: NewDisability,
      reviewTitle: 'New Disabilities',
      itemName: 'Disability',
    },
    // Ideally, this would show the validation on the array itself (or the name field in an array
    //  item), but that's not working.
    'ui:validations': [requireDisability],
    items: {
      condition: autosuggest.uiSchema(
        autoSuggestTitle,
        () =>
          Promise.resolve(
            Object.entries(disabilityLabels).map(([key, value]) => ({
              id: key,
              label: value,
            })),
          ),
        {
          'ui:options': {
            freeInput: true,
            inputTransformers: [
              // Replace a bunch of things that aren't valid with valid equivalents
              input => input.replace(/["”’]/g, `'`),
              input => input.replace(/[;–]/g, ' -- '),
              input => input.replace(/[&]/g, ' and '),
              input => input.replace(/[\\]/g, '/'),
              // Strip out everything that's not valid and doesn't need to be replaced
              input => input.replace(/([^a-zA-Z0-9\-'.,/() ]+)/g, ''),
              // Get rid of extra whitespace characters
              input => input.trim(),
              input => input.replace(/\s{2,}/, ' '),
            ],
          },
          // autoSuggest schema doesn't have any default validations as long as { `freeInput: true` }
          'ui:validations': [validateDisabilityName],
        },
      ),
      'view:descriptionInfo': {
        'ui:description': descriptionInfo,
      },
    },
  },
  // This object only shows up when the user tries to continue without claiming either a rated or new condition
  'view:newDisabilityErrors': {
    'view:newOnlyAlert': {
      'ui:description': newOnlyAlert,
      'ui:options': {
        hideIf: formData =>
          !newConditionsOnly(formData) || hasClaimedConditions(formData),
      },
    },
    'view:increaseAndNewAlert': {
      'ui:description': increaseAndNewAlert,
      'ui:options': {
        hideIf: formData =>
          // Only show this alert if the veteran is claiming both rated and new conditions
          !newAndIncrease(formData) || hasClaimedConditions(formData),
      },
    },
  },
};

export const schema = {
  type: 'object',
  properties: {
    newDisabilities: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'object',
        properties: {
          condition,
          'view:descriptionInfo': { type: 'object', properties: {} },
        },
      },
    },
    'view:newDisabilityErrors': {
      type: 'object',
      properties: {
        'view:newOnlyAlert': { type: 'object', properties: {} },
        'view:increaseAndNewAlert': { type: 'object', properties: {} },
      },
    },
  },
};
<|endoftext|>


<commit_before>/*!
 Copyright (C) 2016 Google Inc.
 Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
 */
(function (can, $) {
  'use strict';

  GGRC.Components('RevisionsComparer', {
    tag: 'revisions-comparer',
    template: '<content/>',
    scope: {
      instance: null,
      leftRevisionId: null,
      rightRevisions: [],
      compareIt: function (scope, el, ev) {
        var currentRevisionID = scope.leftRevisionId;
        var revisionsLength = scope.rightRevisions.length;
        var newRevisionID = scope.rightRevisions[revisionsLength - 1].id;
        var view = scope.instance.view;
        var that = this;
        GGRC.Controllers.Modals.confirm({
          modal_title: 'Compare with the latest version',
          modal_description: 'Loading...',
          header_view: GGRC.mustache_path +
                        '/modals/modal_compare_header.mustache',
          modal_confirm: 'Update',
          skip_refresh: true,
          extraCssClass: 'compare-modal',
          button_view: GGRC.mustache_path +
                        '/modals/prompt_buttons.mustache',
          afterFetch: function (target) {
            that.getRevisions(currentRevisionID, newRevisionID)
              .then(function (data) {
                var revisions = that.prepareInstances(data);
                var fragLeft = can.view(view, revisions[0]);
                var fragRight = can.view(view, revisions[1]);
                fragLeft.appendChild(fragRight);
                target.find('.modal-body').html(fragLeft);
                that.highlightDifference(target);
              });
          }
        }, this.updateRevision.bind(this));
      },
      getRevisions: function (currentRevisionID, newRevisionID) {
        var additionalFilter = {
          expression: {
            op: {name: 'OR'},
            left: {
              op: {name: '='},
              left: 'id',
              right: currentRevisionID
            },
            right: {
              op: {name: '='},
              left: 'id',
              right: newRevisionID
            }
          }
        };
        var params = GGRC.Utils.QueryAPI.buildParam(
          'Revision',
          {},
          undefined,
          undefined,
          additionalFilter
        );
        return can.Model.Cacheable.query({data: [params]});
      },
      prepareInstances: function (data) {
        return data.Revision.values.map(function (value) {
          var content = value.content;
          content.isRevision = true;
          content.class = {
            is_custom_attributable: false
          };
          return {instance: content};
        });
      },
      updateRevision: function () {
        var instance = this.instance.snapshot;
        instance.refresh().then(function () {
          instance.attr('update_revision', 'latest');
          return instance.save();
        }).then(function () {
          GGRC.Utils.Browser.refreshPage(true);
        });
      },

      /**
       * Get Infopanes from modal
       * @param {Object} $target - jQuery object
       * @return {Object} - jQuery object
       */
      getInfopanes: function ($target) {
        return $target.find('.info .tier-content');
      },

      /**
       * Get Attributes from infopanes
       * @param {Object} $infoPanes - jQuery object of infopanes
       * @param {Number} index - index of infopane
       * @return {Object} - jQuery object
       */
      getAttributes: function ($infoPanes, index) {
        return $($infoPanes[index]).find('.row-fluid h6 + *');
      },

      /**
       * Get Custom Attribute panes from modal
       * @param {Object} $target - jQuery object
       * @return {Object} - jQuery object
       */
      getCAPanes: function ($target) {
        return $target.find('.info custom-attributes');
      },

      /**
       * Get Custom Attributes from infopanes
       * @param {Object} CAPanes - jQuery object of CA panes
       * @param {Number} index - index of CA pane
       * @return {Object} - jQuery object
       */
      getCAs: function (CAPanes, index) {
        return $(CAPanes[index]).find('[data-custom-attribute] inline-edit');
      },

      /**
       * Highlight difference
       * @param {Object} $target - jQuery object
       */
      highlightDifference: function ($target) {
        this.highlightAttributes($target);
        this.highlightCustomAttributes($target);
      },

      /**
       * Highlight difference in attributes
       * @param {Object} $target - jQuery object
       */
      highlightAttributes: function ($target) {
        var emptySelector = '.empty-message';
        var highlightClass = 'diff-highlighted';
        var listSelector = 'ul li';
        var infoPanes = this.getInfopanes($target);
        var valuesOld = this.getAttributes(infoPanes, 0);
        var valuesNew = this.getAttributes(infoPanes, 1);

        valuesOld.each(function (index, valueOld) {
          var $valueNew = $(valuesNew[index]);
          var $valueOld = $(valueOld);
          var listOld = [];
          var listNew = [];
          if ($valueOld.html() !== $valueNew.html()) {
            listOld = $valueOld.find(listSelector);
            listNew = $valueNew.find(listSelector);
            if (listOld.length) {
              highlightLists(listOld, listNew);
            } else {
              highlightValues($valueOld);
              highlightValues($valueNew);
            }
            equalValuesHeight($valueOld, $valueNew);
          }
        });

        /**
         * Highlight difference in two DOM lists
         * @param {Object} listFirst - DOM object
         * @param {Object} listLast - DOM object
         */
        function highlightLists(listFirst, listLast) {
          compareLists(listFirst, listLast);
          compareLists(listLast, listFirst);
        }

        /**
         * Compare DOM lists
         * @param {Object} liFirst - DOM object
         * @param {Object} liLast - DOM object
         */
        function compareLists(liFirst, liLast) {
          liFirst.each(function (i, li) {
            var atLeastOneIsEqual = false;
            liLast.each(function (j, li2) {
              if (li.innerHTML === li2.innerHTML) {
                atLeastOneIsEqual = true;
              }
            });
            if (!atLeastOneIsEqual) {
              $(li).addClass(highlightClass);
            }
          });
        }

        /**
         * Highlight difference in simple values
         * @param {Object} $value - jQuery object
         */
        function highlightValues($value) {
          if ($value.html() && !$value.find(emptySelector).length) {
            $value.addClass(highlightClass);
          }
        }

        /**
         * Set max height between two jQuery objects
         * @param {Object} $firstItem - jQuery object
         * @param {Object} $secondItem - jQuery object
         */
        function equalValuesHeight($firstItem, $secondItem) {
          var firstItemHeight = $firstItem.outerHeight();
          var secondItemHeight = $secondItem.outerHeight();
          if (firstItemHeight > secondItemHeight) {
            $secondItem.outerHeight(firstItemHeight);
          } else if (firstItemHeight < secondItemHeight) {
            $firstItem.outerHeight(secondItemHeight);
          }
        }
      },

      /**
       * Highlight difference in custom attributes
       * @param {Object} $target - jQuery object
       */
      highlightCustomAttributes: function ($target) {
        var titleSelector = '.inline-edit__title';
        var valueSelector = '.inline-edit__content';
        var highlightClass = 'diff-highlighted';
        var caSelector = '[data-custom-attribute]';
        var caWrapperSelector = '.span6';
        var cas = this.getCAPanes($target);
        var ca0s = this.getCAs(cas, 0);
        var ca1s = this.getCAs(cas, 1);
        compareCA(ca0s, ca1s);
        compareCA(ca1s, ca0s);

        /**
         * Compare two lists of custom attributes
         * @param {Object} caFirst - jQuery object
         * @param {Object} caLast - jQuery object
         */
        function compareCA(caFirst, caLast) {
          caFirst.each(function (i, caOld) {
            var $sameCA = [];
            var $caOld = $(caOld);
            var caOldScope = $caOld.viewModel();
            caLast.each(function (j, caNew) {
              var $caNew = $(caNew);
              var caNewScope = $caNew.viewModel();
              if (caNewScope.caId === caOldScope.caId) {
                $sameCA = $caNew;
              }
            });
            if ($sameCA.length) {
              highlightProperty('title', $sameCA, $caOld, titleSelector);
              highlightProperty('value', $sameCA, $caOld, valueSelector);
              equalCAHeight($caOld, $sameCA);
            } else {
              fillEmptyCA(caLast, $caOld, i);
            }
          });
        }

        /**
         * Highlight specific property in custom attributes
         * @param {String} name - Property name
         * @param {Object} $caFirst - jQuery object
         * @param {Object} $caLast - jQuery object
         * @param {String} selector - child selector to add class
         */
        function highlightProperty(name, $caFirst, $caLast, selector) {
          var caFirstScope = $caFirst.viewModel();
          var caLastScope = $caLast.viewModel();
          if (caFirstScope[name] !== caLastScope[name]) {
            if (caFirstScope[name]) {
              $caFirst.find(selector).addClass(highlightClass);
            }
            if (caLastScope[name]) {
              $caLast.find(selector).addClass(highlightClass);
            }
          }
        }

        /**
         * Set max height between two custom attributes
         * @param {Object} $caFirst - jQuery object
         * @param {Object} $caLast - jQuery object
         */
        function equalCAHeight($caFirst, $caLast) {
          var caFirstHeight = $caFirst.closest(caSelector).outerHeight();
          var caLastHeight = $caLast.closest(caSelector).outerHeight();
          if (caFirstHeight > caLastHeight) {
            $caLast.closest(caSelector).outerHeight(caFirstHeight);
          } else if (caFirstHeight < caLastHeight) {
            $caFirst.closest(caSelector).outerHeight(caLastHeight);
          }
        }

        /**
         * Fill empty space when CA is not existing
         * @param {Object} caList - List of CA
         * @param {Object} $ca - jQuery object Current attribute
         * @param {Number} index - Index of current attribute
         */
        function fillEmptyCA(caList, $ca, index) {
          var i = 1;
          var caOldHeight = $ca
                              .closest(caWrapperSelector)
                              .outerHeight(true);
          while (!$(caList[index - i]).length && i < index) {
            i++;
          }
          $(caList[index - i])
                  .closest(caWrapperSelector)
                  .css('margin-bottom', '+=' + caOldHeight);
          $ca.closest(caSelector).addClass(highlightClass);
        }
      }
    }
  });
})(window.can, window.can.$);
<commit_msg>Fix highlight of not changed Person CA<commit_after>/*!
 Copyright (C) 2016 Google Inc.
 Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
 */
(function (can, $) {
  'use strict';

  GGRC.Components('RevisionsComparer', {
    tag: 'revisions-comparer',
    template: '<content/>',
    scope: {
      instance: null,
      leftRevisionId: null,
      rightRevisions: [],
      compareIt: function (scope, el, ev) {
        var currentRevisionID = scope.leftRevisionId;
        var revisionsLength = scope.rightRevisions.length;
        var newRevisionID = scope.rightRevisions[revisionsLength - 1].id;
        var view = scope.instance.view;
        var that = this;
        GGRC.Controllers.Modals.confirm({
          modal_title: 'Compare with the latest version',
          modal_description: 'Loading...',
          header_view: GGRC.mustache_path +
                        '/modals/modal_compare_header.mustache',
          modal_confirm: 'Update',
          skip_refresh: true,
          extraCssClass: 'compare-modal',
          button_view: GGRC.mustache_path +
                        '/modals/prompt_buttons.mustache',
          afterFetch: function (target) {
            that.getRevisions(currentRevisionID, newRevisionID)
              .then(function (data) {
                var revisions = that.prepareInstances(data);
                var fragLeft = can.view(view, revisions[0]);
                var fragRight = can.view(view, revisions[1]);
                fragLeft.appendChild(fragRight);
                target.find('.modal-body').html(fragLeft);
                that.highlightDifference(target);
              });
          }
        }, this.updateRevision.bind(this));
      },
      getRevisions: function (currentRevisionID, newRevisionID) {
        var additionalFilter = {
          expression: {
            op: {name: 'OR'},
            left: {
              op: {name: '='},
              left: 'id',
              right: currentRevisionID
            },
            right: {
              op: {name: '='},
              left: 'id',
              right: newRevisionID
            }
          }
        };
        var params = GGRC.Utils.QueryAPI.buildParam(
          'Revision',
          {},
          undefined,
          undefined,
          additionalFilter
        );
        return can.Model.Cacheable.query({data: [params]});
      },
      prepareInstances: function (data) {
        return data.Revision.values.map(function (value) {
          var content = value.content;
          content.isRevision = true;
          content.class = {
            is_custom_attributable: false
          };
          return {instance: content};
        });
      },
      updateRevision: function () {
        var instance = this.instance.snapshot;
        instance.refresh().then(function () {
          instance.attr('update_revision', 'latest');
          return instance.save();
        }).then(function () {
          GGRC.Utils.Browser.refreshPage(true);
        });
      },

      /**
       * Get Infopanes from modal
       * @param {Object} $target - jQuery object
       * @return {Object} - jQuery object
       */
      getInfopanes: function ($target) {
        return $target.find('.info .tier-content');
      },

      /**
       * Get Attributes from infopanes
       * @param {Object} $infoPanes - jQuery object of infopanes
       * @param {Number} index - index of infopane
       * @return {Object} - jQuery object
       */
      getAttributes: function ($infoPanes, index) {
        return $($infoPanes[index]).find('.row-fluid h6 + *');
      },

      /**
       * Get Custom Attribute panes from modal
       * @param {Object} $target - jQuery object
       * @return {Object} - jQuery object
       */
      getCAPanes: function ($target) {
        return $target.find('.info custom-attributes');
      },

      /**
       * Get Custom Attributes from infopanes
       * @param {Object} CAPanes - jQuery object of CA panes
       * @param {Number} index - index of CA pane
       * @return {Object} - jQuery object
       */
      getCAs: function (CAPanes, index) {
        return $(CAPanes[index]).find('[data-custom-attribute] inline-edit');
      },

      /**
       * Highlight difference
       * @param {Object} $target - jQuery object
       */
      highlightDifference: function ($target) {
        this.highlightAttributes($target);
        this.highlightCustomAttributes($target);
      },

      /**
       * Highlight difference in attributes
       * @param {Object} $target - jQuery object
       */
      highlightAttributes: function ($target) {
        var emptySelector = '.empty-message';
        var highlightClass = 'diff-highlighted';
        var listSelector = 'ul li';
        var infoPanes = this.getInfopanes($target);
        var valuesOld = this.getAttributes(infoPanes, 0);
        var valuesNew = this.getAttributes(infoPanes, 1);

        valuesOld.each(function (index, valueOld) {
          var $valueNew = $(valuesNew[index]);
          var $valueOld = $(valueOld);
          var listOld = [];
          var listNew = [];
          if ($valueOld.html() !== $valueNew.html()) {
            listOld = $valueOld.find(listSelector);
            listNew = $valueNew.find(listSelector);
            if (listOld.length) {
              highlightLists(listOld, listNew);
            } else {
              highlightValues($valueOld);
              highlightValues($valueNew);
            }
            equalValuesHeight($valueOld, $valueNew);
          }
        });

        /**
         * Highlight difference in two DOM lists
         * @param {Object} listFirst - DOM object
         * @param {Object} listLast - DOM object
         */
        function highlightLists(listFirst, listLast) {
          compareLists(listFirst, listLast);
          compareLists(listLast, listFirst);
        }

        /**
         * Compare DOM lists
         * @param {Object} liFirst - DOM object
         * @param {Object} liLast - DOM object
         */
        function compareLists(liFirst, liLast) {
          liFirst.each(function (i, li) {
            var atLeastOneIsEqual = false;
            liLast.each(function (j, li2) {
              if (li.innerHTML === li2.innerHTML) {
                atLeastOneIsEqual = true;
              }
            });
            if (!atLeastOneIsEqual) {
              $(li).addClass(highlightClass);
            }
          });
        }

        /**
         * Highlight difference in simple values
         * @param {Object} $value - jQuery object
         */
        function highlightValues($value) {
          if ($value.html() && !$value.find(emptySelector).length) {
            $value.addClass(highlightClass);
          }
        }

        /**
         * Set max height between two jQuery objects
         * @param {Object} $firstItem - jQuery object
         * @param {Object} $secondItem - jQuery object
         */
        function equalValuesHeight($firstItem, $secondItem) {
          var firstItemHeight = $firstItem.outerHeight();
          var secondItemHeight = $secondItem.outerHeight();
          if (firstItemHeight > secondItemHeight) {
            $secondItem.outerHeight(firstItemHeight);
          } else if (firstItemHeight < secondItemHeight) {
            $firstItem.outerHeight(secondItemHeight);
          }
        }
      },

      /**
       * Highlight difference in custom attributes
       * @param {Object} $target - jQuery object
       */
      highlightCustomAttributes: function ($target) {
        var titleSelector = '.inline-edit__title';
        var valueSelector = '.inline-edit__content';
        var highlightClass = 'diff-highlighted';
        var caSelector = '[data-custom-attribute]';
        var caWrapperSelector = '.span6';
        var cas = this.getCAPanes($target);
        var ca0s = this.getCAs(cas, 0);
        var ca1s = this.getCAs(cas, 1);
        compareCA(ca0s, ca1s);
        compareCA(ca1s, ca0s);

        /**
         * Compare two lists of custom attributes
         * @param {Object} caFirst - jQuery object
         * @param {Object} caLast - jQuery object
         */
        function compareCA(caFirst, caLast) {
          caFirst.each(function (i, caOld) {
            var $sameCA = [];
            var $caOld = $(caOld);
            var caOldScope = $caOld.viewModel();
            caLast.each(function (j, caNew) {
              var $caNew = $(caNew);
              var caNewScope = $caNew.viewModel();
              if (caNewScope.caId === caOldScope.caId) {
                $sameCA = $caNew;
              }
            });
            if ($sameCA.length) {
              highlightProperty('titleText', $sameCA, $caOld, titleSelector);
              highlightProperty('value', $sameCA, $caOld, valueSelector);
              equalCAHeight($caOld, $sameCA);
            } else {
              fillEmptyCA(caLast, $caOld, i);
            }
          });
        }

        /**
         * Highlight specific property in custom attributes
         * @param {String} name - Property name
         * @param {Object} $caFirst - jQuery object
         * @param {Object} $caLast - jQuery object
         * @param {String} selector - child selector to add class
         */
        function highlightProperty(name, $caFirst, $caLast, selector) {
          var caFirstScope = $caFirst.viewModel();
          var caLastScope = $caLast.viewModel();
          var firstProp = caFirstScope[name].id || caFirstScope[name];
          var lastProp = caLastScope[name].id || caLastScope[name];
          if (firstProp !== lastProp) {
            if (caFirstScope[name]) {
              $caFirst.find(selector).addClass(highlightClass);
            }
            if (caLastScope[name]) {
              $caLast.find(selector).addClass(highlightClass);
            }
          }
        }

        /**
         * Set max height between two custom attributes
         * @param {Object} $caFirst - jQuery object
         * @param {Object} $caLast - jQuery object
         */
        function equalCAHeight($caFirst, $caLast) {
          var caFirstHeight = $caFirst.closest(caSelector).outerHeight();
          var caLastHeight = $caLast.closest(caSelector).outerHeight();
          if (caFirstHeight > caLastHeight) {
            $caLast.closest(caSelector).outerHeight(caFirstHeight);
          } else if (caFirstHeight < caLastHeight) {
            $caFirst.closest(caSelector).outerHeight(caLastHeight);
          }
        }

        /**
         * Fill empty space when CA is not existing
         * @param {Object} caList - List of CA
         * @param {Object} $ca - jQuery object Current attribute
         * @param {Number} index - Index of current attribute
         */
        function fillEmptyCA(caList, $ca, index) {
          var i = 1;
          var caOldHeight = $ca
                              .closest(caWrapperSelector)
                              .outerHeight(true);
          while (!$(caList[index - i]).length && i < index) {
            i++;
          }
          $(caList[index - i])
                  .closest(caWrapperSelector)
                  .css('margin-bottom', '+=' + caOldHeight);
          $ca.closest(caSelector).addClass(highlightClass);
        }
      }
    }
  });
})(window.can, window.can.$);
<|endoftext|>


<commit_before>var ViewBalanceUI = (function(){

    function updateBalances(response){
        if (response.hasOwnProperty('error')) {
            console.log(response.error.message);
            return;
        }
        var balance = response.balance;
        var currency = balance.currency;
        if (!currency) {
            return;
        }

        var amount = addComma(parseFloat(balance.balance));
        var view = format_money(currency, amount);

        TUser.get().balance = balance.balance;
        PortfolioWS.updateBalance();
        $(".topMenuBalance").text(view)
                     .css('visibility', 'visible');
    }

    return {
        updateBalances: updateBalances
    };
}());

module.exports = {
    ViewBalanceUI: ViewBalanceUI,
};
<commit_msg>Update trading page balance<commit_after>var ViewBalanceUI = (function(){

    function updateBalances(response){
        if (response.hasOwnProperty('error')) {
            console.log(response.error.message);
            return;
        }
        var balance = response.balance;
        var currency = balance.currency;
        if (!currency) {
            return;
        }

        var amount = addComma(parseFloat(balance.balance));
        var view = format_money(currency, amount);

        TUser.get().balance = balance.balance;
        updateContractBalance(balance.balance);
        PortfolioWS.updateBalance();
        $(".topMenuBalance").text(view)
                     .css('visibility', 'visible');
    }

    return {
        updateBalances: updateBalances
    };
}());

module.exports = {
    ViewBalanceUI: ViewBalanceUI,
};
<|endoftext|>


<commit_before>'use strict';

describe('nih.notifications: Notifications', function () {

    // load the controller's module
    beforeEach(module('nih.notifications'));

    var rootScope;
    var timeout;
    var Notifications;
    var testAlert = {
        text: 'Test',
        timeout: 300,
        delay: 0,
        closeButton: false,
        imageClass: 'glyphicon-warning-sign'
    };

    // Initialize the controller and a mock scope
    beforeEach(inject(function ($rootScope, $timeout, _Notifications_) {
        rootScope = $rootScope;
        timeout = $timeout;
        Notifications = _Notifications_;
    }));

    it('should trigger nih.notifications.show on Notifications.show', function () {
        spyOn(rootScope, '$broadcast');
        Notifications.show(testAlert);
        timeout.flush(1);
        expect(rootScope.$broadcast).toHaveBeenCalledWith('nih.notifications.show', testAlert);
    });

    it('should trigger nih.notifications.hide on Notifications.hide', function () {
        spyOn(rootScope, '$broadcast');
        Notifications.hide();
        expect(rootScope.$broadcast).toHaveBeenCalledWith('nih.notifications.hide');
    });

});
<commit_msg>Fix notifications tests<commit_after>'use strict';

describe('nih.notifications: Notifications', function () {

    // load the controller's module
    beforeEach(module('nih.notifications'));

    var rootScope;
    var timeout;
    var Notifications;
    var testAlert = {
        text: 'Test',
        timeout: 300,
        delay: 0,
        top: '8rem',
        closeButton: false,
        imageClass: 'glyphicon-warning-sign'
    };

    // Initialize the controller and a mock scope
    beforeEach(inject(function ($rootScope, $timeout, _Notifications_) {
        rootScope = $rootScope;
        timeout = $timeout;
        Notifications = _Notifications_;
    }));

    it('should trigger nih.notifications.show on Notifications.show', function () {
        spyOn(rootScope, '$broadcast');
        Notifications.show(testAlert);
        timeout.flush(1);
        expect(rootScope.$broadcast).toHaveBeenCalledWith('nih.notifications.show', testAlert);
    });

    it('should trigger nih.notifications.hide on Notifications.hide', function () {
        spyOn(rootScope, '$broadcast');
        Notifications.hide();
        expect(rootScope.$broadcast).toHaveBeenCalledWith('nih.notifications.hide');
    });

});
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

define(function(require) {
  /*
    DEPENDENCIES
   */

//  require('foundation.tab');
  var BaseFormPanel = require('utils/form-panels/form-panel');
  var Sunstone = require('sunstone');
  var Locale = require('utils/locale');
  var Tips = require('utils/tips');
  var RoleTab = require('tabs/oneflow-templates-tab/utils/role-tab');

  /*
    TEMPLATES
   */

  var TemplateWizardHTML = require('hbs!./create/wizard');
  var TemplateAdvancedHTML = require('hbs!./create/advanced');

  /*
    CONSTANTS
   */

  var FORM_PANEL_ID = require('./create/formPanelId');
  var TAB_ID = require('../tabId');

  /*
    CONSTRUCTOR
   */

  function FormPanel() {
    this.formPanelId = FORM_PANEL_ID;
    this.tabId = TAB_ID;
    this.actions = {
      'create': {
        'title': Locale.tr("Create Service Template"),
        'buttonText': Locale.tr("Create"),
        'resetButton': true
      },
      'update': {
        'title': Locale.tr("Update Service Template"),
        'buttonText': Locale.tr("Update"),
        'resetButton': false
      }
    };

    BaseFormPanel.call(this);
  }

  FormPanel.FORM_PANEL_ID = FORM_PANEL_ID;
  FormPanel.prototype = Object.create(BaseFormPanel.prototype);
  FormPanel.prototype.constructor = FormPanel;
  FormPanel.prototype.htmlWizard = _htmlWizard;
  FormPanel.prototype.htmlAdvanced = _htmlAdvanced;
  FormPanel.prototype.submitWizard = _submitWizard;
  FormPanel.prototype.submitAdvanced = _submitAdvanced;
  FormPanel.prototype.onShow = _onShow;
  FormPanel.prototype.fill = _fill;
  FormPanel.prototype.setup = _setup;
  FormPanel.prototype.addRoleTab = _add_role_tab;

  return FormPanel;

  /*
    FUNCTION DEFINITIONS
   */

  function _htmlWizard() {
    return TemplateWizardHTML({
      'formPanelId': this.formPanelId
    });
  }

  function _htmlAdvanced() {
    return TemplateAdvancedHTML({
      'formPanelId': this.formPanelId
    });
  }

  function _setup(context) {
    this.roleTabObjects = {};
    var that = this;

    var roles_index = 0;

    $(".add_service_network", context).on("click", function(){
      $(".service_networks tbody").append(
        '
\
          \
            <input class="service_network_name" type="text" pattern="[a-z]*"/>\
            '+Locale.tr("Can only contain alphanumeric and underscore characters")+'\
          	\
          \
            <textarea class="service_network_description"/>\
          	\
          \
            \
          	\

');
    });

    $(".add_service_network", context).trigger("click");

    context.on("change", ".service_network_name", function(){
      _redo_service_networks_selector(context);
    });

    context.on("click", ".service_networks i.remove-tab", function(){
      var tr = $(this).closest('tr');
      tr.remove();

      _redo_service_networks_selector(context);
    });

    $("#tf_btn_roles", context).bind("click", function(){
      that.addRoleTab(roles_index, context);
      roles_index++;

      return false;
    });

    // Fill parents table
    // Each time a tab is clicked the table is filled with existing tabs (roles)
    // Selected roles are kept
    // TODO If the name of a role is changed and is selected, selection will be lost
    $("#roles_tabs", context).on("click", "a", function() {
      var tab_id = "#"+this.id+"Tab";
      var str = "";

      $(tab_id+" .parent_roles").hide();
      var parent_role_available = false;

      $("#roles_tabs_content #role_name", context).each(function(){
        if ($(this).val() && ($(this).val() != $(tab_id+" #role_name", context).val())) {
          parent_role_available = true;
          str += "
\
            \
              <input class='check_item' type='checkbox' value='"+$(this).val()+"' id='"+$(this).val()+"'/>\
            	\
            "+$(this).val()+"	\

";
        }
      });

      if (parent_role_available) {
        $(tab_id+" .parent_roles", context).show();
      }

      var selected_parents = [];
      $(tab_id+" .parent_roles_body input:checked", context).each(function(){
        selected_parents.push($(this).val());
      });

      $(tab_id+" .parent_roles_body", context).html(str);

      $.each(selected_parents, function(){
        $(tab_id+" .parent_roles_body #"+this, context).attr('checked', true);
      });
    });

    Foundation.reflow(context, 'tabs');

    // Add first role
    $("#tf_btn_roles", context).trigger("click");

    Tips.setup(context);
    return false;
  }

  function _submitWizard(context) {
    var that = this;

    var name = $('input[name="service_name"]', context).val();
    var description = $('#description', context).val();
    var deployment = $('select[name="deployment"]', context).val();
    var shutdown_action_service = $('select[name="shutdown_action_service"]', context).val();
    var ready_status_gate = $('input[name="ready_status_gate"]', context).prop("checked");

    var custom_attrs =  {};

    $(".service_networks tbody tr").each(function(){
      if ($(".service_network_name", $(this)).val()) {
        var attr_name = $(".service_network_name", $(this)).val();
        var attr_type = "vnet_id";
        var attr_desc = $(".service_network_description", $(this)).val();
        custom_attrs[attr_name] = "M|" + attr_type + "|" + attr_desc;
      }
    });

    var roles = [];

    $('.role_content', context).each(function() {
      var role_id = $(this).attr("role_id");

      roles.push( that.roleTabObjects[role_id].retrieve($(this)) );
    });

    var json_template = {
      name: name,
      deployment: deployment,
      description: description,
      roles: roles
    };

    if (!$.isEmptyObject(custom_attrs)){
      json_template['custom_attrs'] = custom_attrs;
    }

    if (shutdown_action_service){
      json_template['shutdown_action'] = shutdown_action_service;
    }

    json_template['ready_status_gate'] = ready_status_gate;

    if (this.action == "create") {
      Sunstone.runAction("ServiceTemplate.create", json_template );
      return false;
    } else if (this.action == "update") {
      Sunstone.runAction("ServiceTemplate.update",this.resourceId, JSON.stringify(json_template));
      return false;
    }
  }

  function _submitAdvanced(context) {
    var json_template = $('textarea#template', context).val();

    if (this.action == "create") {
      Sunstone.runAction("ServiceTemplate.create", JSON.parse(json_template) );
      return false;
    } else if (this.action == "update") {
      Sunstone.runAction("ServiceTemplate.update", this.resourceId, json_template);
      return false;
    }
  }

  function _onShow(context) {
    var that = this;

    $('.role_content', context).each(function() {
      var role_id = $(this).attr("role_id");
      that.roleTabObjects[role_id].onShow();
    });
  }

  function _fill(context, element) {
    var that = this;

    if (this.action != "update") {return;}
    this.setHeader(element);
    this.resourceId = element.ID;

    // Populates the Avanced mode Tab
    $('#template', context).val(JSON.stringify(element.TEMPLATE.BODY, null, "  "));

    $("#service_name", context).attr("disabled", "disabled");
    $("#service_name", context).val(element.NAME);

    $("#description", context).val(element.TEMPLATE.BODY.description);

    $('select[name="deployment"]', context).val(element.TEMPLATE.BODY.deployment);
    $("select[name='shutdown_action_service']", context).val(element.TEMPLATE.BODY.shutdown_action);
    $("input[name='ready_status_gate']", context).prop("checked",element.TEMPLATE.BODY.ready_status_gate || false);

    $(".service_networks i.remove-tab", context).trigger("click");

    if ( ! $.isEmptyObject( element.TEMPLATE.BODY['custom_attrs'] ) ) {
      $("div#network_configuration a.accordion_advanced_toggle", context).trigger("click");

      $.each(element.TEMPLATE.BODY['custom_attrs'], function(key, attr){
        var parts = attr.split("|");
        // 0 mandatory; 1 type; 2 desc;
        var attrs = {
          "name": key,
          "mandatory": parts[0],
          "type": parts[1],
          "description": parts[2],
        };

        switch (parts[1]) {
          case "vnet_id":
            $(".add_service_network", context).trigger("click");

            var tr = $(".service_networks tbody tr", context).last();
            $(".service_network_name", tr).val(attrs.name).change();
            $(".service_network_description", tr).val(attrs.description);

            break;
        }
      });
    }

    $("#roles_tabs i.remove-tab", context).trigger("click");

    var network_names = [];

    $(".service_networks .service_network_name", context).each(function(){
      if ($(this).val()) {
        network_names.push($(this).val());
      }
    });

    var roles_names = [];
    $.each(element.TEMPLATE.BODY.roles, function(index, value){
      roles_names.push(value.name);

      $("#tf_btn_roles", context).click();

      var role_context = $('.role_content', context).last();
      var role_id = $(role_context).attr("role_id");

      that.roleTabObjects[role_id].fill(role_context, value, network_names);
    });

    $.each(element.TEMPLATE.BODY.roles, function(index, value){
        var role_context = $('.role_content', context)[index];
        var str = "";

        $.each(roles_names, function(){
          if (this != value.name) {
            str += "
\
              \
                <input class='check_item' type='checkbox' value='"+this+"' id='"+this+"'/>\
              	\
              "+this+"	\

";
          }
        });

        $(".parent_roles_body", role_context).html(str);

        if (value.parents) {
          $.each(value.parents, function(index, value){
            $(".parent_roles_body #"+this, role_context).attr('checked', true);
          });
        }
    });
  }

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

  function _redo_service_networks_selector(dialog){
    $('#roles_tabs_content .role_content', dialog).each(function(){
      var role_section = this;
      _redo_service_networks_selector_role(dialog, role_section);
    });
  }

  function _redo_service_networks_selector_role(dialog, role_section){
    $('#roles_tabs_content .role_content', dialog).each(function(){
      var role_section = this;

      var selected_networks = [];
      $(".service_network_checkbox:checked", role_section).each(function(){
        selected_networks.push($(this).val());
      });

      $(".networks_role", role_section).hide();
      var service_networks = false;

      var role_tab_id = $(role_section).attr('id');

      var str = "";
      $(".service_networks .service_network_name", dialog).each(function(){
        if ($(this).val()) {
          service_networks = true;
          str += "
\
            \
              <input class='service_network_checkbox check_item' type='checkbox' value='"+$(this).val()+"' id='"+role_tab_id+"_"+$(this).val()+"'/>\
            	\
            \
              "+$(this).val()+"\
            	\

";
        }
      });

      $(".networks_role_body", role_section).html(str);

      if (service_networks) {
        $(".networks_role", role_section).show();
      }

      $(".vm_template_contents", role_section).val("");

      $.each(selected_networks, function(){
        $(".service_network_checkbox[value='"+this+"']", role_section).attr('checked', true).change();
      });
    });
  }

  function _add_role_tab(role_id, dialog) {
    var that = this;
    var html_role_id  = 'role' + role_id;

    var role_tab = new RoleTab(html_role_id);
    that.roleTabObjects[role_id] = role_tab;

    // Append the new div containing the tab and add the tab to the list
    var role_section = $('
'+
        role_tab.html() +
    '
').appendTo($("#roles_tabs_content", dialog));

    _redo_service_networks_selector_role(dialog, role_section);

    Tips.setup(role_section);

    var a = $("
\
      \
        \
          \

\
          "+Locale.tr("Role ")+role_id+"\
        \
        \
      \

").appendTo($("ul#roles_tabs", dialog));

    Foundation.reInit($("ul#roles_tabs", dialog));
    $("a", a).trigger("click");

    // close icon: removing the tab on click
    a.on("click", "i.remove-tab", function() {
      var target = $(this).parent().attr("href");
      var li = $(this).closest('li');
      var ul = $(this).closest('ul');
      var content = $(target);

      var role_id = content.attr("role_id");

      li.remove();
      content.remove();

      if (li.hasClass('is-active')) {
        $('a', ul.children('li').last()).click();
      }

      delete that.roleTabObjects[role_id];

      return false;
    });

    role_tab.setup(role_section);
    role_tab.onShow();
  }

});
<commit_msg>Bug #4665: Fix service template wizard, wrong pattern for network names validation<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2016, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

define(function(require) {
  /*
    DEPENDENCIES
   */

//  require('foundation.tab');
  var BaseFormPanel = require('utils/form-panels/form-panel');
  var Sunstone = require('sunstone');
  var Locale = require('utils/locale');
  var Tips = require('utils/tips');
  var RoleTab = require('tabs/oneflow-templates-tab/utils/role-tab');

  /*
    TEMPLATES
   */

  var TemplateWizardHTML = require('hbs!./create/wizard');
  var TemplateAdvancedHTML = require('hbs!./create/advanced');

  /*
    CONSTANTS
   */

  var FORM_PANEL_ID = require('./create/formPanelId');
  var TAB_ID = require('../tabId');

  /*
    CONSTRUCTOR
   */

  function FormPanel() {
    this.formPanelId = FORM_PANEL_ID;
    this.tabId = TAB_ID;
    this.actions = {
      'create': {
        'title': Locale.tr("Create Service Template"),
        'buttonText': Locale.tr("Create"),
        'resetButton': true
      },
      'update': {
        'title': Locale.tr("Update Service Template"),
        'buttonText': Locale.tr("Update"),
        'resetButton': false
      }
    };

    BaseFormPanel.call(this);
  }

  FormPanel.FORM_PANEL_ID = FORM_PANEL_ID;
  FormPanel.prototype = Object.create(BaseFormPanel.prototype);
  FormPanel.prototype.constructor = FormPanel;
  FormPanel.prototype.htmlWizard = _htmlWizard;
  FormPanel.prototype.htmlAdvanced = _htmlAdvanced;
  FormPanel.prototype.submitWizard = _submitWizard;
  FormPanel.prototype.submitAdvanced = _submitAdvanced;
  FormPanel.prototype.onShow = _onShow;
  FormPanel.prototype.fill = _fill;
  FormPanel.prototype.setup = _setup;
  FormPanel.prototype.addRoleTab = _add_role_tab;

  return FormPanel;

  /*
    FUNCTION DEFINITIONS
   */

  function _htmlWizard() {
    return TemplateWizardHTML({
      'formPanelId': this.formPanelId
    });
  }

  function _htmlAdvanced() {
    return TemplateAdvancedHTML({
      'formPanelId': this.formPanelId
    });
  }

  function _setup(context) {
    this.roleTabObjects = {};
    var that = this;

    var roles_index = 0;

    $(".add_service_network", context).on("click", function(){
      $(".service_networks tbody").append(
        '
\
          \
            <input class="service_network_name" type="text" pattern="^\\w+$"/>\

'+Locale.tr("Can only contain alphanumeric and underscore characters")+'\
          	\
          \
            <textarea class="service_network_description"/>\
          	\
          \
            \
          	\

');
    });

    $(".add_service_network", context).trigger("click");

    context.on("change", ".service_network_name", function(){
      _redo_service_networks_selector(context);
    });

    context.on("click", ".service_networks i.remove-tab", function(){
      var tr = $(this).closest('tr');
      tr.remove();

      _redo_service_networks_selector(context);
    });

    $("#tf_btn_roles", context).bind("click", function(){
      that.addRoleTab(roles_index, context);
      roles_index++;

      return false;
    });

    // Fill parents table
    // Each time a tab is clicked the table is filled with existing tabs (roles)
    // Selected roles are kept
    // TODO If the name of a role is changed and is selected, selection will be lost
    $("#roles_tabs", context).on("click", "a", function() {
      var tab_id = "#"+this.id+"Tab";
      var str = "";

      $(tab_id+" .parent_roles").hide();
      var parent_role_available = false;

      $("#roles_tabs_content #role_name", context).each(function(){
        if ($(this).val() && ($(this).val() != $(tab_id+" #role_name", context).val())) {
          parent_role_available = true;
          str += "
\
            \
              <input class='check_item' type='checkbox' value='"+$(this).val()+"' id='"+$(this).val()+"'/>\
            	\
            "+$(this).val()+"	\

";
        }
      });

      if (parent_role_available) {
        $(tab_id+" .parent_roles", context).show();
      }

      var selected_parents = [];
      $(tab_id+" .parent_roles_body input:checked", context).each(function(){
        selected_parents.push($(this).val());
      });

      $(tab_id+" .parent_roles_body", context).html(str);

      $.each(selected_parents, function(){
        $(tab_id+" .parent_roles_body #"+this, context).attr('checked', true);
      });
    });

    Foundation.reflow(context, 'tabs');

    // Add first role
    $("#tf_btn_roles", context).trigger("click");

    Tips.setup(context);
    return false;
  }

  function _submitWizard(context) {
    var that = this;

    var name = $('input[name="service_name"]', context).val();
    var description = $('#description', context).val();
    var deployment = $('select[name="deployment"]', context).val();
    var shutdown_action_service = $('select[name="shutdown_action_service"]', context).val();
    var ready_status_gate = $('input[name="ready_status_gate"]', context).prop("checked");

    var custom_attrs =  {};

    $(".service_networks tbody tr").each(function(){
      if ($(".service_network_name", $(this)).val()) {
        var attr_name = $(".service_network_name", $(this)).val();
        var attr_type = "vnet_id";
        var attr_desc = $(".service_network_description", $(this)).val();
        custom_attrs[attr_name] = "M|" + attr_type + "|" + attr_desc;
      }
    });

    var roles = [];

    $('.role_content', context).each(function() {
      var role_id = $(this).attr("role_id");

      roles.push( that.roleTabObjects[role_id].retrieve($(this)) );
    });

    var json_template = {
      name: name,
      deployment: deployment,
      description: description,
      roles: roles
    };

    if (!$.isEmptyObject(custom_attrs)){
      json_template['custom_attrs'] = custom_attrs;
    }

    if (shutdown_action_service){
      json_template['shutdown_action'] = shutdown_action_service;
    }

    json_template['ready_status_gate'] = ready_status_gate;

    if (this.action == "create") {
      Sunstone.runAction("ServiceTemplate.create", json_template );
      return false;
    } else if (this.action == "update") {
      Sunstone.runAction("ServiceTemplate.update",this.resourceId, JSON.stringify(json_template));
      return false;
    }
  }

  function _submitAdvanced(context) {
    var json_template = $('textarea#template', context).val();

    if (this.action == "create") {
      Sunstone.runAction("ServiceTemplate.create", JSON.parse(json_template) );
      return false;
    } else if (this.action == "update") {
      Sunstone.runAction("ServiceTemplate.update", this.resourceId, json_template);
      return false;
    }
  }

  function _onShow(context) {
    var that = this;

    $('.role_content', context).each(function() {
      var role_id = $(this).attr("role_id");
      that.roleTabObjects[role_id].onShow();
    });
  }

  function _fill(context, element) {
    var that = this;

    if (this.action != "update") {return;}
    this.setHeader(element);
    this.resourceId = element.ID;

    // Populates the Avanced mode Tab
    $('#template', context).val(JSON.stringify(element.TEMPLATE.BODY, null, "  "));

    $("#service_name", context).attr("disabled", "disabled");
    $("#service_name", context).val(element.NAME);

    $("#description", context).val(element.TEMPLATE.BODY.description);

    $('select[name="deployment"]', context).val(element.TEMPLATE.BODY.deployment);
    $("select[name='shutdown_action_service']", context).val(element.TEMPLATE.BODY.shutdown_action);
    $("input[name='ready_status_gate']", context).prop("checked",element.TEMPLATE.BODY.ready_status_gate || false);

    $(".service_networks i.remove-tab", context).trigger("click");

    if ( ! $.isEmptyObject( element.TEMPLATE.BODY['custom_attrs'] ) ) {
      $("div#network_configuration a.accordion_advanced_toggle", context).trigger("click");

      $.each(element.TEMPLATE.BODY['custom_attrs'], function(key, attr){
        var parts = attr.split("|");
        // 0 mandatory; 1 type; 2 desc;
        var attrs = {
          "name": key,
          "mandatory": parts[0],
          "type": parts[1],
          "description": parts[2],
        };

        switch (parts[1]) {
          case "vnet_id":
            $(".add_service_network", context).trigger("click");

            var tr = $(".service_networks tbody tr", context).last();
            $(".service_network_name", tr).val(attrs.name).change();
            $(".service_network_description", tr).val(attrs.description);

            break;
        }
      });
    }

    $("#roles_tabs i.remove-tab", context).trigger("click");

    var network_names = [];

    $(".service_networks .service_network_name", context).each(function(){
      if ($(this).val()) {
        network_names.push($(this).val());
      }
    });

    var roles_names = [];
    $.each(element.TEMPLATE.BODY.roles, function(index, value){
      roles_names.push(value.name);

      $("#tf_btn_roles", context).click();

      var role_context = $('.role_content', context).last();
      var role_id = $(role_context).attr("role_id");

      that.roleTabObjects[role_id].fill(role_context, value, network_names);
    });

    $.each(element.TEMPLATE.BODY.roles, function(index, value){
        var role_context = $('.role_content', context)[index];
        var str = "";

        $.each(roles_names, function(){
          if (this != value.name) {
            str += "
\
              \
                <input class='check_item' type='checkbox' value='"+this+"' id='"+this+"'/>\
              	\
              "+this+"	\

";
          }
        });

        $(".parent_roles_body", role_context).html(str);

        if (value.parents) {
          $.each(value.parents, function(index, value){
            $(".parent_roles_body #"+this, role_context).attr('checked', true);
          });
        }
    });
  }

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

  function _redo_service_networks_selector(dialog){
    $('#roles_tabs_content .role_content', dialog).each(function(){
      var role_section = this;
      _redo_service_networks_selector_role(dialog, role_section);
    });
  }

  function _redo_service_networks_selector_role(dialog, role_section){
    $('#roles_tabs_content .role_content', dialog).each(function(){
      var role_section = this;

      var selected_networks = [];
      $(".service_network_checkbox:checked", role_section).each(function(){
        selected_networks.push($(this).val());
      });

      $(".networks_role", role_section).hide();
      var service_networks = false;

      var role_tab_id = $(role_section).attr('id');

      var str = "";
      $(".service_networks .service_network_name", dialog).each(function(){
        if ($(this).val()) {
          service_networks = true;
          str += "
\
            \
              <input class='service_network_checkbox check_item' type='checkbox' value='"+$(this).val()+"' id='"+role_tab_id+"_"+$(this).val()+"'/>\
            	\
            \
              "+$(this).val()+"\
            	\

";
        }
      });

      $(".networks_role_body", role_section).html(str);

      if (service_networks) {
        $(".networks_role", role_section).show();
      }

      $(".vm_template_contents", role_section).val("");

      $.each(selected_networks, function(){
        $(".service_network_checkbox[value='"+this+"']", role_section).attr('checked', true).change();
      });
    });
  }

  function _add_role_tab(role_id, dialog) {
    var that = this;
    var html_role_id  = 'role' + role_id;

    var role_tab = new RoleTab(html_role_id);
    that.roleTabObjects[role_id] = role_tab;

    // Append the new div containing the tab and add the tab to the list
    var role_section = $('
'+
        role_tab.html() +
    '
').appendTo($("#roles_tabs_content", dialog));

    _redo_service_networks_selector_role(dialog, role_section);

    Tips.setup(role_section);

    var a = $("
\
      \
        \
          \

\
          "+Locale.tr("Role ")+role_id+"\
        \
        \
      \

").appendTo($("ul#roles_tabs", dialog));

    Foundation.reInit($("ul#roles_tabs", dialog));
    $("a", a).trigger("click");

    // close icon: removing the tab on click
    a.on("click", "i.remove-tab", function() {
      var target = $(this).parent().attr("href");
      var li = $(this).closest('li');
      var ul = $(this).closest('ul');
      var content = $(target);

      var role_id = content.attr("role_id");

      li.remove();
      content.remove();

      if (li.hasClass('is-active')) {
        $('a', ul.children('li').last()).click();
      }

      delete that.roleTabObjects[role_id];

      return false;
    });

    role_tab.setup(role_section);
    role_tab.onShow();
  }

});
<|endoftext|>


<commit_before>;(function($) {
    'use strict';

    /**
     * Shopware Auto Submit Plugin
     *
     * @example
     *
     * HTML:
     *
     * <form method="GET" action="URL">
     *     <input type="checkbox" name="item1" value="1" data-auto-submit="true" />
     *     <input type="radio" name="item2" value="2" data-auto-submit="true" />
     *     <select name="item3" data-auto-submit="true">
     *         <option value="opt1" selected="selected">My option 1</option>
     *         <option value="opt2">My option 2</option>
     *         <option value="opt3">My option 3</option>
     *     </select>
     * </form>
     *
     * JS:
     *
     * $('form *[data-auto-submit="true"]').autoSubmit();
     *
     * If you now change either an input or an option in the select, the form will be submitted.
     *
     */
    $.plugin('autoSubmit', {
        /**
         * Default plugin initialisation function.
         * Registers an event listener on the change event.
         * When it's triggered, the parent form will be submitted.
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this;

            // Will be automatically removed when destroy() is called.
            me._on(me.$el, 'change', function () {
                $(this).parents('form').submit();
            });
        }
    });
})(jQuery);<commit_msg>SW-10433 - added loading indicator for auto submitting forms<commit_after>;(function($) {
    'use strict';

    /**
     * Shopware Auto Submit Plugin
     *
     * @example
     *
     * HTML:
     *
     * <form method="GET" action="URL">
     *     <input type="checkbox" name="item1" value="1" data-auto-submit="true" />
     *     <input type="radio" name="item2" value="2" data-auto-submit="true" />
     *     <select name="item3" data-auto-submit="true">
     *         <option value="opt1" selected="selected">My option 1</option>
     *         <option value="opt2">My option 2</option>
     *         <option value="opt3">My option 3</option>
     *     </select>
     * </form>
     *
     * JS:
     *
     * $('form *[data-auto-submit="true"]').autoSubmit();
     *
     * If you now change either an input or an option in the select, the form will be submitted.
     *
     */
    $.plugin('autoSubmit', {

        defaults: {

            /**
             * Decide if loading indicator is shown until the form is submitted.
             *
             * @property loadingindicator
             * @type {Boolean}
             */
            'loadingindicator': true
        },

        /**
         * Default plugin initialisation function.
         * Registers an event listener on the change event.
         * When it's triggered, the parent form will be submitted.
         *
         * @public
         * @method init
         */
        init: function () {
            var me = this;

            me.applyDataAttributes();

            me.$form = $(me.$el.parents('form')[0]);

            // Will be automatically removed when destroy() is called.
            me._on(me.$el, 'change', $.proxy(me.onChangeSelection, me));
        },

        onChangeSelection: function () {
            var me = this;

            if(me.opts.loadingindicator) {
                $.loadingIndicator.open({
                    closeOnClick: false
                });
            }

            me.$form.submit();
        }
    });
})(jQuery);<|endoftext|>