<commit_before>define([
    'jquery',
    'underscore',
    'knockout',
    'knockout-mapping',
    'views/base-manager',
    'viewmodels/alert',
    'models/graph',
    'models/report',
    'views/graph/graph-manager/graph',
    'views/graph/graph-designer/graph-tree',
    'views/graph/graph-designer/node-form',
    'views/graph/graph-manager/branch-list',
    'views/graph/graph-designer/card-tree',
    'views/graph/permission-designer',
    'graph-designer-data',
    'arches',
    'viewmodels/graph-settings',
    'viewmodels/card',
    'view-data',
    'report-templates',
    'bindings/resizable-sidepanel',
    'datatype-config-components',
    'views/components/simple-switch'
], function($, _, ko, koMapping, BaseManagerView, AlertViewModel, GraphModel, ReportModel, GraphView, GraphTree, NodeFormView, BranchListView, CardTreeViewModel, PermissionDesigner, data, arches, GraphSettingsViewModel, CardViewModel, viewData, reportLookup) {
    var GraphDesignerView = BaseManagerView.extend({

        initialize: function(options) {
            var viewModel = options.viewModel;
            viewModel.graphid = ko.observable(data.graphid);
            viewModel.activeTab = ko.observable('graph');
            viewModel.viewState = ko.observable('design');
            viewModel.helpTemplate(viewData.help);
            viewModel.graphSettingsVisible = ko.observable(false);
            viewModel.graph = koMapping.fromJS(data['graph']);
            viewModel.ontologies = ko.observable(data['ontologies']);
            viewModel.ontologyClasses = ko.observable(data['ontologyClasses']);
            viewModel.cardComponents = data.cardComponents;

            var resources = ko.utils.arrayFilter(viewData.graphs, function(graph) {
                return graph.isresource;
            });
            var graphs = ko.utils.arrayFilter(viewData.graphs, function(graph) {
                return !graph.isresource;
            });

            var newGraph = function(url, data) {
                data = data || {};
                viewModel.loading(true);
                $.ajax({
                    type: "POST",
                    url: url,
                    data: JSON.stringify(data),
                    success: function(response) {
                        window.open(arches.urls.graph_designer(response.graphid), '_blank');
                        viewModel.loading(false);
                    },
                    error: function() {
                        viewModel.loading(false);
                    }
                });
            };
            viewModel.newResource = function() {
                newGraph('/graph/new', {isresource: true});
            };
            viewModel.newBranch = function() {
                newGraph('/graph/new', {isresource: false});
            };

            viewModel.deleteGraph = function() {
                viewModel.alert(new AlertViewModel('ep-alert-red', arches.confirmGraphDelete.title, arches.confirmGraphDelete.text, function() {
                    return;
                }, function(){
                    viewModel.loading(true);
                    $.ajax({
                        type: "DELETE",
                        url: arches.urls.delete_graph(viewModel.graph.graphid()),
                        complete: function(response, status) {
                            viewModel.loading(false);
                            if (status === 'success') {
                                window.location = arches.urls.graph;
                            } else {
                                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
                            }
                        }
                    });
                }));
            };
            viewModel.cloneGraph = function() {
                newGraph(arches.urls.clone_graph(viewModel.graph.graphid()));
            };
            viewModel.exportGraph = function() {
                window.open(arches.urls.export_graph(viewModel.graph.graphid()), '_blank');
            };
            viewModel.importGraph = function(data, e) {
                var formData = new FormData();
                formData.append("importedGraph", e.target.files[0]);

                $.ajax({
                    type: "POST",
                    url: '/graph/import/',
                    processData: false,
                    data: formData,
                    cache: false,
                    contentType: false,
                    success: function(response) {
                        if (response[0].length != 0) {
                            if (typeof(response[0])) {
                                response = response[0].join('<br />');
                            }
                            viewModel.alert(new AlertViewModel('ep-alert-red', arches.graphImportFailed.title, response));
                        } else {
                            viewModel.loading(false);
                            window.open(arches.urls.graph_designer(response[1].graph_id), '_blank');
                        }
                    },
                    error: function(response) {
                        viewModel.alert(new AlertViewModel('ep-alert-red', arches.graphImportFailed.title, 'Please contact your system administrator for more details.'));
                        viewModel.loading(false);
                    },
                });
            };
            viewModel.importButtonClick = function() {
                $("#fileupload").trigger('click');
            };
            viewModel.deleteInstances = function() {
                viewModel.alert(new AlertViewModel('ep-alert-red', arches.confirmAllResourceDelete.title, arches.confirmAllResourceDelete.text, function() {
                    return;
                }, function(){
                    viewModel.loading(true);
                    $.ajax({
                        type: "DELETE",
                        url: arches.urls.delete_instances(viewModel.graph.graphid()),
                        complete: function(response, status) {
                            viewModel.loading(false);
                            if (status === 'success') {
                                viewModel.alert(new AlertViewModel('ep-alert-blue', response.responseJSON.title, response.responseJSON.message));
                            } else {
                                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
                            }
                        }
                    });
                }));
            };
            viewModel.graph.ontology = ko.computed(function() {
                return viewModel.ontologies().find(function(obj) {
                    return obj.ontologyid === viewModel.graph.ontology_id();
                });
            });
            viewModel.groupedGraphs = ko.observable({
                groups: [
                    { name: 'Resource Models', items: resources },
                    { name: 'Branches', items: graphs }
                ]
            });

            viewModel.graphid.subscribe(function(graphid) {
                if (graphid && graphid !== '') {
                    viewModel.navigate(arches.urls.graph_designer(graphid));
                }
            });

            viewModel.graphModel = new GraphModel({
                data: data.graph,
                datatypes: data.datatypes,
                ontology_namespaces: data.ontology_namespaces
            });

            viewModel.datatypes = _.keys(viewModel.graphModel.get('datatypelookup'));

            viewModel.graphModel.on('changed', function(model, response) {
                if (viewModel.graphView) {
                    viewModel.graphView.redraw(true);
                }
                viewModel.alert(null);
                viewModel.loading(false);
                if (response.status !== 200) {
                    var errorMessageTitle = arches.requestFailed.title;
                    var errorMessageText = arches.requestFailed.text;
                    viewModel.alert(null);
                    if (response.responseJSON) {
                        errorMessageTitle = response.responseJSON.title;
                        errorMessageText = response.responseJSON.message;
                    }
                    viewModel.alert(new AlertViewModel('ep-alert-red', errorMessageTitle, errorMessageText));
                }
            });

            viewModel.graphModel.on('error', function(response) {
                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
            });

            viewModel.selectedNode = viewModel.graphModel.get('selectedNode');

            viewModel.saveNode = function(node) {
                if (node) {
                    viewModel.loading(true);
                    node.save(function(data) {
                        if (data.responseJSON.success === false || data.status === 500) {
                            viewModel.alert(new AlertViewModel('ep-alert-red', data.responseJSON.title, data.responseJSON.message));
                        }
                        else {
                            viewModel.cardTree.updateCards(viewModel.selectedNode().nodeGroupId(), data.responseJSON);
                            viewModel.permissionTree.updateCards(viewModel.selectedNode().nodeGroupId(), data.responseJSON);
                        }
                        viewModel.loading(false);
                    });
                }
            };

            viewModel.saveSelectedNode = function() {
                if (viewModel.selectedNode()) {
                    viewModel.saveNode(viewModel.selectedNode());
                }
            };

            viewModel.cardTree = new CardTreeViewModel({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel
            });

            viewModel.permissionTree = new CardTreeViewModel({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                multiselect: true
            });

            viewModel.selectedCards = ko.computed(function() {
                var selection = viewModel.permissionTree.selection();
                if (selection) {
                    if (selection.widgets) {
                        return selection;
                    }
                    return selection.parent;
                } else {
                    return null;
                }
            });

            viewModel.selectedCard = ko.computed(function() {
                var selection = viewModel.cardTree.selection();
                if (selection) {
                    if (selection.widgets) {
                        return selection;
                    }
                    return selection.parent;
                } else {
                    return null;
                }
            });

            viewModel.nodeForm = new NodeFormView({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                loading: viewModel.loading,
                node: viewModel.selectedNode,
                restrictedNodegroups: data.restrictedNodegroups
            });

            viewModel.branchListView = new BranchListView({
                el: $('#branch-library'),
                branches: data.branches,
                graphModel: viewModel.graphModel,
                loading: viewModel.loading,
                disableAppendButton: ko.computed(function() {
                    // self.node() && self.node().dirty();
                })
            });

            viewModel.permissionsDesigner = new PermissionDesigner({
                cardTree: viewModel.permissionTree
            });

            viewModel.graphSettingsViewModel = new GraphSettingsViewModel({
                designerViewModel: viewModel,
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                ontologyClasses: viewModel.ontologyClasses,
                ontologies: viewModel.ontologies,
                ontologyClass: ko.observable(''),
                iconFilter: ko.observable(''),
                node: viewModel.selectedNode,
                rootNodeColor: ko.observable(''),
                "ontology_namespaces": data.ontology_namespaces,
                onReset: function() {
                    var graph = ko.mapping.toJS(viewModel.graphSettingsViewModel.graph);
                    viewModel.report.configJSON(graph.config);
                    viewModel.report.get('template_id')(graph["template_id"]);
                }
            });

            viewModel.report = new ReportModel(_.extend(data, {
                graphModel: viewModel.graphModel,
                cards: viewModel.cardTree.topCards,
                preview: true
            }));

            viewModel.report.configJSON.subscribe(function(config) {
                var graph = ko.mapping.toJS(viewModel.graphSettingsViewModel.graph);
                graph.config = config;
                ko.mapping.fromJS(graph, viewModel.graphSettingsViewModel.graph);
            });

            viewModel.report.get('template_id').subscribe(function(val) {
                viewModel.graphSettingsViewModel.graph["template_id"](val);
            });

            viewModel.reportLookup = reportLookup;
            viewModel.reportTemplates = _.map(reportLookup, function(report, id) {
                report.id = id;
                return report;
            });

            viewModel.graphTree = new GraphTree({
                graphModel: viewModel.graphModel,
                graphSettings: viewModel.graphSettingsViewModel,
                cardTree: viewModel.cardTree,
                permissionTree: viewModel.permissionTree,
                restrictedNodegroups: data.restrictedNodegroups
            });

            viewModel.graphTree.branchListVisible.subscribe(function(visible) {
                if (visible) {
                    viewModel.branchListView.loadDomainConnections();
                }
            }, this);

            viewModel.loadGraphSettings = function() {
                var self = this;
                viewModel.loading(true);
                $.ajax({
                    type: 'GET',
                    url: arches.urls.graph_settings(data.graphid),
                    data: {'search': true, 'csrfmiddlewaretoken': '{{ csrf_token }}'}})
                    .done(function(data) {
                        self.graphSettingsViewModel.resource_data(data.resources);
                        self.graphSettingsViewModel.icon_data(data.icons);
                        self.graphSettingsViewModel.jsonCache(self.graphSettingsViewModel.jsonData());
                        self.graphSettingsViewModel.contentLoading = viewModel.loading;
                        self.graphSettingsVisible(true);
                        viewModel.loading(false);
                    })
                    .fail(function() {
                        throw 'error loading graph settings';
                    });
            };

            var correspondingCard = function(item, cardTree){
                var cardList = cardTree.cachedFlatTree;
                if (cardList === undefined) {
                    var cardList = cardTree.flattenTree(cardTree.topCards(), []);
                    cardTree.cachedFlatTree = cardList;
                }
                var res;
                var matchingWidget;
                if (item && typeof item !== 'string') {
                    if (item.nodeGroupId) { //if the item is a node in graph tree
                        var matchingCards = _.filter(cardList, function(card){
                            return card.nodegroupid === item.nodeGroupId();
                        });
                        _.each(matchingCards, function(card){
                            var match;
                            match = _.find(card.widgets(), function(widget){
                                return widget.node_id() === item.nodeid;
                            });
                            if (match) {
                                matchingWidget = match;
                            }
                        });
                        if (matchingWidget) {
                            res = matchingWidget;
                        } else {
                            res = matchingCards[0];
                        }
                    } else { //if the item is a card or widget in the card tree
                        res = _.find(cardList, function(card){
                            if (item.nodegroupid) {
                                return card.nodegroupid === item.nodegroupid;
                            } else {
                                return card.nodegroupid === item.node.nodeGroupId();
                            }
                        });
                    }
                }
                return res;

            };

            var correspondingNode = function(card, graphTree){
                var nodeMatch = _.find(graphTree.items(), function(node){
                    if (card.node) {
                        return node.nodeid === card.node_id();
                    } else {
                        return node.nodeGroupId() === card.nodegroupid && node.nodeid === node.nodeGroupId();
                    }
                });
                return nodeMatch;
            };

            var updateGraphSelection = function() {
                if (viewModel.activeTab() === 'card') {
                    viewModel.graphTree.collapseAll();
                    var matchingNode = correspondingNode(viewModel.cardTree.selection(), viewModel.graphTree);
                    if (matchingNode) {
                        viewModel.graphTree.selectItem(matchingNode);
                    }
                }
            };

            var updateCardSelection = function() {
                if (viewModel.activeTab() === 'graph') {
                    var graphTreeSelection = viewModel.graphTree.selectedItems().length > 0 ? viewModel.graphTree.selectedItems()[0] : null;
                    var matchingCard;
                    if (graphTreeSelection) {
                        if (graphTreeSelection.istopnode === true) {
                            viewModel.cardTree.selection(viewModel.cardTree.topCards()[0]);
                        } else {
                            matchingCard = correspondingCard(graphTreeSelection, viewModel.cardTree);
                            if (matchingCard) {
                                viewModel.cardTree.selection(matchingCard);
                                viewModel.cardTree.collapseAll();
                                viewModel.cardTree.expandToRoot(viewModel.cardTree.selection());
                            }
                        }
                    }
                }
            };

            var updatePermissionCardSelection = function() {
                var matchingCard = correspondingCard(viewModel.cardTree.selection(), viewModel.permissionTree);
                if (matchingCard) {
                    viewModel.permissionTree.collapseAll();
                    viewModel.permissionTree.expandToRoot(matchingCard);
                    viewModel.permissionTree.selection.removeAll();
                    matchingCard.selectChildCards();
                }
            };

            viewModel.cardTree.selection.subscribe(function(){
                updateGraphSelection();
                updatePermissionCardSelection();
            });

            viewModel.graphTree.selectedItems.subscribe(function(){
                updateCardSelection();
                updatePermissionCardSelection();
            });

            if (viewModel.activeTab() === 'graph') {
                viewModel.loadGraphSettings();
                // here we might load data/views asyncronously
            }

            var loadPermissionData = viewModel.activeTab.subscribe(function(tab) {
                // Loads identities and nodegroup permissions when the permissions tab is opened and then disposes the ko.subscribe.
                if (tab === 'permissions') {
                    viewModel.permissionsDesigner.getPermissionManagerData();
                    loadPermissionData.dispose();
                }
            });

            var helpContentLookup = {
                permissions: {
                    'title': 'Using the Permissions Manager',
                    'template': 'permissions-manager-help',
                },
                graph: {
                    'title':'Using the Graph Designer',
                    'template': 'graph-designer-help',
                },
                card: {
                    'title':'Using the Card Designer',
                    'template': 'card-manager-help',
                }
            };

            viewModel.activeTab.subscribe(function(tab) {
                viewModel.helpTemplate(helpContentLookup[tab]['template']);
                viewModel.getHelp();
            });

            viewModel.graphView = new GraphView({
                el: $('#graph'),
                graphModel: viewModel.graphModel,
                nodeSize: 15,
                nodeSizeOver: 20,
                labelOffset: 10,
                loading: this.loading
            });

            viewModel.graphModel.on('select-node', function(node) {
                viewModel.graphView.zoomTo(node);
                viewModel.graphTree.expandParentNode(node);
            });

            viewModel.showPreview = function() {
                viewModel.viewState('preview');
                viewModel.graphView.resize();
            };

            /**
            * update the sizing of elements on window resize
            */
            var resize = function() {
                $('.grid').height($(window).height() - 208);
                $('#graph').height($(window).height() - 100);
                if (!!viewModel.graphView) {
                    viewModel.graphView.resize();
                }
            };

            $(window).resize(resize);

            resize();

            BaseManagerView.prototype.initialize.apply(this, arguments);
        }
    });

    return new GraphDesignerView();
});
<commit_msg>defers tree selection syncing until tab changes<commit_after>define([
    'jquery',
    'underscore',
    'knockout',
    'knockout-mapping',
    'views/base-manager',
    'viewmodels/alert',
    'models/graph',
    'models/report',
    'views/graph/graph-manager/graph',
    'views/graph/graph-designer/graph-tree',
    'views/graph/graph-designer/node-form',
    'views/graph/graph-manager/branch-list',
    'views/graph/graph-designer/card-tree',
    'views/graph/permission-designer',
    'graph-designer-data',
    'arches',
    'viewmodels/graph-settings',
    'viewmodels/card',
    'view-data',
    'report-templates',
    'bindings/resizable-sidepanel',
    'datatype-config-components',
    'views/components/simple-switch'
], function($, _, ko, koMapping, BaseManagerView, AlertViewModel, GraphModel, ReportModel, GraphView, GraphTree, NodeFormView, BranchListView, CardTreeViewModel, PermissionDesigner, data, arches, GraphSettingsViewModel, CardViewModel, viewData, reportLookup) {
    var GraphDesignerView = BaseManagerView.extend({

        initialize: function(options) {
            var viewModel = options.viewModel;
            viewModel.graphid = ko.observable(data.graphid);
            viewModel.activeTab = ko.observable('graph');
            viewModel.viewState = ko.observable('design');
            viewModel.helpTemplate(viewData.help);
            viewModel.graphSettingsVisible = ko.observable(false);
            viewModel.graph = koMapping.fromJS(data['graph']);
            viewModel.ontologies = ko.observable(data['ontologies']);
            viewModel.ontologyClasses = ko.observable(data['ontologyClasses']);
            viewModel.cardComponents = data.cardComponents;

            var resources = ko.utils.arrayFilter(viewData.graphs, function(graph) {
                return graph.isresource;
            });
            var graphs = ko.utils.arrayFilter(viewData.graphs, function(graph) {
                return !graph.isresource;
            });

            var newGraph = function(url, data) {
                data = data || {};
                viewModel.loading(true);
                $.ajax({
                    type: "POST",
                    url: url,
                    data: JSON.stringify(data),
                    success: function(response) {
                        window.open(arches.urls.graph_designer(response.graphid), '_blank');
                        viewModel.loading(false);
                    },
                    error: function() {
                        viewModel.loading(false);
                    }
                });
            };
            viewModel.newResource = function() {
                newGraph('/graph/new', {isresource: true});
            };
            viewModel.newBranch = function() {
                newGraph('/graph/new', {isresource: false});
            };

            viewModel.deleteGraph = function() {
                viewModel.alert(new AlertViewModel('ep-alert-red', arches.confirmGraphDelete.title, arches.confirmGraphDelete.text, function() {
                    return;
                }, function(){
                    viewModel.loading(true);
                    $.ajax({
                        type: "DELETE",
                        url: arches.urls.delete_graph(viewModel.graph.graphid()),
                        complete: function(response, status) {
                            viewModel.loading(false);
                            if (status === 'success') {
                                window.location = arches.urls.graph;
                            } else {
                                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
                            }
                        }
                    });
                }));
            };
            viewModel.cloneGraph = function() {
                newGraph(arches.urls.clone_graph(viewModel.graph.graphid()));
            };
            viewModel.exportGraph = function() {
                window.open(arches.urls.export_graph(viewModel.graph.graphid()), '_blank');
            };
            viewModel.importGraph = function(data, e) {
                var formData = new FormData();
                formData.append("importedGraph", e.target.files[0]);

                $.ajax({
                    type: "POST",
                    url: '/graph/import/',
                    processData: false,
                    data: formData,
                    cache: false,
                    contentType: false,
                    success: function(response) {
                        if (response[0].length != 0) {
                            if (typeof(response[0])) {
                                response = response[0].join('<br />');
                            }
                            viewModel.alert(new AlertViewModel('ep-alert-red', arches.graphImportFailed.title, response));
                        } else {
                            viewModel.loading(false);
                            window.open(arches.urls.graph_designer(response[1].graph_id), '_blank');
                        }
                    },
                    error: function(response) {
                        viewModel.alert(new AlertViewModel('ep-alert-red', arches.graphImportFailed.title, 'Please contact your system administrator for more details.'));
                        viewModel.loading(false);
                    },
                });
            };
            viewModel.importButtonClick = function() {
                $("#fileupload").trigger('click');
            };
            viewModel.deleteInstances = function() {
                viewModel.alert(new AlertViewModel('ep-alert-red', arches.confirmAllResourceDelete.title, arches.confirmAllResourceDelete.text, function() {
                    return;
                }, function(){
                    viewModel.loading(true);
                    $.ajax({
                        type: "DELETE",
                        url: arches.urls.delete_instances(viewModel.graph.graphid()),
                        complete: function(response, status) {
                            viewModel.loading(false);
                            if (status === 'success') {
                                viewModel.alert(new AlertViewModel('ep-alert-blue', response.responseJSON.title, response.responseJSON.message));
                            } else {
                                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
                            }
                        }
                    });
                }));
            };
            viewModel.graph.ontology = ko.computed(function() {
                return viewModel.ontologies().find(function(obj) {
                    return obj.ontologyid === viewModel.graph.ontology_id();
                });
            });
            viewModel.groupedGraphs = ko.observable({
                groups: [
                    { name: 'Resource Models', items: resources },
                    { name: 'Branches', items: graphs }
                ]
            });

            viewModel.graphid.subscribe(function(graphid) {
                if (graphid && graphid !== '') {
                    viewModel.navigate(arches.urls.graph_designer(graphid));
                }
            });

            viewModel.graphModel = new GraphModel({
                data: data.graph,
                datatypes: data.datatypes,
                ontology_namespaces: data.ontology_namespaces
            });

            viewModel.datatypes = _.keys(viewModel.graphModel.get('datatypelookup'));

            viewModel.graphModel.on('changed', function(model, response) {
                if (viewModel.graphView) {
                    viewModel.graphView.redraw(true);
                }
                viewModel.alert(null);
                viewModel.loading(false);
                if (response.status !== 200) {
                    var errorMessageTitle = arches.requestFailed.title;
                    var errorMessageText = arches.requestFailed.text;
                    viewModel.alert(null);
                    if (response.responseJSON) {
                        errorMessageTitle = response.responseJSON.title;
                        errorMessageText = response.responseJSON.message;
                    }
                    viewModel.alert(new AlertViewModel('ep-alert-red', errorMessageTitle, errorMessageText));
                }
            });

            viewModel.graphModel.on('error', function(response) {
                viewModel.alert(new AlertViewModel('ep-alert-red', response.responseJSON.title, response.responseJSON.message));
            });

            viewModel.selectedNode = viewModel.graphModel.get('selectedNode');

            viewModel.saveNode = function(node) {
                if (node) {
                    viewModel.loading(true);
                    node.save(function(data) {
                        if (data.responseJSON.success === false || data.status === 500) {
                            viewModel.alert(new AlertViewModel('ep-alert-red', data.responseJSON.title, data.responseJSON.message));
                        }
                        else {
                            viewModel.cardTree.updateCards(viewModel.selectedNode().nodeGroupId(), data.responseJSON);
                            viewModel.permissionTree.updateCards(viewModel.selectedNode().nodeGroupId(), data.responseJSON);
                        }
                        viewModel.loading(false);
                    });
                }
            };

            viewModel.saveSelectedNode = function() {
                if (viewModel.selectedNode()) {
                    viewModel.saveNode(viewModel.selectedNode());
                }
            };

            viewModel.cardTree = new CardTreeViewModel({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel
            });

            viewModel.permissionTree = new CardTreeViewModel({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                multiselect: true
            });

            viewModel.selectedCards = ko.computed(function() {
                var selection = viewModel.permissionTree.selection();
                if (selection) {
                    if (selection.widgets) {
                        return selection;
                    }
                    return selection.parent;
                } else {
                    return null;
                }
            });

            viewModel.selectedCard = ko.computed(function() {
                var selection = viewModel.cardTree.selection();
                if (selection) {
                    if (selection.widgets) {
                        return selection;
                    }
                    return selection.parent;
                } else {
                    return null;
                }
            });

            viewModel.nodeForm = new NodeFormView({
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                loading: viewModel.loading,
                node: viewModel.selectedNode,
                restrictedNodegroups: data.restrictedNodegroups
            });

            viewModel.branchListView = new BranchListView({
                el: $('#branch-library'),
                branches: data.branches,
                graphModel: viewModel.graphModel,
                loading: viewModel.loading,
                disableAppendButton: ko.computed(function() {
                    // self.node() && self.node().dirty();
                })
            });

            viewModel.permissionsDesigner = new PermissionDesigner({
                cardTree: viewModel.permissionTree
            });

            viewModel.graphSettingsViewModel = new GraphSettingsViewModel({
                designerViewModel: viewModel,
                graph: viewModel.graph,
                graphModel: viewModel.graphModel,
                ontologyClasses: viewModel.ontologyClasses,
                ontologies: viewModel.ontologies,
                ontologyClass: ko.observable(''),
                iconFilter: ko.observable(''),
                node: viewModel.selectedNode,
                rootNodeColor: ko.observable(''),
                "ontology_namespaces": data.ontology_namespaces,
                onReset: function() {
                    var graph = ko.mapping.toJS(viewModel.graphSettingsViewModel.graph);
                    viewModel.report.configJSON(graph.config);
                    viewModel.report.get('template_id')(graph["template_id"]);
                }
            });

            viewModel.report = new ReportModel(_.extend(data, {
                graphModel: viewModel.graphModel,
                cards: viewModel.cardTree.topCards,
                preview: true
            }));

            viewModel.report.configJSON.subscribe(function(config) {
                var graph = ko.mapping.toJS(viewModel.graphSettingsViewModel.graph);
                graph.config = config;
                ko.mapping.fromJS(graph, viewModel.graphSettingsViewModel.graph);
            });

            viewModel.report.get('template_id').subscribe(function(val) {
                viewModel.graphSettingsViewModel.graph["template_id"](val);
            });

            viewModel.reportLookup = reportLookup;
            viewModel.reportTemplates = _.map(reportLookup, function(report, id) {
                report.id = id;
                return report;
            });

            viewModel.graphTree = new GraphTree({
                graphModel: viewModel.graphModel,
                graphSettings: viewModel.graphSettingsViewModel,
                cardTree: viewModel.cardTree,
                permissionTree: viewModel.permissionTree,
                restrictedNodegroups: data.restrictedNodegroups
            });

            viewModel.graphTree.branchListVisible.subscribe(function(visible) {
                if (visible) {
                    viewModel.branchListView.loadDomainConnections();
                }
            }, this);

            viewModel.loadGraphSettings = function() {
                var self = this;
                viewModel.loading(true);
                $.ajax({
                    type: 'GET',
                    url: arches.urls.graph_settings(data.graphid),
                    data: {'search': true, 'csrfmiddlewaretoken': '{{ csrf_token }}'}})
                    .done(function(data) {
                        self.graphSettingsViewModel.resource_data(data.resources);
                        self.graphSettingsViewModel.icon_data(data.icons);
                        self.graphSettingsViewModel.jsonCache(self.graphSettingsViewModel.jsonData());
                        self.graphSettingsViewModel.contentLoading = viewModel.loading;
                        self.graphSettingsVisible(true);
                        viewModel.loading(false);
                    })
                    .fail(function() {
                        throw 'error loading graph settings';
                    });
            };

            var correspondingCard = function(item, cardTree){
                var cardList = cardTree.cachedFlatTree;
                if (cardList === undefined) {
                    var cardList = cardTree.flattenTree(cardTree.topCards(), []);
                    cardTree.cachedFlatTree = cardList;
                }
                var res;
                var matchingWidget;
                if (item && typeof item !== 'string') {
                    if (item.nodeGroupId) { //if the item is a node in graph tree
                        var matchingCards = _.filter(cardList, function(card){
                            return card.nodegroupid === item.nodeGroupId();
                        });
                        _.each(matchingCards, function(card){
                            var match;
                            match = _.find(card.widgets(), function(widget){
                                return widget.node_id() === item.nodeid;
                            });
                            if (match) {
                                matchingWidget = match;
                            }
                        });
                        if (matchingWidget) {
                            res = matchingWidget;
                        } else {
                            res = matchingCards[0];
                        }
                    } else { //if the item is a card or widget in the card tree
                        res = _.find(cardList, function(card){
                            if (item.nodegroupid) {
                                return card.nodegroupid === item.nodegroupid;
                            } else {
                                return card.nodegroupid === item.node.nodeGroupId();
                            }
                        });
                    }
                }
                return res;

            };

            var correspondingNode = function(card, graphTree){
                var nodeMatch = _.find(graphTree.items(), function(node){
                    if (card.node) {
                        return node.nodeid === card.node_id();
                    } else {
                        return node.nodeGroupId() === card.nodegroupid && node.nodeid === node.nodeGroupId();
                    }
                });
                return nodeMatch;
            };

            var updateGraphSelection = function() {
                viewModel.graphTree.collapseAll();
                var matchingNode = correspondingNode(viewModel.cardTree.selection(), viewModel.graphTree);
                if (matchingNode) {
                    viewModel.graphTree.selectItem(matchingNode);
                }
            };

            var updateCardSelection = function() {
                var graphTreeSelection = viewModel.graphTree.selectedItems().length > 0 ? viewModel.graphTree.selectedItems()[0] : null;
                var matchingCard;
                if (graphTreeSelection) {
                    if (graphTreeSelection.istopnode === true) {
                        viewModel.cardTree.selection(viewModel.cardTree.topCards()[0]);
                    } else {
                        matchingCard = correspondingCard(graphTreeSelection, viewModel.cardTree);
                        if (matchingCard) {
                            viewModel.cardTree.selection(matchingCard);
                            viewModel.cardTree.collapseAll();
                            viewModel.cardTree.expandToRoot(viewModel.cardTree.selection());
                        }
                    }
                }
            };

            var updatePermissionCardSelection = function() {
                var matchingCard = correspondingCard(viewModel.cardTree.selection(), viewModel.permissionTree);
                if (matchingCard) {
                    viewModel.permissionTree.collapseAll();
                    viewModel.permissionTree.expandToRoot(matchingCard);
                    viewModel.permissionTree.selection.removeAll();
                    matchingCard.selectChildCards();
                }
            };

            viewModel.activeTab.subscribe(function(value) {
                switch (value) {
                case 'card':
                    updateCardSelection();
                    break;
                case 'graph':
                    updateGraphSelection();
                    break;
                case 'permissions':
                    updatePermissionCardSelection();
                    break;
                default:
                    return;
                }
            });

            viewModel.cardTree.selection.subscribe(function(){
                updateGraphSelection();
                updatePermissionCardSelection();
            });

            viewModel.graphTree.selectedItems.subscribe(function(){
                updateCardSelection();
                updatePermissionCardSelection();
            });

            if (viewModel.activeTab() === 'graph') {
                viewModel.loadGraphSettings();
                // here we might load data/views asyncronously
            }

            var loadPermissionData = viewModel.activeTab.subscribe(function(tab) {
                // Loads identities and nodegroup permissions when the permissions tab is opened and then disposes the ko.subscribe.
                if (tab === 'permissions') {
                    viewModel.permissionsDesigner.getPermissionManagerData();
                    loadPermissionData.dispose();
                }
            });

            var helpContentLookup = {
                permissions: {
                    'title': 'Using the Permissions Manager',
                    'template': 'permissions-manager-help',
                },
                graph: {
                    'title':'Using the Graph Designer',
                    'template': 'graph-designer-help',
                },
                card: {
                    'title':'Using the Card Designer',
                    'template': 'card-manager-help',
                }
            };

            viewModel.activeTab.subscribe(function(tab) {
                viewModel.helpTemplate(helpContentLookup[tab]['template']);
                viewModel.getHelp();
            });

            viewModel.graphView = new GraphView({
                el: $('#graph'),
                graphModel: viewModel.graphModel,
                nodeSize: 15,
                nodeSizeOver: 20,
                labelOffset: 10,
                loading: this.loading
            });

            viewModel.graphModel.on('select-node', function(node) {
                viewModel.graphView.zoomTo(node);
                viewModel.graphTree.expandParentNode(node);
            });

            viewModel.showPreview = function() {
                viewModel.viewState('preview');
                viewModel.graphView.resize();
            };

            /**
            * update the sizing of elements on window resize
            */
            var resize = function() {
                $('.grid').height($(window).height() - 208);
                $('#graph').height($(window).height() - 100);
                if (!!viewModel.graphView) {
                    viewModel.graphView.resize();
                }
            };

            $(window).resize(resize);

            resize();

            BaseManagerView.prototype.initialize.apply(this, arguments);
        }
    });

    return new GraphDesignerView();
});
<|endoftext|>


