var componentNamespaces = ["kubernetesApp.components.dashboard"];
// APP START
// ****************************
// /www/app/assets/app.js is autogenerated. Do not modify.
// Changes should be made in /master/modules/js or /master/components/<component-name>/js
// ****************************
// -----------------------------------

var app = angular.module('kubernetesApp', [
  'ngRoute',
  'ngMaterial',
  'ngLodash',
  'door3.css',
  'kubernetesApp.config',
  'kubernetesApp.services',
  'angular.filter'
].concat(componentNamespaces));

angular.module('kubernetesApp.config', []);
angular.module('kubernetesApp.services', ['kubernetesApp.config']);

app.config([
  '$routeProvider',
  function($routeProvider) {
    $routeProvider.when("/404", {templateUrl: "views/partials/404.html"})
        // else 404
        .otherwise({redirectTo: "/404"});
  }
])
    .config([
      '$routeProvider',
      'manifestRoutes',
      function($routeProvider, manifestRoutes) {
        angular.forEach(manifestRoutes, function(r) {
          var route = {
            templateUrl: r.templateUrl
          };
          if (r.controller) {
            route.controller = r.controller;
          }
          if (r.css) {
            route.css = r.css;
          }
          $routeProvider.when(r.url, route);
        });
      }
    ]);

app.directive('includeReplace',
              function() {
                'use strict';
                return {
                  require: 'ngInclude',
                  restrict: 'A', /* optional */
                  link: function(scope, el, attrs) { el.replaceWith(el.children()); }
                };
              })
    .directive('compile',
               ["$compile", function($compile) {
                 'use strict';
                 return function(scope, element, attrs) {
                   scope.$watch(function(scope) { return scope.$eval(attrs.compile); },
                                function(value) {
                                  element.html(value);
                                  $compile(element.contents())(scope);
                                });
                 };
               }])
    .directive("kubernetesUiMenu",
               function() {
                 'use strict';
                 return {
                   templateUrl: "views/partials/kubernetes-ui-menu.tmpl.html"
                 };
               })
    .directive('menuToggle', function() {
      'use strict';
      return {
        scope: {section: '='},
        templateUrl: 'views/partials/menu-toggle.tmpl.html',
        link: function($scope, $element) {
          var controller = $element.parent().controller();

          $scope.isOpen = function() { return controller.isOpen($scope.section); };
          $scope.toggle = function() { controller.toggleOpen($scope.section); };

          var parentNode = $element[0].parentNode.parentNode.parentNode;
          if (parentNode.classList.contains('parent-list-item')) {
            var heading = parentNode.querySelector('h2');
            $element[0].firstChild.setAttribute('aria-describedby', heading.id);
          }
        }
      };
    });

app.filter('startFrom',
           function() {
             'use strict';
             return function(input, start) { return input.slice(start); };
           })
    .filter('nospace', function() {
      'use strict';
      return function(value) { return (!value) ? '' : value.replace(/ /g, ''); };
    });

app.run(['$route', angular.noop])
    .run(["lodash", function(lodash) {
      // Alias lodash
      window['_'] = lodash;
    }]);

app.service('SidebarService', [
  '$rootScope',
  function($rootScope) {
    var service = this;
    service.sidebarItems = [];

    service.clearSidebarItems = function() { service.sidebarItems = []; };

    service.renderSidebar = function() {
      var _entries = '';
      service.sidebarItems.forEach(function(entry) { _entries += entry.Html; });

      if (_entries) {
        $rootScope.sidenavLeft = '<div layout="column">' + _entries + '</div>';
      }
    };

    service.addSidebarItem = function(item) {

      service.sidebarItems.push(item);

      service.sidebarItems.sort(function(a, b) { return (a.order > b.order) ? 1 : ((b.order > a.order) ? -1 : 0); });
    };
  }
]);


app.value("tabs", [{"component":"dashboard","title":"Dashboard"}]);
app.constant("manifestRoutes", [{"description":"Dashboard visualization.","url":"/dashboard/","templateUrl":"components/dashboard/pages/home.html"},{"description":"Pods","url":"/dashboard/pods","templateUrl":"components/dashboard/views/listPods.html"},{"description":"Pod Visualizer","url":"/dashboard/visualpods","templateUrl":"components/dashboard/views/listPodsVisualizer.html"},{"description":"Services","url":"/dashboard/services","templateUrl":"components/dashboard/views/listServices.html"},{"description":"Replication Controllers","url":"/dashboard/replicationcontrollers","templateUrl":"components/dashboard/views/listReplicationControllers.html"},{"description":"Events","url":"/dashboard/events","templateUrl":"components/dashboard/views/listEvents.html"},{"description":"Minions","url":"/dashboard/minions","templateUrl":"components/dashboard/views/listMinions.html"},{"description":"Replication Controller","url":"/dashboard/replicationcontrollers/:replicationControllerId","templateUrl":"components/dashboard/views/replication.html"},{"description":"Service","url":"/dashboard/services/:serviceId","templateUrl":"components/dashboard/views/service.html"},{"description":"Explore","url":"/dashboard/groups/:grouping*?/selector/:selector*?","templateUrl":"components/dashboard/views/groups.html"},{"description":"Pod","url":"/dashboard/pods/:podId","templateUrl":"components/dashboard/views/pod.html"}]);

angular.module("kubernetesApp.config", [])

.constant("ENV", {
	"/": {
		"k8sApiServer": "/api/v1beta2",
		"k8sDataServer": "/cluster",
		"k8sDataPollMinIntervalSec": 10,
		"k8sDataPollMaxIntervalSec": 120,
		"k8sDataPollErrorThreshold": 5,
		"cAdvisorProxy": "",
		"cAdvisorPort": "4194"
	}
})

.constant("ngConstant", true)

;
/**=========================================================
 * Module: config.js
 * App routes and resources configuration
 =========================================================*/
/**=========================================================
 * Module: constants.js
 * Define constants to inject across the application
 =========================================================*/
/**=========================================================
 * Module: main.js
 * Main Application Controller
 =========================================================*/
/**=========================================================
 * Module: tabs-global.js
 * Page Controller
 =========================================================*/

app.controller('TabCtrl', [
  '$scope',
  '$location',
  'tabs',
  function($scope, $location, tabs) {
    $scope.tabs = tabs;

    $scope.switchTab = function(index) {
      var location_path = $location.path();
      var tab = tabs[index];

      if (tab) {
        var path = '/%s'.format(tab.component);
        if (location_path.indexOf(path) == -1) {
          $location.path(path);
        }
      }
    };
  }
]);

/**=========================================================
 * Module: sidebar.js
 * Wraps the sidebar and handles collapsed state
 =========================================================*/
