<commit_before>'use strict';

var app = angular.module('myApp.boxes.directives', []);

app.directive('showBox', ['Box', '$routeParams', 'Auth', '$pusher', '$location', '$mdBottomSheet', 'Zone', 'ZoneListing', '$cookies', 'showToast', 'showErrors', '$mdDialog', '$q', 'ClientDetails', '$timeout', '$rootScope', 'Report', 'menu', 'gettextCatalog', function(Box, $routeParams, Auth, $pusher, $location, $mdBottomSheet, Zone, ZoneListing, $cookies, showToast, showErrors, $mdDialog, $q, ClientDetails, $timeout, $rootScope, Report, menu, gettextCatalog) {

  var link = function(scope,attrs,element,controller) {

    var prefs = {};
    var zoneAlert;
    var timeout;
    scope.streamingUpdates = true;

    scope.zone     = ZoneListing;
    scope.staff    = Auth.currentUser().pss;
    scope.location = { slug: $routeParams.id };
    scope.period   = $routeParams.period || '6h';

    scope.setPrefs = function(a) {
      if (prefs[scope.box.slug] === undefined) {
        prefs[scope.box.slug] = {};
      }
      var expireDate = new Date();
      expireDate.setDate(expireDate.getDate() + 1);
      prefs[scope.box.slug].igz = true;
      $cookies.put('_ctapref', JSON.stringify(prefs), {'expires': expireDate});
    };

    var c = $cookies.get('_ctapref');
    var ignoreZone;
    if ( c !== undefined ) {
      prefs = JSON.parse(c);
      if ( prefs[$routeParams.box_id] ) {
        ignoreZone = prefs[$routeParams.box_id].igz;
      }
    }

    // User Permissions //

    scope.allowed = true;

    scope.menuAction = function(type) {
      switch(type) {
        case 'Edit':
          editBox();
          break;
        case 'Reboot':
          scope.rebootBox();
          break;
        case 'Transfer':
          scope.transferBox();
          break;
        case 'Payloads':
          scope.payloads();
          break;
        case 'Resync':
          scope.resyncBox();
          break;
        case 'Changelog':
          viewHistory();
          break;
        case 'Reset':
          scope.resetBox();
          break;
        case 'Delete':
          scope.deleteBox();
          break;
      }
    };

    var createMenu = function() {
      scope.menu = [];

      scope.menu.push({
        name: 'Edit',
        icon: 'settings'
      });

      if (scope.box.is_polkaspots) {

        scope.menu.push({
          name: 'Reboot',
          icon: 'autorenew',
          disabled: !scope.box.allowed_job
        });

        scope.menu.push({
          name: 'Payloads',
          icon: 'present_to_all',
        });

        scope.menu.push({
          name: 'Changelog',
          icon: 'history',
        });
      }

      scope.menu.push({
        name: 'Transfer',
        icon: 'transform',
      });

      scope.menu.push({
        name: 'Delete',
        icon: 'delete_forever'
      });

      if (scope.box.is_polkaspots) {
        scope.menu.push({
          name: 'Resync',
          icon: 'settings_backup_restore',
          disabled: !scope.box.allowed_job
        });

        scope.menu.push({
          name: 'Reset',
          icon: 'clear',
        });
      }

    };

    var notInZone = function(results) {
      scope.not_in_zone = ((results._info && results._info.total > 0)  && (results.zones && results.zones.length === 0));
      return scope.not_in_zone;
    };

    function ZoneAlertCtrl($scope, $mdBottomSheet, prefs) {
      $scope.add = function() {
        $mdBottomSheet.hide();
        $location.path('/locations/' + scope.location.slug + '/zones').search({ap_mac: scope.box.calledstationid, box_id: scope.box.id});
      };
      $scope.cancel = function() {
        prefs();
        $mdBottomSheet.hide();
      };
    }
    ZoneAlertCtrl.$inject = ['$scope','$mdBottomSheet','prefs'];

    var showResetConfirm = function() {
      $mdBottomSheet.show({
        template:
          '<md-bottom-sheet class="md-list md-has-header" ng-cloak>'+
          '<md-subheader>Action Required. This box has been manually reset. Please confirm this action. Or click cancel to ignore.</md-subheader>'+
          '<md-button ng-click="cancel()" class="md-list-item-content">'+
          '<span class="md-inline-list-icon-label">Cancel</span>'+
          '</md-button>'+
          '<md-button ng-click="reset()" md-autofocus="true" class="md-list-item-content md-accent" >'+
          '<span class="md-inline-list-icon-label">CONFIRM</span>'+
          '</md-button>'+
          '</md-bottom-sheet>',
        controller: Ctrl
      });
    };

    function Ctrl($scope) {
      $scope.reset = function() {
        $mdBottomSheet.hide();
        resetBox();
      };
    }

    Ctrl.$inject = ['$scope'];

    var showZoneAlert = function() {
      $mdBottomSheet.show({
        template:
          '<md-bottom-sheet class="md-list md-has-header" ng-cloak>'+
          '<md-subheader>Action Required. This box is not part of a zone. The box will function but will not broadcast an SSID. Add this box to a zone to complete setup.</md-subheader>'+
          '<md-button ng-click="cancel()" class="md-list-item-content" >'+
          '<span class="md-inline-list-icon-label">Cancel</span>'+
          '</md-button>'+
          '<md-button ng-click="add()" class="md-list-item-content md-accent">'+
          '<span class="md-inline-list-icon-label">Add To Zone</span>'+
          '</md-button>'+
          '</md-bottom-sheet>',
        locals: {
          prefs: scope.setPrefs
        },
        controller: ZoneAlertCtrl
      });
    };

    var editBox = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/edit';
    };

    scope.payloads = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/payloads';
    };

    scope.resetBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Reset this Device'))
      .textContent(gettextCatalog.getString('Resetting your box is not recommended. If you are having problems with it, please resync first. If the device is offline, it will reset next time it restarts.'))
      .ariaLabel(gettextCatalog.getString('Reset Device'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Reset it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        resetBox();
      });
    };

    var resetBox = function() {
      scope.resetting = true;
      Box.reset({id: scope.box.slug}).$promise.then(function(results) {
        showToast(gettextCatalog.getString('Device reset in progress, please wait.'));
        scope.box.allowed_job = false;
        scope.box.state = 'resetting';
        scope.resetting = undefined;
      }, function(errors) {
        var err;
        if (errors && errors.data && errors.data.errors && errors.data.errors.base) {
          err = errors.data.errors.base;
        } else {
          err = gettextCatalog.getString('Could not reset this device, please try again');
        }
        console.log(errors);
        showToast(err);
        scope.box.state = 'failed';
        scope.resetting = undefined;
      });
    };

    scope.resyncBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Resync this Device'))
      .textContent(gettextCatalog.getString('This will force a complete refresh of it\'s configuration files. A resync will disconnect any wireless clients temporarily'))
      .ariaLabel(gettextCatalog.getString('Resync Device'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Resync it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        resyncBox();
      });
    };

    var resyncBox = function() {
      scope.box.state = 'processing';
      scope.box.allowed_job = false;
      Box.update({
        location_id: scope.location.slug,
        id: scope.box.slug,
        box: {
          resync: true
        }
      }).$promise.then(function(res) {
        showToast(gettextCatalog.getString('Device resync in progress. Please wait.'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Failed to resync device, please try again.'));
        console.log('Could not resync device:', errors);
        scope.box.state = 'online';
      });
    };

    scope.rebootBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Would you like to reboot this device?'))
      .textContent(gettextCatalog.getString('Rebooting will disconnect your clients.\nA reboot takes about 60 seconds to complete'))
      .ariaLabel(gettextCatalog.getString('Reboot Box'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Reboot it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        rebootBox();
      });
    };

    var rebootBox = function() {
      scope.box.state = 'processing';
      scope.box.allowed_job = false;
      Box.reboot({id: scope.box.slug}).$promise.then(function(results) {
        scope.box.state = 'rebooting';
        showToast(gettextCatalog.getString('Box successfully rebooted.'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Failed to reboot box, please try again.'));
        console.log('Could not reboot box:', errors);
        scope.box.state = 'online';
      });
    };

    scope.deleteBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Are you sure you want to delete this device?'))
      .textContent(gettextCatalog.getString('This cannot be reversed, please be careful. Deleting a box is permanent.'))
      .ariaLabel(gettextCatalog.getString('Delete Box'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Delete it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        deleteBox();
      });
    };

    var deleteBox = function() {
      Box.destroy({id: scope.box.slug}).$promise.then(function(results) {
        $location.path('/locations/' + scope.location.slug);
        showToast(gettextCatalog.getString('Box successfully deleted'));
      }, function(errors) {
        console.log(errors);
        showToast(gettextCatalog.getString('Could not delete box'));
      });
    };

    scope.transferBox = function(ev) {
      $mdDialog.show({
        controller: transferCtrl,
        locals: {
          transfer: transferBox
        },
        templateUrl: 'components/boxes/show/_transfer.html',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose:true
      });
    };

    function transferCtrl($scope, transfer) {
      $scope.obj = {};
      $scope.cancel = function() {
        $mdDialog.cancel();
      };
      $scope.transfer = function(id) {
        $mdDialog.cancel();
        transfer(id);
      };
    }
    transferCtrl.$inject = ['$scope', 'transfer'];

    var transferBox = function(id) {
      Box.transfer({id: scope.box.slug, transfer_to: id}).$promise.then(function(results) {
        scope.back();
        showToast(gettextCatalog.getString('Box transfer was a success.'));
      }, function(errors) {
        showErrors(errors);
      });
    };

    scope.muteBox = function() {
      scope.box.ignored = !scope.box.ignored;
      Box.update({
        location_id: scope.location.slug,
        id: scope.box.slug,
        box: {
          ignored: scope.box.ignored
        }
      }).$promise.then(function(res) {
        var val = scope.box.ignored ? gettextCatalog.getString('muted') : gettextCatalog.getString('unmuted');
        showToast(gettextCatalog.getString('Box successfully ') + val + '.');
      }, function(errors) {
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug;
    };

    var j = 0;
    var counter = 0;

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe(key);
        channel.bind('general', function(data) {
          console.log('Message recvd.', data);
          init();
        });
      }
    }

    var processAlertMessages = function() {
      if (scope.box.is_polkaspots) {
        if (scope.box.reset_confirmation) {
          showResetConfirm();
        } else if ( zoneAlert ) {
          showZoneAlert();
        }
      }
    };

    scope.updatePeriod = function(period) {
      scope.period = period;
      updatePage();
    };

    var updatePage = function(item) {
      var hash            = {};
      hash.interval       = scope.interval;
      hash.period         = scope.period;
      hash.fn             = scope.fn;
      hash.type           = scope.type;
      $location.search(hash);

      timeout = $timeout(function() {
        // loadTput(); // not unless we want to adjust dynamically
        loadCharts();
      }, 500);
    };

    scope.refresh = function() {
      scope.period = '6h';
      updatePage();
    };

    scope.streamingUpdater = function() {
      if (scope.streamingUpdates) {
        loadPusher(scope.box.sockets_hash);
        showToast(gettextCatalog.getString('Streaming updates enabled'));
      } else {
        if (channel) {
          channel.unbind();
        }
        showToast(gettextCatalog.getString('Streaming updates disabled'));
      }
    };

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

    scope.toggle = function(section) {
      menu.toggleSelectSection(section);
    };

    var init = function() {
      var deferred = $q.defer();
      Box.get({id: $routeParams.box_id, metadata: true}).$promise.then(function(box) {
        scope.box = box;
        ClientDetails.client = { location_id: box.location_id, ap_mac: box.calledstationid };
        createMenu();
        sortSsids();
        loadPusher(box.sockets_hash);
        scope.loading = undefined;
        deferred.resolve(box);

      }, function() {
        deferred.reject();
      });
      return deferred.promise;
    };

    var getZones = function() {
      var deferred = $q.defer();
      Zone.get({location_id: scope.location.slug, box_id: scope.box.slug}).$promise.then(function(results) {
        scope.zone.zones    = results.zones;
        scope._info         = results._info;
        if (notInZone(results) && !ignoreZone) {
          zoneAlert = true;
        }
        deferred.resolve(results);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    var loadCharts = function() {
      controller.$scope.$broadcast('loadClientChart', 'device');
    };

    var loadTput = function() {
      var deferred = $q.defer();
      Report.clientstats({
        type:         'tput',
        ap_mac:       scope.box.calledstationid,
        location_id:  scope.box.location_id,
        resource:     'device',
        interval:     '60s',
        period:       '60m' // $routeParams.period,
      }).$promise.then(function(data) {
        scope.box.throughput = data.throughput;
        deferred.resolve(data);
      }, function() {
        deferred.reject();
      });
      return deferred.promise;
    };

    var sortSsids = function() {
      if (scope.box.metadata && scope.box.metadata.ssids && scope.box.metadata.ssids.length > 0) {
        if (scope.box.metadata.ssids.length === 1) {
          scope.box.ssids = scope.box.metadata.ssids[0];
        } else {
          var ssids = scope.box.metadata.ssids;
          if (ssids.length > 2) {
            var temp = ssids.slice(0,2).join(', ');
            scope.box.all_ssids = scope.box.metadata.ssids.join(', ');
            scope.box.ssids = temp + ' and ' + (ssids.length - 2) + ' more.';
          } else {
            scope.box.ssids = ssids.slice(0,2).join(' & ');
          }
        }
      } else {
        scope.box.ssids = 'N/A';
      }
    };

    var viewHistory = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/versions';
    };

    init().then(function() {
      loadTput();
      getZones().then(function() {
        loadCharts(); // seems to need to wait for something....
        processAlertMessages();
      });
    });

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      if (channel) {
        channel.unbind();
      }
      $mdBottomSheet.hide();
      $timeout.cancel(timeout);
    });


  };

  return {
    link: link,
    require: '^clientChart',
    templateUrl: 'components/boxes/show/_index.html'
  };

}]);