<commit_before>const { prep, component, on, emits, wired, notifies } = capsid
const genel = require('genel')

const { HIDE, RESET_SCROLL } = require('./edit-item-card-wrapper')

const CLASS_ERROR = 'has-error'

@component('edit-item-card')
export default class EditItemCard {
  @wired('.edit-item-card__date')
  get date () {}

  @wired('.edit-item-card__desc')
  get desc () {}

  @wired.all('.edit-item-card__debit')
  get debits () {}

  @wired.all('.edit-item-card__credit')
  get credits () {}

  @wired('.edit-item-card__debit-total')
  get debitTotalLabel () {}

  @wired('.edit-item-card__credit-total')
  get creditTotalLabel () {}

  @wired('.edit-item-card__debit-total-diff')
  get debitTotalDiffLabel () {}

  @wired('.edit-item-card__credit-total-diff')
  get creditTotalDiffLabel () {}

  @wired('.add-debit-button')
  get addDebitButton () {}

  @wired('.add-credit-button')
  get addCreditButton () {}

  @wired('.account-error-holder')
  get accountErrorHolder () {}

  @wired.all('.edit-item-card__account-input')
  get accountInputs () {}

  @on(Action.MODEL_UPDATE)
  update ({ detail: { user, currentChart } }) {
    this.currentChart = currentChart
    this.debitTypes = user.currentDocument.recentDebitTypes(this.currentChart)
    this.creditTypes = user.currentDocument.recentCreditTypes(this.currentChart)
  }