(function() {
  "use strict";

  angular.module('kubernetesApp.services')
      .service('cAdvisorService', ["$http", "$q", "ENV", function($http, $q, ENV) {
        var _baseUrl = function(minionIp) {
          var minionPort = ENV['/']['cAdvisorPort'] || "8081";
          var proxy = ENV['/']['cAdvisorProxy'] || "/api/v1beta2/proxy/nodes/";

          return proxy + minionIp + ':' + minionPort + '/api/v1.0/';
        };

        this.getMachineInfo = getMachineInfo;

        function getMachineInfo(minionIp) {
          var fullUrl = _baseUrl(minionIp) + 'machine';
          var deferred = $q.defer();

          // hack
          $http.get(fullUrl).success(function(data) {
            deferred.resolve(data);
          }).error(function(data, status) { deferred.reject('There was an error') });
          return deferred.promise;
        }

        this.getContainerInfo = getContainerInfo;
        // containerId optional
        function getContainerInfo(minionIp, containerId) {
          containerId = (typeof containerId === "undefined") ? "/" : containerId;

          var fullUrl = _baseUrl(minionIp) + 'containers' + containerId;
          var deferred = $q.defer();

          var request = {
            "num_stats": 10,
            "num_samples": 0
          };

          $http.post(fullUrl, request)
              .success(function(data) { deferred.resolve(data); })
              .error(function() { deferred.reject('There was an error') });
          return deferred.promise;
        }

        this.getDataForMinion = function(minionIp) {
          var machineData, containerData;
          var deferred = $q.defer();

          var p = $q.all([getMachineInfo(minionIp), getContainerInfo(minionIp)])
                      .then(
                          function(dataArray) {
                            machineData = dataArray[0];
                            containerData = dataArray[1];

                            var memoryData = parseMemory(machineData, containerData);
                            var cpuData = parseCpu(machineData, containerData);
                            var fsData = parseFilesystems(machineData, containerData);
                            deferred.resolve({
                              memoryData: memoryData,
                              cpuData: cpuData,
                              filesystemData: fsData,
                              machineData: machineData,
                              containerData: containerData
                            });

                          },
                          function(errorData) { deferred.reject(errorData); });

          return deferred.promise;
        };

        // Utils to process cadvisor data
        function humanize(num, size, units) {
          var unit;
          for (unit = units.pop(); units.length && num >= size; unit = units.pop()) {
            num /= size;
          }
          return [num, unit];
        }

        // Following the IEC naming convention
        function humanizeIEC(num) {
          var ret = humanize(num, 1024, ["TiB", "GiB", "MiB", "KiB", "Bytes"]);
          return ret[0].toFixed(2) + " " + ret[1];
        }

        // Following the Metric naming convention
        function humanizeMetric(num) {
          var ret = humanize(num, 1000, ["TB", "GB", "MB", "KB", "Bytes"]);
          return ret[0].toFixed(2) + " " + ret[1];
        }

        function hasResource(stats, resource) { return stats.stats.length > 0 && stats.stats[0][resource]; }

        // Gets the length of the interval in nanoseconds.
        function getInterval(current, previous) {
          var cur = new Date(current);
          var prev = new Date(previous);

          // ms -> ns.
          return (cur.getTime() - prev.getTime()) * 1000000;
        }

        function parseCpu(machineInfo, containerInfo) {
          var cur = containerInfo.stats[containerInfo.stats.length - 1];
          var results = [];

          var cpuUsage = 0;
          if (containerInfo.spec.has_cpu && containerInfo.stats.length >= 2) {
            var prev = containerInfo.stats[containerInfo.stats.length - 2];
            var rawUsage = cur.cpu.usage.total - prev.cpu.usage.total;
            var intervalInNs = getInterval(cur.timestamp, prev.timestamp);

            // Convert to millicores and take the percentage
            cpuUsage = Math.round(((rawUsage / intervalInNs) / machineInfo.num_cores) * 100);
            if (cpuUsage > 100) {
              cpuUsage = 100;
            }
          }

          return {
            cpuPercentUsage: cpuUsage
          };
        }

        function parseFilesystems(machineInfo, containerInfo) {
          var cur = containerInfo.stats[containerInfo.stats.length - 1];
          if (!cur.filesystem) {
            return;
          }

          var filesystemData = [];
          for (var i = 0; i < cur.filesystem.length; i++) {
            var data = cur.filesystem[i];
            var totalUsage = Math.floor((data.usage * 100.0) / data.capacity);

            var f = {
              device: data.device,
              filesystemNumber: i + 1,
              usage: data.usage,
              usageDescription: humanizeMetric(data.usage),
              capacity: data.capacity,
              capacityDescription: humanizeMetric(data.capacity),
              totalUsage: Math.floor((data.usage * 100.0) / data.capacity)
            };

            filesystemData.push(f);
          }
          return filesystemData;
        }

        var oneMegabyte = 1024 * 1024;
        var oneGigabyte = 1024 * oneMegabyte;

        function parseMemory(machineInfo, containerInfo) {
          if (containerInfo.spec.has_memory && !hasResource(containerInfo, "memory")) {
            return;
          }

          // var titles = ["Time", "Total", "Hot"];
          var data = [];
          for (var i = 0; i < containerInfo.stats.length; i++) {
            var cur = containerInfo.stats[i];

            var elements = [];
            elements.push(cur.timestamp);
            elements.push(cur.memory.usage / oneMegabyte);
            elements.push(cur.memory.working_set / oneMegabyte);
            data.push(elements);
          }

          // Get the memory limit, saturate to the machine size.
          var memory_limit = machineInfo.memory_capacity;
          if (containerInfo.spec.memory.limit && (containerInfo.spec.memory.limit < memory_limit)) {
            memory_limit = containerInfo.spec.memory.limit;
          }

          var cur = containerInfo.stats[containerInfo.stats.length - 1];

          var r = {
            current: {
              memoryUsage: cur.memory.usage,
              workingMemoryUsage: cur.memory.working_set,
              memoryLimit: memory_limit,
              memoryUsageDescription: humanizeMetric(cur.memory.usage),
              workingMemoryUsageDescription: humanizeMetric(cur.memory.working_set),
              memoryLimitDescription: humanizeMetric(memory_limit)
            },
            historical: data
          };

          return r;
        }
      }]);
})();

app.provider('k8sApi',
             function() {

               var urlBase = '';

               this.setUrlBase = function(value) { urlBase = value; };

               var _get = function($http, baseUrl, query) {
                 var _fullUrl = baseUrl;
                 if (query !== undefined) {
                   _fullUrl += '/' + query;
                 }

                 return $http.get(_fullUrl);
               };

               this.$get = ["$http", "$q", function($http, $q) {
                 var api = {};

                 api.getUrlBase = function() { return urlBase; };

                 api.getPods = function(query) { return _get($http, urlBase + '/pods', query); };

                 api.getMinions = function(query) { return _get($http, urlBase + '/minions', query); };

                 api.getServices = function(query) { return _get($http, urlBase + '/services', query); };

                 api.getReplicationControllers = function(query) {
                   return _get($http, urlBase + '/replicationControllers', query)
                 };

                 api.getEvents = function(query) { return _get($http, urlBase + '/events', query); };

                 return api;
               }];
             })
    .config(["k8sApiProvider", "ENV", function(k8sApiProvider, ENV) {
      if (ENV && ENV['/'] && ENV['/']['k8sApiServer']) {
        var proxy = ENV['/']['cAdvisorProxy'] || '';
        k8sApiProvider.setUrlBase(proxy + ENV['/']['k8sApiServer']);
      }
    }]);

(function() {
  "use strict";

  var pollK8sDataServiceProvider = function PollK8sDataServiceProvider(_) {
    // A set of configuration controlling the polling behavior.
    // Their values should be configured in the application before
    // creating the service instance.

    var useSampleData = false;
    this.setUseSampleData = function(value) { useSampleData = value; };

    var sampleDataFiles = ["shared/assets/sampleData1.json"];
    this.setSampleDataFiles = function(value) { sampleDataFiles = value; };

    var dataServer = "http://localhost:5555/cluster";
    this.setDataServer = function(value) { dataServer = value; };

    var pollMinIntervalSec = 10;
    this.setPollMinIntervalSec = function(value) { pollMinIntervalSec = value; };

    var pollMaxIntervalSec = 120;
    this.setPollMaxIntervalSec = function(value) { pollMaxIntervalSec = value; };

    var pollErrorThreshold = 5;
    this.setPollErrorThreshold = function(value) { pollErrorThreshold = value; };

    this.$get = function($http, $timeout) {
      // Now the sequenceNumber will be used for debugging and verification purposes.
      var k8sdatamodel = {
        "data": undefined,
        "sequenceNumber": 0,
        "useSampleData": useSampleData
      };
      var pollingError = 0;
      var promise = undefined;

      // Implement fibonacci back off when the service is down.
      var pollInterval = pollMinIntervalSec;
      var pollIncrement = pollInterval;

      // Reset polling interval.
      var resetCounters = function() {
        pollInterval = pollMinIntervalSec;
        pollIncrement = pollInterval;
      };

      // Bump error count and polling interval.
      var bumpCounters = function() {
        // Bump the error count.
        pollingError++;

        // TODO: maybe display an error in the UI to the end user.
        if (pollingError % pollErrorThreshold === 0) {
          console.log("Error: " + pollingError + " consecutive polling errors for " + dataServer + ".");
        }

        // Bump the polling interval.
        var oldIncrement = pollIncrement;
        pollIncrement = pollInterval;
        pollInterval += oldIncrement;

        // Reset when limit reached.
        if (pollInterval > pollMaxIntervalSec) {
          resetCounters();
        }
      };

      var updateModel = function(newModel) {
        var dedupe = function(dataModel) {
          if (dataModel.resources) {
            dataModel.resources = _.uniq(dataModel.resources, function(resource) { return resource.id; });
          }

          if (dataModel.relations) {
            dataModel.relations =
                _.uniq(dataModel.relations, function(relation) { return relation.source + relation.target; });
          }
        };

        dedupe(newModel);

        var newModelString = JSON.stringify(newModel);
        var oldModelString = "";
        if (k8sdatamodel.data) {
          oldModelString = JSON.stringify(k8sdatamodel.data);
        }

        if (newModelString !== oldModelString) {
          k8sdatamodel.data = newModel;
          k8sdatamodel.sequenceNumber++;
        }

        pollingError = 0;
        resetCounters();
      };

      var nextSampleDataFile = 0;
      var getSampleDataFile = function() {
        var result = "";
        if (sampleDataFiles.length > 0) {
          result = sampleDataFiles[nextSampleDataFile % sampleDataFiles.length];
          ++nextSampleDataFile;
        }

        return result;
      };

      var pollOnce = function(scope, repeat) {
        var dataSource = (k8sdatamodel.useSampleData) ? getSampleDataFile() : dataServer;
        $.getJSON(dataSource)
            .done(function(newModel, jqxhr, textStatus) {
              if (newModel && newModel.success) {
                delete newModel.success;    // Remove success indicator.
                delete newModel.timestamp;  // Remove changing timestamp.
                updateModel(newModel);
                scope.$apply();
                promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined;
                return;
              }

              bumpCounters();
              promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined;
            })
            .fail(function(jqxhr, textStatus, error) {
              bumpCounters();
              promise = repeat ? $timeout(function() { pollOnce(scope, true); }, pollInterval * 1000) : undefined;
            });
      };

      var isPolling = function() { return promise ? true : false; };

      var start = function(scope) {
        // If polling has already started, then calling start() again would
        // just reset the counters and polling interval, but it will not
        // start a new thread polling in parallel to the existing polling
        // thread.
        resetCounters();
        if (!promise) {
          k8sdatamodel.data = undefined;
          pollOnce(scope, true);
        }
      };

      var stop = function() {
        if (promise) {
          $timeout.cancel(promise);
          promise = undefined;
        }
      };

      var refresh = function(scope) {
        stop(scope);
        resetCounters();
        k8sdatamodel.data = undefined;
        pollOnce(scope, false);
      };

      return {
        "k8sdatamodel": k8sdatamodel,
        "isPolling": isPolling,
        "refresh": refresh,
        "start": start,
        "stop": stop
      };
    };
  };

  angular.module("kubernetesApp.services")
      .provider("pollK8sDataService", ["lodash", pollK8sDataServiceProvider])
      .config(["pollK8sDataServiceProvider", "ENV", function(pollK8sDataServiceProvider, ENV) {
        if (ENV && ENV['/']) {
          if (ENV['/']['k8sDataServer']) {
            pollK8sDataServiceProvider.setDataServer(ENV['/']['k8sDataServer']);
          }
          if (ENV['/']['k8sDataPollIntervalMinSec']) {
            pollK8sDataServiceProvider.setPollIntervalSec(ENV['/']['k8sDataPollIntervalMinSec']);
          }
          if (ENV['/']['k8sDataPollIntervalMaxSec']) {
            pollK8sDataServiceProvider.setPollIntervalSec(ENV['/']['k8sDataPollIntervalMaxSec']);
          }
          if (ENV['/']['k8sDataPollErrorThreshold']) {
            pollK8sDataServiceProvider.setPollErrorThreshold(ENV['/']['k8sDataPollErrorThreshold']);
          }
        }
      }]);

}());