app.directive('boxPayloads', ['Box', 'Payload', 'showToast', 'showErrors', '$routeParams', '$pusher', '$mdDialog', 'gettextCatalog', function(Box, Payload, showToast, showErrors, $routeParams, $pusher, $mdDialog, gettextCatalog) {

  var link = function(scope,element,attrs) {

    scope.location = { slug: $routeParams.id };
    scope.command = { save: true };

    var init = function() {
      return Box.get({id: $routeParams.box_id}).$promise.then(function(box) {
        scope.box = box;
        scope.loading = undefined;
        loadPayloads();
      }, function(err) {
        scope.loading = undefined;
        console.log(err);
      });
    };

    scope.deletePayload = function(index,id) {
      Payload.destroy({box_id: scope.box.slug, id: id}).$promise.then(function() {
        scope.payloads.splice(index, 1);
        showToast(gettextCatalog.getString('Payload deleted successfully'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Could not delete the payload.'));
      });
    };

    scope.showPayload = function(index,ev) {
      $mdDialog.show({
        template:
        '<md-dialog aria-label="Payload Output" ng-cloak>'+
        '<md-toolbar>'+
        '<div class="md-toolbar-tools">'+
        '<h2>Command Output</h2>'+
        '<span flex></span>'+
        '</div>'+
        '</md-toolbar>'+
        '<md-dialog-content>'+
        '<div class="md-dialog-content">'+
        '<div flex-xs flex="100" ng-hide="prefs.now">'+
        '<pre>{{ command.body }}</pre>'+
        '</div>'+
        '</div>'+
        '</md-dialog-content>'+
        '<md-dialog-actions layout="row">'+
        '<span flex></span>'+
        '<md-button ng-click="cancel()">'+
        'CLOSE'+
        '</md-button>'+
        '</md-dialog-actions>'+
        '</md-dialog>',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose:true,
        controller: Ctrl,
        locals: {
          command: scope.payloads[index]
        }
      });
    };

    function Ctrl ($scope, command) {
      $scope.command = command;
      $scope.cancel = function() {
        $mdDialog.cancel();
      };
    }
    Ctrl.$inject = ['$scope', 'command'];

    var loadPayloads = function() {
      Payload.query({controller: 'boxes', box_id: scope.box.slug}, function(data) {
        scope.payloads = data;
        loadPusher(scope.box.sockets_hash);
      });
    };

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe(key);
        channel.bind('general', function(data) {
          scope.command.success = undefined;
          showToast(gettextCatalog.getString('Payload completed!'))
          loadPayloads();
        });
      }
    }

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug;
    };

    init();

  };

  return {
    link: link,
    scope: {
      loading: '=',
    },
    templateUrl: 'components/boxes/payloads/_index.html'
  };

}]);