  @emits(RESET_SCROLL)
  resetHtml () {
    this.el.innerHTML = `
      <form class="js-form">

            Date

              <input
                class="input js-field js-pickadate edit-item-card__date"
                data-validate="required"
              />

app.description

                <input
                  class="js-field input edit-item-card__desc"
                  data-validate="required"
                />

              <t>domain.debits</t>

            <button class="button is-primary is-outlined add-debit-button">

            </button>

              <t>domain.credits</t>

            <button class="button is-primary is-outlined add-credit-button">

            </button>

            ui.form.cancel

            <button class="button is-primary t-text edit-item-save-button disable-on-error">ui.form.save</button>

      </form>
    `

    this.addDebitRow()
    this.addCreditRow()

    this.prep()
  }

  @on('click', { at: '.add-debit-button' })
  onClickAddDebitButton (e) {
    e.preventDefault()

    this.addDebitRow()

    this.prep()
  }

  @on('click', { at: '.add-credit-button' })
  onClickCreditButton (e) {
    e.preventDefault()

    this.addCreditRow()

    this.prep()
  }

  /**
   * @param {string} side debit or credit
   * @param {AccountType[]} accountTypes
   * @param {HTMLElement} insertBefore
   */
  addAccountInput (side, accountTypes, insertBefore) {
    const div = genel.div`

            <select class="input edit-item-card__account-type">
              <option value="" class="t-text">ui.form.select_account_title</option>
              ${this.options(accountTypes)}
            </select>

<t>error.form.account_type_not_selected</t>

          <input
            class="input js-field js-number-input t-attr edit-item-card__account-amount"
            data-validate="number"
            placeholder="t:domain.amount"
          />

    `

    div.classList.add(
      `edit-item-card__${side}`,
      'edit-item-card__account-input'
    )

    insertBefore.parentElement.insertBefore(div, insertBefore)
  }