/**=========================================================
 * Module: toggle-state.js
 * Services to share toggle state functionality
 =========================================================*/


app.controller('cAdvisorController', [
  '$scope',
  '$routeParams',
  'k8sApi',
  'lodash',
  'cAdvisorService',
  '$q',
  '$interval',
  function($scope, $routeParams, k8sApi, lodash, cAdvisorService, $q, $interval) {
    $scope.k8sApi = k8sApi;

    $scope.activeMinionDataById = {};
    $scope.maxDataByById = {};

    $scope.getData = function() {
      $scope.loading = true;

      k8sApi.getMinions().success(angular.bind(this, function(res) {
        $scope.minions = res;
        // console.log(res);
        var promises = lodash.map(res.items, function(m) { return cAdvisorService.getDataForMinion(m.id); });

        $q.all(promises).then(
            function(dataArray) {
              lodash.each(dataArray, function(data, i) {
                var m = res.items[i];

                var maxData = maxMemCpuInfo(m.id, data.memoryData, data.cpuData, data.filesystemData);

                // console.log("maxData", maxData);

                $scope.activeMinionDataById[m.id] =
                    transformMemCpuInfo(data.memoryData, data.cpuData, data.filesystemData, maxData, m.hostIP)
              });

            },
            function(errorData) {
              // console.log("Error: " + errorData);
              $scope.loading = false;
            });

        $scope.loading = false;
      })).error(angular.bind(this, this.handleError));
    };

    function getcAdvisorDataForMinion(m) {
      var p = cAdvisorService.getDataForMinion(m.hostIP);
      return p;
    }

    function handleError(data, status, headers, config) {
      // console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    };

    // d3
    function getColorForIndex(i, percentage) {
      // var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
      // return colors[i];
      var c = "color-" + (i + 1);
      if (percentage && percentage >= 90)
        c = c + ' color-critical';
      else if (percentage && percentage >= 80)
        c = c + ' color-warning';

      return c;
    }

    function getMaxColorForIndex(i, percentage) {
      // var colors = ['red', 'blue', 'yellow', 'pink', 'purple', 'green', 'orange'];
      // return colors[i];
      var c = "color-max-" + (i + 1);
      if (percentage && percentage >= 90)
        c = c + ' color-max-critical';
      else if (percentage && percentage >= 80)
        c = c + ' color-max-warning';

      return c;
    }

    function maxMemCpuInfo(mId, mem, cpu, fsDataArray) {
      if ($scope.maxDataByById[mId] === undefined) $scope.maxDataByById[mId] = {};

      var currentMem = mem.current;
      var currentCpu = cpu;

      var items = [];

      if ($scope.maxDataByById[mId]['cpu'] === undefined ||
          $scope.maxDataByById[mId]['cpu'] < currentCpu.cpuPercentUsage) {
        // console.log("New max cpu " + mId, $scope.maxDataByById[mId].cpu, currentCpu.cpuPercentUsage);
        $scope.maxDataByById[mId]['cpu'] = currentCpu.cpuPercentUsage;
      }
      items.push({
        maxValue: $scope.maxDataByById[mId]['cpu'],
        maxTickClassNames: getColorForIndex(0, $scope.maxDataByById[mId]['cpu']),
        maxClassNames: getMaxColorForIndex(0, $scope.maxDataByById[mId]['cpu'])
      });

      var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
      if ($scope.maxDataByById[mId]['mem'] === undefined || $scope.maxDataByById[mId]['mem'] < memPercentage)
        $scope.maxDataByById[mId]['mem'] = memPercentage;
      items.push({
        maxValue: $scope.maxDataByById[mId]['mem'],
        maxTickClassNames: getColorForIndex(1, $scope.maxDataByById[mId]['mem']),
        maxClassNames: getMaxColorForIndex(1, $scope.maxDataByById[mId]['mem'])
      });

      for (var i = 0; i < fsDataArray.length; i++) {
        var f = fsDataArray[i];
        var fid = 'FS #' + f.filesystemNumber;
        if ($scope.maxDataByById[mId][fid] === undefined || $scope.maxDataByById[mId][fid] < f.totalUsage)
          $scope.maxDataByById[mId][fid] = f.totalUsage;
        items.push({
          maxValue: $scope.maxDataByById[mId][fid],
          maxTickClassNames: getColorForIndex(2 + i, $scope.maxDataByById[mId][fid]),
          maxClassNames: getMaxColorForIndex(2 + i, $scope.maxDataByById[mId][fid])
        });
      }

      // console.log("Max Data is now " + mId, $scope.maxDataByById[mId]);
      return items;
    }

    function transformMemCpuInfo(mem, cpu, fsDataArray, maxData, hostName) {
      var currentMem = mem.current;
      var currentCpu = cpu;

      var items = [];

      items.push({
        label: 'CPU',
        stats: currentCpu.cpuPercentUsage + '%',
        value: currentCpu.cpuPercentUsage,
        classNames: getColorForIndex(0, currentCpu.cpuPercentUsage),
        maxData: maxData[0],
        hostName: hostName
      });

      var memPercentage = Math.floor((currentMem.memoryUsage * 100.0) / currentMem.memoryLimit);
      items.push({
        label: 'Memory',
        stats: currentMem.memoryUsageDescription + ' / ' + currentMem.memoryLimitDescription,
        value: memPercentage,
        classNames: getColorForIndex(1, memPercentage),
        maxData: maxData[1],
        hostName: hostName
      });

      for (var i = 0; i < fsDataArray.length; i++) {
        var f = fsDataArray[i];

        items.push({
          label: 'FS #' + f.filesystemNumber,
          stats: f.usageDescription + ' / ' + f.capacityDescription,
          value: f.totalUsage,
          classNames: getColorForIndex(2 + i, f.totalUsage),
          maxData: maxData[2 + i],
          hostName: hostName

        });
      }

      var a = [];
      var segments = {
        segments: items
      };
      a.push(segments);
      return a;
    };

    // end d3
    var promise = $interval($scope.getData, 3000);

    // Cancel interval on page changes
    $scope.$on('$destroy', function() {
      if (angular.isDefined(promise)) {
        $interval.cancel(promise);
        promise = undefined;
      }
    });

    $scope.getData();

  }
]);

/**=========================================================
 * Module: Dashboard
 * Visualizer for clusters
 =========================================================*/

app.controller('DashboardCtrl', ['$scope', function($scope) {}]);

/**=========================================================
 * Module: Group
 * Visualizer for groups
 =========================================================*/

app.controller('GroupCtrl', [
  '$scope',
  '$route',
  '$interval',
  '$routeParams',
  'k8sApi',
  '$rootScope',
  '$location',
  'lodash',
  function($scope, $route, $interval, $routeParams, k8sApi, $rootScope, $location, _) {
    'use strict';
    $scope.doTheBack = function() { window.history.back(); };

    $scope.capitalize = function(s) { return _.capitalize(s); };

    $rootScope.doTheBack = $scope.doTheBack;

    $scope.resetGroupLayout = function(group) { delete group.settings; };

    $scope.handlePath = function(path) {
      var parts = path.split("/");
      // split leaves an empty string at the beginning.
      parts = parts.slice(1);

      if (parts.length === 0) {
        return;
      }
      this.handleGroups(parts.slice(1));
    };

    $scope.getState = function(obj) { return Object.keys(obj)[0]; };

    $scope.clearSelector = function(grouping) { $location.path("/dashboard/groups/" + grouping + "/selector/"); };

    $scope.changeGroupBy = function() {
      var grouping = $scope.selectedGroupBy;

      var s = _.clone($location.search());
      if ($scope.routeParams.grouping != grouping)
        $location.path("/dashboard/groups/" + grouping + "/selector/").search(s);
    };

    $scope.createBarrier = function(count, callback) {
      var barrier = count;
      var barrierFunction = angular.bind(this, function(data) {
        // JavaScript is single threaded so this is safe.
        barrier--;
        if (barrier === 0) {
          if (callback) {
            callback();
          }
        }
      });
      return barrierFunction;
    };

    $scope.handleGroups = function(parts, selector) {
      $scope.groupBy = parts;
      $scope.loading = true;
      $scope.selector = selector;
      var args = [];
      var type = "";
      if (selector && selector.length > 0) {
        $scope.selectorPieces = selector.split(",");
        var labels = [];
        var fields = [];
        for (var i = 0; i < $scope.selectorPieces.length; i++) {
          var piece = $scope.selectorPieces[i];
          if (piece[0] == '$') {
            fields.push(piece.slice(2));
          } else {
            if (piece.indexOf("type=") === 0) {
              var labelParts = piece.split("=");
              if (labelParts.length > 1) {
                type = labelParts[1];
              }
            } else {
              labels.push(piece);
            }
          }
        }
        if (labels.length > 0) {
          args.push("labels=" + encodeURI(labels.join(",")));
        }
        if (fields.length > 0) {
          args.push("fields=" + encodeURI(fields.join(",")));
        }
      }
      var query = "?" + args.join("&");
      var list = [];
      var count = type.length > 0 ? 1 : 3;
      var barrier = $scope.createBarrier(count, function() {
        $scope.groups = $scope.groupData(list, 0);
        $scope.loading = false;
        $scope.groupByOptions = buildGroupByOptions();
        $scope.selectedGroupBy = $routeParams.grouping;
      });

      if (type === "" || type == "pod") {
        k8sApi.getPods(query).success(function(data) {
          $scope.addLabel("type", "pod", data.items);
          for (var i = 0; data.items && i < data.items.length; ++i) {
            data.items[i].labels.host = data.items[i].currentState.host;
            list.push(data.items[i]);
          }
          barrier();
        }).error($scope.handleError);
      }
      if (type === "" || type == "service") {
        k8sApi.getServices(query).success(function(data) {
          $scope.addLabel("type", "service", data.items);
          for (var i = 0; data.items && i < data.items.length; ++i) {
            list.push(data.items[i]);
          }
          barrier();
        }).error($scope.handleError);
      }
      if (type === "" || type == "replicationController") {
        k8sApi.getReplicationControllers(query).success(angular.bind(this, function(data) {
          $scope.addLabel("type", "replicationController", data.items);
          for (var i = 0; data.items && i < data.items.length; ++i) {
            list.push(data.items[i]);
          }
          barrier();
        })).error($scope.handleError);
      }
    };

    $scope.addLabel = function(key, value, items) {
      if (!items) {
        return;
      }
      for (var i = 0; i < items.length; i++) {
        if (!items[i].labels) {
          items[i].labels = [];
        }
        items[i].labels[key] = value;
      }
    };

    $scope.groupData = function(items, index) {
      var result = {
        "items": {},
        "kind": "grouping"
      };
      for (var i = 0; i < items.length; i++) {
        key = items[i].labels[$scope.groupBy[index]];
        if (!key) {
          key = "";
        }
        var list = result.items[key];
        if (!list) {
          list = [];
          result.items[key] = list;
        }
        list.push(items[i]);
      }

      if (index + 1 < $scope.groupBy.length) {
        for (var key in result.items) {
          result.items[key] = $scope.groupData(result.items[key], index + 1);
        }
      }
      return result;
    };
    $scope.getGroupColor = function(type) {
      if (type === 'pod') {
        return '#6193F0';
      } else if (type === 'replicationController') {
        return '#E008FE';
      } else if (type === 'service') {
        return '#7C43FF';
      }
    };

    var groups = $routeParams.grouping;
    if (!groups) {
      groups = '';
    }

    $scope.routeParams = $routeParams;
    $scope.route = $route;

    $scope.handleGroups(groups.split('/'), $routeParams.selector);

    $scope.handleError = function(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope_.loading = false;
    };

    function getDefaultGroupByOptions() { return [{name: 'Type', value: 'type'}, {name: 'Name', value: 'name'}]; }

    function buildGroupByOptions() {
      var g = $scope.groups;
      var options = getDefaultGroupByOptions();
      var newOptions = _.map(g.items, function(vals) { return _.map(vals, function(v) { return _.keys(v.labels); }); });
      newOptions =
          _.reject(_.uniq(_.flattenDeep(newOptions)), function(o) { return o == 'name' || o == 'type' || o == ""; });
      newOptions = _.map(newOptions, function(o) {
        return {
          name: o,
          value: o
        };
      });

      options = options.concat(newOptions);
      return options;
    }

    $scope.changeFilterBy = function(selector) {
      var grouping = $scope.selectedGroupBy;

      var s = _.clone($location.search());
      if ($scope.routeParams.selector != selector)
        $location.path("/dashboard/groups/" + $scope.routeParams.grouping + "/selector/" + selector).search(s);
    };
  }
]);

/**=========================================================
 * Module: Header
 * Visualizer for clusters
 =========================================================*/

angular.module('kubernetesApp.components.dashboard', [])
    .controller('HeaderCtrl', [
      '$scope',
      '$location',
      function($scope, $location) {
        'use strict';
        $scope.$watch('Pages', function(newValue, oldValue) {
          if (typeof newValue !== 'undefined') {
            $location.path(newValue);
          }
        });

        $scope.subPages = [
          {category: 'dashboard', name: 'Explore', value: '/dashboard/groups/type/selector/'},
          {category: 'dashboard', name: 'Pods', value: '/dashboard/pods'},
          {category: 'dashboard', name: 'Minions', value: '/dashboard/minions'},
          {category: 'dashboard', name: 'Replication Controllers', value: '/dashboard/replicationcontrollers'},
          {category: 'dashboard', name: 'Services', value: '/dashboard/services'},
          {category: 'dashboard', name: 'Events', value: '/dashboard/events'}
        ];
      }
    ]);

/**=========================================================
 * Module: List Events
 * Visualizer list events
 =========================================================*/

app.controller('ListEventsCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  '$location',
  '$filter',
  function($scope, $routeParams, k8sApi, $location, $filter) {
    'use strict';
    $scope.getData = getData;
    $scope.loading = true;
    $scope.k8sApi = k8sApi;
    $scope.pods = null;
    $scope.groupedPods = null;
    $scope.serverView = false;

    $scope.headers = [
      {name: 'Time', field: 'time'},
      {name: 'From', field: 'from'},
      {name: 'Sub Object Path', field: 'subobject'},
      {name: 'Reason', field: 'reason'},
      {name: 'Message', field: 'message'}
    ];

    $scope.custom = {
      time: '',
      from: 'grey',
      subobject: 'grey',
      reason: 'grey',
      message: 'grey'
    };
    $scope.sortable = ['time', 'from', 'subobject'];
    $scope.thumbs = 'thumb';
    $scope.count = 10;

    $scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };

    $scope.moreClick = function(d, e) {
      $location.path('/dashboard/pods/' + d.id);
      e.stopPropagation();
    };

    function handleError(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    }

    $scope.content = [];

    function getData(dataId) {
      $scope.loading = true;
      k8sApi.getEvents().success(function(data) {
        $scope.loading = false;

        var _fixComma = function(str) {
          if (str.substring(0, 1) == ',') {
            return str.substring(1);
          } else {
            return str;
          }
        };

        data.items.forEach(function(event) {

          $scope.content.push({
            time: $filter('date')(event.timestamp, 'medium'),
            from: event.source,
            subobject: event.involvedObject.fieldPath,
            reason: event.reason,
            message: event.message
          });

        });

      }).error($scope.handleError);
    }

    getData($routeParams.serviceId);

  }
]);