app.directive('splashOnly', 'gettextCatalog', ['Box', 'showToast', 'showErrors', function(Box, showToast, showErrors, gettextCatalog) {

  var link = function(scope,element,attrs) {

    scope.machine_types = [{ key: 'Cisco Meraki MR-12', value: 'meraki-mr-12'}, {key: 'Cisco Meraki MR-16', value: 'meraki-mr-16'}, {key:'Cisco Meraki MR-18',value: 'meraki-mr-18'}, {key: 'Cisco Meraki MR-24', value: 'meraki-mr-24'}, {key: 'Cisco Meraki MR-34', value: 'meraki-mr-34'}, {key: 'Cisco Meraki MR-62', value: 'meraki-mr-62'}, {key: 'Cisco Meraki MR-66', value: 'meraki-mr-66'}, {key: 'Aruba', value: 'aruba'}, {key: 'Aerohive', value: 'aerohive'}, {key: 'Ruckus', value: 'ruckus'}, {key: 'Xirrus', value: 'xirrus'}, {key: 'Mikrotik', value: 'mikrotik' }];

    scope.$watch('box',function(nv){
      if (nv !== undefined) {
        scope.box.tony = scope.box.is_polkaspots;
      }
    });

    scope.update = function(form) {
      form.$setPristine();
      scope.box.is_polkaspots = scope.box.tony;
      return Box.update({
        id: scope.box.slug,
        box: scope.box
      }).$promise.then(function(box) {
        showToast(gettextCatalog.getString('Settings updated successfully'));
      }, function(errors) {
        showErrors(errors);
      });
    };

  };

  return {
    link: link,
    scope: {
      box: '='
    },
    templateUrl: 'components/boxes/show/_non_ps.html'
  };

}]);

app.directive('editBox', ['Box', '$routeParams', 'showToast', 'showErrors', 'moment', 'gettextCatalog', function(Box, $routeParams, showToast, showErrors, moment, gettextCatalog) {

  var link = function(scope) {

    scope.location = { slug: $routeParams.id };
    scope.timezones = moment.tz.names();

    var ht20_channels  = ['auto', '01','02','03','04','05','06','07','08','09','10','11'];
    var ht40m_channels = ['auto','05','06','07','08','09','10','11'];
    var ht40p_channels = ['auto','01','02','03','04','05','06','07'];

    var ht20_channels_5 = ['auto','36','40','44','48','52','56','60','64','149','153','157','161','165'];
    var ht40m_channels_5 = ['auto','40','44','48','52','56','60','64','153','157','161','165'];
    var ht40p_channels_5 = ['auto','36','40','44','48','52','56','60','153','157','161','165'];

    var vht20_channels_5 = ['auto','36','40','44','48'];
    var vht40_channels_5 = ['auto','40','44','48'];
    var vht80_channels_5 = ['auto','36','40','44','48'];
    var vht160_channels_5 = ['auto','36','40','44','48'];

    scope.protos = [{ key: 'DHCP', value: 'dhcp'}, { key: 'Static', value: 'static'}];

    scope.ht_modes = [{key: 'HT20', value: 'HT20' }, { key: 'HT40-', value: 'HT40-'}, {key: 'HT40+', value: 'HT40+'}];
    var htModes = function() {
      if (scope.box.is_ac) {
        scope.ht_modes_5 = [
          {key: 'VHT20', value: 'VHT20' },
          {key: 'VHT40', value: 'VHT40'},
          {key: 'VHT80', value: 'VHT80'},
          {key: 'VHT160', value: 'VHT160'}
        ];
      } else {
        scope.ht_modes_5 = scope.ht_modes;
      }
    };

    scope.updateChannels = function() {
      scope.updateChannelTwo();
      scope.updateChannelFive();
    };

    scope.updateChannelTwo = function() {
      if (scope.box.ht_mode_2 === 'HT20') {
        scope.channels = ht20_channels;
      } else if (scope.box.ht_mode_2 === 'HT40-') {
        scope.channels = ht40m_channels;
      } else if (scope.box.ht_mode_2 === 'HT40+') {
        scope.channels = ht40p_channels;
      }
    };

    scope.updateChannelFive = function() {
      if (scope.box.ht_mode_5 === 'HT20') {
        scope.channels5 = ht20_channels_5;
      } else if (scope.box.ht_mode_5 === 'HT40-') {
        scope.channels5 = ht40m_channels_5;
      } else if (scope.box.ht_mode_5 === 'HT40+') {
        scope.channels5 = ht40p_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT20') {
        scope.channels5 = vht20_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT40') {
        scope.channels5 = vht40_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT80') {
        scope.channels5 = vht80_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT1600') {
        scope.channels5 = vht160_channels_5;
      }
    };

    scope.update = function(form) {
      form.$setPristine();
      scope.box.is_polkaspots = scope.box.tony;
      return Box.update({
        id: scope.box.slug,
        box: scope.box
      }).$promise.then(function(box) {
        showToast(gettextCatalog.getString('Settings updated successfully'));
      }, function(errors) {
        form.$setPristine();
        showErrors(errors);
      });
    };

    var init = function() {
      return Box.get({id: $routeParams.box_id}).$promise.then(function(box) {
        scope.box = box;
        scope.box.location_slug = scope.location.slug;
        htModes();
        scope.updateChannels();
        scope.box.tx_power_2 = parseInt(scope.box.tx_power_2,0);
        scope.box.tx_power_5 = parseInt(scope.box.tx_power_5,0);
        scope.box.tony       = scope.box.is_polkaspots;
        scope.loading = undefined;
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug;
    };

    init();
  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/boxes/edit/_edit.html'
  };
}]);

app.directive('transferBox', ['Box', '$location', '$routeParams', '$q', 'Location', function(Box, $location, $routeParams, $q, Location) {

  var link = function( scope, element, attrs ) {

    scope.loading = true;

    var init = function() {
      var deferred = $q.defer();
      Location.shortquery({}).$promise.then(function (res) {
        if (res.length > 1) {
          scope.locations = res;
          deferred.resolve();
        }
        scope.loading = undefined;
        deferred.reject();
      }, function() {
        scope.loading = undefined;
        deferred.reject();
      });
    };

    init();

  };

  return {
    link: link,
    scope: {
      box: '='
    },
    templateUrl: 'components/boxes/show/_transfer_select.html',
  };
}]);


app.directive('runPayloads', ['Command', function(Command) {

  var link = function(scope) {

    scope.loading = true;
    scope.command = {};

    function getCommands() {
      Command.query().$promise.then(function(res) {
        scope.commands = res;
        scope.loading = undefined;
      });
    }

    getCommands();
  };

  return {
    scope: {
      command: '='
    },
    link: link,
    templateUrl: 'components/boxes/payloads/_payloads_mass.html',
  };

}]);

app.directive('boxStatus', ['Box', '$routeParams', '$location', '$timeout', '$rootScope', function(Box, $routeParams, $location, $timeout, $rootScope) {

  var link = function( scope, element ) {

    var i = 0;
    var chart;
    scope.status = 0;

    function status () {
      Box.status({id: $routeParams.id}).$promise.then(function(results) {
        scope.status = results.status ? 5 : 0;
        if ( results.status === 1) {
          scope.color = '#43ac6a';
        } else {
          scope.color = '#d92525';
        }
      });

    }

    var series;
    var refreshData = function() {
      // if ( i < 120 ) {
      // scope.timeout = $timeout(function() {
      //   scope.time = new Date().getTime() / 1000;
      //   refreshChart();
      //   i++;
      // }, 1000);
      // } else {
      //   $timeout.cancel(scope.timeout);
      // }
    };

    var refreshChart = function() {
      status();
      series = chart.series[0];
      var x = (new Date()).getTime(), y = scope.status;
      var shift = series.data.length > 20;
      series.addPoint([x, y], true, shift);
      refreshData();
    };

    var loadChart = function() {
      chart = new Highcharts.Chart({
          chart: {
              renderTo: 'live_chart',
              backgroundColor: 'transparent',
              plotBorderWidth: null,
              plotShadow: false,
              margin: [100, 0, 60, 0],
              spacingRight: 0,
              defaultSeriesType: 'spline',
              animation: Highcharts.svg,
              events: {
                  load: refreshData
              }
          },
          credits: {
            enabled: false
          },
          title: {
              text: '',
              enabled: false
          },
          xAxis: {
              type: 'datetime',
              spacingLeft: 10,
              gridLineColor: '#ECECEC',
              gridLineWidth: 1,
              title: {
                  text: null,
              },
              labels: {
                enabled: true,
                style: {
                  fontSize: '9px'
                }
              },
              tickPixelInterval: 150,
              maxZoom: 20 * 1000
          },
          yAxis: {
              // minPadding: 10,
              // maxPadding: 10,
              min: -1,
              minRange: 0.1,
              gridLineColor: '#ECECEC',
              gridLineWidth: 1,
              labels: {
                enabled: false
              },
              title: {
                  text: null,
              }
          },
          tooltip: {
            enabled: false
          },
          legend: {
            enabled: false
          },
          plotOptions: {
            line: {
              marker: {
                enabled: false
              }
            },
            series: {
              fillColor: null,
              fillOpacity: 0.1,
              lineWidth: 1.5,
              marker: {
                enabled: false,
              },
              shadow: false,
              states: {
                hover: {
                  enabled: false
                }
              }
            }
          },
          series: [{
              name: null,
              lineWidth: 5,
              zones: [{
                  value: 0.5,
                  color: '#d92525'
              }, {
                  color: '#43ac6a'
              }],
              data: (function () {
                  var data = [],
                      time = (new Date()).getTime(),
                      i;

                  for (i = -2; i <= 0; i += 1) {
                      data.push({
                          x: time + i * 1000,
                          y: scope.status
                      });
                  }
                  return data;
              }()),
          }]
      });
    };

    status();

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      $timeout.cancel(scope.timeout);
    });

    $timeout(function() {
      loadChart();
    }, 10);

  };


  return {
    link: link,
    scope: {
      timeout: '=',
      time: '='
    },
    template: '<div id="live"><div id="live_chart"></div></div>'
  };

}]);