  addDebitRow () {
    this.addAccountInput('debit', this.debitTypes, this.addDebitButton)
  }

  addCreditRow () {
    this.addAccountInput('credit', this.creditTypes, this.addCreditButton)
  }

  options (accountTypes) {
    return accountTypes
      .map(
        type =>
          `<option value="${type.name}">${type.name} (${t10.t(
            `domain.${this.currentChart.getMajorTypeByAccountType(type).name}`
          )})</option>`
      )
      .join('')
  }

  @emits(Action.SCAN_LANGUAGE)
  prep () {
    prep(null, this.el)
  }

  @on('click', { at: '.edit-item-save-button' })
  @emits(Action.CREATE_TRADE)
  onCreate (e) {
    e.preventDefault()

    const date = this.date.dataset.date
    const desc = this.desc.value
    const dr = this.createDebitObject()
    const cr = this.createCreditObject()

    this.hide()

    return { date, desc, dr, cr }
  }

  @on('click', { at: '.edit-item-cancel-button' })
  onCancel (e) {
    e.preventDefault()
    this.hide()
  }

  /**
   * Removes the component at the next tick.
   */
  @emits(HIDE)
  async hide () {
    await Promise.resolve()
  }

  @on('input')
  adjustPopper () {
    capsidPopper.updateAll()
  }

