<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;
            });

            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));
                        }
                        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
            });

            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
            }));

            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
            });

            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.flattenTree(cardTree.topCards(), []);
                var res;
                var matchingWidget;
                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') {
                    var matchingNode;
                    viewModel.graphTree.collapseAll();
                    matchingNode = correspondingNode(viewModel.cardTree.selection(), viewModel.graphTree);
                    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.selected(true);
                }
            };

            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: 'permissions-manager-help',
                graph: 'graph-designer-help',
                card: 'card-manager-help'
            };

            viewModel.activeTab.subscribe(function(tab) {
                viewModel.helpTemplate(helpContentLookup[tab]);
                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.viewState.subscribe(function(state) {
                if (state === '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>minor fixes<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;
            });

            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));
                        }
                        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
            });

            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
            }));

            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
            });

            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.flattenTree(cardTree.topCards(), []);
                var res;
                var matchingWidget;
                if (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.selected(true);
                }
            };

            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: 'permissions-manager-help',
                graph: 'graph-designer-help',
                card: 'card-manager-help'
            };

            viewModel.activeTab.subscribe(function(tab) {
                viewModel.helpTemplate(helpContentLookup[tab]);
                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.viewState.subscribe(function(state) {
                if (state === '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>angular.module('notification', [])
    .directive('notification', () => ({
        restrict: 'E',
        scope: {show: '=', msg: '@'},
        templateUrl: 'common/notification/notification.html',
        controller: ($scope, $timeout) => {
            var timeout;
            var showNotification = () => $scope.show = true;
            var hideNotification = () => $scope.show = false;
            var finishTimeout = () => timeout = false;
            $scope.$watch('show', (show) => {
                if(show) {
                    if(timeout) {
                        $timeout.cancel(timeout);
                        $scope.show = false;
                        timeout = $timeout(400)
                            .then(showNotification)
                            .then(() => $timeout(3000))
                            .then(hideNotification)
                            .then(finishTimeout);
                    } else {
                        timeout = $timeout(3000).then(hideNotification);
                    }
                }
            });
        }
    }));
<commit_msg>debug wip<commit_after>angular.module('notification', [])
    .directive('notification', () => ({
        restrict: 'E',
        scope: {show: '=', msg: '@'},
        templateUrl: 'common/notification/notification.html',
        controller: ($scope, $timeout) => {
            var timeout;
            var showNotification = () => $scope.show = true;
            var hideNotification = () => $scope.show = false;
            var finishTimeout = () => timeout = false;
            $scope.$watch('show', (show) => {
                console.log('watch triggered, show = ', show, 'timeout', timeout);
                if(show) {
                    // Oh I see the problem is that when we try to set show =
                    // true temporarily we end up back here with show and
                    // timeout both truthy
                    if(timeout) {

                        console.log('timeout exists canceling');
                        $timeout.cancel(timeout);
                        $scope.show = false;
                        timeout = $timeout(400)
                            .then(() => console.log('done hiding, now show'))
                            .then(showNotification)
                            .then(() => $timeout(3000))
                            .then(() => console.log('done'))
                            .then(hideNotification)
                            .then(finishTimeout);
                    } else {
                        console.log('timeout was false, showing then hiding');
                        timeout = $timeout(3000).then(hideNotification);
                    }
                }
            });
        }
    }));
<|endoftext|>


<commit_before>gpub.book.latex = {
  /**
   * Generate a LaTeX book!
   *
   * We assume that the options have already been generated.
   *
   * spec: A bookSpec -- i.e., a set of glift options.
   * templateString: the book template to use for the book
   * diagramType: The diagram format.
   * options: optional parameters. Including:
   *    Title: The title of the book
   *    Subtitle: Optional Subtitle
   *    Authors: Array of author names
   *    Publisher: Publisher Name
   *
   * Note: these parameters can also be specified in the spec metadata.
   */
  generate: function(spec, templateString, diagramType, options) {
    if (!spec) {
      throw new Error('Options must be defined. Was: ' + spec);
    }

    var diagramsPerPage = 2;

    var templateString = templateString || gpub.templates.latexBase;
    var diagramType = diagramType || gpub.diagrams.diagramType.GOOE

    var mgr = glift.widgets.createNoDraw(spec);
    var template = gpub.templates.parseLatexTemplate(templateString);
    var diagramTypePkg = gpub.diagrams[glift.enums.toCamelCase(diagramType)];
    var diagramTypeHeaders = diagramTypePkg.latexHeaders;

    var latexDefs = gpub.diagrams.latex;

    template.setExtraPackages(diagramTypeHeaders.packageDef())
        .setDiagramTypeDefs(diagramTypeHeaders.extraDefs())
        .setDiagramWrapperDefs(latexDefs.diagramDefs())
        .setTitleDef(this.basicTitleDef(
            'Relentless',
            'Gu Li vs Lee Sedol',
            ['Younggil An', 'David Ormerod', 'Josh Hoak'],
            'Go Game Guru'));

    var content = [];
    var diagramBuffer = []
    var chapter = 1;
    var section = 1;
    var lastPurpose = null;
    var diagramPurpose = gpub.diagrams.diagramPurpose;
    for (var i = 0; i < mgr.sgfCollection.length; i++) {
      var sgfObj = mgr.loadSgfStringSync(mgr.getSgfObj(i));
      var mt = glift.rules.movetree.getFromSgf(
          sgfObj.sgfString, sgfObj.initialPosition);
      var flattened = glift.flattener.flatten(mt, {
          nextMovesTreepath: sgfObj.nextMovesPath,
          boardRegion: sgfObj.boardRegion
      });

      var nodeData = gpub.book.NodeData.fromContext(
          mt, flattened, sgfObj.metadata, sgfObj.nextMovesPath || []);
      section = nodeData.setSectionFromCtx(mt, lastPurpose, section);
      chapter = nodeData.setChapterFromCtx(mt, lastPurpose, chapter);

      var diagram = gpub.diagrams.forPurpose(
          flattened,
          diagramType,
          gpub.book.bookFormat.LATEX,
          nodeData.purpose,
          nodeData);

      if (nodeData.purpose === diagramPurpose.SECTION_INTRO ||
          nodeData.purpose === diagramPurpose.GAME_REVIEW_CHAPTER ||
          nodeData.purpose !== lastPurpose) {
        // Flush the previous buffer centent to the page.
        content.push(gpub.book.latex.renderPage(diagramBuffer));

        diagramBuffer = [];
        diagramBuffer.push(diagram);
        content.push(gpub.book.latex.renderPage(diagramBuffer));
        diagramBuffer = [];
      } else {
        diagramBuffer.push(diagram);
        if (diagramBuffer.length === diagramsPerPage) {
          content.push(gpub.book.latex.renderPage(diagramBuffer));
          diagramBuffer = [];
        }
      }
      lastPurpose = nodeData.purpose;
    }
    return template.setContent(content.join('\n')).compile();
  },

  /**
   * Generates a basic title.
   *
   * title: title of the book as string
   * author: array of one or several authors as array af string
   * subtitle: the subtitle as string
   * publisher: the publisher as string
   *
   * Note: The LaTeX template expects the command \mainBookTitle.
   *
   * returns: filled in string.
   */
  // TODO(kashomon): Perhaps there should be a 'titles' package. Or perhaps each
  // booktype should get its own sub-package?
  basicTitleDef: function(title, subtitle, authors, publisher) {
    var strbuff = [
      '\\definecolor{light-gray}{gray}{0.55}',
      '\\newcommand*{\\mainBookTitle}{\\begingroup',
      '  \\raggedleft'];
    for (var i = 0; i < authors.length; i++) {
      strbuff.push('  {\\Large ' + authors[i] + '} \\\\')
      if (i === 0) {
        strbuff.push('  \\vspace*{0.5 em}');
      } else if (i < authors.length - 1) {
        strbuff.push('  \\vspace*{0.5 em}');
      }
    }
    return strbuff.concat(['  \\vspace*{5 em}',
      '  {\\textcolor{light-gray}{\\Huge ' + title + '}}\\\\',
      '  \\vspace*{\\baselineskip}',
      '  {\\small \\bfseries ' + subtitle + '}\\par',
      '  \\vfill',
      '  {\\Large ' + publisher + '}\\par',
      '  \\vspace*{2\\baselineskip}',
      '\\endgroup}']).join('\n');
  },

  renderPage: function(buffer) {
    buffer.push('\\newpage');
    return buffer.join('\n');
  }
};
<commit_msg>Add comments to latex book.<commit_after>/**
 * LaTeX book processor. Should implement methods present in
 * gpub.book.processor.
 */
gpub.book.latex = {
  /**
   * Generate a LaTeX book!
   *
   * We assume that the options have already been generated.
   *
   * spec: A bookSpec -- i.e., a set of glift options.
   * templateString: the book template to use for the book
   * diagramType: The diagram format.
   * options: optional parameters. Including:
   *    Title: The title of the book
   *    Subtitle: Optional Subtitle
   *    Authors: Array of author names
   *    Publisher: Publisher Name
   *
   * Note: these parameters can also be specified in the spec metadata.
   */
  generate: function(spec, templateString, diagramType, options) {
    if (!spec) {
      throw new Error('Options must be defined. Was: ' + spec);
    }

    var diagramsPerPage = 2;

    var templateString = templateString || gpub.templates.latexBase;
    var diagramType = diagramType || gpub.diagrams.diagramType.GOOE

    var mgr = glift.widgets.createNoDraw(spec);
    var template = gpub.templates.parseLatexTemplate(templateString);
    var diagramTypePkg = gpub.diagrams[glift.enums.toCamelCase(diagramType)];
    var diagramTypeHeaders = diagramTypePkg.latexHeaders;

    var latexDefs = gpub.diagrams.latex;

    template.setExtraPackages(diagramTypeHeaders.packageDef())
        .setDiagramTypeDefs(diagramTypeHeaders.extraDefs())
        .setDiagramWrapperDefs(latexDefs.diagramDefs())
        .setTitleDef(this.basicTitleDef(
            'Relentless',
            'Gu Li vs Lee Sedol',
            ['Younggil An', 'David Ormerod', 'Josh Hoak'],
            'Go Game Guru'));

    var content = [];
    var diagramBuffer = []
    var chapter = 1;
    var section = 1;
    var lastPurpose = null;
    var diagramPurpose = gpub.diagrams.diagramPurpose;
    for (var i = 0; i < mgr.sgfCollection.length; i++) {
      var sgfObj = mgr.loadSgfStringSync(mgr.getSgfObj(i));
      var mt = glift.rules.movetree.getFromSgf(
          sgfObj.sgfString, sgfObj.initialPosition);
      var flattened = glift.flattener.flatten(mt, {
          nextMovesTreepath: sgfObj.nextMovesPath,
          boardRegion: sgfObj.boardRegion
      });

      var nodeData = gpub.book.NodeData.fromContext(
          mt, flattened, sgfObj.metadata, sgfObj.nextMovesPath || []);
      section = nodeData.setSectionFromCtx(mt, lastPurpose, section);
      chapter = nodeData.setChapterFromCtx(mt, lastPurpose, chapter);

      var diagram = gpub.diagrams.forPurpose(
          flattened,
          diagramType,
          gpub.book.bookFormat.LATEX,
          nodeData.purpose,
          nodeData);

      if (nodeData.purpose === diagramPurpose.SECTION_INTRO ||
          nodeData.purpose === diagramPurpose.GAME_REVIEW_CHAPTER ||
          nodeData.purpose !== lastPurpose) {
        // Flush the previous buffer centent to the page.
        content.push(gpub.book.latex.renderPage(diagramBuffer));

        diagramBuffer = [];
        diagramBuffer.push(diagram);
        content.push(gpub.book.latex.renderPage(diagramBuffer));
        diagramBuffer = [];
      } else {
        diagramBuffer.push(diagram);
        if (diagramBuffer.length === diagramsPerPage) {
          content.push(gpub.book.latex.renderPage(diagramBuffer));
          diagramBuffer = [];
        }
      }
      lastPurpose = nodeData.purpose;
    }
    return template.setContent(content.join('\n')).compile();
  },

  /**
   * Generates a basic title.
   *
   * title: title of the book as string
   * author: array of one or several authors as array af string
   * subtitle: the subtitle as string
   * publisher: the publisher as string
   *
   * Note: The LaTeX template expects the command \mainBookTitle.
   *
   * returns: filled in string.
   */
  // TODO(kashomon): Perhaps there should be a 'titles' package. Or perhaps each
  // booktype should get its own sub-package?
  basicTitleDef: function(title, subtitle, authors, publisher) {
    var strbuff = [
      '\\definecolor{light-gray}{gray}{0.55}',
      '\\newcommand*{\\mainBookTitle}{\\begingroup',
      '  \\raggedleft'];
    for (var i = 0; i < authors.length; i++) {
      strbuff.push('  {\\Large ' + authors[i] + '} \\\\')
      if (i === 0) {
        strbuff.push('  \\vspace*{0.5 em}');
      } else if (i < authors.length - 1) {
        strbuff.push('  \\vspace*{0.5 em}');
      }
    }
    return strbuff.concat(['  \\vspace*{5 em}',
      '  {\\textcolor{light-gray}{\\Huge ' + title + '}}\\\\',
      '  \\vspace*{\\baselineskip}',
      '  {\\small \\bfseries ' + subtitle + '}\\par',
      '  \\vfill',
      '  {\\Large ' + publisher + '}\\par',
      '  \\vspace*{2\\baselineskip}',
      '\\endgroup}']).join('\n');
  },

  renderPage: function(buffer) {
    buffer.push('\\newpage');
    return buffer.join('\n');
  }
};
<|endoftext|>


<commit_before>/**
 * Created by eurides on 25/04/16.
 */

(function ($) {

    $.fn.serverStatus = function () {
        $.ajax({
            type: 'GET',
            dataType: 'json',
            url: 'serviceStatus',
            success: function (data) {
                if ((!data) || (!data.statusServidor)) {
                    $.fn.statusError();
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                $.fn.statusError();
            }
        });
    };

    $.fn.statusError = function (message) {
        message = typeof message !== 'undefined' ? message : 'Serviço offline';

        if (!$('#content').length) {
            $('body').prepend('

');
        }

        if (!$('#content').find('#flash_error').length) {
            $('#content').prepend('

');
        }

        $('#flash_error').html('' + message + '');
    };

    $.fn.taskRedmineExists = function () {
        var el = $(this);
        var chamado_nro = $('#chamado_nro').val();

        if (chamado_nro > 0) {
            $.ajax({
                type: 'GET',
                dataType: 'json',
                url: 'taskRedmineExists/' + chamado_nro,
                success: function (data) {
                    el.removeAttr("disabled");
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    el.attr("disabled", true);
                }
            });
        } else {
            el.attr("disabled", true);
        }

    };

}(jQuery));<commit_msg>Adjust disable button create task.<commit_after>/**
 * Created by eurides on 25/04/16.
 */

(function ($) {

    $.fn.serverStatus = function () {
        $.ajax({
            type: 'GET',
            dataType: 'json',
            url: 'serviceStatus',
            success: function (data) {
                if ((!data) || (!data.statusServidor)) {
                    $.fn.statusError();
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                $.fn.statusError();
            }
        });
    };

    $.fn.statusError = function (message) {
        message = typeof message !== 'undefined' ? message : 'Serviço offline';

        if (!$('#content').length) {
            $('body').prepend('

');
        }

        if (!$('#content').find('#flash_error').length) {
            $('#content').prepend('

');
        }

        $('#flash_error').html('' + message + '');
    };

    $.fn.taskRedmineExists = function () {
        var el = $(this);
        var chamado_nro = $('#chamado_nro').val();

        if (chamado_nro > 0) {
            $.ajax({
                type: 'GET',
                dataType: 'json',
                url: 'taskRedmineExists/' + chamado_nro,
                success: function (data) {
                    el.attr("disabled", true);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    el.removeAttr("disabled");
                }
            });
        } else {
            el.attr("disabled", true);
        }

    };

}(jQuery));
<|endoftext|>


<commit_before>'use strict';

var gulp = require('gulp');
var watch = require('gulp-watch');
var livereload = require('gulp-livereload');
livereloadServer = livereload(config.ports.livereloadServer);

module.exports = gulp.task('watch', function () {
  gulp.watch(config.paths.src.livereload).on('change', function (file) {
    livereloadServer.changed(file.path);
  });

  watch({ glob: [config.paths.src.scripts]}, ['lint']);
  watch({ glob: [config.paths.src.index]}, ['index']);
  watch({ glob: [config.paths.src.templates]}, ['templates']);
  watch({ glob: [config.paths.src.stylesGlob]}, ['styles']);
});
<commit_msg>fix livereload variable<commit_after>'use strict';

var gulp = require('gulp');
var watch = require('gulp-watch');
var livereload = require('gulp-livereload');
var livereloadServer = livereload(config.ports.livereloadServer);

module.exports = gulp.task('watch', function () {
  gulp.watch(config.paths.src.livereload).on('change', function (file) {
    livereloadServer.changed(file.path);
  });

  watch({ glob: [config.paths.src.scripts]}, ['lint']);
  watch({ glob: [config.paths.src.index]}, ['index']);
  watch({ glob: [config.paths.src.templates]}, ['templates']);
  watch({ glob: [config.paths.src.stylesGlob]}, ['styles']);
});
<|endoftext|>


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

/**
 * External dependencies
 */
import { ChipSet, Chip } from '@material/react-chips';

/**
 * WordPress dependencies
 */
import { useCallback, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import {
	ANCHOR_ID_CONTENT,
	ANCHOR_ID_MONETIZATION,
	ANCHOR_ID_SPEED,
	ANCHOR_ID_TRAFFIC,
} from '../googlesitekit/constants';
import { CORE_WIDGETS } from '../googlesitekit/widgets/datastore/constants';
import {
	CONTEXT_ENTITY_DASHBOARD_TRAFFIC,
	CONTEXT_ENTITY_DASHBOARD_CONTENT,
	CONTEXT_ENTITY_DASHBOARD_SPEED,
	CONTEXT_ENTITY_DASHBOARD_MONETIZATION,
	CONTEXT_MAIN_DASHBOARD_TRAFFIC,
	CONTEXT_MAIN_DASHBOARD_CONTENT,
	CONTEXT_MAIN_DASHBOARD_SPEED,
	CONTEXT_MAIN_DASHBOARD_MONETIZATION,
} from '../googlesitekit/widgets/default-contexts';
import useDashboardType, {
	DASHBOARD_TYPE_MAIN,
} from '../hooks/useDashboardType';

const { useSelect } = Data;

export default function DashboardNavigation() {
	const dashboardType = useDashboardType();

	const [ showTraffic, showContent, showSpeed, showMonitization ] = useSelect(
		( select ) => {
			if ( dashboardType === DASHBOARD_TYPE_MAIN ) {
				return [
					select( CORE_WIDGETS ).isWidgetContextActive(
						CONTEXT_MAIN_DASHBOARD_TRAFFIC
					),
					select( CORE_WIDGETS ).isWidgetContextActive(
						CONTEXT_MAIN_DASHBOARD_CONTENT
					),
					select( CORE_WIDGETS ).isWidgetContextActive(
						CONTEXT_MAIN_DASHBOARD_SPEED
					),
					select( CORE_WIDGETS ).isWidgetContextActive(
						CONTEXT_MAIN_DASHBOARD_MONETIZATION
					),
				];
			}

			return [
				select( CORE_WIDGETS ).isWidgetContextActive(
					CONTEXT_ENTITY_DASHBOARD_TRAFFIC
				),
				select( CORE_WIDGETS ).isWidgetContextActive(
					CONTEXT_ENTITY_DASHBOARD_CONTENT
				),
				select( CORE_WIDGETS ).isWidgetContextActive(
					CONTEXT_ENTITY_DASHBOARD_SPEED
				),
				select( CORE_WIDGETS ).isWidgetContextActive(
					CONTEXT_ENTITY_DASHBOARD_MONETIZATION
				),
			];
		}
	);

	const [ selectedIds, setSelectedIds ] = useState( [] );

	const handleSelect = useCallback( ( selections ) => {
		const [ hash ] = selections;
		if ( hash ) {
			global.location.hash = hash;
		}
		setSelectedIds( selections );
	}, [] );

	return (
		<ChipSet
			className="googlesitekit-navigation"
			selectedChipIds={ selectedIds }
			handleSelect={ handleSelect }
			choice
		>
			{ showTraffic && (
				<Chip
					id={ ANCHOR_ID_TRAFFIC }
					label={ __( 'Traffic', 'google-site-kit' ) }
				/>
			) }
			{ showContent && (
				<Chip
					id={ ANCHOR_ID_CONTENT }
					label={ __( 'Content', 'google-site-kit' ) }
				/>
			) }
			{ showSpeed && (
				<Chip
					id={ ANCHOR_ID_SPEED }
					label={ __( 'Speed', 'google-site-kit' ) }
				/>
			) }
			{ showMonitization && (
				<Chip
					id={ ANCHOR_ID_MONETIZATION }
					label={ __( 'Monetization', 'google-site-kit' ) }
				/>
			) }
		</ChipSet>
	);
}
<commit_msg>Break up useSelect calls.<commit_after>/**
 * DashboardNavigation component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { ChipSet, Chip } from '@material/react-chips';

/**
 * WordPress dependencies
 */
import { useCallback, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import {
	ANCHOR_ID_CONTENT,
	ANCHOR_ID_MONETIZATION,
	ANCHOR_ID_SPEED,
	ANCHOR_ID_TRAFFIC,
} from '../googlesitekit/constants';
import { CORE_WIDGETS } from '../googlesitekit/widgets/datastore/constants';
import {
	CONTEXT_ENTITY_DASHBOARD_TRAFFIC,
	CONTEXT_ENTITY_DASHBOARD_CONTENT,
	CONTEXT_ENTITY_DASHBOARD_SPEED,
	CONTEXT_ENTITY_DASHBOARD_MONETIZATION,
	CONTEXT_MAIN_DASHBOARD_TRAFFIC,
	CONTEXT_MAIN_DASHBOARD_CONTENT,
	CONTEXT_MAIN_DASHBOARD_SPEED,
	CONTEXT_MAIN_DASHBOARD_MONETIZATION,
} from '../googlesitekit/widgets/default-contexts';
import useDashboardType, {
	DASHBOARD_TYPE_MAIN,
} from '../hooks/useDashboardType';

const { useSelect } = Data;

export default function DashboardNavigation() {
	const dashboardType = useDashboardType();

	const showTraffic = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_TRAFFIC
				: CONTEXT_ENTITY_DASHBOARD_TRAFFIC
		)
	);

	const showContent = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_CONTENT
				: CONTEXT_ENTITY_DASHBOARD_CONTENT
		)
	);

	const showSpeed = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_SPEED
				: CONTEXT_ENTITY_DASHBOARD_SPEED
		)
	);

	const showMonitization = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_MONETIZATION
				: CONTEXT_ENTITY_DASHBOARD_MONETIZATION
		)
	);

	const [ selectedIds, setSelectedIds ] = useState( [] );

	const handleSelect = useCallback( ( selections ) => {
		const [ hash ] = selections;
		if ( hash ) {
			global.location.hash = hash;
		}
		setSelectedIds( selections );
	}, [] );

	return (
		<ChipSet
			className="googlesitekit-navigation"
			selectedChipIds={ selectedIds }
			handleSelect={ handleSelect }
			choice
		>
			{ showTraffic && (
				<Chip
					id={ ANCHOR_ID_TRAFFIC }
					label={ __( 'Traffic', 'google-site-kit' ) }
				/>
			) }
			{ showContent && (
				<Chip
					id={ ANCHOR_ID_CONTENT }
					label={ __( 'Content', 'google-site-kit' ) }
				/>
			) }
			{ showSpeed && (
				<Chip
					id={ ANCHOR_ID_SPEED }
					label={ __( 'Speed', 'google-site-kit' ) }
				/>
			) }
			{ showMonitization && (
				<Chip
					id={ ANCHOR_ID_MONETIZATION }
					label={ __( 'Monetization', 'google-site-kit' ) }
				/>
			) }
		</ChipSet>
	);
}
<|endoftext|>


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

/**
 * External dependencies
 */
import classnames from 'classnames';
import { useMount } from 'react-use';
import { Chip } from '@material/react-chips';
import throttle from 'lodash/throttle';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect, useState, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import NavTrafficIcon from '../../svg/icons/nav-traffic-icon.svg';
import NavContentIcon from '../../svg/icons/nav-content-icon.svg';
import NavSpeedIcon from '../../svg/icons/nav-speed-icon.svg';
import NavMonetizationIcon from '../../svg/icons/nav-monetization-icon.svg';
import {
	ANCHOR_ID_CONTENT,
	ANCHOR_ID_MONETIZATION,
	ANCHOR_ID_SPEED,
	ANCHOR_ID_TRAFFIC,
} from '../googlesitekit/constants';
import { CORE_WIDGETS } from '../googlesitekit/widgets/datastore/constants';
import {
	CONTEXT_ENTITY_DASHBOARD_TRAFFIC,
	CONTEXT_ENTITY_DASHBOARD_CONTENT,
	CONTEXT_ENTITY_DASHBOARD_SPEED,
	CONTEXT_ENTITY_DASHBOARD_MONETIZATION,
	CONTEXT_MAIN_DASHBOARD_TRAFFIC,
	CONTEXT_MAIN_DASHBOARD_CONTENT,
	CONTEXT_MAIN_DASHBOARD_SPEED,
	CONTEXT_MAIN_DASHBOARD_MONETIZATION,
} from '../googlesitekit/widgets/default-contexts';
import useDashboardType, {
	DASHBOARD_TYPE_MAIN,
} from '../hooks/useDashboardType';
import { useBreakpoint } from '../hooks/useBreakpoint';
import { getContextScrollTop } from '../util/scroll';
const { useSelect } = Data;

export default function DashboardNavigation() {
	const dashboardType = useDashboardType();
	const elementRef = useRef();
	const [ isSticky, setIsSticky ] = useState( false );

	const showTraffic = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_TRAFFIC
				: CONTEXT_ENTITY_DASHBOARD_TRAFFIC
		)
	);

	const showContent = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_CONTENT
				: CONTEXT_ENTITY_DASHBOARD_CONTENT
		)
	);

	const showSpeed = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_SPEED
				: CONTEXT_ENTITY_DASHBOARD_SPEED
		)
	);

	const showMonetization = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_MONETIZATION
				: CONTEXT_ENTITY_DASHBOARD_MONETIZATION
		)
	);

	const breakpoint = useBreakpoint();

	const [ selectedID, setSelectedID ] = useState(
		global.location.hash.substring( 1 )
	);

	const handleSelect = useCallback(
		( { target } ) => {
			const chip = target.closest( '.mdc-chip' );
			const chipID = chip?.dataset?.contextId; // eslint-disable-line sitekit/acronym-case

			global.scrollTo( {
				top:
					chipID !== ANCHOR_ID_TRAFFIC
						? getContextScrollTop( `#${ chipID }`, breakpoint )
						: 0,
				behavior: 'smooth',
			} );
		},
		[ breakpoint ]
	);

	useMount( () => {
		const { hash } = global.location;
		if ( ! hash ) {
			return;
		}

		setTimeout( () => {
			global.scrollTo( {
				top:
					hash.substring( 1 ) !== ANCHOR_ID_TRAFFIC
						? getContextScrollTop( hash, breakpoint )
						: 0,
				behavior: 'smooth',
			} );
		}, 25 );
	} );

	useEffect( () => {
		const onScroll = () => {
			const yScrollPosition = global.scrollY;
			const entityHeader = document
				.querySelector( '.googlesitekit-entity-header' )
				?.getBoundingClientRect()?.bottom;
			const {
				bottom: navigation,
				top: navigationTop,
			} = elementRef?.current?.getBoundingClientRect();
			const margin = 20;

			const areas = [
				...( showTraffic ? [ ANCHOR_ID_TRAFFIC ] : [] ),
				...( showContent ? [ ANCHOR_ID_CONTENT ] : [] ),
				...( showSpeed ? [ ANCHOR_ID_SPEED ] : [] ),
				...( showMonetization ? [ ANCHOR_ID_MONETIZATION ] : [] ),
			];

			let closest;
			let closestID = ANCHOR_ID_TRAFFIC;

			if ( yScrollPosition === 0 ) {
				setIsSticky( false );
			} else {
				const headerBottom = document
					.querySelector( '.googlesitekit-header' )
					?.getBoundingClientRect().bottom;
				setIsSticky( navigationTop === headerBottom );
			}

			for ( const areaID of areas ) {
				const area = document.getElementById( areaID );
				if ( ! area ) {
					continue;
				}

				const top =
					area.getBoundingClientRect().top -
					margin -
					( entityHeader || navigation || 0 );

				if ( top < 0 && ( closest === undefined || closest < top ) ) {
					closest = top;
					closestID = areaID;
				}
			}

			const { hash } = global.location;
			if ( closestID !== hash?.substring( 1 ) ) {
				global.history.replaceState( {}, '', `#${ closestID }` );
				setSelectedID( closestID );
			}
		};

		const throttledOnScroll = throttle( onScroll, 50 );
		global.addEventListener( 'scroll', throttledOnScroll );

		throttledOnScroll();

		return () => {
			global.removeEventListener( 'scroll', throttledOnScroll );
		};
	}, [ showTraffic, showContent, showSpeed, showMonetization ] );

	return (
		<nav
			className={ classnames(
				'mdc-chip-set',
				'googlesitekit-navigation',
				{
					'googlesitekit-navigation--is-sticky': isSticky,
				}
			) }
			ref={ elementRef }
		>
			{ showTraffic && (
				<Chip
					id={ ANCHOR_ID_TRAFFIC }
					label={ __( 'Traffic', 'google-site-kit' ) }
					leadingIcon={ <NavTrafficIcon width="18" height="16" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_TRAFFIC }
					data-context-id={ ANCHOR_ID_TRAFFIC }
				/>
			) }
			{ showContent && (
				<Chip
					id={ ANCHOR_ID_CONTENT }
					label={ __( 'Content', 'google-site-kit' ) }
					leadingIcon={ <NavContentIcon width="18" height="18" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_CONTENT }
					data-context-id={ ANCHOR_ID_CONTENT }
				/>
			) }
			{ showSpeed && (
				<Chip
					id={ ANCHOR_ID_SPEED }
					label={ __( 'Speed', 'google-site-kit' ) }
					leadingIcon={ <NavSpeedIcon width="20" height="16" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_SPEED }
					data-context-id={ ANCHOR_ID_SPEED }
				/>
			) }
			{ showMonetization && (
				<Chip
					id={ ANCHOR_ID_MONETIZATION }
					label={ __( 'Monetization', 'google-site-kit' ) }
					leadingIcon={
						<NavMonetizationIcon width="18" height="16" />
					}
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_MONETIZATION }
					data-context-id={ ANCHOR_ID_MONETIZATION }
				/>
			) }
		</nav>
	);
}
<commit_msg>Add more meaningful name to the variable.<commit_after>/**
 * DashboardNavigation component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import classnames from 'classnames';
import { useMount } from 'react-use';
import { Chip } from '@material/react-chips';
import throttle from 'lodash/throttle';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect, useState, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import NavTrafficIcon from '../../svg/icons/nav-traffic-icon.svg';
import NavContentIcon from '../../svg/icons/nav-content-icon.svg';
import NavSpeedIcon from '../../svg/icons/nav-speed-icon.svg';
import NavMonetizationIcon from '../../svg/icons/nav-monetization-icon.svg';
import {
	ANCHOR_ID_CONTENT,
	ANCHOR_ID_MONETIZATION,
	ANCHOR_ID_SPEED,
	ANCHOR_ID_TRAFFIC,
} from '../googlesitekit/constants';
import { CORE_WIDGETS } from '../googlesitekit/widgets/datastore/constants';
import {
	CONTEXT_ENTITY_DASHBOARD_TRAFFIC,
	CONTEXT_ENTITY_DASHBOARD_CONTENT,
	CONTEXT_ENTITY_DASHBOARD_SPEED,
	CONTEXT_ENTITY_DASHBOARD_MONETIZATION,
	CONTEXT_MAIN_DASHBOARD_TRAFFIC,
	CONTEXT_MAIN_DASHBOARD_CONTENT,
	CONTEXT_MAIN_DASHBOARD_SPEED,
	CONTEXT_MAIN_DASHBOARD_MONETIZATION,
} from '../googlesitekit/widgets/default-contexts';
import useDashboardType, {
	DASHBOARD_TYPE_MAIN,
} from '../hooks/useDashboardType';
import { useBreakpoint } from '../hooks/useBreakpoint';
import { getContextScrollTop } from '../util/scroll';
const { useSelect } = Data;

export default function DashboardNavigation() {
	const dashboardType = useDashboardType();
	const elementRef = useRef();
	const [ isSticky, setIsSticky ] = useState( false );

	const showTraffic = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_TRAFFIC
				: CONTEXT_ENTITY_DASHBOARD_TRAFFIC
		)
	);

	const showContent = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_CONTENT
				: CONTEXT_ENTITY_DASHBOARD_CONTENT
		)
	);

	const showSpeed = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_SPEED
				: CONTEXT_ENTITY_DASHBOARD_SPEED
		)
	);

	const showMonetization = useSelect( ( select ) =>
		select( CORE_WIDGETS ).isWidgetContextActive(
			dashboardType === DASHBOARD_TYPE_MAIN
				? CONTEXT_MAIN_DASHBOARD_MONETIZATION
				: CONTEXT_ENTITY_DASHBOARD_MONETIZATION
		)
	);

	const breakpoint = useBreakpoint();

	const [ selectedID, setSelectedID ] = useState(
		global.location.hash.substring( 1 )
	);

	const handleSelect = useCallback(
		( { target } ) => {
			const chip = target.closest( '.mdc-chip' );
			const chipID = chip?.dataset?.contextId; // eslint-disable-line sitekit/acronym-case

			global.scrollTo( {
				top:
					chipID !== ANCHOR_ID_TRAFFIC
						? getContextScrollTop( `#${ chipID }`, breakpoint )
						: 0,
				behavior: 'smooth',
			} );
		},
		[ breakpoint ]
	);

	useMount( () => {
		const { hash } = global.location;
		if ( ! hash ) {
			return;
		}

		setTimeout( () => {
			global.scrollTo( {
				top:
					hash.substring( 1 ) !== ANCHOR_ID_TRAFFIC
						? getContextScrollTop( hash, breakpoint )
						: 0,
				behavior: 'smooth',
			} );
		}, 25 );
	} );

	useEffect( () => {
		const onScroll = () => {
			const yScrollPosition = global.scrollY;
			const entityHeader = document
				.querySelector( '.googlesitekit-entity-header' )
				?.getBoundingClientRect()?.bottom;
			const {
				bottom: navigationBottom,
				top: navigationTop,
			} = elementRef?.current?.getBoundingClientRect();
			const margin = 20;

			const areas = [
				...( showTraffic ? [ ANCHOR_ID_TRAFFIC ] : [] ),
				...( showContent ? [ ANCHOR_ID_CONTENT ] : [] ),
				...( showSpeed ? [ ANCHOR_ID_SPEED ] : [] ),
				...( showMonetization ? [ ANCHOR_ID_MONETIZATION ] : [] ),
			];

			let closest;
			let closestID = ANCHOR_ID_TRAFFIC;

			if ( yScrollPosition === 0 ) {
				setIsSticky( false );
			} else {
				const headerBottom = document
					.querySelector( '.googlesitekit-header' )
					?.getBoundingClientRect().bottom;
				setIsSticky( navigationTop === headerBottom );
			}

			for ( const areaID of areas ) {
				const area = document.getElementById( areaID );
				if ( ! area ) {
					continue;
				}

				const top =
					area.getBoundingClientRect().top -
					margin -
					( entityHeader || navigationBottom || 0 );

				if ( top < 0 && ( closest === undefined || closest < top ) ) {
					closest = top;
					closestID = areaID;
				}
			}

			const { hash } = global.location;
			if ( closestID !== hash?.substring( 1 ) ) {
				global.history.replaceState( {}, '', `#${ closestID }` );
				setSelectedID( closestID );
			}
		};

		const throttledOnScroll = throttle( onScroll, 50 );
		global.addEventListener( 'scroll', throttledOnScroll );

		throttledOnScroll();

		return () => {
			global.removeEventListener( 'scroll', throttledOnScroll );
		};
	}, [ showTraffic, showContent, showSpeed, showMonetization ] );

	return (
		<nav
			className={ classnames(
				'mdc-chip-set',
				'googlesitekit-navigation',
				{
					'googlesitekit-navigation--is-sticky': isSticky,
				}
			) }
			ref={ elementRef }
		>
			{ showTraffic && (
				<Chip
					id={ ANCHOR_ID_TRAFFIC }
					label={ __( 'Traffic', 'google-site-kit' ) }
					leadingIcon={ <NavTrafficIcon width="18" height="16" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_TRAFFIC }
					data-context-id={ ANCHOR_ID_TRAFFIC }
				/>
			) }
			{ showContent && (
				<Chip
					id={ ANCHOR_ID_CONTENT }
					label={ __( 'Content', 'google-site-kit' ) }
					leadingIcon={ <NavContentIcon width="18" height="18" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_CONTENT }
					data-context-id={ ANCHOR_ID_CONTENT }
				/>
			) }
			{ showSpeed && (
				<Chip
					id={ ANCHOR_ID_SPEED }
					label={ __( 'Speed', 'google-site-kit' ) }
					leadingIcon={ <NavSpeedIcon width="20" height="16" /> }
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_SPEED }
					data-context-id={ ANCHOR_ID_SPEED }
				/>
			) }
			{ showMonetization && (
				<Chip
					id={ ANCHOR_ID_MONETIZATION }
					label={ __( 'Monetization', 'google-site-kit' ) }
					leadingIcon={
						<NavMonetizationIcon width="18" height="16" />
					}
					onClick={ handleSelect }
					selected={ selectedID === ANCHOR_ID_MONETIZATION }
					data-context-id={ ANCHOR_ID_MONETIZATION }
				/>
			) }
		</nav>
	);
}
<|endoftext|>


<commit_before>/**
 * ownCloud - Music app
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
 * @copyright Pauli Järvinen 2021
 */