app.directive('boxesTemplate', ['Box', '$routeParams', '$location', '$modal', function(Box, $routeParams, $location, $modal) {

  var link = function( scope, element, attrs, controller ) {

    scope.selected = {};

    scope.updatePage = function(page) {
      scope.page = scope._links.current_page;
      locationSearch();
    };

    scope.activatePayload = function() {
      scope.activeBox = true;
    };

    var locationSearch = function() {
      var hash = {};
      hash.machine_type = scope.machine_type;
      hash.firmware_version = scope.firmware;
      hash.state = scope.state;
      hash.q = scope.query;
      hash.page = scope.page;
      hash.environment = scope.environment;
      $location.search(hash);
    };

    scope.selectAll = function() {
      if (scope.boxes.length) {
        if (scope.selectall === true) {
          angular.forEach(scope.boxes, function(v) {
            if (v.allowed_job) {
              scope.selected[v.slug] = true;
            }
          });
        } else {
          scope.selected = {};
        }
      }
    };

  };

  return {
    link: link,
    scope: {
      slug: '@',
      boxes: '=',
      query: '@',
      _links: '=links',
      selected: '='
    },
    templateUrl: 'components/boxes/list/list.html',
  };

}]);

app.directive('boxDataRates', ['$compile', function($compile) {

  var link = function(scope, element, attrs) {

    scope.loading = true;

    scope.two   = [{key: '6Mbps', value: 6000}, {key: '12Mbps', value: 12000}, {key: '18Mbps', value: 18000}, {key: '24Mbps', value: 24000}, {key: '36Mbps', value: 36000}, {key: '48Mbps', value: 48000}, {key: '54Mbps', value: 54000}];

    scope.five   = [{key: '6Mbps', value: 6000}, {key: '12Mbps', value: 12000}, {key: '18Mbps', value: 18000}, {key: '24Mbps', value: 24000}, {key: '36Mbps', value: 36000}, {key: '48Mbps', value: 48000}, {key: '54Mbps', value: 54000}];

    scope.$watch('nas',function(nv){
      if (nv !== undefined && nv.id !== undefined) {
        scope.loading = undefined;
      }
    });

  };

  return {
    link: link,
    scope: {
      nas: '='
    },
    templateUrl: 'components/boxes/edit/_minimum_datarates.html',
  };

}]);

app.directive('boxClientsGraph', ['Box', 'Report', '$routeParams', '$location', function(Box, Report, $routeParams, $location) {

  var link = function( scope, element, attrs ) {

    scope.chart = {};

    attrs.$observe('apMac', function(val){
      if (val !== '' && !scope.chart.length ) {
        scope.clientsChart(attrs.apMac);
      }
    });

    scope.chart.interval = $routeParams.interval || 'day';
    scope.chart.distance = $routeParams.distance || 5;

    scope.clientsChart = function(ap_mac) {
      scope.chart.loading = true;
      Report.clients({clients: true, location_id: attrs.locationId, interval: scope.chart.interval, distance: scope.chart.distance, ap_mac: ap_mac}).$promise.then(function(data) {
        scope.stats = data._stats;
        scope.chart = data.timeline;
      });
    };

  };

  return {
    link: link,
    restrict: 'E',
    scope: {
      ap_mac: '@',
      location_id: '@',
      interval: '@',
      distance: '@',
      chart: '='
    },
    template:
      '<line-chart data="{{chart}}" legend="true" interval="{{stats.interval}}" align="right" start="{{ stats.start }}"></line-chart>'
  };
}]);