  @on('change', { at: '.edit-item-card__account-type' })
  @on('input', { at: '.edit-item-card__account-amount' })
  @notifies('field-error', '.js-form')
  onAccountChange () {
    const dt = this.debitTotal()
    const ct = this.creditTotal()

    this.fillAccountTotalLabels(dt, ct)
    ;[].forEach.call(this.accountInputs, el => {
      this.validateAccountInput(el)
    })

    this.validateTotal(dt, ct)
  }

  /**
   * @param {HTMLElement} el
   */
  validateAccountInput (el) {
    const { type, amount } = this.getAccountObject(el)

    el.querySelector('.edit-item-card__account-type-wrapper').classList.toggle(
      CLASS_ERROR,
      this.isValidAmount(amount) && type === ''
    )
  }

  /**
   * @param {number} dt The debit total
   * @param {number} ct The credit total
   */
  validateTotal (dt, ct) {
    this.accountErrorHolder.classList.toggle(
      CLASS_ERROR,
      !(dt > 0 && ct > 0 && dt === ct)
    )
  }

  /**
   * @param {number} dt The debit total
   * @param {number} ct The credit total
   */
  fillAccountTotalLabels (dt, ct) {
    this.setAccountLabel(this.debitTotalLabel, dt)
    this.setAccountLabel(this.creditTotalLabel, ct)

    const diff = Math.abs(dt - ct)

    if (diff === 0) {
      this.debitTotalDiffLabel.textContent = ''
      this.creditTotalDiffLabel.textContent = ''
    } else if (dt > ct) {
      this.debitTotalDiffLabel.textContent = ''
      this.setAccountLabel(
        this.creditTotalDiffLabel,
        diff,
        label => `(-${label})`
      )
    } else {
      this.setAccountLabel(
        this.debitTotalDiffLabel,
        diff,
        label => `(-${label})`
      )
      this.creditTotalDiffLabel.textContent = ''
    }
  }

  @emits(Action.REQUEST_MONEY_FORMAT)
  setAccountLabel (el, amount, format = x => x) {
    return {
      amount,
      send: label => {
        el.textContent = format(label)
      }
    }
  }

  /**
   * @return {number}
   */
  debitTotal () {
    return this.accountTotal(this.createDebitArray())
  }

  /**
   * @return {number}
   */
  creditTotal () {
    return this.accountTotal(this.createCreditArray())
  }

  /**
   * @param {Object[]} accountArray
   * @return {number}
   */
  accountTotal (accountArray) {
    return accountArray.reduce((sum, account) => sum + account.amount, 0)
  }

  /**
   * @param {Object[]} accountArray
   */
  createAccountMap (accountArray) {
    const accountMap = {}

    accountArray.map(item => {
      accountMap[item.type] = item.amount
    })

    return accountMap
  }

  /**
   * @param {NodeList} accountRows
   * @return {Object[]}
   */
  createAccountArray (accountRows) {
    return [].map
      .call(accountRows, row => this.getAccountObject(row))
      .filter(account => !!account.type && this.isValidAmount(account.amount))
  }

  /**
   * @param {number} amount
   * @returns {boolean}
   */
  isValidAmount (amount) {
    return amount > 0 && amount < Infinity
  }

  /**
   * @param {HTMLElement} el
   * @returns {{type: string, amount: number}}
   */
  getAccountObject (el) {
    return {
      type: el.querySelector('.edit-item-card__account-type').value,
      amount: +el.querySelector('.edit-item-card__account-amount').dataset
        .amount
    }
  }

  createDebitArray () {
    return this.createAccountArray(this.debits)
  }

  createCreditArray () {
    return this.createAccountArray(this.credits)
  }

  createDebitObject () {
    return this.createAccountMap(this.createDebitArray())
  }

  createCreditObject () {
    return this.createAccountMap(this.createCreditArray())
  }
}
<commit_msg>refactor: use on.click.at<commit_after>const { prep, component, on, emits, wired, notifies } = capsid
const genel = require('genel')

const { HIDE, RESET_SCROLL } = require('./edit-item-card-wrapper')

const CLASS_ERROR = 'has-error'

@component('edit-item-card')
export default class EditItemCard {
  @wired('.edit-item-card__date')
  get date () {}

  @wired('.edit-item-card__desc')
  get desc () {}

  @wired.all('.edit-item-card__debit')
  get debits () {}

  @wired.all('.edit-item-card__credit')
  get credits () {}

  @wired('.edit-item-card__debit-total')
  get debitTotalLabel () {}

  @wired('.edit-item-card__credit-total')
  get creditTotalLabel () {}

  @wired('.edit-item-card__debit-total-diff')
  get debitTotalDiffLabel () {}

  @wired('.edit-item-card__credit-total-diff')
  get creditTotalDiffLabel () {}

  @wired('.add-debit-button')
  get addDebitButton () {}

  @wired('.add-credit-button')
  get addCreditButton () {}

  @wired('.account-error-holder')
  get accountErrorHolder () {}

  @wired.all('.edit-item-card__account-input')
  get accountInputs () {}

  @on(Action.MODEL_UPDATE)
  update ({ detail: { user, currentChart } }) {
    this.currentChart = currentChart
    this.debitTypes = user.currentDocument.recentDebitTypes(this.currentChart)
    this.creditTypes = user.currentDocument.recentCreditTypes(this.currentChart)
  }

  @emits(RESET_SCROLL)
  resetHtml () {
    this.el.innerHTML = `
      <form class="js-form">

            Date

              <input
                class="input js-field js-pickadate edit-item-card__date"
                data-validate="required"
              />

app.description

                <input
                  class="js-field input edit-item-card__desc"
                  data-validate="required"
                />

              <t>domain.debits</t>

            <button class="button is-primary is-outlined add-debit-button">

            </button>

              <t>domain.credits</t>

            <button class="button is-primary is-outlined add-credit-button">

            </button>

            ui.form.cancel

            <button class="button is-primary t-text edit-item-save-button disable-on-error">ui.form.save</button>

      </form>
    `

    this.addDebitRow()
    this.addCreditRow()

    this.prep()
  }

  @on.click.at('.add-debit-button')
  onClickAddDebitButton (e) {
    e.preventDefault()

    this.addDebitRow()

    this.prep()
  }

  @on.click.at('.add-credit-button')
  onClickCreditButton (e) {
    e.preventDefault()

    this.addCreditRow()

    this.prep()
  }

  /**
   * @param {string} side debit or credit
   * @param {AccountType[]} accountTypes
   * @param {HTMLElement} insertBefore
   */
  addAccountInput (side, accountTypes, insertBefore) {
    const div = genel.div`

            <select class="input edit-item-card__account-type">
              <option value="" class="t-text">ui.form.select_account_title</option>
              ${this.options(accountTypes)}
            </select>

<t>error.form.account_type_not_selected</t>

          <input
            class="input js-field js-number-input t-attr edit-item-card__account-amount"
            data-validate="number"
            placeholder="t:domain.amount"
          />

    `

    div.classList.add(
      `edit-item-card__${side}`,
      'edit-item-card__account-input'
    )

    insertBefore.parentElement.insertBefore(div, insertBefore)
  }

  addDebitRow () {
    this.addAccountInput('debit', this.debitTypes, this.addDebitButton)
  }

  addCreditRow () {
    this.addAccountInput('credit', this.creditTypes, this.addCreditButton)
  }

  options (accountTypes) {
    return accountTypes
      .map(
        type =>
          `<option value="${type.name}">${type.name} (${t10.t(
            `domain.${this.currentChart.getMajorTypeByAccountType(type).name}`
          )})</option>`
      )
      .join('')
  }

  @emits(Action.SCAN_LANGUAGE)
  prep () {
    prep(null, this.el)
  }

