<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 timeout;
    var j = 0;
    var counter = 0;

    scope.zone             = ZoneListing;
    scope.location         = { slug: $routeParams.id };
    scope.period           = $routeParams.period || '6h';
    scope.streamingUpdates = true;

    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({
        type: 'edit',
        name: gettextCatalog.getString('Edit'),
        icon: 'settings'
      });

      if (scope.box.cucumber) {

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

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

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

      // Removed temporarily while we sort the API permissions
      // scope.menu.push({
      //   name: gettextCatalog.getString('Transfer'),
      //   icon: 'transform',
      //   type: 'transfer',
      // });

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

      if (scope.box.cucumber) {
        scope.menu.push({
          name: gettextCatalog.getString('Resync'),
          icon: 'settings_backup_restore',
          disabled: !scope.box.allowed_job,
          type: 'resync',
        });

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

    var checkZones = function(results) {
      scope.not_in_zone = (results._info && results._info.total > 0);
    };

    var showResetConfirm = function() {
      $mdBottomSheet.show({
        templateUrl: 'components/boxes/show/_toast_reset_confirm.html',
        controller: ResetCtrl
      });
    };

    function ResetCtrl($scope) {
      $scope.reset = function() {
        $mdBottomSheet.hide();
        resetBox();
      };
      $scope.cancel = function() {
        $mdBottomSheet.hide();
      };
    }
    ResetCtrl.$inject = ['$scope'];

    var showZoneAlert = function() {
      $mdBottomSheet.show({
        templateUrl: 'components/boxes/show/_toast_zone.html',
        locals: {
          prefs: scope.setPrefs
        },
        controller: ZoneAlertCtrl
      });
    };

    var ZoneAlertCtrl = function($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 editBox = function() {
      $location.path('/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/edit');
    };

    scope.payloads = function() {
      $location.path('/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.update({
        id: scope.box.slug,
        box: { action: 'reset' }
      }).$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: {
          action: 'resync'
        }
      }).$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.update({
        id: scope.box.slug,
        box: { action: 'reboot' }
      }).$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.update({
    //     id: scope.box.slug,
    //     box: {
    //       transfer_to: id
    //     }
    //   }).$promise.then(function(results) {
    //     scope.back();
    //     showToast(gettextCatalog.getString('Box transferred successfully.'));
    //   }, 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}}.', {val: val}));
      }, function(errors) {
      });
    };

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

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe('private-' + scope.box.location_pubsub);
        console.log('Binding to:', channel.name);
        channel.bind('boxes_' + scope.box.pubsub_token, function(data) {
          console.log('Message received at', new Date().getTime() / 1000);
          processNotification(data.message);
        });
      }
    }

    var processNotification = function(data) {
      if (data){
        try{
          data = JSON.parse(data);
        }catch(e){
          console.log(e);
        }
      }
      switch(data.type) {
        case 'heartbeat':
          heartbeat(data);
          break;
        case 'speedtest':
          scope.box.speedtest_running = undefined;
          scope.box.allowed_job = true;
          scope.box.latest_speedtest = {
            result: data.message.val,
            timestamp: data.message.timestamp
          };
          break;
        case 'installer':
          if (data.status === true) {
            init();
            showToast(gettextCatalog.getString('Device installed successfully.'));
          } else {
            scope.box.state = 'new';
            showToast(gettextCatalog.getString('Device failed to install, please wait.'));
          }
          break;
        case 'upgrade':
          if (data.status === true) {
            scope.box.state = 'upgrading';
            showToast(gettextCatalog.getString('Upgrade running, please wait while it completes.'));
          } else {
            showToast(gettextCatalog.getString('Upgrade failed to run. Please try again.'));
          }
          break;
        default:
          console.log(data, 'Unknown Event');
      }
    };

    var heartbeat = function(data) {
      scope.box.last_heartbeat = data.last_heartbeat;
      scope.box.state          = data.state;
      scope.box.wan_ip         = data.wan_ip;
      if (scope.box.state === 'offline') {
        scope.box.allowed_job = false;
      } else {
        scope.box.allowed_job = true;
      }
    };

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

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

    var updatePage = function(item) {
      var hash            = {};
      hash.interval       = scope.interval;
      hash.period         = scope.period;<commit_msg>Fixes menu issue, missing items<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 timeout;
    var j = 0;
    var counter = 0;

    scope.zone             = ZoneListing;
    scope.location         = { slug: $routeParams.id };
    scope.period           = $routeParams.period || '6h';
    scope.streamingUpdates = true;

    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({
        type: 'edit',
        name: gettextCatalog.getString('Edit'),
        icon: 'settings'
      });

      if (scope.box.is_cucumber) {

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

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

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

      // Removed temporarily while we sort the API permissions
      // scope.menu.push({
      //   name: gettextCatalog.getString('Transfer'),
      //   icon: 'transform',
      //   type: 'transfer',
      // });

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

      if (scope.box.is_cucumber) {
        scope.menu.push({
          name: gettextCatalog.getString('Resync'),
          icon: 'settings_backup_restore',
          disabled: !scope.box.allowed_job,
          type: 'resync',
        });

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

    var checkZones = function(results) {
      scope.not_in_zone = (results._info && results._info.total > 0);
    };

    var showResetConfirm = function() {
      $mdBottomSheet.show({
        templateUrl: 'components/boxes/show/_toast_reset_confirm.html',
        controller: ResetCtrl
      });
    };

    function ResetCtrl($scope) {
      $scope.reset = function() {
        $mdBottomSheet.hide();
        resetBox();
      };
      $scope.cancel = function() {
        $mdBottomSheet.hide();
      };
    }
    ResetCtrl.$inject = ['$scope'];

    var showZoneAlert = function() {
      $mdBottomSheet.show({
        templateUrl: 'components/boxes/show/_toast_zone.html',
        locals: {
          prefs: scope.setPrefs
        },
        controller: ZoneAlertCtrl
      });
    };

    var ZoneAlertCtrl = function($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 editBox = function() {
      $location.path('/locations/' + scope.location.slug + '/boxes/' + scope.box.slug + '/edit');
    };

    scope.payloads = function() {
      $location.path('/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.update({
        id: scope.box.slug,
        box: { action: 'reset' }
      }).$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: {
          action: 'resync'
        }
      }).$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.update({
        id: scope.box.slug,
        box: { action: 'reboot' }
      }).$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.update({
    //     id: scope.box.slug,
    //     box: {
    //       transfer_to: id
    //     }
    //   }).$promise.then(function(results) {
    //     scope.back();
    //     showToast(gettextCatalog.getString('Box transferred successfully.'));
    //   }, 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}}.', {val: val}));
      }, function(errors) {
      });
    };

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

    var channel;
    function loadPusher(key) {
      if (scope.pusherLoaded === undefined && typeof client !== 'undefined') {
        scope.pusherLoaded = true;
        var pusher = $pusher(client);
        channel = pusher.subscribe('private-' + scope.box.location_pubsub);
        console.log('Binding to:', channel.name);
        channel.bind('boxes_' + scope.box.pubsub_token, function(data) {
          console.log('Message received at', new Date().getTime() / 1000);
          processNotification(data.message);
        });
      }
    }

    var processNotification = function(data) {
      if (data){
        try{
          data = JSON.parse(data);
        }catch(e){
          console.log(e);
        }
      }
      switch(data.type) {
        case 'heartbeat':
          heartbeat(data);
          break;
        case 'speedtest':
          scope.box.speedtest_running = undefined;
          scope.box.allowed_job = true;
          scope.box.latest_speedtest = {
            result: data.message.val,
            timestamp: data.message.timestamp
          };
          break;
        case 'installer':
          if (data.status === true) {
            init();
            showToast(gettextCatalog.getString('Device installed successfully.'));
          } else {
            scope.box.state = 'new';
            showToast(gettextCatalog.getString('Device failed to install, please wait.'));
          }
          break;
        case 'upgrade':
          if (data.status === true) {
            scope.box.state = 'upgrading';
            showToast(gettextCatalog.getString('Upgrade running, please wait while it completes.'));
          } else {
            showToast(gettextCatalog.getString('Upgrade failed to run. Please try again.'));
          }
          break;
        default:
          console.log(data, 'Unknown Event');
      }
    };

    var heartbeat = function(data) {
      scope.box.last_heartbeat = data.last_heartbeat;
      scope.box.state          = data.state;
      scope.box.wan_ip         = data.wan_ip;
      if (scope.box.state === 'offline') {
        scope.box.allowed_job = false;
      } else {
        scope.box.allowed_job = true;
      }
    };

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

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

    var updatePage = function(item) {
      var hash            = {};
      hash.interval       = scope.interval;
      hash.period         = scope.period;
{'fraction_non_alphanumeric': 0.14722739899433876, 'fraction_numerical': 0.000773585569112838, 'mean_word_length': 2.153342942676572, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '2755634', 'n_tokens_mistral': 8357, 'n_tokens_neox': 8349, 'n_words': 1581}