/**=========================================================
 * Module: Minions
 * Visualizer for minions
 =========================================================*/

app.controller('ListMinionsCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  '$location',
  function($scope, $routeParams, k8sApi, $location) {
    'use strict';
    $scope.getData = getData;
    $scope.loading = true;
    $scope.k8sApi = k8sApi;
    $scope.pods = null;
    $scope.groupedPods = null;
    $scope.serverView = false;

    $scope.headers = [{name: 'Name', field: 'name'}, {name: 'IP', field: 'ip'}, {name: 'Status', field: 'status'}];

    $scope.custom = {
      name: '',
      status: 'grey',
      ip: 'grey'
    };
    $scope.sortable = ['name', 'status', 'ip'];
    $scope.thumbs = 'thumb';
    $scope.count = 10;

    $scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };

    $scope.moreClick = function(d, e) {
      $location.path('/dashboard/pods/' + d.id);
      e.stopPropagation();
    };

    function handleError(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    }

    $scope.content = [];

    function getData(dataId) {
      $scope.loading = true;
      k8sApi.getMinions().success(function(data) {
        $scope.loading = false;

        var _fixComma = function(str) {
          if (str.substring(0, 1) == ',') {
            return str.substring(1);
          } else {
            return str;
          }
        };

        data.items.forEach(function(minion) {
          var _kind = '';

          if (minion.status.conditions) {
            Object.keys(minion.status.conditions)
                .forEach(function(key) { _kind += minion.status.conditions[key].kind; });
          }

          $scope.content.push({name: minion.id, ip: minion.hostIP, status: _kind});

        });

      }).error($scope.handleError);
    }

    getData($routeParams.serviceId);

  }
]);