  @on.click.at('.edit-item-save-button')
  @emits(Action.CREATE_TRADE)
  onCreate (e) {
    e.preventDefault()

    const date = this.date.dataset.date
    const desc = this.desc.value
    const dr = this.createDebitObject()
    const cr = this.createCreditObject()

    this.hide()

    return { date, desc, dr, cr }
  }

  @on.click.at('.edit-item-cancel-button')
  onCancel (e) {
    e.preventDefault()
    this.hide()
  }

  /**
   * Removes the component at the next tick.
   */
  @emits(HIDE)
  async hide () {
    await Promise.resolve()
  }

  @on('input')
  adjustPopper () {
    capsidPopper.updateAll()
  }

  @on('change', { at: '.edit-item-card__account-type' })
  @on('input', { at: '.edit-item-card__account-amount' })
  @notifies('field-error', '.js-form')
  onAccountChange () {
    const dt = this.debitTotal()
    const ct = this.creditTotal()

    this.fillAccountTotalLabels(dt, ct)
    ;[].forEach.call(this.accountInputs, el => {
      this.validateAccountInput(el)
    })

    this.validateTotal(dt, ct)
  }

  /**
   * @param {HTMLElement} el
   */
  validateAccountInput (el) {
    const { type, amount } = this.getAccountObject(el)

    el.querySelector('.edit-item-card__account-type-wrapper').classList.toggle(
      CLASS_ERROR,
      this.isValidAmount(amount) && type === ''
    )
  }

  /**
   * @param {number} dt The debit total
   * @param {number} ct The credit total
   */
  validateTotal (dt, ct) {
    this.accountErrorHolder.classList.toggle(
      CLASS_ERROR,
      !(dt > 0 && ct > 0 && dt === ct)
    )
  }

  /**
   * @param {number} dt The debit total
   * @param {number} ct The credit total
   */
  fillAccountTotalLabels (dt, ct) {
    this.setAccountLabel(this.debitTotalLabel, dt)
    this.setAccountLabel(this.creditTotalLabel, ct)

    const diff = Math.abs(dt - ct)

    if (diff === 0) {
      this.debitTotalDiffLabel.textContent = ''
      this.creditTotalDiffLabel.textContent = ''
    } else if (dt > ct) {
      this.debitTotalDiffLabel.textContent = ''
      this.setAccountLabel(
        this.creditTotalDiffLabel,
        diff,
        label => `(-${label})`
      )
    } else {
      this.setAccountLabel(
        this.debitTotalDiffLabel,
        diff,
        label => `(-${label})`
      )
      this.creditTotalDiffLabel.textContent = ''
    }
  }

  @emits(Action.REQUEST_MONEY_FORMAT)
  setAccountLabel (el, amount, format = x => x) {
    return {
      amount,
      send: label => {
        el.textContent = format(label)
      }
    }
  }

  /**
   * @return {number}
   */
  debitTotal () {
    return this.accountTotal(this.createDebitArray())
  }

  /**
   * @return {number}
   */
  creditTotal () {
    return this.accountTotal(this.createCreditArray())
  }

  /**
   * @param {Object[]} accountArray
   * @return {number}
   */
  accountTotal (accountArray) {
    return accountArray.reduce((sum, account) => sum + account.amount, 0)
  }

  /**
   * @param {Object[]} accountArray
   */
  createAccountMap (accountArray) {
    const accountMap = {}

    accountArray.map(item => {
      accountMap[item.type] = item.amount
    })

    return accountMap
  }

  /**
   * @param {NodeList} accountRows
   * @return {Object[]}
   */
  createAccountArray (accountRows) {
    return [].map
      .call(accountRows, row => this.getAccountObject(row))
      .filter(account => !!account.type && this.isValidAmount(account.amount))
  }

  /**
   * @param {number} amount
   * @returns {boolean}
   */
  isValidAmount (amount) {
    return amount > 0 && amount < Infinity
  }

  /**
   * @param {HTMLElement} el
   * @returns {{type: string, amount: number}}
   */
  getAccountObject (el) {
    return {
      type: el.querySelector('.edit-item-card__account-type').value,
      amount: +el.querySelector('.edit-item-card__account-amount').dataset
        .amount
    }
  }

  createDebitArray () {
    return this.createAccountArray(this.debits)
  }

  createCreditArray () {
    return this.createAccountArray(this.credits)
  }

  createDebitObject () {
    return this.createAccountMap(this.createDebitArray())
  }

  createCreditObject () {
    return this.createAccountMap(this.createCreditArray())
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import Analyzer from 'parser/core/Analyzer';

import Combatants from 'parser/core/modules/Combatants';
import { formatNumber } from 'common/format';
import MAGIC_SCHOOLS from 'common/MAGIC_SCHOOLS';
import RACES from 'game/RACES';
import SpellIcon from 'common/SpellIcon';
import SPELLS from 'common/SPELLS';
import StatisticBox from 'interface/others/StatisticBox';

/**
 * Removes all poison, disease, curse, magic, and bleed effects and reduces all physical damage taken by 10% for 8 sec.
 *
 * Example log: https://www.warcraftlogs.com/reports/gYCR2kZaJHVbjfxP/#fight=52&source=16&type=damage-taken
 */

const STONEFORM_DAMAGE_REDUCTION = 0.1;
const FALLING_DAMAGE_ABILITY_ID = 3;

class Stoneform extends Analyzer {
  static dependencies = {
    combatants: Combatants,
  };

  damageReduced = 0;
  physicalDamageTaken = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.race === RACES.Dwarf;
  }

  get drps() {
    return this.damageReduced / this.owner.fightDuration * 1000;
  }

  on_toPlayer_damage(event) {
    const spellId = event.ability.guid;
    if (spellId === FALLING_DAMAGE_ABILITY_ID) { // Falling damage is the same type as physical but ignores DRs.
      return;
    }
    if (event.ability.type === MAGIC_SCHOOLS.ids.PHYSICAL) {
      return;
    }
    const damageTaken = event.amount + (event.absorbed || 0);
    const isStoneformActive = this.selectedCombatant.hasBuff(SPELLS.STONEFORM_BUFF.id, event.timestamp, this.owner.playerId);

    if (isStoneformActive) {
      this.physicalDamageTaken += damageTaken;
      this.damageReduced += damageTaken / (1 - STONEFORM_DAMAGE_REDUCTION) * STONEFORM_DAMAGE_REDUCTION;
    }
  }

  statistic() {
    return(
      <StatisticBox
        icon={<SpellIcon id={SPELLS.STONEFORM_BUFF.id} />}
        value={`≈${formatNumber(this.drps)} DRPS`}
        label="Stoneform damage reduced"
        tooltip={`You took a total of ${formatNumber(this.physicalDamageTaken)} physical damage while Stoneform was active. </br>
                  Stoneform reduced a total of ${formatNumber(this.damageReduced)} physical damage taken.`}
      />
    );

  }

}

export default Stoneform;
<commit_msg>fixed error<commit_after>import React from 'react';
import Analyzer from 'parser/core/Analyzer';

import Combatants from 'parser/core/modules/Combatants';
import { formatNumber } from 'common/format';
import MAGIC_SCHOOLS from 'common/MAGIC_SCHOOLS';
import RACES from 'game/RACES';
import SpellIcon from 'common/SpellIcon';
import SPELLS from 'common/SPELLS';
import StatisticBox from 'interface/others/StatisticBox';

/**
 * Removes all poison, disease, curse, magic, and bleed effects and reduces all physical damage taken by 10% for 8 sec.
 *
 * Example log: https://www.warcraftlogs.com/reports/gYCR2kZaJHVbjfxP/#fight=52&source=16&type=damage-taken
 */

const STONEFORM_DAMAGE_REDUCTION = 0.1;
const FALLING_DAMAGE_ABILITY_ID = 3;

class Stoneform extends Analyzer {
  static dependencies = {
    combatants: Combatants,
  };

  damageReduced = 0;
  physicalDamageTaken = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.race === RACES.Dwarf;
  }

  get drps() {
    return this.damageReduced / this.owner.fightDuration * 1000;
  }

  on_toPlayer_damage(event) {
    const spellId = event.ability.guid;

    if (event.ability.type !== MAGIC_SCHOOLS.ids.PHYSICAL) {
      return;
    }

    if (spellId === FALLING_DAMAGE_ABILITY_ID) { // Falling damage is the same type as physical but ignores DRs.
      return;
    }

    const damageTaken = event.amount + (event.absorbed || 0);
    const isStoneformActive = this.selectedCombatant.hasBuff(SPELLS.STONEFORM_BUFF.id, event.timestamp, this.owner.playerId);

    if (isStoneformActive) {
      this.physicalDamageTaken += damageTaken;
      this.damageReduced += damageTaken / (1 - STONEFORM_DAMAGE_REDUCTION) * STONEFORM_DAMAGE_REDUCTION;
    }
  }

  statistic() {
    return(
      <StatisticBox
        icon={<SpellIcon id={SPELLS.STONEFORM_BUFF.id} />}
        value={`≈${formatNumber(this.drps)} DRPS`}
        label="Stoneform damage reduced"
        tooltip={`You took a total of ${formatNumber(this.physicalDamageTaken)} physical damage while Stoneform was active. </br>
                  Stoneform reduced a total of ${formatNumber(this.damageReduced)} physical damage taken.`}
      />
    );

  }

}

