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

app.factory('menu', [
  '$location',
  '$rootScope',
  'sections',
  '$route',
  function($location, $rootScope, sections, $route) {

    var self;

    $rootScope.$on('$locationChangeSuccess', onLocationChange);

    return self = {

      sections: sections,

      setSections: function(_sections) { this.sections = _sections; },
      selectSection: function(section) { self.openedSection = section; },
      toggleSelectSection: function(section) {
        self.openedSection = (self.openedSection === section ? null : section);
      },
      isSectionSelected: function(section) { return self.openedSection === section; },
      selectPage: function(section, page) {
        self.currentSection = section;
        self.currentPage = page;
      },
      isPageSelected: function(page) { return self.currentPage === page; }
    };

    function onLocationChange() {
      var path = $route.current.originalPath;

      var matchPage = function(section, page) {
        if (path === page.url || path === (page.url + '/')) {
          self.selectSection(section);
          self.selectPage(section, page);
        }
      };

      sections.forEach(function(section) {
        if (section.children) {
          section.children.forEach(function(childSection) {
            if (childSection.pages) {
              childSection.pages.forEach(function(page) { matchPage(childSection, page); });
            }
          });
        } else if (section.pages) {
          section.pages.forEach(function(page) { matchPage(section, page); });
        } else if (section.type === 'link') {
          matchPage(section, section);
        }
      });
    }
  }
]);

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.value("sections", [{"name":"Dashboard","url":"/dashboard","type":"link","templateUrl":"/components/dashboard/pages/home.html"},{"name":"Dashboard","type":"heading","children":[{"name":"Dashboard","type":"toggle","url":"/dashboard","templateUrl":"/components/dashboard/pages/home.html","pages":[{"name":"Pods","url":"/dashboard/pods","templateUrl":"/components/dashboard/views/listPods.html","type":"link"},{"name":"Pod Visualizer","url":"/dashboard/visualpods","templateUrl":"/components/dashboard/views/listPodsVisualizer.html","type":"link"},{"name":"Services","url":"/dashboard/services","templateUrl":"/components/dashboard/views/listServices.html","type":"link"},{"name":"Replication Controllers","url":"/dashboard/replicationcontrollers","templateUrl":"/components/dashboard/views/listReplicationControllers.html","type":"link"},{"name":"Events","url":"/dashboard/events","templateUrl":"/components/dashboard/views/listEvents.html","type":"link"},{"name":"Nodes","url":"/dashboard/nodes","templateUrl":"/components/dashboard/views/listMinions.html","type":"link"},{"name":"Replication Controller","url":"/dashboard/replicationcontrollers/:replicationControllerId","templateUrl":"/components/dashboard/views/replication.html","type":"link"},{"name":"Service","url":"/dashboard/services/:serviceId","templateUrl":"/components/dashboard/views/service.html","type":"link"},{"name": "Node","url": "/dashboard/nodes/:nodeId","templateUrl": "/components/dashboard/views/node.html","type": "link"},{"name":"Explore","url":"/dashboard/groups/:grouping*?/selector/:selector*?","templateUrl":"/components/dashboard/views/groups.html","type":"link"},{"name":"Pod","url":"/dashboard/pods/:podId","templateUrl":"/components/dashboard/views/pod.html","type":"link"}]}]},{"name":"Graph","url":"/graph","type":"link","templateUrl":"/components/graph/pages/home.html"},{"name":"Graph","url":"/graph/inspect","type":"link","templateUrl":"/components/graph/pages/inspect.html","css":"/components/graph/css/show-details-table.css"},{"name":"Graph","type":"heading","children":[{"name":"Graph","type":"toggle","url":"/graph","templateUrl":"/components/graph/pages/home.html","pages":[{"name":"Test","url":"/graph/test","type":"link","templateUrl":"/components/graph/pages/home.html"}]}]}]);

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":"Nodes","url":"/dashboard/nodes","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":"Node","url":"/dashboard/nodes/:nodeId","templateUrl":"components/dashboard/views/node.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/v1",
		"k8sDataServer": "",
		"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: home-page.js
 * Page Controller
 =========================================================*/