app.directive('adjustInterval', ['$routeParams', '$location', function($routeParams, $location) {

  var link = function( scope, element, attrs ) {

    scope.interval = $routeParams.interval || 'day';
    scope.distance = $routeParams.distance;

    scope.adjustInterval = function(dist,int) {
      scope.distance = dist;
      scope.interval = int || 'day';
      $location.search({distance: scope.distance, interval: scope.interval});
    };

  };

  return {<commit_msg>removed box clients graph test<commit_after>'use strict';

var app = angular.module('myApp.boxes.directives', []);

app.directive('showBox', ['Box', '$routeParams', 'Auth', '$pusher', '$location', '$mdBottomSheet', 'Zone', 'ZoneListing', '$cookies', 'showToast', 'showErrors', '$mdDialog', '$q', 'ClientDetails', '$timeout', '$rootScope', 'Report', 'menu', 'gettextCatalog', function(Box, $routeParams, Auth, $pusher, $location, $mdBottomSheet, Zone, ZoneListing, $cookies, showToast, showErrors, $mdDialog, $q, ClientDetails, $timeout, $rootScope, Report, menu, gettextCatalog) {

  var link = function(scope,attrs,element,controller) {

    var prefs = {};
    var zoneAlert;
    var timeout;
    scope.streamingUpdates = true;

    scope.zone     = ZoneListing;
    scope.staff    = Auth.currentUser().pss;
    scope.location = { slug: $routeParams.id };
    scope.period   = $routeParams.period || '6h';

    scope.setPrefs = function(a) {
      if (prefs[scope.box.slug] === undefined) {
        prefs[scope.box.slug] = {};
      }
      var expireDate = new Date();
      expireDate.setDate(expireDate.getDate() + 1);
      prefs[scope.box.slug].igz = true;
      $cookies.put('_ctapref', JSON.stringify(prefs), {'expires': expireDate});
    };

    var c = $cookies.get('_ctapref');
    var ignoreZone;
    if ( c !== undefined ) {
      prefs = JSON.parse(c);
      if ( prefs[$routeParams.box_id] ) {
        ignoreZone = prefs[$routeParams.box_id].igz;
      }
    }

    // User Permissions //

    scope.allowed = true;

    scope.menuAction = function(type) {
      switch(type) {
        case 'Edit':
          editBox();
          break;
        case 'Reboot':
          scope.rebootBox();
          break;
        case 'Transfer':
          scope.transferBox();
          break;
        case 'Payloads':
          scope.payloads();
          break;
        case 'Resync':
          scope.resyncBox();
          break;
        case 'Changelog':
          viewHistory();
          break;
        case 'Reset':
          scope.resetBox();
          break;
        case 'Delete':
          scope.deleteBox();
          break;
      }
    };

    var createMenu = function() {
      scope.menu = [];

      scope.menu.push({
        name: 'Edit',
        icon: 'settings'
      });

      if (scope.box.is_polkaspots) {

        scope.menu.push({
          name: 'Reboot',
          icon: 'autorenew',
          disabled: !scope.box.allowed_job
        });

        scope.menu.push({
          name: 'Payloads',
          icon: 'present_to_all',
        });

        scope.menu.push({
          name: 'Changelog',
          icon: 'history',
        });
      }

      scope.menu.push({
        name: 'Transfer',
        icon: 'transform',
      });

      scope.menu.push({
        name: 'Delete',
        icon: 'delete_forever'
      });

      if (scope.box.is_polkaspots) {
        scope.menu.push({
          name: 'Resync',
          icon: 'settings_backup_restore',
          disabled: !scope.box.allowed_job
        });

        scope.menu.push({
          name: 'Reset',
          icon: 'clear',
        });
      }

    };

    var notInZone = function(results) {
      scope.not_in_zone = ((results._info && results._info.total > 0)  && (results.zones && results.zones.length === 0));
      return scope.not_in_zone;
    };

    function ZoneAlertCtrl($scope, $mdBottomSheet, prefs) {
      $scope.add = function() {
        $mdBottomSheet.hide();
        $location.path('/locations/' + scope.location.slug + '/zones').search({ap_mac: scope.box.calledstationid, box_id: scope.box.id});
      };
      $scope.cancel = function() {
        prefs();
        $mdBottomSheet.hide();
      };
    }
    ZoneAlertCtrl.$inject = ['$scope','$mdBottomSheet','prefs'];

    var showResetConfirm = function() {
      $mdBottomSheet.show({
        template:
          '<md-bottom-sheet class="md-list md-has-header" ng-cloak>'+
          '<md-subheader>Action Required. This box has been manually reset. Please confirm this action. Or click cancel to ignore.</md-subheader>'+
          '<md-button ng-click="cancel()" class="md-list-item-content">'+
          '<span class="md-inline-list-icon-label">Cancel</span>'+
          '</md-button>'+
          '<md-button ng-click="reset()" md-autofocus="true" class="md-list-item-content md-accent" >'+
          '<span class="md-inline-list-icon-label">CONFIRM</span>'+
          '</md-button>'+
          '</md-bottom-sheet>',
        controller: Ctrl
      });
    };

    function Ctrl($scope) {
      $scope.reset = function() {
        $mdBottomSheet.hide();
        resetBox();
      };
    }

    Ctrl.$inject = ['$scope'];

    var showZoneAlert = function() {
      $mdBottomSheet.show({
        template:
          '<md-bottom-sheet class="md-list md-has-header" ng-cloak>'+
          '<md-subheader>Action Required. This box is not part of a zone. The box will function but will not broadcast an SSID. Add this box to a zone to complete setup.</md-subheader>'+
          '<md-button ng-click="cancel()" class="md-list-item-content" >'+
          '<span class="md-inline-list-icon-label">Cancel</span>'+
          '</md-button>'+
          '<md-button ng-click="add()" class="md-list-item-content md-accent">'+
          '<span class="md-inline-list-icon-label">Add To Zone</span>'+
          '</md-button>'+
          '</md-bottom-sheet>',
        locals: {
          prefs: scope.setPrefs
        },
        controller: ZoneAlertCtrl
      });
    };

    var editBox = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/edit';
    };

    scope.payloads = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/payloads';
    };

    scope.resetBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Reset this Device'))
      .textContent(gettextCatalog.getString('Resetting your box is not recommended. If you are having problems with it, please resync first. If the device is offline, it will reset next time it restarts.'))
      .ariaLabel(gettextCatalog.getString('Reset Device'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Reset it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        resetBox();
      });
    };

    var resetBox = function() {
      scope.resetting = true;
      Box.reset({id: scope.box.slug}).$promise.then(function(results) {
        showToast(gettextCatalog.getString('Device reset in progress, please wait.'));
        scope.box.allowed_job = false;
        scope.box.state = 'resetting';
        scope.resetting = undefined;
      }, function(errors) {
        var err;
        if (errors && errors.data && errors.data.errors && errors.data.errors.base) {
          err = errors.data.errors.base;
        } else {
          err = gettextCatalog.getString('Could not reset this device, please try again');
        }
        console.log(errors);
        showToast(err);
        scope.box.state = 'failed';
        scope.resetting = undefined;
      });
    };

    scope.resyncBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Resync this Device'))
      .textContent(gettextCatalog.getString('This will force a complete refresh of it\'s configuration files. A resync will disconnect any wireless clients temporarily'))
      .ariaLabel(gettextCatalog.getString('Resync Device'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Resync it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        resyncBox();
      });
    };

    var resyncBox = function() {
      scope.box.state = 'processing';
      scope.box.allowed_job = false;
      Box.update({
        location_id: scope.location.slug,
        id: scope.box.slug,
        box: {
          resync: true
        }
      }).$promise.then(function(res) {
        showToast(gettextCatalog.getString('Device resync in progress. Please wait.'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Failed to resync device, please try again.'));
        console.log('Could not resync device:', errors);
        scope.box.state = 'online';
      });
    };

    scope.rebootBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Would you like to reboot this device?'))
      .textContent(gettextCatalog.getString('Rebooting will disconnect your clients.\nA reboot takes about 60 seconds to complete'))
      .ariaLabel(gettextCatalog.getString('Reboot Box'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Reboot it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        rebootBox();
      });
    };

    var rebootBox = function() {
      scope.box.state = 'processing';
      scope.box.allowed_job = false;
      Box.reboot({id: scope.box.slug}).$promise.then(function(results) {
        scope.box.state = 'rebooting';
        showToast(gettextCatalog.getString('Box successfully rebooted.'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Failed to reboot box, please try again.'));
        console.log('Could not reboot box:', errors);
        scope.box.state = 'online';
      });
    };

    scope.deleteBox = function(ev) {
      var confirm = $mdDialog.confirm()
      .title(gettextCatalog.getString('Are you sure you want to delete this device?'))
      .textContent(gettextCatalog.getString('This cannot be reversed, please be careful. Deleting a box is permanent.'))
      .ariaLabel(gettextCatalog.getString('Delete Box'))
      .targetEvent(ev)
      .ok(gettextCatalog.getString('Delete it'))
      .cancel(gettextCatalog.getString('Cancel'));
      $mdDialog.show(confirm).then(function() {
        deleteBox();
      });
    };

    var deleteBox = function() {
      Box.destroy({id: scope.box.slug}).$promise.then(function(results) {
        $location.path('/locations/' + scope.location.slug);
        showToast(gettextCatalog.getString('Box successfully deleted'));
      }, function(errors) {
        console.log(errors);
        showToast(gettextCatalog.getString('Could not delete box'));
      });
    };

    scope.transferBox = function(ev) {
      $mdDialog.show({
        controller: transferCtrl,
        locals: {
          transfer: transferBox
        },
        templateUrl: 'components/boxes/show/_transfer.html',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose:true
      });
    };

    function transferCtrl($scope, transfer) {
      $scope.obj = {};
      $scope.cancel = function() {
        $mdDialog.cancel();
      };
      $scope.transfer = function(id) {
        $mdDialog.cancel();
        transfer(id);
      };
    }
    transferCtrl.$inject = ['$scope', 'transfer'];

    var transferBox = function(id) {
      Box.transfer({id: scope.box.slug, transfer_to: id}).$promise.then(function(results) {
        scope.back();
        showToast(gettextCatalog.getString('Box transfer was a success.'));
      }, function(errors) {
        showErrors(errors);
      });
    };

    scope.muteBox = function() {
      scope.box.ignored = !scope.box.ignored;
      Box.update({
        location_id: scope.location.slug,
        id: scope.box.slug,
        box: {
          ignored: scope.box.ignored
        }
      }).$promise.then(function(res) {
        var val = scope.box.ignored ? gettextCatalog.getString('muted') : gettextCatalog.getString('unmuted');
        showToast(gettextCatalog.getString('Box successfully ') + val + '.');
      }, function(errors) {
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug;
    };

    var j = 0;
    var counter = 0;

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe(key);
        channel.bind('general', function(data) {
          console.log('Message recvd.', data);
          init();
        });
      }
    }

    var processAlertMessages = function() {
      if (scope.box.is_polkaspots) {
        if (scope.box.reset_confirmation) {
          showResetConfirm();
        } else if ( zoneAlert ) {
          showZoneAlert();
        }
      }
    };

    scope.updatePeriod = function(period) {
      scope.period = period;
      updatePage();
    };

    var updatePage = function(item) {
      var hash            = {};
      hash.interval       = scope.interval;
      hash.period         = scope.period;
      hash.fn             = scope.fn;
      hash.type           = scope.type;
      $location.search(hash);

      timeout = $timeout(function() {
        // loadTput(); // not unless we want to adjust dynamically
        loadCharts();
      }, 500);
    };

    scope.refresh = function() {
      scope.period = '6h';
      updatePage();
    };

    scope.streamingUpdater = function() {
      if (scope.streamingUpdates) {
        loadPusher(scope.box.sockets_hash);
        showToast(gettextCatalog.getString('Streaming updates enabled'));
      } else {
        if (channel) {
          channel.unbind();
        }
        showToast(gettextCatalog.getString('Streaming updates disabled'));
      }
    };

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

    scope.toggle = function(section) {
      menu.toggleSelectSection(section);
    };

    var init = function() {
      var deferred = $q.defer();
      Box.get({id: $routeParams.box_id, metadata: true}).$promise.then(function(box) {
        scope.box = box;
        ClientDetails.client = { location_id: box.location_id, ap_mac: box.calledstationid };
        createMenu();
        sortSsids();
        loadPusher(box.sockets_hash);
        scope.loading = undefined;
        deferred.resolve(box);

      }, function() {
        deferred.reject();
      });
      return deferred.promise;
    };

    var getZones = function() {
      var deferred = $q.defer();
      Zone.get({location_id: scope.location.slug, box_id: scope.box.slug}).$promise.then(function(results) {
        scope.zone.zones    = results.zones;
        scope._info         = results._info;
        if (notInZone(results) && !ignoreZone) {
          zoneAlert = true;
        }
        deferred.resolve(results);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    var loadCharts = function() {
      controller.$scope.$broadcast('loadClientChart', 'device');
    };

    var loadTput = function() {
      var deferred = $q.defer();
      Report.clientstats({
        type:         'tput',
        ap_mac:       scope.box.calledstationid,
        location_id:  scope.box.location_id,
        resource:     'device',
        interval:     '60s',
        period:       '60m' // $routeParams.period,
      }).$promise.then(function(data) {
        scope.box.throughput = data.throughput;
        deferred.resolve(data);
      }, function() {
        deferred.reject();
      });
      return deferred.promise;
    };

    var sortSsids = function() {
      if (scope.box.metadata && scope.box.metadata.ssids && scope.box.metadata.ssids.length > 0) {
        if (scope.box.metadata.ssids.length === 1) {
          scope.box.ssids = scope.box.metadata.ssids[0];
        } else {
          var ssids = scope.box.metadata.ssids;
          if (ssids.length > 2) {
            var temp = ssids.slice(0,2).join(', ');
            scope.box.all_ssids = scope.box.metadata.ssids.join(', ');
            scope.box.ssids = temp + ' and ' + (ssids.length - 2) + ' more.';
          } else {
            scope.box.ssids = ssids.slice(0,2).join(' & ');
          }
        }
      } else {
        scope.box.ssids = 'N/A';
      }
    };

    var viewHistory = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/versions';
    };

    init().then(function() {
      loadTput();
      getZones().then(function() {
        loadCharts(); // seems to need to wait for something....
        processAlertMessages();
      });
    });

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      if (channel) {
        channel.unbind();
      }
      $mdBottomSheet.hide();
      $timeout.cancel(timeout);
    });


  };

  return {
    link: link,
    require: '^clientChart',
    templateUrl: 'components/boxes/show/_index.html'
  };

}]);

app.directive('boxPayloads', ['Box', 'Payload', 'showToast', 'showErrors', '$routeParams', '$pusher', '$mdDialog', 'gettextCatalog', function(Box, Payload, showToast, showErrors, $routeParams, $pusher, $mdDialog, gettextCatalog) {

  var link = function(scope,element,attrs) {

    scope.location = { slug: $routeParams.id };
    scope.command = { save: true };

    var init = function() {
      return Box.get({id: $routeParams.box_id}).$promise.then(function(box) {
        scope.box = box;
        scope.loading = undefined;
        loadPayloads();
      }, function(err) {
        scope.loading = undefined;
        console.log(err);
      });
    };

    scope.deletePayload = function(index,id) {
      Payload.destroy({box_id: scope.box.slug, id: id}).$promise.then(function() {
        scope.payloads.splice(index, 1);
        showToast(gettextCatalog.getString('Payload deleted successfully'));
      }, function(errors) {
        showToast(gettextCatalog.getString('Could not delete the payload.'));
      });
    };

    scope.showPayload = function(index,ev) {
      $mdDialog.show({
        template:
        '<md-dialog aria-label="Payload Output" ng-cloak>'+
        '<md-toolbar>'+
        '<div class="md-toolbar-tools">'+
        '<h2>Command Output</h2>'+
        '<span flex></span>'+
        '</div>'+
        '</md-toolbar>'+
        '<md-dialog-content>'+
        '<div class="md-dialog-content">'+
        '<div flex-xs flex="100" ng-hide="prefs.now">'+
        '<pre>{{ command.body }}</pre>'+
        '</div>'+
        '</div>'+
        '</md-dialog-content>'+
        '<md-dialog-actions layout="row">'+
        '<span flex></span>'+
        '<md-button ng-click="cancel()">'+
        'CLOSE'+
        '</md-button>'+
        '</md-dialog-actions>'+
        '</md-dialog>',
        parent: angular.element(document.body),
        targetEvent: ev,
        clickOutsideToClose:true,
        controller: Ctrl,
        locals: {
          command: scope.payloads[index]
        }
      });
    };

    function Ctrl ($scope, command) {
      $scope.command = command;
      $scope.cancel = function() {
        $mdDialog.cancel();
      };
    }
    Ctrl.$inject = ['$scope', 'command'];

    var loadPayloads = function() {
      Payload.query({controller: 'boxes', box_id: scope.box.slug}, function(data) {
        scope.payloads = data;
        loadPusher(scope.box.sockets_hash);
      });
    };

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe(key);
        channel.bind('general', function(data) {
          scope.command.success = undefined;
          showToast(gettextCatalog.getString('Payload completed!'))
          loadPayloads();
        });
      }
    }

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug;
    };

    init();

  };

  return {
    link: link,
    scope: {
      loading: '=',
    },
    templateUrl: 'components/boxes/payloads/_index.html'
  };

}]);

