Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
directives.js 7.89 KiB
/******************************************************************************
 * Vipra Application
 * Directives
 ******************************************************************************/
(function() {

  var app = angular.module('vipra.directives', [
    'ui.router'
  ]);

  var slideDuration = 250;

  app.directive('topicLink', function() {
    return {
      scope: {
        topic: '='
      },
      restrict: 'E',
      replace: true,
      transclude: true,
      template: '<a class="topic-link" ui-sref="topics.show({id:topic.id})"><span ng-bind="topic.name"></span><ng-transclude/></a>'
    }
  });

  app.directive('articleLink', function() {
    return {
      scope: {
        article: '='
      },
      restrict: 'E',
      replace: true,
      transclude: true,
      template: '<a class="article-link" ui-sref="articles.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>'
    }
  });

  app.directive('queryTime', function() {
    return {
      restrict: 'E',
      replace: true,
      template: '<small class="text-muted">(took <span ng-bind-template="{{queryTime}}ms"></span>)</small>'
    };
  });

  app.directive('pagination', function() {
    return {
      restrict: 'E',
      replace: true,
      scope: {
        total: '=',
        page: '=',
        limit: '='
      },
      controller: 'PaginationController',
      templateUrl: 'html/directives/pagination.html'
    };
  });

  app.directive('visGraph', ['$state', '$timeout', 'ArticleFactory', 'TopicFactory',
    function($state, $timeout, ArticleFactory, TopicFactory) {

    return {
      scope: {
        ngModel: '=',
        visType: '=',
        visData: '=',
        visOptions: '='
      },
      transclude: true,
      template: '<ng-transclude/><div class="fullsize navpadding" id="visgraph"></div>',
      link: function($scope, $element) {
        var id = 0,
            ids = {},
            nodes = [],
            edges = [],
            articleColor = '#BBC9D2',
            topicColor = '#DBB234',
            container = $element.find("#visgraph")[0];

        $scope.articleColor = articleColor;
        $scope.topicColor = topicColor;
        $scope.nodes = new vis.DataSet();
        $scope.edges = new vis.DataSet();
        $scope.data = {
          nodes: $scope.nodes,
          edges: $scope.edges
        };

        $scope.options = {
          nodes: {
            font: { size: 11 },
            shape: 'dot',
            borderWidth: 0
          },
          layout: { randomSeed: 1 },
          physics: {
            barnesHut: {
              springConstant: 0.005,
              gravitationalConstant: -5000
            }
          }
        };

        var topicNode = function(topic) {
          return {
            id: ++id,
            title: topic.name,
            label: topic.name.ellipsize(20),
            type: 'topic',
            topic: topic.id,
            color: {
              background: topicColor,
              highlight: { background: topicColor }
            }
          };
        };

        var articleNode = function(article) {
          return {
            id: ++id,
            title: article.title,
            label: article.title.ellipsize(20),
            type: 'article',
            article: article.id,
            color: {
              background: articleColor,
              highlight: { background: articleColor }
            }
          };
        };
        
        // on node select
        var selectTimeout;
        $scope.select = function(props) {
          $timeout.cancel(selectTimeout);
          selectTimeout = $timeout(function() {
            var node = $scope.nodes.get(props.nodes[0]);
            if(node && !node.loaded) {
              if(node.type === 'article') {
                // node is article, load article to get topics
                ArticleFactory.get({id:node.article}, function(res) {
                  if(res.data && res.data.topics) {
                    var topics = res.data.topics,
                        newNodes = [],
                        newEdges = [];
                    for(var i = 0; i < topics.length; i++) {
                      var topic =  topics[i].topic;
                      if(ids.hasOwnProperty(topic.id)) {
                        if(!$scope.nodes.get(ids[topic.id]).loaded)
                          newEdges.push({from:node.id, to:ids[topic.id]});
                      } else {
                        newNodes.push(topicNode(topic));
                        newEdges.push({from:node.id, to:id});
                        ids[topic.id] = id;
                      }
                    }
                    if(newNodes.length) $scope.nodes.add(newNodes);
                    if(newEdges.length) $scope.edges.add(newEdges);
                  }
                });
              } else {
                // node is topic, load topic to get articles
                TopicFactory.get({id:node.topic}, function(res) {
                  if(res.data && res.data.articles) {
                    var articles = res.data.articles,
                        newNodes = [],
                        newEdges = [];
                    for(var i = 0; i < articles.length; i++) {
                      var article = articles[i];
                      if(ids.hasOwnProperty(article.id)) {
                        if(!$scope.nodes.get(ids[article.id]).loaded)
                          newEdges.push({from:ids[article.id], to:node.id});
                      } else {
                        newNodes.push(articleNode(article));
                        newEdges.push({from:id, to:node.id});
                        ids[article.id] = id;
                      }
                    }
                    if(newNodes.length) $scope.nodes.add(newNodes);
                    if(newEdges.length) $scope.edges.add(newEdges);
                  }
                });
              }
              node.loaded = true;
              $scope.nodes.update(node);
            }
          }, 500);
        };

        // on node open
        $scope.open = function(props) {
          $timeout.cancel(selectTimeout);
          var node = $scope.nodes.get(props.nodes[0]);
          if(node.type === 'article')
            $state.transitionTo('articles.show', {id:node.article});
          else
            $state.transitionTo('topics.show', {id:node.topic});
        };

        // watch for changes to model
        $scope.$watch('ngModel', function(newVal, oldVal) {
          if(!newVal) return;

          // root node
          if($scope.visType === 'articles')
            nodes.push(articleNode($scope.ngModel));
          else
            nodes.push(topicNode($scope.ngModel));
          ids[$scope.ngModel.id] = id;
          // add nodes and edges
          $scope.nodes.add(nodes);
          $scope.edges.add(edges);

          // create graph
          $scope.visGraph = new vis.Network(container, $scope.data, $scope.options);
          $scope.visGraph.on('selectNode', $scope.select);
          $scope.visGraph.on('doubleClick', $scope.open);
        });
      }
    };
  }]);

  app.directive('highcharts', function() {
    return {
      scope: {
        highcharts: '='
      },
      link: function($scope, $element) {
        $scope.$watch('highcharts', function(newVal) {
          if(!newVal) return;

          $element.highcharts($scope.highcharts);
        });
      }
    };
  });

  app.directive('hideLink', function() {
    return {
      scope: {
        target: '@'
      },
      replace: true,
      template: '<a class="hide-link" href="#" ng-bind="text" ng-click="change($event)"></a>',
      link: function($scope) {
        var target = $($scope.target);
        if(!target.length) return;

        var setText = function(b) {
          $scope.text = b ? 'hide' : 'show';
        };

        $scope.change = function(e) {
          e.preventDefault();
          setText(!target.is(':visible'));
          target.slideToggle();
        };

        setText(target.is(':visible'));
      }
    };
  });

})();