app.controller('PageCtrl', [
  '$scope',
  '$timeout',
  '$mdSidenav',
  'menu',
  '$rootScope',
  function($scope, $timeout, $mdSidenav, menu, $rootScope) {
  $scope.menu = menu;

  $scope.path = path;
  $scope.goHome = goHome;
  $scope.openMenu = openMenu;
  $rootScope.openMenu = openMenu;
  $scope.closeMenu = closeMenu;
  $scope.isSectionSelected = isSectionSelected;

  $rootScope.$on('$locationChangeSuccess', openPage);

  // Methods used by menuLink and menuToggle directives
  this.isOpen = isOpen;
  this.isSelected = isSelected;
  this.toggleOpen = toggleOpen;
  this.shouldLockOpen = shouldLockOpen;
  $scope.toggleKubernetesUiMenu = toggleKubernetesUiMenu;

  var mainContentArea = document.querySelector("[role='main']");
  var kubernetesUiMenu = document.querySelector("[role='kubernetes-ui-menu']");

  // *********************
  // Internal methods
  // *********************

  var _t = false;

  $scope.showKubernetesUiMenu = false;

  function shouldLockOpen() {
    return _t;
  }

  function toggleKubernetesUiMenu() {
    $scope.showKubernetesUiMenu = !$scope.showKubernetesUiMenu;
  }

  function closeMenu() {
    $timeout(function() {
      $mdSidenav('left').close();
    });
  }

  function openMenu() {
    $timeout(function() {
      _t = !$mdSidenav('left').isOpen();
      $mdSidenav('left').toggle();
    });
  }

  function path() {
    return $location.path();
  }

  function goHome($event) {
    menu.selectPage(null, null);
    $location.path( '/' );
  }

  function openPage() {
    $scope.closeMenu();
    mainContentArea.focus();
  }

  function isSelected(page) {
    return menu.isPageSelected(page);
  }

  function isSectionSelected(section) {
    var selected = false;
    var openedSection = menu.openedSection;
    if(openedSection === section){
      selected = true;
    }
    else if(section.children) {
      section.children.forEach(function(childSection) {
        if(childSection === openedSection){
          selected = true;
        }
      });
    }
    return selected;
  }

  function isOpen(section) {
    return menu.isSectionSelected(section);
  }

  function toggleOpen(section) {
    menu.toggleSelectSection(section);
  }

  }
]).filter('humanizeDoc', function() {
  return function(doc) {
    if (!doc) return;
    if (doc.type === 'directive') {
      return doc.name.replace(/([A-Z])/g, function($1) {
        return '-'+$1.toLowerCase();
      });
    }
    return doc.label || doc.name;
  }; });