app.controller('ListPodsCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  'lodash',
  '$location',
  function($scope, $routeParams, k8sApi, lodash, $location) {
    var _ = lodash;
    $scope.getData = getData;
    $scope.loading = true;
    $scope.k8sApi = k8sApi;
    $scope.pods = null;
    $scope.groupedPods = null;
    $scope.serverView = false;

    $scope.headers = [
      {name: '', field: 'thumb'},
      {name: 'Pod', field: 'pod'},
      {name: 'IP', field: 'ip'},
      {name: 'Status', field: 'status'},
      {name: 'Containers', field: 'containers'},
      {name: 'Images', field: 'images'},
      {name: 'Host', field: 'host'},
      {name: 'Labels', field: 'labels'}
    ];

    $scope.custom = {
      pod: '',
      ip: 'grey',
      containers: 'grey',
      images: 'grey',
      host: 'grey',
      labels: 'grey',
      status: 'grey'
    };
    $scope.sortable = ['pod', 'ip', 'status'];
    $scope.thumbs = 'thumb';
    $scope.count = 10;

    $scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };

    $scope.moreClick = function(d, e) {
      $location.path('/dashboard/pods/' + d.id);
      e.stopPropagation();
    };

    var orderedPodNames = [];

    function handleError(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    };

    function getPodName(pod) { return _.has(pod.labels, 'name') ? pod.labels.name : pod.id; }

    $scope.content = [];

    function getData(dataId) {
      $scope.loading = true;
      k8sApi.getPods().success(angular.bind(this, function(data) {
        $scope.loading = false;

        var _fixComma = function(str) {
          if (str.substring(0, 1) == ',') {
            return str.substring(1);
          } else {
            return str;
          }
        };

        data.items.forEach(function(pod) {
          var _containers = '', _images = '', _labels = '', _uses = '';

          if (pod.desiredState.manifest) {
            Object.keys(pod.desiredState.manifest.containers)
                .forEach(function(key) {
                  _containers += ', ' + pod.desiredState.manifest.containers[key].name;
                  _images += ', ' + pod.desiredState.manifest.containers[key].image;
                });
          }

          Object.keys(pod.labels)
              .forEach(function(key) {
                if (key == 'name') {
                  _labels += ', ' + pod.labels[key];
                }
                if (key == 'uses') {
                  _uses += ', ' + pod.labels[key];
                }
              });

          $scope.content.push({
            thumb: '"assets/img/kubernetes.svg"',
            pod: pod.id,
            ip: pod.currentState.podIP,
            containers: _fixComma(_containers),
            images: _fixComma(_images),
            host: pod.currentState.host,
            labels: _fixComma(_labels) + ':' + _fixComma(_uses),
            status: pod.currentState.status
          });

        });

      })).error(angular.bind(this, handleError));
    };

    $scope.getPodRestarts = function(pod) {
      var r = null;
      var container = _.first(pod.desiredState.manifest.containers);
      if (container) r = pod.currentState.info[container.name].restartCount;
      return r;
    };

    $scope.otherLabels = function(labels) { return _.omit(labels, 'name') };

    $scope.podStatusClass = function(pod) {

      var s = pod.currentState.status.toLowerCase();

      if (s == 'running' || s == 'succeeded')
        return null;
      else
        return "status-" + s;
    };

    $scope.podIndexFromName = function(pod) {
      var name = getPodName(pod);
      return _.indexOf(orderedPodNames, name) + 1;
    };

    getData($routeParams.serviceId);

  }
]);

/**=========================================================
 * Module: Replication Controllers
 * Visualizer for replication controllers
 =========================================================*/

app.controller('ListReplicationControllersCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  '$location',
  function($scope, $routeParams, k8sApi, $location) {
    'use strict';
    $scope.getData = getData;
    $scope.loading = true;
    $scope.k8sApi = k8sApi;
    $scope.pods = null;
    $scope.groupedPods = null;
    $scope.serverView = false;

    $scope.headers = [
      {name: 'Controller', field: 'controller'},
      {name: 'Containers', field: 'containers'},
      {name: 'Images', field: 'images'},
      {name: 'Selector', field: 'selector'},
      {name: 'Replicas', field: 'replicas'}
    ];

    $scope.custom = {
      controller: '',
      containers: 'grey',
      images: 'grey',
      selector: 'grey',
      replicas: 'grey'
    };
    $scope.sortable = ['controller', 'containers', 'images'];
    $scope.thumbs = 'thumb';
    $scope.count = 10;

    $scope.go = function(d) { $location.path('/dashboard/pods/' + d.id); };

    $scope.moreClick = function(d, e) {
      $location.path('/dashboard/pods/' + d.id);
      e.stopPropagation();
    };

    function handleError(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    }

    $scope.content = [];

    function getData(dataId) {
      $scope.loading = true;
      k8sApi.getReplicationControllers().success(function(data) {
        $scope.loading = false;

        var _fixComma = function(str) {
          if (str.substring(0, 1) == ',') {
            return str.substring(1);
          } else {
            return str;
          }
        };

        data.items.forEach(function(replicationController) {

          var _name = '', _image = '';

          if (replicationController.desiredState.podTemplate.desiredState.manifest.containers) {
            Object.keys(replicationController.desiredState.podTemplate.desiredState.manifest.containers)
                .forEach(function(key) {
                  _name += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].name;
                  _image += replicationController.desiredState.podTemplate.desiredState.manifest.containers[key].image;
                });
          }

          var _name_selector = '';

          if (replicationController.desiredState.replicaSelector) {
            Object.keys(replicationController.desiredState.replicaSelector)
                .forEach(function(key) { _name_selector += replicationController.desiredState.replicaSelector[key]; });
          }

          $scope.content.push({
            controller: replicationController.id,
            containers: _name,
            images: _image,
            selector: _name_selector,
            replicas: replicationController.currentState.replicas
          });

        });

      }).error($scope.handleError);
    }

    getData($routeParams.serviceId);

  }
]);

/**=========================================================
 * Module: Services
 * Visualizer for services
 =========================================================*/