app.directive('splashOnly', 'gettextCatalog', ['Box', 'showToast', 'showErrors', function(Box, showToast, showErrors, gettextCatalog) {

  var link = function(scope,element,attrs) {

    scope.machine_types = [{ key: 'Cisco Meraki MR-12', value: 'meraki-mr-12'}, {key: 'Cisco Meraki MR-16', value: 'meraki-mr-16'}, {key:'Cisco Meraki MR-18',value: 'meraki-mr-18'}, {key: 'Cisco Meraki MR-24', value: 'meraki-mr-24'}, {key: 'Cisco Meraki MR-34', value: 'meraki-mr-34'}, {key: 'Cisco Meraki MR-62', value: 'meraki-mr-62'}, {key: 'Cisco Meraki MR-66', value: 'meraki-mr-66'}, {key: 'Aruba', value: 'aruba'}, {key: 'Aerohive', value: 'aerohive'}, {key: 'Ruckus', value: 'ruckus'}, {key: 'Xirrus', value: 'xirrus'}, {key: 'Mikrotik', value: 'mikrotik' }];

    scope.$watch('box',function(nv){
      if (nv !== undefined) {
        scope.box.tony = scope.box.is_polkaspots;
      }
    });

    scope.update = function(form) {
      form.$setPristine();
      scope.box.is_polkaspots = scope.box.tony;
      return Box.update({
        id: scope.box.slug,
        box: scope.box
      }).$promise.then(function(box) {
        showToast(gettextCatalog.getString('Settings updated successfully'));
      }, function(errors) {
        showErrors(errors);
      });
    };

  };

  return {
    link: link,
    scope: {
      box: '='
    },
    templateUrl: 'components/boxes/show/_non_ps.html'
  };

}]);