angular.module('Music').controller('PodcastsViewController', [
	'$scope', '$rootScope', 'playlistService', 'podcastService', 'libraryService', '$timeout', 'gettextCatalog',
	function ($scope, $rootScope, playlistService, podcastService, libraryService, $timeout, gettextCatalog) {

		$rootScope.currentView = window.location.hash;

		// $rootScope listeneres must be unsubscribed manually when the control is destroyed
		var unsubFuncs = [];

		function subscribe(event, handler) {
			unsubFuncs.push( $rootScope.$on(event, handler) );
		}

		$scope.$on('$destroy', function () {
			_.each(unsubFuncs, function(func) { func(); });
		});

		// Wrap the supplied tracks as a playlist and pass it to the service for playing
		function playEpisodes(listId, episodes) {
			var playlist = _.map(episodes, (episode) => ({track: episode}));
			playlistService.setPlaylist(listId, playlist);
			playlistService.publish('play');
		}

		function playPlaylistFromEpisode(listId, playlist, episode) {
			var index = _.findIndex(playlist, function(i) {return i.track.id == episode.id;});
			playlistService.setPlaylist(listId, playlist, index);
			playlistService.publish('play');
		}

		$scope.playEpisode = function(episodeId) {
			var episode = libraryService.getPodcastEpisode(episodeId);
			var currentTrack = $scope.$parent.currentTrack;

			// play/pause if currently playing track clicked
			if (currentTrack && episode.id === currentTrack.id) {
				playlistService.publish('togglePlayback');
			}
			else {
				var currentListId = playlistService.getCurrentPlaylistId();

				// start playing the channel from this episode if the clicked track belongs
				// to a channel which is the current play scope
				if (currentListId === 'podcast-channel-' + episode.channel.id) {
					playPlaylistFromEpisode(currentListId, playlistService.getCurrentPlaylist(), episode);
				}
				// on any other episode, start playing just the episode
				else {
					playEpisodes('podcast-episode-' + episode.id, [episode]);
				}
			}
		};

		$scope.playChannel = function(channel) {
			playEpisodes('podcast-channel-' + channel.id, channel.episodes);
		};

		$scope.showAddPodcast = podcastService.showAddPodcastDialog;
		$scope.reloadChannel = podcastService.reloadPodcastChannel;
		$scope.removeChannel = podcastService.removePodcastChannel;

		/**
		 * Two functions for the alphabet-navigation directive integration
		 */
		$scope.getChannelName = function(index) {
			return $scope.channels[index].title;
		};
		$scope.getChannelElementId = function(index) {
			return 'podcast-channel-' + $scope.channels[index].id;
		};

		/**
		 * Two functions for the tracklist directive integration
		 */
		$scope.getEpisodeData = function(episode, _index, _scope) {
			return {
				title: episode.title,
				tooltip: episode.title,
				number: episode.ordinal,
				id: episode.id
			};
		};
		$scope.getMoreEpisodesText = function(count) {
			return gettextCatalog.getString('Show all {{ count }} episodes …', { count: count });
		};

		// emited on end of playlist by playerController
		subscribe('playlistEnded', function() {
			updateHighlight(null);
		});

		subscribe('playlistChanged', function(_event, playlistId) {
			updateHighlight(playlistId);
		});

		subscribe('scrollToPodcastEpisode', function(_event, episodeId, animationTime /* optional */) {
			var episode = libraryService.getPodcastEpisode(episodeId);
			if (episode) {
				$scope.$parent.scrollToItem('podcast-channel-' + episode.channel.id, animationTime);
			}
		});

		subscribe('scrollToPodcastChannel', function(_event, channelId, animationTime /* optional */) {
			$scope.$parent.scrollToItem('podcast-channel-' + channelId, animationTime);
		});

		function updateHighlight(playlistId) {
			// remove any previous highlight
			$('.highlight').removeClass('highlight');

			// add highlighting if a channel is being played
			if (OCA.Music.Utils.startsWith(playlistId, 'podcast-channel-')) {
				$('#' + playlistId).addClass('highlight');
			}
		}

		function updateColumnLayout() {
			// Use the single-column layout if there's not enough room for two columns or more
			var containerWidth = $('#podcasts').width();
			var colWidth = 480;
			$('#podcasts').toggleClass('single-col', containerWidth < 2 * colWidth);
		}

		subscribe('resize', updateColumnLayout);

		function onContentReady() {
			// show content only if the view is not already (being) deactivated
			if ($rootScope.currentView && $scope.$parent) {
				$scope.channels = libraryService.getAllPodcastChannels();
				$rootScope.loading = false;
				$timeout(() => $rootScope.$emit('viewActivated'));
			}
		}

		// Make the content visible immediatedly if the podcasts are already loaded.
		// Otherwise it happens on the 'podcastsLoaded' event handler.
		if (libraryService.podcastsLoaded()) {
			onContentReady();
		}

		subscribe('podcastsLoaded', function() {
			onContentReady();
		});

		subscribe('deactivateView', function() {
			$timeout(() => $rootScope.$emit('viewDeactivated'));
		});
	}
]);
<commit_msg>When playing a whole podcast channel, enqueue the episodes chronologically<commit_after>/**
 * ownCloud - Music app
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
 * @copyright Pauli Järvinen 2021
 */