app.controller('ListServicesCtrl', [
  '$scope',
  '$interval',
  '$routeParams',
  'k8sApi',
  '$rootScope',
  function($scope, $interval, $routeParams, k8sApi, $rootScope) {
    'use strict';
    $scope.doTheBack = function() { window.history.back(); };

    $scope.headers = [
      {name: 'Name', field: 'name'},
      {name: 'Labels', field: 'labels'},
      {name: 'Selector', field: 'selector'},
      {name: 'IP', field: 'ip'},
      {name: 'Port', field: 'port'}
    ];

    $scope.custom = {
      name: '',
      ip: 'grey',
      selector: 'grey',
      port: 'grey',
      labels: 'grey'
    };
    $scope.sortable = ['name', 'ip', 'port'];
    $scope.count = 10;

    $scope.content = [];

    $rootScope.doTheBack = $scope.doTheBack;

    $scope.handleError = function(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope_.loading = false;
    };

    $scope.getData = function(dataId) {
      $scope.loading = true;
      k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
        $scope.services = data;
        $scope.loading = false;

        var _fixComma = function(str) {
          if (str.substring(0, 1) == ',') {
            return str.substring(1);
          } else {
            return str;
          }
        };

        var addLabel = function(str, label) {
          if (str) {
            str = label + str;
          }
          return str;
        };

        if (data.items.constructor === Array) {
          data.items.forEach(function(service) {

            var _name = '', _uses = '', _component = '', _provider = '';

            if (service.labels !== null && typeof service.labels === 'object') {
              Object.keys(service.labels)
                  .forEach(function(key) {
                    if (key == 'name') {
                      _name += ',' + service.labels[key];
                    }
                    if (key == 'component') {
                      _component += ',' + service.labels[key];
                    }
                    if (key == 'provider') {
                      _provider += ',' + service.labels[key];
                    }
                  });
            }

            var _selectors = '';

            if (service.selector !== null && typeof service.selector === 'object') {
              Object.keys(service.selector)
                  .forEach(function(key) {
                    if (key == 'name') {
                      _selectors += ',' + service.selector[key];
                    }
                  });
            }

            $scope.content.push({
              name: service.id,
              ip: service.portalIP,
              port: service.port,
              selector: addLabel(_fixComma(_selectors), 'name='),
              labels: addLabel(_fixComma(_name), 'name=') + ' ' + addLabel(_fixComma(_component), 'component=') + ' ' +
                          addLabel(_fixComma(_provider), 'provider=')
            });
          });
        }
      })).error($scope.handleError);
    };

    $scope.getData($routeParams.serviceId);
  }
]);

/**=========================================================
 * Module: Pods
 * Visualizer for pods
 =========================================================*/

app.controller('PodCtrl', [
  '$scope',
  '$interval',
  '$routeParams',
  'k8sApi',
  '$rootScope',
  function($scope, $interval, $routeParams, k8sApi, $rootScope) {
    'use strict';
    $scope.doTheBack = function() { window.history.back(); };

    $rootScope.doTheBack = $scope.doTheBack;

    $scope.handleError = function(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope_.loading = false;
    };

    $scope.handlePod = function(podId) {
      $scope.loading = true;
      k8sApi.getPods(podId).success(angular.bind(this, function(data) {
        $scope.pod = data;
        $scope.loading = false;
      })).error($scope.handleError);
    };

    $scope.handlePod($routeParams.podId);
  }
]);

/**=========================================================
 * Module: Replication
 * Visualizer for replication controllers
 =========================================================*/

function ReplicationController() {
}

ReplicationController.prototype.getData = function(dataId) {
  this.scope.loading = true;
  this.k8sApi.getReplicationControllers(dataId).success(angular.bind(this, function(data) {
    this.scope.replicationController = data;
    this.scope.loading = false;
  })).error(angular.bind(this, this.handleError));
};

ReplicationController.prototype.handleError = function(data, status, headers, config) {
  console.log("Error (" + status + "): " + data);
  this.scope.loading = false;
};

app.controller('ReplicationControllerCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  function($scope, $routeParams, k8sApi) {
    $scope.controller = new ReplicationController();
    $scope.controller.k8sApi = k8sApi;
    $scope.controller.scope = $scope;
    $scope.controller.getData($routeParams.replicationControllerId);
  }
]);

/**=========================================================
 * Module: Services
 * Visualizer for services
 =========================================================*/

function ServiceController() {
}

ServiceController.prototype.getData = function(dataId) {
  this.scope.loading = true;
  this.k8sApi.getServices(dataId).success(angular.bind(this, function(data) {
    this.scope.service = data;
    this.scope.loading = false;
  })).error(angular.bind(this, this.handleError));
};

ServiceController.prototype.handleError = function(data, status, headers, config) {
  console.log("Error (" + status + "): " + data);
  this.scope.loading = false;
};

app.controller('ServiceCtrl', [
  '$scope',
  '$routeParams',
  'k8sApi',
  '$location',
  function($scope, $routeParams, k8sApi, $location) {
    $scope.controller = new ServiceController();
    $scope.controller.k8sApi = k8sApi;
    $scope.controller.scope = $scope;
    $scope.controller.getData($routeParams.serviceId);

    $scope.go = function(d) { $location.path('/dashboard/services/' + d.id); }

                $scope.moreClick = function(d, e) {
      $location.path('/dashboard/services/' + d.id);
      e.stopPropagation();
    }
  }
]);