app.directive('editBox', ['Box', '$routeParams', 'showToast', 'showErrors', 'moment', 'gettextCatalog', function(Box, $routeParams, showToast, showErrors, moment, gettextCatalog) {

  var link = function(scope) {

    scope.location = { slug: $routeParams.id };
    scope.timezones = moment.tz.names();

    var ht20_channels  = ['auto', '01','02','03','04','05','06','07','08','09','10','11'];
    var ht40m_channels = ['auto','05','06','07','08','09','10','11'];
    var ht40p_channels = ['auto','01','02','03','04','05','06','07'];

    var ht20_channels_5 = ['auto','36','40','44','48','52','56','60','64','149','153','157','161','165'];
    var ht40m_channels_5 = ['auto','40','44','48','52','56','60','64','153','157','161','165'];
    var ht40p_channels_5 = ['auto','36','40','44','48','52','56','60','153','157','161','165'];

    var vht20_channels_5 = ['auto','36','40','44','48'];
    var vht40_channels_5 = ['auto','40','44','48'];
    var vht80_channels_5 = ['auto','36','40','44','48'];
    var vht160_channels_5 = ['auto','36','40','44','48'];

    scope.protos = [{ key: 'DHCP', value: 'dhcp'}, { key: 'Static', value: 'static'}];

    scope.ht_modes = [{key: 'HT20', value: 'HT20' }, { key: 'HT40-', value: 'HT40-'}, {key: 'HT40+', value: 'HT40+'}];
    var htModes = function() {
      if (scope.box.is_ac) {
        scope.ht_modes_5 = [
          {key: 'VHT20', value: 'VHT20' },
          {key: 'VHT40', value: 'VHT40'},
          {key: 'VHT80', value: 'VHT80'},
          {key: 'VHT160', value: 'VHT160'}
        ];
      } else {
        scope.ht_modes_5 = scope.ht_modes;
      }
    };

    scope.updateChannels = function() {
      scope.updateChannelTwo();
      scope.updateChannelFive();
    };

    scope.updateChannelTwo = function() {
      if (scope.box.ht_mode_2 === 'HT20') {
        scope.channels = ht20_channels;
      } else if (scope.box.ht_mode_2 === 'HT40-') {
        scope.channels = ht40m_channels;
      } else if (scope.box.ht_mode_2 === 'HT40+') {
        scope.channels = ht40p_channels;
      }
    };

    scope.updateChannelFive = function() {
      if (scope.box.ht_mode_5 === 'HT20') {
        scope.channels5 = ht20_channels_5;
      } else if (scope.box.ht_mode_5 === 'HT40-') {
        scope.channels5 = ht40m_channels_5;
      } else if (scope.box.ht_mode_5 === 'HT40+') {
        scope.channels5 = ht40p_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT20') {
        scope.channels5 = vht20_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT40') {
        scope.channels5 = vht40_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT80') {
        scope.channels5 = vht80_channels_5;
      } else if (scope.box.ht_mode_5 === 'VHT1600') {
        scope.channels5 = vht160_channels_5;
      }
    };

    scope.update = function(form) {
      form.$setPristine();
      scope.box.is_polkaspots = scope.box.tony;
      return Box.update({
        id: scope.box.slug,
        box: scope.box
      }).$promise.then(function(box) {
        showToast(gettextCatalog.getString('Settings updated successfully'));
      }, function(errors) {
        form.$setPristine();
        showErrors(errors);
      });
    };

    var init = function() {
      return Box.get({id: $routeParams.box_id}).$promise.then(function(box) {
        scope.box = box;
        scope.box.location_slug = scope.location.slug;
        htModes();
        scope.updateChannels();
        scope.box.tx_power_2 = parseInt(scope.box.tx_power_2,0);
        scope.box.tx_power_5 = parseInt(scope.box.tx_power_5,0);
        scope.box.tony       = scope.box.is_polkaspots;
        scope.loading = undefined;
      });
    };

    scope.back = function() {
      window.location.href = '/#/locations/' + scope.location.slug + '/boxes/' + scope.box.slug;
    };

    init();
  };

  return {
    link: link,
    scope: {
      loading: '='
    },
    templateUrl: 'components/boxes/edit/_edit.html'
  };
}]);