/**=========================================================
 * 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/v1/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 = '';
               var _namespace = 'default';

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

               this.setNamespace = function(value) { _namespace = value; };
               this.getNamespace = function() { return _namespace; };

               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 + '/namespaces/' + _namespace; };

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

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

                 api.getMinions = api.getNodes;

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

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

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

                 return api;
               }];
             })
    .config(["k8sApiProvider", "ENV", function(k8sApiProvider, ENV) {
      if (ENV && ENV['/'] && ENV['/']['k8sApiServer']) {
        k8sApiProvider.setUrlBase(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.metadata.name); });

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

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

                // console.log("maxData", maxData);
                var hostname = "";
                if(m.status.addresses)
                  hostname = m.status.addresses[0].address;

                $scope.activeMinionDataById[m.metadata.name] =
                    transformMemCpuInfo(data.memoryData, data.cpuData, data.filesystemData, maxData, hostname);
              });

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

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

    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: 'Filesystem #' + 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 = encodeURIComponent($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;
      $scope.selectorName = decodeURIComponent(selector);
      var args = [];
      var type = "";
      var selectedHost = "";
      if (selector && selector.length > 0) {
        $scope.selectorPieces = selector.split(",");
        var labels = [];
        var fields = [];
        for (var i = 0; i < $scope.selectorPieces.length; i++) {
          var piece = decodeURIComponent($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 if (piece.indexOf("host=") === 0){
              var labelParts = piece.split("=");
              if (labelParts.length > 1) {
                selectedHost = labelParts[1];
              }
            }
            else {
              labels.push(piece);
            }
          }
        }

        if (labels.length > 0) {
          args.push("labelSelector=" + 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;

      $scope.selectedGroupByName = decodeURIComponent($routeParams.grouping)

      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].metadata.labels.host = data.items[i].spec.nodeName;
            if(selectedHost.length == 0 || selectedHost == data.items[i].metadata.labels.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].metadata.labels) {
          items[i].metadata.labels = {};
        }
        items[i].metadata.labels[key] = value;
      }
    };

    $scope.groupData = function(items, index) {
      var result = {
        "items": {},
        "kind": "grouping"
      };
      for (var i = 0; i < items.length; i++) {
        key = items[i].metadata.labels[decodeURIComponent($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.metadata.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: 'Nodes', value: '/dashboard/nodes'},
          {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: 'Last Seen', field: 'lastSeen'},
      {name: 'First Seen', field: 'firstSeen'},
      {name: 'Count', field: 'count'},
      {name: 'Name', field: 'name'},
      {name: 'Kind', field: 'kind'},
      {name: 'SubObject', field: 'subObject'},
      {name: 'Reason', field: 'reason'},
      {name: 'Source', field: 'source'},
      {name: 'Message', field: 'message'}
    ];


    $scope.sortable = ['lastSeen', 'firstSeen', 'count', 'name', 'kind', 'subObject', 'reason', 'source', 'message'];
    $scope.count = 50;
    function handleError(data, status, headers, config) {
      console.log("Error (" + status + "): " + data);
      $scope.loading = false;
    }

    $scope.content = [];

    function getData() {
      $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) {
          var _sources = '';
          if (event.source) {
            _sources = event.source.component + ' ' + event.source.host;
          }


          $scope.content.push({
            firstSeen: $filter('date')(event.firstTimestamp, 'medium'),
            lastSeen: $filter('date')(event.lastTimestamp, 'medium'),
            count: event.count,
            name: event.involvedObject.name,
            kind: event.involvedObject.kind,
            subObject: event.involvedObject.fieldPath,
            reason: event.reason,
            source: _sources,
            message: event.message
          });



        });

        $scope.content = _.sortBy($scope.content, function(e){
          return e.lastSeen;
        }).reverse();


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

    getData();

  }
]);

/**=========================================================
 * 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: 'Addresses', field: 'addresses'}, {name: 'Status', field: 'status'}];

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

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


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

    $scope.content = [];

    function getData() {
      $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 _statusType = '';

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


          $scope.content.push({name: minion.metadata.name, addresses: _.map(minion.status.addresses, function(a) { return a.address }).join(', '), status: _statusType});
        });

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

    getData();

  }
]);



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: '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','containers','images','host','labels'];
    $scope.count = 50;

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

    var orderedPodNames = [];

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

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

    $scope.content = [];

    function getData() {
      $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.spec) {
            Object.keys(pod.spec.containers)
                .forEach(function(key) {
                  _containers += ', ' + pod.spec.containers[key].name;
                  _images += ', ' + pod.spec.containers[key].image;
                });
          }

          if (pod.metadata.labels) {
            _labels = _.map(pod.metadata.labels, function(v, k) { return k + ': ' + v }).join(', ');
          }

          $scope.content.push({
            pod: pod.metadata.name,
            ip: pod.status.podIP,
            containers: _fixComma(_containers),
            images: _fixComma(_images),
            host: pod.spec.nodeName,
            labels: _labels,
            status: pod.status.phase
          });

        });

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

    $scope.getPodRestarts = function(pod) {
      var r = null;
      var container = _.first(pod.spec.containers);
      if (container) r = pod.status.containerStatuses[container.name].restartCount;
      return r;
    };

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

    $scope.podStatusClass = function(pod) {

      var s = pod.status.phase.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();

  }
]);

/**=========================================================
 * 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', 'selector', 'replicas'];
    $scope.thumbs = 'thumb';
    $scope.count = 50;

    $scope.go = function(data) { $location.path('/dashboard/replicationcontrollers/' + data.controller); };

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

    $scope.content = [];

    function getData() {
      $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.spec.template.spec.containers) {
            Object.keys(replicationController.spec.template.spec.containers)
                .forEach(function(key) {
                  _name += replicationController.spec.template.spec.containers[key].name;
                  _image += replicationController.spec.template.spec.containers[key].image;
                });
          }

          var _selectors = '';

          if (replicationController.spec.selector) {
            _selectors = _.map(replicationController.spec.selector, function(v, k) { return k + '=' + v }).join(', ');
          }

          $scope.content.push({
            controller: replicationController.metadata.name,
            containers: _name,
            images: _image,
            selector: _selectors,
            replicas: replicationController.status.replicas
          });

        });

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

    getData();

  }
]);

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

app.controller('ListServicesCtrl', [
  '$scope',
  '$interval',
  '$routeParams',
  'k8sApi',
  '$rootScope',
  '$location',
  function($scope, $interval, $routeParams, k8sApi, $rootScope, $location) {
    '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: 'Ports', field: 'port'}
    ];

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

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

    $scope.content = [];

    $rootScope.doTheBack = $scope.doTheBack;

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

    $scope.getData = function() {
      $scope.loading = true;
      k8sApi.getServices().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 _labels = '';

            if (service.metadata.labels) {
              _labels = _.map(service.metadata.labels, function(v, k) { return k + ': ' + v }).join(', ');
            }

            var _selectors = '';

            if (service.spec.selector) {
              _selectors = _.map(service.spec.selector, function(v, k) { return k + '=' + v }).join(', ');
            }

            var _ports = '';

            if (service.spec.ports) {
              _ports = _.map(service.spec.ports, function(p) {
                var n = '';
                if(p.name)
                  n = p.name + ': ';
                n = n + p.port;
                return n;
               }).join(', ');
            }

            $scope.content.push({
              name: service.metadata.name,
              ip: service.spec.clusterIP,
              port: _ports,
              selector: _selectors,
              labels: _labels
            });
          });
        }
      })).error($scope.handleError);
    };

    $scope.getData();
  }
]);

/**=========================================================
 * Module: Nodes
 * Visualizer for nodes
 =========================================================*/