(function() {
  'use strict';

  angular.module('kubernetesApp.components.dashboard')
      .directive('d3MinionBarGauge', [
        'd3DashboardService',
        function(d3DashboardService) {

          return {
            restrict: 'E',
            scope: {
              data: '=',
              thickness: '@',
              graphWidth: '@',
              graphHeight: '@'

            },
            link: function(scope, element, attrs) {

              var draw = function(d3) {
                var svg = d3.select("svg.chart");
                var legendSvg = d3.select("svg.legend");
                window.onresize = function() { return scope.$apply(); };

                scope.$watch(function() { return angular.element(window)[0].innerWidth; },
                             function() { return scope.render(scope.data); });

                scope.$watch('data', function(newVals, oldVals) {
                  return initOrUpdate(newVals, oldVals);

                }, true);

                function initOrUpdate(newVals, oldVals) {
                  if (oldVals === null || oldVals === undefined) {
                    return scope.render(newVals);
                  } else {
                    return update(oldVals, newVals);
                  }
                }

                var textOffset = 10;
                var el = null;
                var radius = 100;
                var oldData = [];

                function init(options) {
                  var clone = options.data;
                  var preparedData = setData(clone);
                  setup(preparedData, options.width, options.height);
                }

                function setup(data, w, h) {
                  svg = d3.select(element[0]).append("svg").attr("width", "100%");

                  legendSvg = d3.select(element[0]).append("svg").attr("width", "100%");

                  var chart = svg.attr("class", "chart")
                                  .attr("width", w)
                                  .attr("height", h - 25)
                                  .append("svg:g")
                                  .attr("class", "concentricchart")
                                  .attr("transform", "translate(" + ((w / 2)) + "," + h / 4 + ")");

                  var legend = legendSvg.attr("class", "legend").attr("width", w);

                  radius = Math.min(w, h) / 2;

                  var hostName = legendSvg.append("text")
                                     .attr("class", "hostName")
                                     .attr("transform", "translate(" + ((w - 120) / 2) + "," + 15 + ")");

                  var label_legend_area = legendSvg.append("svg:g")
                                              .attr("class", "label_legend_area")
                                              .attr("transform", "translate(" + ((w - 185) / 2) + "," + 35 + ")");

                  var legend_group = label_legend_area.append("svg:g").attr("class", "legend_group");

                  var label_group = label_legend_area.append("svg:g")
                                        .attr("class", "label_group")
                                        .attr("transform", "translate(" + 25 + "," + 11 + ")");

                  var stats_group = label_legend_area.append("svg:g")
                                        .attr("class", "stats_group")
                                        .attr("transform", "translate(" + 85 + "," + 11 + ")");

                  var path_group = chart.append("svg:g")
                                       .attr("class", "path_group")
                                       .attr("transform", "translate(0," + (h / 4) + ")");
                  var value_group = chart.append("svg:g")
                                        .attr("class", "value_group")
                                        .attr("transform", "translate(" + -(w * 0.205) + "," + -(h * 0.10) + ")");
                  generateArcs(chart, data);
                }

                function update(_oldData, _newData) {
                  if (_newData === undefined || _newData === null) {
                    return;
                  }

                  var clone = jQuery.extend(true, {}, _newData);
                  var cloneOld = jQuery.extend(true, {}, _oldData);
                  var preparedData = setData(clone);
                  oldData = setData(cloneOld);
                  animate(preparedData);
                }

                function animate(data) { generateArcs(null, data); }

                function setData(data) {
                  var diameter = 2 * Math.PI * radius;
                  var localData = [];

                  $.each(data[0].segments, function(ri, value) {

                    function calcAngles(v) {
                      var segmentValueSum = 200;
                      if (v > segmentValueSum) {
                        v = segmentValueSum;
                      }

                      var segmentValue = v;
                      var fraction = segmentValue / segmentValueSum;
                      var arcBatchLength = fraction * 4 * Math.PI;
                      var arcPartition = arcBatchLength;
                      var startAngle = Math.PI * 2;
                      var endAngle = startAngle + arcPartition;

                      return {
                        startAngle: startAngle,
                        endAngle: endAngle
                      };
                    }

                    var valueData = calcAngles(value.value);
                    data[0].segments[ri].startAngle = valueData.startAngle;
                    data[0].segments[ri].endAngle = valueData.endAngle;

                    var maxData = value.maxData;
                    var maxTickData = calcAngles(maxData.maxValue + 0.2);
                    data[0].segments[ri].maxTickStartAngle = maxTickData.startAngle;
                    data[0].segments[ri].maxTickEndAngle = maxTickData.endAngle;

                    var maxArcData = calcAngles(maxData.maxValue);
                    data[0].segments[ri].maxArcStartAngle = maxArcData.startAngle;
                    data[0].segments[ri].maxArcEndAngle = maxArcData.endAngle;

                    data[0].segments[ri].index = ri;
                  });
                  localData.push(data[0].segments);
                  return localData[0];
                }

                function generateArcs(_svg, data) {
                  var chart = svg;
                  var transitionTime = 750;
                  $.each(data, function(index, value) {
                    if (oldData[index] !== undefined) {
                      data[index].previousEndAngle = oldData[index].endAngle;
                    } else {
                      data[index].previousEndAngle = 0;
                    }
                  });
                  var thickness = parseInt(scope.thickness, 10);
                  var ir = (parseInt(scope.graphWidth, 10) / 3);
                  var path_group = svg.select('.path_group');
                  var arc_group = path_group.selectAll(".arc_group").data(data);
                  var arcEnter = arc_group.enter().append("g").attr("class", "arc_group");

                  arcEnter.append("path").attr("class", "bg-circle").attr("d", getBackgroundArc(thickness, ir));

                  arcEnter.append("path")
                      .attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; });

                  arcEnter.append("path")
                      .attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; });

                  arcEnter.append("path").attr("class", function(d, i) { return 'value_arc ' + d.classNames; });

                  var max_tick_arc = arc_group.select(".max_tick_arc");

                  max_tick_arc.transition()
                      .attr("class", function(d, i) { return 'max_tick_arc ' + d.maxData.maxTickClassNames; })
                      .attr("d", function(d) {
                        var arc = maxArc(thickness, ir);
                        arc.startAngle(d.maxTickStartAngle);
                        arc.endAngle(d.maxTickEndAngle);
                        return arc(d);
                      });

                  var max_bg_arc = arc_group.select(".max_bg_arc");

                  max_bg_arc.transition()
                      .attr("class", function(d, i) { return 'max_bg_arc ' + d.maxData.maxClassNames; })
                      .attr("d", function(d) {
                        var arc = maxArc(thickness, ir);
                        arc.startAngle(d.maxArcStartAngle);
                        arc.endAngle(d.maxArcEndAngle);
                        return arc(d);
                      });

                  var value_arc = arc_group.select(".value_arc");

                  value_arc.transition().ease("exp").attr("class", function(d, i) {
                    return 'value_arc ' + d.classNames;
                  }).duration(transitionTime).attrTween("d", function(d) { return arcTween(d, thickness, ir); });

                  arc_group.exit()
                      .select(".value_arc")
                      .transition()
                      .ease("exp")
                      .duration(transitionTime)
                      .attrTween("d", function(d) { return arcTween(d, thickness, ir); })
                      .remove();

                  drawLabels(chart, data, ir, thickness);
                  buildLegend(chart, data);
                }

                function arcTween(b, thickness, ir) {
                  var prev = JSON.parse(JSON.stringify(b));
                  prev.endAngle = b.previousEndAngle;
                  var i = d3.interpolate(prev, b);
                  return function(t) { return getArc(thickness, ir)(i(t)); };
                }

                function maxArc(thickness, ir) {
                  var arc = d3.svg.arc().innerRadius(function(d) {
                    return getRadiusRing(ir, d.index);
                  }).outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); });
                  return arc;
                }

                function drawLabels(chart, data, ir, thickness) {
                  svg.select('.value_group').selectAll("*").remove();
                  var counts = data.length;
                  var value_group = chart.select('.value_group');
                  var valueLabels = value_group.selectAll("text.value").data(data);
                  valueLabels.enter()
                      .append("svg:text")
                      .attr("class", "value")
                      .attr(
                           "transform", function(d) { return "translate(" + (getRadiusRing(ir, counts - 1)) + ", 0)"; })
                      .attr("dx", function(d, i) { return 0; })
                      .attr("dy", function(d, i) { return (thickness + 3) * i; })
                      .attr("text-anchor", function(d) { return "start"; })
                      .text(function(d) { return d.value; });
                  valueLabels.transition().duration(300).attrTween(
                      "d", function(d) { return arcTween(d, thickness, ir); });
                  valueLabels.exit().remove();
                }

                function buildLegend(chart, data) {
                  var svg = legendSvg;
                  svg.select('.label_group').selectAll("*").remove();
                  svg.select('.legend_group').selectAll("*").remove();
                  svg.select('.stats_group').selectAll("*").remove();

                  var host_name = svg.select('.hostName');
                  var label_group = svg.select('.label_group');
                  var stats_group = svg.select('.stats_group');

                  host_name.text(data[0].hostName);

                  host_name = svg.selectAll("text.hostName").data(data);

                  host_name.attr("text-anchor", function(d) { return "start"; })
                      .text(function(d) { return d.hostName; });
                  host_name.exit().remove();

                  var labels = label_group.selectAll("text.labels").data(data);
                  labels.enter()
                      .append("svg:text")
                      .attr("class", "labels")
                      .attr("dy", function(d, i) { return 19 * i; })
                      .attr("text-anchor", function(d) { return "start"; })
                      .text(function(d) { return d.label; });
                  labels.exit().remove();

                  var stats = stats_group.selectAll("text.stats").data(data);
                  stats.enter()
                      .append("svg:text")
                      .attr("class", "stats")
                      .attr("dy", function(d, i) { return 19 * i; })
                      .attr("text-anchor", function(d) { return "start"; })
                      .text(function(d) { return d.stats; });
                  stats.exit().remove();

                  var legend_group = svg.select('.legend_group');
                  var legend = legend_group.selectAll("rect").data(data);
                  legend.enter()
                      .append("svg:rect")
                      .attr("x", 2)
                      .attr("y", function(d, i) { return 19 * i; })
                      .attr("width", 13)
                      .attr("height", 13)
                      .attr("class", function(d, i) { return "rect " + d.classNames; });

                  legend.exit().remove();
                }

                function getRadiusRing(ir, i) { return ir - (i * 20); }

                function getArc(thickness, ir) {
                  var arc = d3.svg.arc()
                                .innerRadius(function(d) { return getRadiusRing(ir, d.index); })
                                .outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
                                .startAngle(function(d, i) { return d.startAngle; })
                                .endAngle(function(d, i) { return d.endAngle; });
                  return arc;
                }

                function getBackgroundArc(thickness, ir) {
                  var arc = d3.svg.arc()
                                .innerRadius(function(d) { return getRadiusRing(ir, d.index); })
                                .outerRadius(function(d) { return getRadiusRing(ir + thickness, d.index); })
                                .startAngle(0)
                                .endAngle(function() { return 2 * Math.PI; });
                  return arc;
                }

                scope.render = function(data) {
                  if (data === undefined || data === null) {
                    return;
                  }

                  svg.selectAll("*").remove();

                  var graph = $(element[0]);
                  var w = scope.graphWidth;
                  var h = scope.graphHeight;

                  var options = {
                    data: data,
                    width: w,
                    height: h
                  };

                  init(options);
                };
              };
              d3DashboardService.d3().then(draw);
            }
          };
        }
      ]);
}());