app.directive('transferBox', ['Box', '$location', '$routeParams', '$q', 'Location', function(Box, $location, $routeParams, $q, Location) {

  var link = function( scope, element, attrs ) {

    scope.loading = true;

    var init = function() {
      var deferred = $q.defer();
      Location.shortquery({}).$promise.then(function (res) {
        if (res.length > 1) {
          scope.locations = res;
          deferred.resolve();
        }
        scope.loading = undefined;
        deferred.reject();
      }, function() {
        scope.loading = undefined;
        deferred.reject();
      });
    };

    init();

  };

  return {
    link: link,
    scope: {
      box: '='
    },
    templateUrl: 'components/boxes/show/_transfer_select.html',
  };
}]);


app.directive('runPayloads', ['Command', function(Command) {

  var link = function(scope) {

    scope.loading = true;
    scope.command = {};

    function getCommands() {
      Command.query().$promise.then(function(res) {
        scope.commands = res;
        scope.loading = undefined;
      });
    }

    getCommands();
  };

  return {
    scope: {
      command: '='
    },
    link: link,
    templateUrl: 'components/boxes/payloads/_payloads_mass.html',
  };

}]);

app.directive('boxStatus', ['Box', '$routeParams', '$location', '$timeout', '$rootScope', function(Box, $routeParams, $location, $timeout, $rootScope) {

  var link = function( scope, element ) {

    var i = 0;
    var chart;
    scope.status = 0;

    function status () {
      Box.status({id: $routeParams.id}).$promise.then(function(results) {
        scope.status = results.status ? 5 : 0;
        if ( results.status === 1) {
          scope.color = '#43ac6a';
        } else {
          scope.color = '#d92525';
        }
      });

    }

    var series;
    var refreshData = function() {
      // if ( i < 120 ) {
      // scope.timeout = $timeout(function() {
      //   scope.time = new Date().getTime() / 1000;
      //   refreshChart();
      //   i++;
      // }, 1000);
      // } else {
      //   $timeout.cancel(scope.timeout);
      // }
    };

    var refreshChart = function() {
      status();
      series = chart.series[0];
      var x = (new Date()).getTime(), y = scope.status;
      var shift = series.data.length > 20;
      series.addPoint([x, y], true, shift);
      refreshData();
    };

    var loadChart = function() {
      chart = new Highcharts.Chart({
          chart: {
              renderTo: 'live_chart',
              backgroundColor: 'transparent',
              plotBorderWidth: null,
              plotShadow: false,
              margin: [100, 0, 60, 0],
              spacingRight: 0,
              defaultSeriesType: 'spline',
              animation: Highcharts.svg,
              events: {
                  load: refreshData
              }
          },
          credits: {
            enabled: false
          },
          title: {
              text: '',
              enabled: false
          },
          xAxis: {
              type: 'datetime',
              spacingLeft: 10,
              gridLineColor: '#ECECEC',
              gridLineWidth: 1,
              title: {
                  text: null,
              },
              labels: {
                enabled: true,
                style: {
                  fontSize: '9px'
                }
              },
              tickPixelInterval: 150,
              maxZoom: 20 * 1000
          },
          yAxis: {
              // minPadding: 10,
              // maxPadding: 10,
              min: -1,
              minRange: 0.1,
              gridLineColor: '#ECECEC',
              gridLineWidth: 1,
              labels: {
                enabled: false
              },
              title: {
                  text: null,
              }
          },
          tooltip: {
            enabled: false
          },
          legend: {
            enabled: false
          },
          plotOptions: {
            line: {
              marker: {
                enabled: false
              }
            },
            series: {
              fillColor: null,
              fillOpacity: 0.1,
              lineWidth: 1.5,
              marker: {
                enabled: false,
              },
              shadow: false,
              states: {
                hover: {
                  enabled: false
                }
              }
            }
          },
          series: [{
              name: null,
              lineWidth: 5,
              zones: [{
                  value: 0.5,
                  color: '#d92525'
              }, {
                  color: '#43ac6a'
              }],
              data: (function () {
                  var data = [],
                      time = (new Date()).getTime(),
                      i;

                  for (i = -2; i <= 0; i += 1) {
                      data.push({
                          x: time + i * 1000,
                          y: scope.status
                      });
                  }
                  return data;
              }()),
          }]
      });
    };

    status();

    $rootScope.$on('$routeChangeStart', function (event, next, current) {
      $timeout.cancel(scope.timeout);
    });

    $timeout(function() {
      loadChart();
    }, 10);

  };


  return {
    link: link,
    scope: {
      timeout: '=',
      time: '='
    },
    template: '<div id="live"><div id="live_chart"></div></div>'
  };

}]);

app.directive('boxesTemplate', ['Box', '$routeParams', '$location', '$modal', function(Box, $routeParams, $location, $modal) {

  var link = function( scope, element, attrs, controller ) {

    scope.selected = {};

    scope.updatePage = function(page) {
      scope.page = scope._links.current_page;
      locationSearch();
    };

    scope.activatePayload = function() {
      scope.activeBox = true;
    };

    var locationSearch = function() {
      var hash = {};
      hash.machine_type = scope.machine_type;
      hash.firmware_version = scope.firmware;
      hash.state = scope.state;
      hash.q = scope.query;
      hash.page = scope.page;
      hash.environment = scope.environment;
      $location.search(hash);
    };

    scope.selectAll = function() {
      if (scope.boxes.length) {
        if (scope.selectall === true) {
          angular.forEach(scope.boxes, function(v) {
            if (v.allowed_job) {
              scope.selected[v.slug] = true;
            }
          });
        } else {
          scope.selected = {};
        }
      }
    };

  };

  return {
    link: link,
    scope: {
      slug: '@',
      boxes: '=',
      query: '@',
      _links: '=links',
      selected: '='
    },
    templateUrl: 'components/boxes/list/list.html',
  };

}]);

app.directive('boxDataRates', ['$compile', function($compile) {

  var link = function(scope, element, attrs) {

    scope.loading = true;

    scope.two   = [{key: '6Mbps', value: 6000}, {key: '12Mbps', value: 12000}, {key: '18Mbps', value: 18000}, {key: '24Mbps', value: 24000}, {key: '36Mbps', value: 36000}, {key: '48Mbps', value: 48000}, {key: '54Mbps', value: 54000}];

    scope.five   = [{key: '6Mbps', value: 6000}, {key: '12Mbps', value: 12000}, {key: '18Mbps', value: 18000}, {key: '24Mbps', value: 24000}, {key: '36Mbps', value: 36000}, {key: '48Mbps', value: 48000}, {key: '54Mbps', value: 54000}];

    scope.$watch('nas',function(nv){
      if (nv !== undefined && nv.id !== undefined) {
        scope.loading = undefined;
      }
    });

  };

  return {
    link: link,
    scope: {
      nas: '='
    },
    templateUrl: 'components/boxes/edit/_minimum_datarates.html',
  };

}]);

app.directive('adjustInterval', ['$routeParams', '$location', function($routeParams, $location) {

  var link = function( scope, element, attrs ) {

    scope.interval = $routeParams.interval || 'day';
    scope.distance = $routeParams.distance;

    scope.adjustInterval = function(dist,int) {
      scope.distance = dist;
      scope.interval = int || 'day';
      $location.search({distance: scope.distance, interval: scope.interval});
    };

  };

  return {
{'fraction_non_alphanumeric': 0.16294117647058823, 'fraction_numerical': 0.015602941176470589, 'mean_word_length': 2.1273454746136866, 'pattern_counts': {'":': 0, '<': 113, '<?xml version=': 0, '>': 119, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2639574', 'n_tokens_mistral': 21730, 'n_tokens_neox': 20928, 'n_words': 3934}