export default Stoneform;
<|endoftext|>


<commit_before>// @flow
import React from 'react';
import { connect } from 'react-redux';
import { Row, Col, ListGroup, ListGroupItem } from 'react-bootstrap';

import './SystemInfo.css';

const SystemInfo = ({ info }) => {
  const systemInfo = [
    ['OTP release', info.otpRelease],
    ['ERTS version', info.version],
    ['node PID', info.nodePid],
    ['schedulers', info.schedulers],
    ['build type', info.buildType],
    ['wordsize', info.wordsize]
  ];

  const ertsSettings = [
    ['kernel poll', info.kernelPoll ? 'true' : 'false'],
    ['SMP support', info.smpSupport ? 'true' : 'false'],
    [
      'full sweep',
      info.garbageCollection ? info.garbageCollection.fullsweepAfter : undefined
    ],
    ['proces limit', info.processLimit],
    ['thread', info.threads ? 'true' : 'false'],
    ['async threads', info.threadPoolSize],
    ['port limit', info.portLimit]
  ];

  return (
    <Row>
      <Col xs={4}>

System info

        <ListGroup className="SystemInfo-list">
          {systemInfo.map(([name, value], i) => (
            <ListGroupItem key={i}>
              {name}
              {value || 'N/A'}
            </ListGroupItem>
          ))}
        </ListGroup>
      </Col>
      <Col xs={4}>

ERTS settings

        <ListGroup className="SystemInfo-list">
          {ertsSettings.map(([name, value], i) => (
            <ListGroupItem key={i}>
              {name}
              {value || 'N/A'}
            </ListGroupItem>
          ))}
        </ListGroup>
      </Col>
    </Row>
  );
};

export default connect(
  state => {
    return { info: state.eplDashboard.systemInfo };
  },
  {}
)(SystemInfo);
<commit_msg>Update typo<commit_after>// @flow
import React from 'react';
import { connect } from 'react-redux';
import { Row, Col, ListGroup, ListGroupItem } from 'react-bootstrap';

import './SystemInfo.css';

const SystemInfo = ({ info }) => {
  const systemInfo = [
    ['OTP release', info.otpRelease],
    ['ERTS version', info.version],
    ['node PID', info.nodePid],
    ['schedulers', info.schedulers],
    ['build type', info.buildType],
    ['wordsize', info.wordsize]
  ];

  const ertsSettings = [
    ['kernel poll', info.kernelPoll ? 'true' : 'false'],
    ['SMP support', info.smpSupport ? 'true' : 'false'],
    [
      'full sweep',
      info.garbageCollection ? info.garbageCollection.fullsweepAfter : undefined
    ],
    ['process limit', info.processLimit],
    ['thread', info.threads ? 'true' : 'false'],
    ['async threads', info.threadPoolSize],
    ['port limit', info.portLimit]
  ];

  return (
    <Row>
      <Col xs={4}>

System info

        <ListGroup className="SystemInfo-list">
          {systemInfo.map(([name, value], i) => (
            <ListGroupItem key={i}>
              {name}
              {value || 'N/A'}
            </ListGroupItem>
          ))}
        </ListGroup>
      </Col>
      <Col xs={4}>

ERTS settings

        <ListGroup className="SystemInfo-list">
          {ertsSettings.map(([name, value], i) => (
            <ListGroupItem key={i}>
              {name}
              {value || 'N/A'}
            </ListGroupItem>
          ))}
        </ListGroup>
      </Col>
    </Row>
  );
};

export default connect(
  state => {
    return { info: state.eplDashboard.systemInfo };
  },
  {}
)(SystemInfo);
<|endoftext|>


<commit_before>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

  /**
 * @ngdoc function
 * @name forms.function:Projects
 * @description This form is for adding/editing projects
*/