(function() {
  'use strict';

  angular.module('kubernetesApp.components.dashboard')
      .directive(
           'dashboardHeader',
           function() {
             'use strict';
             return {
               restrict: 'A',
               replace: true,
               scope: {user: '='},
               templateUrl: "components/dashboard/pages/header.html",
               controller: [
                 '$scope',
                 '$filter',
                 '$location',
                 '$rootScope',
                 function($scope, $filter, $location, $rootScope) {
                   $scope.$watch('page', function(newValue, oldValue) {
                     if (typeof newValue !== 'undefined') {
                       $location.path(newValue);
                     }
                   });

                   $scope.subpages = [
                     {
                       category: 'dashboard',
                       name: 'Groups',
                       value: '/dashboard/groups/type/selector/',
                       id: 'groupsView'
                     },
                     {category: 'dashboard', name: 'Pods', value: '/dashboard/pods', id: 'podsView'},
                     {category: 'dashboard', name: 'Minions', value: '/dashboard/minions', id: 'minionsView'},
                     {
                       category: 'dashboard',
                       name: 'Replication Controllers',
                       value: '/dashboard/replicationcontrollers',
                       id: 'rcView'
                     },
                     {category: 'dashboard', name: 'Services', value: '/dashboard/services', id: 'servicesView'},
                     {category: 'dashboard', name: 'Events', value: '/dashboard/events', id: 'eventsView'},
                   ];
                 }
               ]
             };
           })
      .directive('dashboardFooter',
                 function() {
                   'use strict';
                   return {
                     restrict: 'A',
                     replace: true,
                     templateUrl: "components/dashboard/pages/footer.html",
                     controller: ['$scope', '$filter', function($scope, $filter) {}]
                   };
                 })
      .directive('mdTable', function() {
        'use strict';
        return {
          restrict: 'E',
          scope: {
            headers: '=',
            content: '=',
            sortable: '=',
            filters: '=',
            customClass: '=customClass',
            thumbs: '=',
            count: '='
          },
          controller: ["$scope", "$filter", "$window", "$location", function($scope, $filter, $window, $location) {
            var orderBy = $filter('orderBy');
            $scope.currentPage = 0;
            $scope.nbOfPages = function() { return Math.ceil($scope.content.length / $scope.count); };
            $scope.handleSort = function(field) {
              if ($scope.sortable.indexOf(field) > -1) {
                return true;
              } else {
                return false;
              }
            };
            $scope.go = function(d) {
              if (d.pod) {
                $location.path('/dashboard/pods/' + d.pod);
              } else if (d.name) {
                $location.path('/dashboard/services/' + d.name);
              }
            };
            $scope.order = function(predicate, reverse) {
              $scope.content = orderBy($scope.content, predicate, reverse);
              $scope.predicate = predicate;
            };
            $scope.order($scope.sortable[0], false);
            $scope.getNumber = function(num) { return new Array(num); };
            $scope.goToPage = function(page) { $scope.currentPage = page; };
          }],
          templateUrl: 'views/partials/md-table.tmpl.html'
        };
      });

}());

angular.module('kubernetesApp.components.dashboard')
    .factory('d3DashboardService', [
      '$document',
      '$q',
      '$rootScope',
      function($document, $q, $rootScope) {
        var d = $q.defer();
        function onScriptLoad() {
          // Load client in the browser
          $rootScope.$apply(function() { d.resolve(window.d3); });
        }
        // Create a script tag with d3 as the source
        // and call our onScriptLoad callback when it
        // has been loaded
        var scriptTag = $document[0].createElement('script');
        scriptTag.type = 'text/javascript';
        scriptTag.async = true;
        scriptTag.src = 'vendor/d3/d3.min.js';
        scriptTag.onreadystatechange = function() {
          if (this.readyState == 'complete') onScriptLoad();
        };
        scriptTag.onload = onScriptLoad;

        var s = $document[0].getElementsByTagName('body')[0];
        s.appendChild(scriptTag);

        return {
          d3: function() { return d.promise; }
        };
      }
    ]);

(function() {
  'use strict';

  angular.module('pods', []).service('podService', PodDataService);

  /**
   * Pod DataService
   * Mock async data service.
   *
   * @returns {{loadAll: Function}}
   * @constructor
   */
  function PodDataService($q) {
    var pods = {
      "kind": "PodList",
      "creationTimestamp": null,
      "selfLink": "/api/v1beta1/pods",
      "resourceVersion": 166552,
      "apiVersion": "v1beta1",
      "items": [{
        "id": "hello",
        "uid": "0fe3644e-ab53-11e4-8ae8-061695c59fcf",
        "creationTimestamp": "2015-02-03T03:16:36Z",
        "selfLink": "/api/v1beta1/pods/hello?namespace=default",
        "resourceVersion": 466,
        "namespace": "default",
        "labels": {"environment": "testing", "name": "hello"},
        "desiredState": {
          "manifest": {
            "version": "v1beta2",
            "id": "",
            "volumes": null,
            "containers": [{
              "name": "hello",
              "image": "quay.io/kelseyhightower/hello",
              "ports": [{"hostPort": 80, "containerPort": 80, "protocol": "TCP"}],
              "imagePullPolicy": "PullIfNotPresent"
            }],
            "restartPolicy": {"always": {}},
            "dnsPolicy": "ClusterFirst"
          }
        },
        "currentState": {
          "manifest": {"version": "", "id": "", "volumes": null, "containers": null, "restartPolicy": {}},
          "status": "Running",
          "host": "172.31.12.204",
          "podIP": "10.244.73.2",
          "info": {
            "hello": {
              "state": {"running": {"startedAt": "2015-02-03T03:16:51Z"}},
              "restartCount": 0,
              "image": "quay.io/kelseyhightower/hello",
              "containerID": "docker://96ade8ff30a44c4489969eaf343a7899317671b07a9766ecd0963e9b41501256"
            },
            "net": {
              "state": {"running": {"startedAt": "2015-02-03T03:16:41Z"}},
              "restartCount": 0,
              "podIP": "10.244.73.2",
              "image": "kubernetes/pause:latest",
              "containerID": "docker://93d32603cafbff7165dadb1d4527899c24246bca2f5e6770b8297fd3721b272c"
            }
          }
        }
      }]
    };

    // Uses promises
    return {
      loadAll: function() {
        // Simulate async call
        return $q.when(pods);
      }
    };
  }
  PodDataService.$inject = ["$q"];

})();

(function() {
  'use strict';

  angular.module('replicationControllers', [])
      .service('replicationControllerService', ReplicationControllerDataService);

  /**
   * Replication Controller DataService
   * Mock async data service.
   *
   * @returns {{loadAll: Function}}
   * @constructor
   */
  function ReplicationControllerDataService($q) {
    var replicationControllers = {
      "kind": "ReplicationControllerList",
      "creationTimestamp": null,
      "selfLink": "/api/v1beta1/replicationControllers",
      "resourceVersion": 166552,
      "apiVersion": "v1beta1",
      "items": []
    };

    // Uses promises
    return {
      loadAll: function() {
        // Simulate async call
        return $q.when(replicationControllers);
      }
    };
  }
  ReplicationControllerDataService.$inject = ["$q"];

})();

(function() {
  'use strict';

  angular.module('services', []).service('serviceService', ServiceDataService);

  /**
   * Service DataService
   * Mock async data service.
   *
   * @returns {{loadAll: Function}}
   * @constructor
   */
  function ServiceDataService($q) {
    var services = {
      "kind": "ServiceList",
      "creationTimestamp": null,
      "selfLink": "/api/v1beta1/services",
      "resourceVersion": 166552,
      "apiVersion": "v1beta1",
      "items": [
        {
          "id": "kubernetes",
          "uid": "626dd08d-ab51-11e4-8ae8-061695c59fcf",
          "creationTimestamp": "2015-02-03T03:04:36Z",
          "selfLink": "/api/v1beta1/services/kubernetes?namespace=default",
          "resourceVersion": 11,
          "namespace": "default",
          "port": 443,
          "protocol": "TCP",
          "labels": {"component": "apiserver", "provider": "kubernetes"},
          "selector": null,
          "containerPort": 0,
          "portalIP": "10.244.66.215",
          "sessionAffinity": "None"
        },
        {
          "id": "kubernetes-ro",
          "uid": "626f9584-ab51-11e4-8ae8-061695c59fcf",
          "creationTimestamp": "2015-02-03T03:04:36Z",
          "selfLink": "/api/v1beta1/services/kubernetes-ro?namespace=default",
          "resourceVersion": 12,
          "namespace": "default",
          "port": 80,
          "protocol": "TCP",
          "labels": {"component": "apiserver", "provider": "kubernetes"},
          "selector": null,
          "containerPort": 0,
          "portalIP": "10.244.182.142",
          "sessionAffinity": "None"
        }
      ]
    };

    // Uses promises
    return {
      loadAll: function() {
        // Simulate async call
        return $q.when(services);
      }
    };
  }
  ServiceDataService.$inject = ["$q"];

})();