app.controller('NodeCtrl', [
  '$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.handleNode = function(nodeId) {
      $scope.loading = true;
      k8sApi.getNodes(nodeId).success(angular.bind(this, function(data) {
        $scope.node = data;
        $scope.loading = false;
      })).error($scope.handleError);
    };

    $scope.handleNode($routeParams.nodeId);
  }
]);

/**=========================================================
 * 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);

    $scope.doTheBack = function() { window.history.back(); };
    $scope.getSelectorUrlFragment = function(sel){ return _.map(sel, function(v, k) { return k + '=' + v }).join(','); };

  }
]);

/**=========================================================
 * 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.doTheBack = function() { window.history.back(); };
    $scope.go = function(d) { $location.path('/dashboard/services/' + d.metadata.name); }
    $scope.getSelectorUrlFragment = function(sel){ return _.map(sel, function(v, k) { return k + '=' + v }).join(','); };

  }
]);

(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 - 215) / 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(" + 115 + "," + 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("dx", function(d, i) { return 68; })
                      .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;
                  }

                  d3.select(element[0]).select("svg.chart").remove();
                  d3.select(element[0]).select("svg.legend").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',
                 'menu',
                 '$rootScope',
                 function($scope, $filter, $location, menu, $rootScope) {
                  $scope.menu = menu;
                   $scope.$watch('page', function(newValue, oldValue) {
                     if (typeof newValue !== 'undefined') {
                       $location.path(newValue);
                     }
                   });
                   $scope.subpages = [
                     {
                       category: 'dashboard',
                       name: 'Explore',
                       value: '/dashboard/groups/type/selector/',
                       id: 'groupsView'
                     },
                     {category: 'dashboard', name: 'Pods', value: '/dashboard/pods', id: 'podsView'},
                     {category: 'dashboard', name: 'Nodes', value: '/dashboard/nodes', 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: '=',
            reverse: '=',
            doSelect: '&onSelect'
          },
          transclude: true,
          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.order = function(predicate, reverse) {
              $scope.content = orderBy($scope.content, predicate, reverse);
              $scope.predicate = predicate;
            };
            var reverse = false;
            if($scope.reverse)
              reverse = $scope.reverse;

            $scope.order($scope.sortable[0], reverse);
            $scope.getNumber = function(num) { return new Array(num); };
            $scope.goToPage = function(page) { $scope.currentPage = page; };
            $scope.showMore = function() { return angular.isDefined($scope.moreClick);}
          }],
          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": "Pod",
    "apiVersion": "v1",
    "metadata": {
        "name": "redis-master-c0r1n",
        "generateName": "redis-master-",
        "namespace": "default",
        "selfLink": "/api/v1/namespaces/default/pods/redis-master-c0r1n",
        "uid": "f12ddfaf-ff77-11e4-8f2d-080027213276",
        "resourceVersion": "39",
        "creationTimestamp": "2015-05-21T05:12:14Z",
        "labels": {
            "name": "redis-master"
        },
        "annotations": {
            "kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicationController\",\"namespace\":\"default\",\"name\":\"redis-master\",\"uid\":\"f12969e0-ff77-11e4-8f2d-080027213276\",\"apiVersion\":\"v1\",\"resourceVersion\":\"26\"}}"
        }
    },
    "spec": {
        "volumes": [
            {
                "name": "default-token-zb4rq",
                "secret": {
                    "secretName": "default-token-zb4rq"
                }
            }
        ],
        "containers": [
            {
                "name": "master",
                "image": "redis",
                "ports": [
                    {
                        "containerPort": 6379,
                        "protocol": "TCP"
                    }
                ],
                "resources": {},
                "volumeMounts": [
                    {
                        "name": "default-token-zb4rq",
                        "readOnly": true,
                        "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"
                    }
                ],
                "terminationMessagePath": "/dev/termination-log",
                "imagePullPolicy": "IfNotPresent",
                "capabilities": {},
                "securityContext": {
                    "capabilities": {},
                    "privileged": false
                }
            }
        ],
        "restartPolicy": "Always",
        "dnsPolicy": "ClusterFirst",
        "serviceAccount": "default",
        "host": "127.0.0.1"
    },
    "status": {
        "phase": "Running",
        "Condition": [
            {
                "type": "Ready",
                "status": "True"
            }
        ],
        "hostIP": "127.0.0.1",
        "podIP": "172.17.0.1",
        "startTime": "2015-05-21T05:12:14Z",
        "containerStatuses": [
            {
                "name": "master",
                "state": {
                    "running": {
                        "startedAt": "2015-05-21T05:12:14Z"
                    }
                },
                "lastState": {},
                "ready": true,
                "restartCount": 0,
                "image": "redis",
                "imageID": "docker://95af5842ddb9b03f7c6ec7601e65924cec516fcedd7e590ae31660057085cf67",
                "containerID": "docker://ae2a1e0a91a8b1015191a0b8e2ce8c55a86fb1a9a2b1e8e3b29430c9d93c8c09"
            }
        ]
    }
};

    // 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": "List",
    "apiVersion": "v1",
    "metadata": {},
    "items": [
        {
            "kind": "ReplicationController",
            "apiVersion": "v1",
            "metadata": {
                "name": "redis-master",
                "namespace": "default",
                "selfLink": "/api/v1/namespaces/default/replicationcontrollers/redis-master",
                "uid": "f12969e0-ff77-11e4-8f2d-080027213276",
                "resourceVersion": "28",
                "creationTimestamp": "2015-05-21T05:12:14Z",
                "labels": {
                    "name": "redis-master"
                }
            },
            "spec": {
                "replicas": 1,
                "selector": {
                    "name": "redis-master"
                },
                "template": {
                    "metadata": {
                        "creationTimestamp": null,
                        "labels": {
                            "name": "redis-master"
                        }
                    },
                    "spec": {
                        "containers": [
                            {
                                "name": "master",
                                "image": "redis",
                                "ports": [
                                    {
                                        "containerPort": 6379,
                                        "protocol": "TCP"
                                    }
                                ],
                                "resources": {},
                                "terminationMessagePath": "/dev/termination-log",
                                "imagePullPolicy": "IfNotPresent",
                                "capabilities": {},
                                "securityContext": {
                                    "capabilities": {},
                                    "privileged": false
                                }
                            }
                        ],
                        "restartPolicy": "Always",
                        "dnsPolicy": "ClusterFirst",
                        "serviceAccount": ""
                    }
                }
            },
            "status": {
                "replicas": 1
            }
        }
    ]};

    // 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": "List",
    "apiVersion": "v1",
    "metadata": {},
    "items": [
        {
            "kind": "Service",
            "apiVersion": "v1",
            "metadata": {
                "name": "kubernetes",
                "namespace": "default",
                "selfLink": "/api/v1/namespaces/default/services/kubernetes",
                "resourceVersion": "6",
                "creationTimestamp": null,
                "labels": {
                    "component": "apiserver",
                    "provider": "kubernetes"
                }
            },
            "spec": {
                "ports": [
                    {
                        "protocol": "TCP",
                        "port": 443,
                        "targetPort": 443
                    }
                ],
                "portalIP": "10.0.0.2",
                "sessionAffinity": "None"
            },
            "status": {}
        },
        {
            "kind": "Service",
            "apiVersion": "v1",
            "metadata": {
                "name": "kubernetes-ro",
                "namespace": "default",
                "selfLink": "/api/v1/namespaces/default/services/kubernetes-ro",
                "resourceVersion": "8",
                "creationTimestamp": null,
                "labels": {
                    "component": "apiserver",
                    "provider": "kubernetes"
                }
            },
            "spec": {
                "ports": [
                    {
                        "protocol": "TCP",
                        "port": 80,
                        "targetPort": 80
                    }
                ],
                "portalIP": "10.0.0.1",
                "sessionAffinity": "None"
            },
            "status": {}
        },
        {
            "kind": "Service",
            "apiVersion": "v1",
            "metadata": {
                "name": "redis-master",
                "namespace": "default",
                "selfLink": "/api/v1/namespaces/default/services/redis-master",
                "uid": "a6fde246-ff78-11e4-8f2d-080027213276",
                "resourceVersion": "72",
                "creationTimestamp": "2015-05-21T05:17:19Z",
                "labels": {
                    "name": "redis-master"
                }
            },
            "spec": {
                "ports": [
                    {
                        "protocol": "TCP",
                        "port": 6379,
                        "targetPort": 6379
                    }
                ],
                "selector": {
                    "name": "redis-master"
                },
                "portalIP": "10.0.0.124",
                "sessionAffinity": "None"
            },
            "status": {}
        }
    ]
};

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

})();