export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) {
    return function() {
    var projectsFormObj = {

        addTitle: i18n._('NEW PROJECT'),
        editTitle: '{{ name }}',
        name: 'project',
        basePath: 'projects',
        // the top-most node of generated state tree
        stateTree: 'projects',
        forceListeners: true,
        tabs: true,
        subFormTitles: {
            sourceSubForm: i18n._('Source Details'),
        },
        fields: {
            name: {
                label: i18n._('Name'),
                type: 'text',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                required: true,
                capitalize: false
            },
            description: {
                label: i18n._('Description'),
                type: 'text',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            organization: {
                label: i18n._('Organization'),
                type: 'lookup',
                list: 'OrganizationList',
                sourceModel: 'organization',
                basePath: 'organizations',
                sourceField: 'name',
                dataTitle: i18n._('Organization'),
                required: true,
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_type: {
                label: i18n._('SCM Type'),
                type: 'select',
                class: 'Form-dropDown--scmType',
                ngOptions: 'type.label for type in scm_type_options track by type.value',
                ngChange: 'scmChange()',
                required: true,
                hasSubForm: true,
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            missing_path_alert: {
                type: 'alertblock',
                ngShow: "showMissingPlaybooksAlert && scm_type.value == 'manual'",
                alertTxt: '
WARNING: There are no available playbook directories in {{ base_dir }}.  ' +
                    'Either that directory is empty, or all of the contents are already assigned to other projects.  ' +
                    'Create a new directory there and make sure the playbook files can be read by the "awx" system user, ' +
                    'or have Tower directly retrieve your playbooks from source control using the SCM Type option above.
',
                closeable: false
            },
            base_dir: {
                label: i18n._('Project Base Path'),
                type: 'text',
                class: 'Form-textUneditable',
                showonly: true,
                ngShow: "scm_type.value == 'manual' " ,
                awPopOver: '
' + i18n._('Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
                    'Together the base path and selected playbook directory provide the full path used to locate playbooks.') + '
' +
                    '
' + i18n.sprintf(i18n._('Change %s under "Configure Tower" to change this location.'), 'PROJECTS_ROOT') + '
',
                dataTitle: i18n._('Project Base Path'),
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            local_path: {
                label: i18n._('Playbook Directory'),
                type: 'select',
                id: 'local-path-select',
                ngOptions: 'path.label for path in project_local_paths',
                awRequiredWhen: {
                    reqExpression: "pathRequired",
                    init: false
                },
                ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
                awPopOver: '
' + i18n._('Select from the list of directories found in the Project Base Path. ' +
                    'Together the base path and the playbook directory provide the full path used to locate playbooks.') + '
',
                dataTitle: i18n._('Project Path'),
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_url: {
                label: 'SCM URL',
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual' && scm_type.value !== 'insights' ",
                awRequiredWhen: {
                    reqExpression: "scmRequired",
                    init: false
                },
                subForm: 'sourceSubForm',
                hideSubForm: "scm_type.value === 'manual'",
                awPopOverWatch: "urlPopover",
                awPopOver: "set in controllers/projects",
                dataTitle: 'SCM URL',
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_branch: {
                labelBind: "scmBranchLabel",
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual' && scm_type.value !== 'insights'",
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                subForm: 'sourceSubForm',
            },
            credential: {
                labelBind: 'credentialLabel',
                type: 'lookup',
                basePath: 'credentials',
                list: 'CredentialList',
                // apply a default search filter to show only scm credentials
                search: {
                    kind: 'scm'
                },
                ngShow: "scm_type && scm_type.value !== 'manual'",
                sourceModel: 'credential',
                awLookupType: 'scm_credential',
                sourceField: 'name',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                subForm: 'sourceSubForm'
            },
            checkbox_group: {
                label: i18n._('SCM Update Options'),
                type: 'checkbox_group',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                subForm: 'sourceSubForm',
                fields: [{
                    name: 'scm_clean',
                    label: i18n._('Clean'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Remove any local modifications prior to performing an update.') + '
',
                    dataTitle: i18n._('SCM Clean'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }, {
                    name: 'scm_delete_on_update',
                    label: i18n._('Delete on Update'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Delete the local repository in its entirety prior to performing an update.') + '

' + i18n._('Depending on the size of the ' +
                        'repository this may significantly increase the amount of time required to complete an update.') + '
',
                    dataTitle: i18n._('SCM Delete'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }, {
                    name: 'scm_update_on_launch',
                    label: i18n._('Update on Launch'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Each time a job runs using this project, perform an update to the local repository prior to starting the job.') + '
',
                    dataTitle: i18n._('SCM Update'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }]
            },
            scm_update_cache_timeout: {
                label: i18n.sprintf(i18n._('Cache Timeout%s (seconds)%s'), '', ''),
                id: 'scm-cache-timeout',
                type: 'number',
                integer: true,
                min: 0,
                ngShow: "scm_update_on_launch && scm_type.value !== 'manual'",
                spinner: true,
                "default": '0',
                awPopOver: '
' + i18n._('Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
                    'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
                    'and a new project update will be performed.') + '
',
                dataTitle: i18n._('Cache Timeout'),
                dataPlacement: 'right',
                dataContainer: "body",
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            }
        },

        buttons: {
            cancel: {
                ngClick: 'formCancel()',
                ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            close: {
                ngClick: 'formCancel()',
                ngShow: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            save: {
                ngClick: 'formSave()',
                ngDisabled: true,
                ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            }
        },

        related: {
            permissions: {
                name: 'permissions',
                awToolTip: i18n._('Please save before assigning permissions'),
                djangoModel: 'access_list',
                dataPlacement: 'top',
                basePath: 'api/v1/projects/{{$stateParams.project_id}}/access_list/',
                search: {
                    order_by: 'username'
                },
                type: 'collection',
                title: i18n._('Permissions'),
                iterator: 'permission',
                index: false,
                open: false,
                actions: {
                    add: {
                        ngClick: "$state.go('.add')",
                        label: 'Add',
                        awToolTip: i18n._('Add a permission'),
                        actionClass: 'btn List-buttonSubmit',
                        buttonContent: '+ ' + i18n._('ADD'),
                        ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                    }
                },

                fields: {
                    username: {
                        label: i18n._('User'),
                        uiSref: 'users({user_id: field.id})',
                        class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
                    },
                    role: {
                        label: i18n._('Role'),
                        type: 'role',
                        nosort: true,
                        class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
                    },
                    team_roles: {
                        label: i18n._('Team Roles'),
                        type: 'team_roles',
                        nosort: true,
                        class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
                    }
                }
            },
            notifications: {
                include: "NotificationsList",
            }
        }

    };

    var itm;

    for (itm in projectsFormObj.related) {
        if (projectsFormObj.related[itm].include === "NotificationsList") {
            projectsFormObj.related[itm] = NotificationsList;
            projectsFormObj.related[itm].generateList = true;   // tell form generator to call list generator and inject a list
        }
    }
    return projectsFormObj;
}}];
<commit_msg>fix jshint<commit_after>/*************************************************
 * Copyright (c) 2015 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/

  /**
 * @ngdoc function
 * @name forms.function:Projects
 * @description This form is for adding/editing projects
*/

export default ['i18n', 'NotificationsList', function(i18n, NotificationsList) {
    return function() {
    var projectsFormObj = {

        addTitle: i18n._('NEW PROJECT'),
        editTitle: '{{ name }}',
        name: 'project',
        basePath: 'projects',
        // the top-most node of generated state tree
        stateTree: 'projects',
        forceListeners: true,
        tabs: true,
        subFormTitles: {
            sourceSubForm: i18n._('Source Details'),
        },
        fields: {
            name: {
                label: i18n._('Name'),
                type: 'text',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                required: true,
                capitalize: false
            },
            description: {
                label: i18n._('Description'),
                type: 'text',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            organization: {
                label: i18n._('Organization'),
                type: 'lookup',
                list: 'OrganizationList',
                sourceModel: 'organization',
                basePath: 'organizations',
                sourceField: 'name',
                dataTitle: i18n._('Organization'),
                required: true,
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_type: {
                label: i18n._('SCM Type'),
                type: 'select',
                class: 'Form-dropDown--scmType',
                ngOptions: 'type.label for type in scm_type_options track by type.value',
                ngChange: 'scmChange()',
                required: true,
                hasSubForm: true,
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            missing_path_alert: {
                type: 'alertblock',
                ngShow: "showMissingPlaybooksAlert && scm_type.value == 'manual'",
                alertTxt: '
WARNING: There are no available playbook directories in {{ base_dir }}.  ' +
                    'Either that directory is empty, or all of the contents are already assigned to other projects.  ' +
                    'Create a new directory there and make sure the playbook files can be read by the "awx" system user, ' +
                    'or have Tower directly retrieve your playbooks from source control using the SCM Type option above.
',
                closeable: false
            },
            base_dir: {
                label: i18n._('Project Base Path'),
                type: 'text',
                class: 'Form-textUneditable',
                showonly: true,
                ngShow: "scm_type.value == 'manual' " ,
                awPopOver: '
' + i18n._('Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
                    'Together the base path and selected playbook directory provide the full path used to locate playbooks.') + '
' +
                    '
' + i18n.sprintf(i18n._('Change %s under "Configure Tower" to change this location.'), 'PROJECTS_ROOT') + '
',
                dataTitle: i18n._('Project Base Path'),
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            local_path: {
                label: i18n._('Playbook Directory'),
                type: 'select',
                id: 'local-path-select',
                ngOptions: 'path.label for path in project_local_paths',
                awRequiredWhen: {
                    reqExpression: "pathRequired",
                    init: false
                },
                ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
                awPopOver: '
' + i18n._('Select from the list of directories found in the Project Base Path. ' +
                    'Together the base path and the playbook directory provide the full path used to locate playbooks.') + '
',
                dataTitle: i18n._('Project Path'),
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_url: {
                label: 'SCM URL',
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual' && scm_type.value !== 'insights' ",
                awRequiredWhen: {
                    reqExpression: "scmRequired",
                    init: false
                },
                subForm: 'sourceSubForm',
                hideSubForm: "scm_type.value === 'manual'",
                awPopOverWatch: "urlPopover",
                awPopOver: "set in controllers/projects",
                dataTitle: 'SCM URL',
                dataContainer: 'body',
                dataPlacement: 'right',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            scm_branch: {
                labelBind: "scmBranchLabel",
                type: 'text',
                ngShow: "scm_type && scm_type.value !== 'manual' && scm_type.value !== 'insights'",
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                subForm: 'sourceSubForm',
            },
            credential: {
                labelBind: 'credentialLabel',
                type: 'lookup',
                basePath: 'credentials',
                list: 'CredentialList',
                // apply a default search filter to show only scm credentials
                search: {
                    kind: 'scm'
                },
                ngShow: "scm_type && scm_type.value !== 'manual'",
                sourceModel: 'credential',
                awLookupType: 'scm_credential',
                sourceField: 'name',
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)',
                subForm: 'sourceSubForm'
            },
            checkbox_group: {
                label: i18n._('SCM Update Options'),
                type: 'checkbox_group',
                ngShow: "scm_type && scm_type.value !== 'manual'",
                subForm: 'sourceSubForm',
                fields: [{
                    name: 'scm_clean',
                    label: i18n._('Clean'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Remove any local modifications prior to performing an update.') + '
',
                    dataTitle: i18n._('SCM Clean'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }, {
                    name: 'scm_delete_on_update',
                    label: i18n._('Delete on Update'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Delete the local repository in its entirety prior to performing an update.') + '

' + i18n._('Depending on the size of the ' +
                        'repository this may significantly increase the amount of time required to complete an update.') + '
',
                    dataTitle: i18n._('SCM Delete'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }, {
                    name: 'scm_update_on_launch',
                    label: i18n._('Update on Launch'),
                    type: 'checkbox',
                    awPopOver: '
' + i18n._('Each time a job runs using this project, perform an update to the local repository prior to starting the job.') + '
',
                    dataTitle: i18n._('SCM Update'),
                    dataContainer: 'body',
                    dataPlacement: 'right',
                    labelClass: 'checkbox-options stack-inline',
                    ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                }]
            },
            scm_update_cache_timeout: {
                label: i18n.sprintf(i18n._('Cache Timeout%s (seconds)%s'), '', ''),
                id: 'scm-cache-timeout',
                type: 'number',
                integer: true,
                min: 0,
                ngShow: "scm_update_on_launch && scm_type.value !== 'manual'",
                spinner: true,
                "default": '0',
                awPopOver: '
' + i18n._('Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
                    'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
                    'and a new project update will be performed.') + '
',
                dataTitle: i18n._('Cache Timeout'),
                dataPlacement: 'right',
                dataContainer: "body",
                ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            }
        },

        buttons: {
            cancel: {
                ngClick: 'formCancel()',
                ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            close: {
                ngClick: 'formCancel()',
                ngShow: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            },
            save: {
                ngClick: 'formSave()',
                ngDisabled: true,
                ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
            }
        },

        related: {
            permissions: {
                name: 'permissions',
                awToolTip: i18n._('Please save before assigning permissions'),
                djangoModel: 'access_list',
                dataPlacement: 'top',
                basePath: 'api/v1/projects/{{$stateParams.project_id}}/access_list/',
                search: {
                    order_by: 'username'
                },
                type: 'collection',
                title: i18n._('Permissions'),
                iterator: 'permission',
                index: false,
                open: false,
                actions: {
                    add: {
                        ngClick: "$state.go('.add')",
                        label: 'Add',
                        awToolTip: i18n._('Add a permission'),
                        actionClass: 'btn List-buttonSubmit',
                        buttonContent: '+ ' + i18n._('ADD'),
                        ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
                    }
                },

                fields: {
                    username: {
                        label: i18n._('User'),
                        uiSref: 'users({user_id: field.id})',
                        class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
                    },
                    role: {
                        label: i18n._('Role'),
                        type: 'role',
                        nosort: true,
                        class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
                    },
                    team_roles: {
                        label: i18n._('Team Roles'),
                        type: 'team_roles',
                        nosort: true,
                        class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
                    }
                }
            },
            notifications: {
                include: "NotificationsList",
            }
        }

    };

    var itm;

    for (itm in projectsFormObj.related) {
        if (projectsFormObj.related[itm].include === "NotificationsList") {
            projectsFormObj.related[itm] = NotificationsList;
            projectsFormObj.related[itm].generateList = true;   // tell form generator to call list generator and inject a list
        }
    }
    return projectsFormObj;
};}];
<|endoftext|>