angular.module('Music').controller('PodcastsViewController', [
	'$scope', '$rootScope', 'playlistService', 'podcastService', 'libraryService', '$timeout', 'gettextCatalog',
	function ($scope, $rootScope, playlistService, podcastService, libraryService, $timeout, gettextCatalog) {

		$rootScope.currentView = window.location.hash;

		// $rootScope listeneres must be unsubscribed manually when the control is destroyed
		var unsubFuncs = [];

		function subscribe(event, handler) {
			unsubFuncs.push( $rootScope.$on(event, handler) );
		}

		$scope.$on('$destroy', function () {
			_.each(unsubFuncs, function(func) { func(); });
		});

		// Wrap the supplied tracks as a playlist and pass it to the service for playing
		function playEpisodes(listId, episodes) {
			var playlist = _.map(episodes, (episode) => ({track: episode}));
			playlistService.setPlaylist(listId, playlist);
			playlistService.publish('play');
		}

		function playPlaylistFromEpisode(listId, playlist, episode) {
			var index = _.findIndex(playlist, function(i) {return i.track.id == episode.id;});
			playlistService.setPlaylist(listId, playlist, index);
			playlistService.publish('play');
		}

		$scope.playEpisode = function(episodeId) {
			var episode = libraryService.getPodcastEpisode(episodeId);
			var currentTrack = $scope.$parent.currentTrack;

			// play/pause if currently playing track clicked
			if (currentTrack && episode.id === currentTrack.id) {
				playlistService.publish('togglePlayback');
			}
			else {
				var currentListId = playlistService.getCurrentPlaylistId();

				// start playing the channel from this episode if the clicked track belongs
				// to a channel which is the current play scope
				if (currentListId === 'podcast-channel-' + episode.channel.id) {
					playPlaylistFromEpisode(currentListId, playlistService.getCurrentPlaylist(), episode);
				}
				// on any other episode, start playing just the episode
				else {
					playEpisodes('podcast-episode-' + episode.id, [episode]);
				}
			}
		};

		$scope.playChannel = function(channel) {
			// play the episodes from bottom to top ie. from the oldest to newest
			const episodes = _.clone(channel.episodes).reverse();
			playEpisodes('podcast-channel-' + channel.id, episodes);
		};

		$scope.showAddPodcast = podcastService.showAddPodcastDialog;
		$scope.reloadChannel = podcastService.reloadPodcastChannel;
		$scope.removeChannel = podcastService.removePodcastChannel;

		/**
		 * Two functions for the alphabet-navigation directive integration
		 */
		$scope.getChannelName = function(index) {
			return $scope.channels[index].title;
		};
		$scope.getChannelElementId = function(index) {
			return 'podcast-channel-' + $scope.channels[index].id;
		};

		/**
		 * Two functions for the tracklist directive integration
		 */
		$scope.getEpisodeData = function(episode, _index, _scope) {
			return {
				title: episode.title,
				tooltip: episode.title,
				number: episode.ordinal,
				id: episode.id
			};
		};
		$scope.getMoreEpisodesText = function(count) {
			return gettextCatalog.getString('Show all {{ count }} episodes …', { count: count });
		};

		// emited on end of playlist by playerController
		subscribe('playlistEnded', function() {
			updateHighlight(null);
		});

		subscribe('playlistChanged', function(_event, playlistId) {
			updateHighlight(playlistId);
		});

		subscribe('scrollToPodcastEpisode', function(_event, episodeId, animationTime /* optional */) {
			var episode = libraryService.getPodcastEpisode(episodeId);
			if (episode) {
				$scope.$parent.scrollToItem('podcast-channel-' + episode.channel.id, animationTime);
			}
		});

		subscribe('scrollToPodcastChannel', function(_event, channelId, animationTime /* optional */) {
			$scope.$parent.scrollToItem('podcast-channel-' + channelId, animationTime);
		});

		function updateHighlight(playlistId) {
			// remove any previous highlight
			$('.highlight').removeClass('highlight');

			// add highlighting if a channel is being played
			if (OCA.Music.Utils.startsWith(playlistId, 'podcast-channel-')) {
				$('#' + playlistId).addClass('highlight');
			}
		}

		function updateColumnLayout() {
			// Use the single-column layout if there's not enough room for two columns or more
			var containerWidth = $('#podcasts').width();
			var colWidth = 480;
			$('#podcasts').toggleClass('single-col', containerWidth < 2 * colWidth);
		}

		subscribe('resize', updateColumnLayout);

		function onContentReady() {
			// show content only if the view is not already (being) deactivated
			if ($rootScope.currentView && $scope.$parent) {
				$scope.channels = libraryService.getAllPodcastChannels();
				$rootScope.loading = false;
				$timeout(() => $rootScope.$emit('viewActivated'));
			}
		}

		// Make the content visible immediatedly if the podcasts are already loaded.
		// Otherwise it happens on the 'podcastsLoaded' event handler.
		if (libraryService.podcastsLoaded()) {
			onContentReady();
		}

		subscribe('podcastsLoaded', function() {
			onContentReady();
		});

		subscribe('deactivateView', function() {
			$timeout(() => $rootScope.$emit('viewDeactivated'));
		});
	}
]);
<|endoftext|>