<commit_before>/*
 * @copyright 2012 - present by OpenGamma Inc
 * @license See distribution for license
 */
$.register_module({
    name: 'og.analytics.Selector',
    dependencies: ['og.analytics.Grid'],
    obj: function () {
        var module = this;
        return function (grid) {
            var selector = this, $ = grid.$, grid_offset, grid_width, grid_height;
            var auto_scroll = function (event, scroll_top, scroll_left, start) {
                var x = event.pageX - grid_offset.left, y = event.pageY - grid_offset.top,
                    fixed_width = grid.meta.columns.width.fixed, increment = 35, interval = 100,
                    scroll_body = grid.elements.scroll_body, over_fixed = x < fixed_width;
                clearTimeout(auto_scroll.timeout);
                if (x > grid_width) scroll_body.scrollLeft(scroll_left + increment);
                else if (over_fixed && start.x > fixed_width) scroll_body.scrollLeft(scroll_left - increment);
                else
                    if (over_fixed && (auto_scroll.scroll = auto_scroll.scroll || scroll_body.find('.OG-g-sel').length))
                        scroll_body.scrollLeft(scroll_left - increment);
                if (y < grid.meta.header_height) scroll_body.scrollTop(scroll_top - increment);
                else if (y > grid_height) scroll_body.scrollTop(scroll_top + increment);
                auto_scroll.timeout = setTimeout(function () {
                    auto_scroll(event, scroll_body.scrollTop(), scroll_body.scrollLeft(), start);
                }, interval);
            };
            auto_scroll.timeout = null;
            auto_scroll.scroll = false;
            var cleanup = function () {
                $(document).off('mousemove', mousemove_observer).off('mouseup', mouseup_observer);
                clearTimeout(auto_scroll.timeout);
                auto_scroll.scroll = false;
                grid.set_viewport();
            };
            var initialize = function () {
                grid_offset = grid.elements.parent.offset();
                grid_width = grid.elements.parent.width();
                grid_height = grid.elements.parent.height();
            };
            var mousedown_observer = function (event) {
                initialize();
                var $cell, $target, right_click = event.which === 3 || event.button === 2,
                    scroll_left = grid.elements.scroll_body.scrollLeft(),
                    scroll_top = grid.elements.scroll_body.scrollTop(),
                    fixed_width = grid.meta.columns.width.fixed,
                    x = event.pageX - grid_offset.left + (event.pageX > fixed_width ? scroll_left : 0),
                    y = event.pageY - grid_offset.top + scroll_top - grid.meta.header_height;
                if (right_click) return; else cleanup();
                $cell = ($target = $(event.target)).is('.OG-g-cell') ? $target : $target.parents('.OG-g-cell:first');
                if (!$cell.length && !$target.is('.OG-g-sel')) return;
                (mousemove_observer = function (event) {mousemove({x: x, y: y}, event);})(event);
                $(document).on('mousemove', mousemove_observer).on('mouseup', mouseup_observer);
            };
            var mousemove = function (start, event) {
                event.preventDefault();
                var scroll_left = grid.elements.scroll_body.scrollLeft(),
                    scroll_top = grid.elements.scroll_body.scrollTop(),
                    fixed_width = grid.meta.columns.width.fixed,
                    x = event.pageX - grid_offset.left + (event.pageX > fixed_width ? scroll_left : 0),
                    y = event.pageY - grid_offset.top + scroll_top - grid.meta.header_height,
                    areas = [], rectangle = {};
                auto_scroll(event, scroll_top, scroll_left, start);
                rectangle.top_left = nearest_cell(Math.min(start.x, x), Math.min(start.y, y));
                rectangle.bottom_right = nearest_cell(Math.max(start.x, x), Math.max(start.y, y));
                rectangle.width = rectangle.bottom_right.right - rectangle.top_left.left;
                rectangle.height = rectangle.bottom_right.bottom - rectangle.top_left.top;
                if (rectangle.top_left.left < fixed_width) areas.push({ // fixed overlay
                    position: {top: rectangle.top_left.top, left: rectangle.top_left.left},
                    dimensions: {
                        height: rectangle.height,
                        width: rectangle.width + rectangle.top_left.left > fixed_width ?
                            fixed_width - rectangle.top_left.left : rectangle.width
                    },
                    fixed: true
                });
                if (rectangle.bottom_right.right > fixed_width) areas.push({ // scroll overlay
                    position: {top: rectangle.top_left.top, left: areas[0] ? 0 : rectangle.top_left.left - fixed_width},
                    dimensions: {
                        height: rectangle.height,
                        width: areas[0] ? rectangle.width - areas[0].dimensions.width : rectangle.width
                    },
                    fixed: false
                });
                areas.rectangle = rectangle;
                selector.render(areas);
            };
            var mousemove_observer = $.noop;
            var mouseup_observer = cleanup;
            var nearest_cell = function (x, y) {
                var top, bottom, lcv, scan = grid.meta.columns.scan.all, len = scan.length;
                for (lcv = 0; lcv < len; lcv += 1) if (scan[lcv] > x) break;
                bottom = (Math.floor(y / grid.meta.row_height) + 1) * grid.meta.row_height;
                top = bottom - grid.meta.row_height;
                return {top: top, bottom: bottom, left: scan[lcv - 1] || 0, right: scan[lcv]};
            };
            var scroll_observer = function (timeout) {
                return function () { // sync scroll instantaneously and set viewport after scroll stops
                    grid.dataman.busy(true);
                    grid.elements.scroll_head.scrollLeft(grid.elements.scroll_body.scrollLeft());
                    grid.elements.fixed_body.scrollTop(grid.elements.scroll_body.scrollTop());
                    timeout = clearTimeout(timeout) ||
                        setTimeout(function () {grid.set_viewport(function () {grid.dataman.busy(false);})}, 200);
                }
            };
            selector.render = function (rectangles) {
                if (!selector.render.memo && !rectangles) return;
                if (rectangles) selector.render.memo = rectangles;
                $(grid.id + ' .OG-g-sel').remove();
                selector.render.memo.forEach(function (rectangle) {
                    $('
').css(rectangle.position).css(rectangle.dimensions)
                        .appendTo(grid.elements[rectangle.fixed ? 'fixed_body' : 'scroll_body']);
                });
            };
            selector.render.memo = null;
            selector.selection = function () {
                if (!selector.render.memo) return null;
                var bottom_right = selector.render.memo.rectangle.bottom_right,
                    top_left = selector.render.memo.rectangle.top_left,
                    row_start = Math.floor(top_left.top / grid.meta.row_height),
                    row_end = Math.floor(bottom_right.bottom / grid.meta.row_height),
                    lcv, scan = grid.meta.columns.scan.all, rows = [], cols = [];
                for (lcv = 0; lcv < scan.length; lcv += 1)
                    if (scan[lcv] <= bottom_right.right && scan[lcv] > top_left.left) cols.push(lcv);
                    else if (scan[lcv] > bottom_right.right) break;
                for (lcv = row_start; lcv < row_end; lcv += 1) rows.push(lcv);
                return {rows: rows, cols: cols};
            };
            // initialize
            grid.elements.parent[0].onselectstart = function () {return false;};
            grid.elements.parent.on('mousedown', mousedown_observer);
            grid.elements.scroll_body.on('scroll', scroll_observer(null));
        };
    }
});<commit_msg>less scope = smaller memory footprint<commit_after>/*
 * @copyright 2012 - present by OpenGamma Inc
 * @license See distribution for license
 */
$.register_module({
    name: 'og.analytics.Selector',
    dependencies: ['og.analytics.Grid'],
    obj: function () {
        var module = this;
        return function (grid) {
            var selector = this, $ = grid.$, grid_offset, grid_width, grid_height;
            var auto_scroll = function (event, scroll_top, scroll_left, start) {
                var x = event.pageX - grid_offset.left, y = event.pageY - grid_offset.top,
                    fixed_width = grid.meta.columns.width.fixed, increment = 35, interval = 100,
                    scroll_body = grid.elements.scroll_body, over_fixed = x < fixed_width;
                clearTimeout(auto_scroll.timeout);
                if (x > grid_width) scroll_body.scrollLeft(scroll_left + increment);
                else if (over_fixed && start.x > fixed_width) scroll_body.scrollLeft(scroll_left - increment);
                else
                    if (over_fixed && (auto_scroll.scroll = auto_scroll.scroll || scroll_body.find('.OG-g-sel').length))
                        scroll_body.scrollLeft(scroll_left - increment);
                if (y < grid.meta.header_height) scroll_body.scrollTop(scroll_top - increment);
                else if (y > grid_height) scroll_body.scrollTop(scroll_top + increment);
                auto_scroll.timeout = setTimeout(function () {
                    auto_scroll(event, scroll_body.scrollTop(), scroll_body.scrollLeft(), start);
                }, interval);
            };
            auto_scroll.timeout = null;
            auto_scroll.scroll = false;
            var cleanup = function () {
                $(document).off('mousemove', mousemove_observer).off('mouseup', mouseup_observer);
                clearTimeout(auto_scroll.timeout);
                auto_scroll.scroll = false;
                grid.set_viewport();
            };
            var initialize = function () {
                grid_offset = grid.elements.parent.offset();
                grid_width = grid.elements.parent.width();
                grid_height = grid.elements.parent.height();
            };
            var mousedown_observer = function (event) {
                initialize();
                var $cell, $target, right_click = event.which === 3 || event.button === 2,
                    scroll_left = grid.elements.scroll_body.scrollLeft(),
                    scroll_top = grid.elements.scroll_body.scrollTop(),
                    fixed_width = grid.meta.columns.width.fixed,
                    x = event.pageX - grid_offset.left + (event.pageX > fixed_width ? scroll_left : 0),
                    y = event.pageY - grid_offset.top + scroll_top - grid.meta.header_height;
                if (right_click) return; else cleanup();
                $cell = ($target = $(event.target)).is('.OG-g-cell') ? $target : $target.parents('.OG-g-cell:first');
                if (!$cell.length && !$target.is('.OG-g-sel')) return;
                (mousemove_observer = (function (x, y) {
                    return function (event) {mousemove({x: x, y: y}, event);};
                })(x, y))(event);
                $(document).on('mousemove', mousemove_observer).on('mouseup', mouseup_observer);
            };
            var mousemove = function (start, event) {
                event.preventDefault();
                var scroll_left = grid.elements.scroll_body.scrollLeft(),
                    scroll_top = grid.elements.scroll_body.scrollTop(),
                    fixed_width = grid.meta.columns.width.fixed,
                    x = event.pageX - grid_offset.left + (event.pageX > fixed_width ? scroll_left : 0),
                    y = event.pageY - grid_offset.top + scroll_top - grid.meta.header_height,
                    areas = [], rectangle = {};
                auto_scroll(event, scroll_top, scroll_left, start);
                rectangle.top_left = nearest_cell(Math.min(start.x, x), Math.min(start.y, y));
                rectangle.bottom_right = nearest_cell(Math.max(start.x, x), Math.max(start.y, y));
                rectangle.width = rectangle.bottom_right.right - rectangle.top_left.left;
                rectangle.height = rectangle.bottom_right.bottom - rectangle.top_left.top;
                if (rectangle.top_left.left < fixed_width) areas.push({ // fixed overlay
                    position: {top: rectangle.top_left.top, left: rectangle.top_left.left},
                    dimensions: {
                        height: rectangle.height,
                        width: rectangle.width + rectangle.top_left.left > fixed_width ?
                            fixed_width - rectangle.top_left.left : rectangle.width
                    },
                    fixed: true
                });
                if (rectangle.bottom_right.right > fixed_width) areas.push({ // scroll overlay
                    position: {top: rectangle.top_left.top, left: areas[0] ? 0 : rectangle.top_left.left - fixed_width},
                    dimensions: {
                        height: rectangle.height,
                        width: areas[0] ? rectangle.width - areas[0].dimensions.width : rectangle.width
                    },
                    fixed: false
                });
                areas.rectangle = rectangle;
                selector.render(areas);
            };
            var mousemove_observer = $.noop;
            var mouseup_observer = cleanup;
            var nearest_cell = function (x, y) {
                var top, bottom, lcv, scan = grid.meta.columns.scan.all, len = scan.length;
                for (lcv = 0; lcv < len; lcv += 1) if (scan[lcv] > x) break;
                bottom = (Math.floor(y / grid.meta.row_height) + 1) * grid.meta.row_height;
                top = bottom - grid.meta.row_height;
                return {top: top, bottom: bottom, left: scan[lcv - 1] || 0, right: scan[lcv]};
            };
            var scroll_observer = function (timeout) {
                return function () { // sync scroll instantaneously and set viewport after scroll stops
                    grid.dataman.busy(true);
                    grid.elements.scroll_head.scrollLeft(grid.elements.scroll_body.scrollLeft());
                    grid.elements.fixed_body.scrollTop(grid.elements.scroll_body.scrollTop());
                    timeout = clearTimeout(timeout) ||
                        setTimeout(function () {grid.set_viewport(function () {grid.dataman.busy(false);})}, 200);
                }
            };
            selector.render = function (rectangles) {
                if (!selector.render.memo && !rectangles) return;
                if (rectangles) selector.render.memo = rectangles;
                $(grid.id + ' .OG-g-sel').remove();
                selector.render.memo.forEach(function (rectangle) {
                    $('
').css(rectangle.position).css(rectangle.dimensions)
                        .appendTo(grid.elements[rectangle.fixed ? 'fixed_body' : 'scroll_body']);
                });
            };
            selector.render.memo = null;
            selector.selection = function () {
                if (!selector.render.memo) return null;
                var bottom_right = selector.render.memo.rectangle.bottom_right,
                    top_left = selector.render.memo.rectangle.top_left,
                    row_start = Math.floor(top_left.top / grid.meta.row_height),
                    row_end = Math.floor(bottom_right.bottom / grid.meta.row_height),
                    lcv, scan = grid.meta.columns.scan.all, rows = [], cols = [];
                for (lcv = 0; lcv < scan.length; lcv += 1)
                    if (scan[lcv] <= bottom_right.right && scan[lcv] > top_left.left) cols.push(lcv);
                    else if (scan[lcv] > bottom_right.right) break;
                for (lcv = row_start; lcv < row_end; lcv += 1) rows.push(lcv);
                return {rows: rows, cols: cols};
            };
            // initialize
            grid.elements.parent[0].onselectstart = function () {return false;};
            grid.elements.parent.on('mousedown', mousedown_observer);
            grid.elements.scroll_body.on('scroll', scroll_observer(null));
        };
    }
});<|endoftext|>


<commit_before>'use strict';

function cdMyDojosCtrl($q, $rootScope, $localStorage, $scope, $state, $stateParams, cdDojoService, $location, auth, tableUtils, alertService, $translate, AlertBanner) {
  $scope.itemsPerPage = 10;
  var currentUser;
  $scope.myDojosPageTitle = $translate.instant('My Dojos'); //sets breadcrumb page title
  var errorMsg = $translate.instant('error.general');

  $rootScope.$watch(function() {
      return $location.path();
    },
    function(path){
      if(angular.element('.alert-message').hasClass('active') &&
        angular.element('.alert-message').hasClass('info') && path !== '/'){
        angular.element('.alert-message').removeClass('active');
      }
    }
  );

  $scope.pageChanged = function(){
    $scope.loadPage(currentUser, false);
  };

  $scope.editDojo = function(dojo) {
    cdDojoService.setDojo(dojo, function(response) {
      $location.path('/dashboard/edit-dojo');
    }, function (err){
      if(err){
        alertService.showError(
          $translate.instant('An error has occurred while editing dojo')
        );
      }
    });
  };

  $scope.deleteDojo = function(dojo) {
    var hasPermission = false;
    canDeleteDojo(dojo, function (result) {
      hasPermission = result;
      if(hasPermission) {
        cdDojoService.delete(dojo.id, function(response) {
          $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true });
        }, function (err) {
          if(err){
            alertService.showError(
              $translate.instant('An error has occurred while deleting dojo')
            );
          }
        });
      } else {
        alertService.showAlert($translate.instant('You do not have permission to delete this Dojo'));
      }
    });
  };

  function canDeleteDojo (dojo, cb) {
    //User can delete dojo if:
    // - They have the champion user type
    // - They have the Dojo Admin Permission
    // - They are the current owner of the Dojo
    var isChampion;
    var isDojoAdmin;
    var isDojoOwner;
    var query = {userId: currentUser.id, dojoId:dojo.id};

    function getUsersDojos() {
      return $q(function(resolve, reject) {
        cdDojoService.getUsersDojos(query, function (response) {
          var userDojo    = response[0];
          isChampion  = _.contains(userDojo.userTypes, 'champion');
          isDojoAdmin = _.find(userDojo.userPermissions, function(userPermission) {
                          return userPermission.name === 'dojo-admin';
                        });
          if(userDojo.owner === 1) isDojoOwner = true;
          else isDojoOwner = false;
           if(isChampion && isDojoAdmin && isDojoOwner) resolve(true);
           else resolve(false);
        }, function (err) {
          reject( $translate.instant('Error loading user dojo entity'));
        });
      });
    }

    getUsersDojos().then(function (result) {
      cb(result);
    }, function (err) {
      alertService.showError(errorMsg);
    });
  }

  function getUsersDojos(query, cb) {
    cdDojoService.getUsersDojos(query, function (response) {
      return cb(null, response);
    }, function (err) {
      return cb(err);
    });
  }

  $scope.loadPage = function(currentUser, resetFlag, cb){
    cb = cb || function(){};

    var loadPageData = tableUtils.loadPage(resetFlag, $scope.itemsPerPage, $scope.pageNo, $scope.filterQuery, $scope.sort);
    $scope.pageNo = loadPageData.pageNo;
    $scope.myDojos = [];

    var search = {
      sort: {
        created: 1
      },
      from: loadPageData.skip,
      size: $scope.itemsPerPage
    };

    cdDojoService.myDojos(search, currentUser).then(function(result) {
      async.each(result.records, function (dojo, cb) {
          var query = {userId: currentUser.id, dojoId: dojo.id};
          getUsersDojos(query, function (err, response) {
            if(err) return cb(err);
            var userDojo = response[0];
            var isChampion = _.contains(userDojo.userTypes, 'champion');
            var isTicketingAdmin = _.find(userDojo.userPermissions, function (permission) {
              return permission.name === 'ticketing-admin';
            });
            var isDojoAdmin = _.find(userDojo.userPermissions, function (permission) {
              return permission.name === 'dojo-admin';
            });
            dojo.isChampion = isChampion;
            dojo.isTicketingAdmin = isTicketingAdmin;
            dojo.isDojoAdmin = isDojoAdmin;
            dojo.country = dojo.country.alpha2.toLowerCase();
            var path = dojo.urlSlug.split('/');
            path.splice(0, 1);
            path = path.join('/');
            dojo.path = path;
            canDeleteDojo(dojo, function (result) {
              dojo.canDelete$ = result;
            });
            return cb();
        });
      }, function (err) {
        $scope.myDojos = result.records;
        $scope.totalItems = result.total;

        cdDojoService.uncompletedDojos(function(response){
          if(response.length > 0 && !$localStorage.recommendedPracticesAlertShown){
            var uncompletedDojo = response[0];
            AlertBanner.publish({
              type: 'info',
              message: '' + $translate.instant('Please click here to complete all of the recommended practices for') + ' ' + uncompletedDojo.name + '',
              autoClose: false,
              onOpen: function() {
                angular.element('.a-no-float').on('click', function(e){
                  if(angular.element('.alert-message').hasClass('active')){
                    angular.element('.alert-message').removeClass('active');
                  }
                });
              },
              onClose: function() {
                $localStorage.recommendedPracticesAlertShown = true;
              }
            });
          }
          return cb();
        });
      });
    }, function(err) {
      alertService.showError(
        $translate.instant('An error has occurred while loading Dojos')
      );

      return cb(err);
    });

  };

  auth.get_loggedin_user(function(user) {
    currentUser = user;
    $scope.loadPage(currentUser, true);
  });
}

angular.module('cpZenPlatform')
  .controller('my-dojos-controller', ['$q', '$rootScope', '$localStorage', '$scope', '$state', '$stateParams', 'cdDojoService', '$location', 'auth', 'tableUtils', 'alertService', '$translate', 'AlertBanner', cdMyDojosCtrl]);
<commit_msg>patched so it now works with migrated data<commit_after>'use strict';

function cdMyDojosCtrl($q, $rootScope, $localStorage, $scope, $state, $stateParams, cdDojoService, $location, auth, tableUtils, alertService, $translate, AlertBanner) {
  $scope.itemsPerPage = 10;
  var currentUser;
  $scope.myDojosPageTitle = $translate.instant('My Dojos'); //sets breadcrumb page title
  var errorMsg = $translate.instant('error.general');

  $rootScope.$watch(function() {
      return $location.path();
    },
    function(path){
      if(angular.element('.alert-message').hasClass('active') &&
        angular.element('.alert-message').hasClass('info') && path !== '/'){
        angular.element('.alert-message').removeClass('active');
      }
    }
  );

  $scope.pageChanged = function(){
    $scope.loadPage(currentUser, false);
  };

  $scope.editDojo = function(dojo) {
    cdDojoService.setDojo(dojo, function(response) {
      $location.path('/dashboard/edit-dojo');
    }, function (err){
      if(err){
        alertService.showError(
          $translate.instant('An error has occurred while editing dojo')
        );
      }
    });
  };

  $scope.deleteDojo = function(dojo) {
    var hasPermission = false;
    canDeleteDojo(dojo, function (result) {
      hasPermission = result;
      if(hasPermission) {
        cdDojoService.delete(dojo.id, function(response) {
          $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true });
        }, function (err) {
          if(err){
            alertService.showError(
              $translate.instant('An error has occurred while deleting dojo')
            );
          }
        });
      } else {
        alertService.showAlert($translate.instant('You do not have permission to delete this Dojo'));
      }
    });
  };

  function canDeleteDojo (dojo, cb) {
    //User can delete dojo if:
    // - They have the champion user type
    // - They have the Dojo Admin Permission
    // - They are the current owner of the Dojo
    var isChampion;
    var isDojoAdmin;
    var isDojoOwner;
    var query = {userId: currentUser.id, dojoId:dojo.id};

    function getUsersDojos() {
      return $q(function(resolve, reject) {
        cdDojoService.getUsersDojos(query, function (response) {
          var userDojo    = response[0];
          isChampion  = _.contains(userDojo.userTypes, 'champion');
          isDojoAdmin = _.find(userDojo.userPermissions, function(userPermission) {
                          return userPermission.name === 'dojo-admin';
                        });
          if(userDojo.owner === 1) isDojoOwner = true;
          else isDojoOwner = false;
           if(isChampion && isDojoAdmin && isDojoOwner) resolve(true);
           else resolve(false);
        }, function (err) {
          reject( $translate.instant('Error loading user dojo entity'));
        });
      });
    }

    getUsersDojos().then(function (result) {
      cb(result);
    }, function (err) {
      alertService.showError(errorMsg);
    });
  }

  function getUsersDojos(query, cb) {
    cdDojoService.getUsersDojos(query, function (response) {
      return cb(null, response);
    }, function (err) {
      return cb(err);
    });
  }

  $scope.loadPage = function(currentUser, resetFlag, cb){
    cb = cb || function(){};

    var loadPageData = tableUtils.loadPage(resetFlag, $scope.itemsPerPage, $scope.pageNo, $scope.filterQuery, $scope.sort);
    $scope.pageNo = loadPageData.pageNo;
    $scope.myDojos = [];

    var search = {
      sort: {
        created: 1
      },
      from: loadPageData.skip,
      size: $scope.itemsPerPage
    };

    cdDojoService.myDojos(search, currentUser).then(function(result) {
      async.each(result.records, function (dojo, cb) {
          var query = {userId: currentUser.id, dojoId: dojo.id};
          getUsersDojos(query, function (err, response) {
            if(err) return cb(err);
            var userDojo = response[0];
            var isChampion = _.contains(userDojo.userTypes, 'champion');
            var isTicketingAdmin = _.find(userDojo.userPermissions, function (permission) {
              return permission.name === 'ticketing-admin';
            });
            var isDojoAdmin = _.find(userDojo.userPermissions, function (permission) {
              return permission.name === 'dojo-admin';
            });
            dojo.isChampion = isChampion;
            dojo.isTicketingAdmin = isTicketingAdmin;
            dojo.isDojoAdmin = isDojoAdmin;
            dojo.country = dojo.alpha2.toLowerCase();
            var path = dojo.urlSlug.split('/');
            path.splice(0, 1);
            path = path.join('/');
            dojo.path = path;
            canDeleteDojo(dojo, function (result) {
              dojo.canDelete$ = result;
            });
            return cb();
        });
      }, function (err) {
        $scope.myDojos = result.records;
        $scope.totalItems = result.total;

        cdDojoService.uncompletedDojos(function(response){
          if(response.length > 0 && !$localStorage.recommendedPracticesAlertShown){
            var uncompletedDojo = response[0];
            AlertBanner.publish({
              type: 'info',
              message: '' + $translate.instant('Please click here to complete all of the recommended practices for') + ' ' + uncompletedDojo.name + '',
              autoClose: false,
              onOpen: function() {
                angular.element('.a-no-float').on('click', function(e){
                  if(angular.element('.alert-message').hasClass('active')){
                    angular.element('.alert-message').removeClass('active');
                  }
                });
              },
              onClose: function() {
                $localStorage.recommendedPracticesAlertShown = true;
              }
            });
          }
          return cb();
        });
      });
    }, function(err) {
      alertService.showError(
        $translate.instant('An error has occurred while loading Dojos')
      );

      return cb(err);
    });

  };

  auth.get_loggedin_user(function(user) {
    currentUser = user;
    $scope.loadPage(currentUser, true);
  });
}

angular.module('cpZenPlatform')
  .controller('my-dojos-controller', ['$q', '$rootScope', '$localStorage', '$scope', '$state', '$stateParams', 'cdDojoService', '$location', 'auth', 'tableUtils', 'alertService', '$translate', 'AlertBanner', cdMyDojosCtrl]);
<|endoftext|>


<commit_before>/* global _su, strWrap, Point, HexGrid */

class SkillPlanner {
  constructor () {
    this.checkDependencies()

    // Skill tree data
    this.skills = {}
    this.groups = {}
    this.coords = {}
    this.links = {}
    this.tree = {}
    this.locale = {}
    this.colors = {}

    this.reset()

    // DEBUG
    this.DEBUG = false
    // END DEBUG
    this.initGrid()
  }

  checkDependencies () {
    if (typeof HexGrid !== 'function' || !_su) {
      console.log('Warning! Required dependencies not found. Please contact the dev for more info.')
    }
  }

  reset () {
    this.category = 'Firepower'
    this.active = new Set()
    this.orphan = new Set()
    this.possible = new Set()
    this.visibleActive = new Set()
    this.visibleOrphan = new Set()
    this.visiblePossible = new Set()
    this.effects = {}
    this.mouseDown = false
    this.prevHexId = null
  }

  initGrid () {
    let radius = 48
    let padding = 16
    let origin = new Point(-96, 128)

    let hexGrid = new HexGrid(radius, padding, origin)
    hexGrid.bindCanvas(document.getElementById('hex'))
    this.hexGrid = hexGrid

    let linkGrid = new HexGrid(radius, padding, origin)
    linkGrid.bindCanvas(document.getElementById('link'))
    this.linkGrid = linkGrid

    this.initEvents()
    this.initInterface()
  }

  initEvents () {
    let hexGrid = this.hexGrid
    let onMouseDown = (e) => {
      e.preventDefault()
      this.mouseDown = true
    }

    let onMouseMove = (e) => {
      e.preventDefault()
      if (this.mouseDown) {
        let point = hexGrid.getCursorCanvasPoint(e)
        let hex = hexGrid.getHexAtPoint(point)

        if (hex) {
          let hexId = this.skillHexToId(hex)

          if (hexId !== this.prevHexId) {
            this.toggleNode(hexId)
            this.prevHexId = hexId
          }
        }
      }
    }

    let onMouseUp = (e) => {
      e.preventDefault()
      let point = hexGrid.getCursorCanvasPoint(e)
      let hex = hexGrid.getHexAtPoint(point)

      if (hex) {
        let hexId = this.skillHexToId(hex)

        if (hexId !== this.prevHexId) {
          this.toggleNode(hexId)
          this.prevHexId = hexId
        }
      }
      this.prevHexId = null
      this.mouseDown = false
    }

    this.bind('mousedown', onMouseDown, this)
    this.bind('mousemove', onMouseMove, this)
    this.bind('mouseup', onMouseUp, this)
  }

  initInterface () {
    let bindEvent = (element, event, fn) => {
      document.getElementById(element).addEventListener('click', fn, false)
    }

    bindEvent('toggle-firepower', 'click', () => {
      this.category = 'Firepower'
      this.draw()
    })

    bindEvent('toggle-survival', 'click', () => {
      this.category = 'Survival'
      this.draw()
    })

    bindEvent('toggle-mobility', 'click', () => {
      this.category = 'Mobility'
      this.draw()
    })

    bindEvent('toggle-jumpjets', 'click', () => {
      this.category = 'Jump Jets'
      this.draw()
    })

    bindEvent('toggle-operations', 'click', () => {
      this.category = 'Operations'
      this.draw()
    })

    bindEvent('toggle-sensors', 'click', () => {
      this.category = 'Sensors'
      this.draw()
    })

    bindEvent('toggle-auxiliary', 'click', () => {
      this.category = 'Auxiliary'
      this.draw()
    })

    bindEvent('add-section', 'click', () => {
      this.active = _su.union(this.active, this.tree[this.category].nodes)
      this.draw()
    })

    bindEvent('clear-section', 'click', () => {
      this.active = _su.difference(this.active, this.tree[this.category].nodes)
      this.draw()
    })

    bindEvent('clear-all', 'click', () => {
      this.active = new Set()
      this.draw()
    })

    bindEvent('export', 'click', () => {
      window.prompt('Please copy this Session String:', this.exportSession())
    })

    bindEvent('import', 'click', () => {
      let sessionString = window.prompt('Please enter the Session String to import:')
      this.importSession(sessionString)
    })
  }

  loadData (cb) {
    let loadColors = this.getJSON('./data/colors.json')
      .then((colors) => {
        this.colors = colors
      })
      .catch((err) => {
        console.error(err)
      })

    let loadSkills = this.getJSON('./data/latest.json')
      .then((tree) => {
        for (let category in tree) {
          let subtree = tree[category]
          // Load all skill nodes
          Object.assign(this.skills, subtree.nodes)

          this.tree[category] = {
            'root': subtree['root'],
            'nodes': new Set()
          }

          // Hex coord to node lookup
          this.coords[category] = {}
          for (let id in subtree.nodes) {
            let skill = this.skills[id]
            let coord = `(${skill.col}, ${skill.row})`
            this.coords[category][coord] = id

            // Add node to subtree
            this.tree[category]['nodes'].add(id)
          }
        }

        for (let id in this.skills) {
          let skill = this.skills[id]

          // Add node to group
          let group = skill['group']
          if (this.groups[group]) {
            this.groups[group].add(id)
          } else {
            this.groups[group] = new Set([id])
          }

          // Process node links
          this.links[id] = []
          let links = skill.links
          for (let i = 0, len = links.length; i < len; i++) {
            if (links[i]) {
              this.links[id].push(links[i])
            }
          }
        }
      })
      .catch((err) => {
        console.error(err)
      })

    let loadLocale = this.getJSON('./data/locale.json')
      .then((locale) => {
        this.locale = locale
      })
      .catch((err) => {
        console.error(err)
      })

    Promise.all([loadColors, loadSkills, loadLocale])
      .then(() => {
        typeof cb === 'function' && cb()
      })
  }

  bind (action, fn, ctx) {
    document.addEventListener(action, fn.bind(ctx || this), false)
  }

  getJSON (url) {
    return new Promise((resolve, reject) => {
      let req = new window.XMLHttpRequest()
      req.open('GET', url, true)

      req.onload = function () {
        if (this.status >= 200 && this.status < 400) {
          resolve(JSON.parse(this.response))
        } else {
          reject(new Error('Failed to retrieve URL!'))
        }
      }

      req.send()
    })
  }

  skillIdToHex (id) {
    let col = this.skills[id].col
    let row = this.skills[id].row
    return `(${col}, ${row})`
  }

  skillHexToId (hex) {
    let col = hex.col
    let row = hex.row
    return this.coords[this.category][`(${col}, ${row})`]
  }

  // Toggle node state by ID
  toggleNode (nodeId) {
    // Ignore invalid nodes
    if (!nodeId || !this.tree[this.category].nodes.has(nodeId)) { return }

    if (this.active.has(nodeId)) {
      this.active.delete(nodeId)
    } else {
      this.active.add(nodeId)
    }
    this.draw()
  }

  // Draw skill node and label
  drawNode (skill, state) {
    let col = skill.col
    let row = skill.row
    this.hexGrid.drawHexCell(col, row, this.colors['cell'][state])

    let name = skill.name
    let text = (name.length > 12) ? strWrap(name) : name
    this.hexGrid.drawHexLabel(col, row, text, undefined, this.colors['label'][state])
  }

  // Recalculate state using DFS
  update () {
    let updateNodeState = () => {
      let active = new Set(this.active)
      let possible = new Set()
      let visited = new Set()

      let root = this.tree[this.category].root
      let stack = [root]

      if (!active.has(root)) {
        this.orphan = this.active
        this.possible = new Set(stack)
        return
      }

      while (stack.length) {
        let cur = stack.pop()
        active.delete(cur)
        visited.add(cur)
        let neighbors = planner.links[cur]
        for (let i = 0, len = neighbors.length; i < len; i++) {
          let neighbor = neighbors[i]
          // Neighbor not visited nor queued
          if (!visited.has(neighbor) && stack.indexOf(neighbor) < 0) {
            // Neighbor active and reachable
            if (active.has(neighbor)) {
              stack.push(neighbor)
            } else {
              possible.add(neighbor)
            }
          }
        }
      }

      this.orphan = active
      this.possible = possible
    }

    let updateNodeVisibility = () => {
      let categoryNodes = this.tree[this.category].nodes
      this.visibleActive = _su.intersect(this.active, categoryNodes)
      this.visibleOrphan = _su.intersect(this.orphan, categoryNodes)
      this.visiblePossible = _su.intersect(this.possible, categoryNodes)
    }

    let updateNodeEffects = () => {
      this.effects = {}

      for (let id of this.active) {
        let effects = this.skills[id]['effects']

        for (let i = 0, len = effects.length; i < len; i++) {
          let effect = effects[i]
          if (!this.effects[effect]) {
            this.effects[effect] = 0
          }
          this.effects[effect] += 1
        }
      }
    }

    // TODO: make interface updates a separate step
    let updateInterface = () => {
      document.getElementById('active-nodes').value = `${this.active.size} / 91`
      document.getElementById('category-nodes').value = `${this.visibleActive.size} / ${this.tree[this.category].nodes.size}`

      let effectText = ''
      for (let effect in this.effects) {
        // TODO: load proper values later!
        let effectValue = 1
        effectText += `${this.locale[effect]}: ${this.effects[effect] * effectValue}\n`
      }
      document.getElementById('effects-display').value = effectText
    }

    updateNodeState()
    updateNodeVisibility()
    updateNodeEffects()
    updateInterface()
    this.saveSession()
  }

  // Draw planner; calls state change internally
  draw () {
    this.update()
    this.hexGrid.clear()
    this.linkGrid.clear()
    let categoryNodes = this.tree[this.category].nodes

    // Draw nodes of current category
    for (let id of categoryNodes) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'inactive')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['inactive'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['inactive'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, this.colors['label']['inactive'])
        }  // END DEBUG
      }
    }

    // Draw visible active nodes and links
    for (let id of this.visibleActive) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'active')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['active'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['active'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['active'])
        }  // END DEBUG
      }
    }

    // Draw visible possible nodes and links
    for (let id of this.visiblePossible) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'possible')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['possible'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['possible'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['possible'])
        }  // END DEBUG
      }
    }

    // Draw visible orphan nodes and links
    for (let id of this.visibleOrphan) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'orphan')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['orphan'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['orphan'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['orphan'])
        }  // END DEBUG
      }
    }
  }

  exportImage () {
    let image = this.hexGrid.canvas.toDataURL('image/png')
    let file = image.replace('image/png', 'image/octet-stream')
    window.location.href = file
  }

  saveSession () {
    let session = JSON.stringify([...this.active])
    window.sessionStorage.setItem('active', session)
  }

  loadSesssion () {
    let session = window.sessionStorage.getItem('active')
    if (session) {
      this.active = new Set(JSON.parse(session))
    }
  }

  exportSession () {
    return [...this.active].map((id) => parseInt(id, 10)).join('-')
  }

  importSession (sessionString) {
    this.active = new Set(sessionString.split('-')) || new Set()
    this.draw()
  }
}

const planner = new SkillPlanner()
planner.loadData(() => {
  planner.loadSesssion()
  planner.draw()
})
<commit_msg>Added Ctrl+click bulk de/activation of nodes<commit_after>/* global _su, strWrap, Point, HexGrid */

class SkillPlanner {
  constructor () {
    this.checkDependencies()

    // Skill tree data
    this.skills = {}
    this.groups = {}
    this.coords = {}
    this.links = {}
    this.tree = {}
    this.locale = {}
    this.colors = {}

    this.reset()

    // DEBUG
    this.DEBUG = false
    // END DEBUG
    this.initGrid()
  }

  checkDependencies () {
    if (typeof HexGrid !== 'function' || !_su) {
      console.log('Warning! Required dependencies not found. Please contact the dev for more info.')
    }
  }

  reset () {
    this.category = 'Firepower'
    this.active = new Set()
    this.orphan = new Set()
    this.possible = new Set()
    this.visibleActive = new Set()
    this.visibleOrphan = new Set()
    this.visiblePossible = new Set()
    this.effects = {}
    this.mouseDown = false
    this.prevHexId = null
  }

  initGrid () {
    let radius = 48
    let padding = 16
    let origin = new Point(-96, 128)

    let hexGrid = new HexGrid(radius, padding, origin)
    hexGrid.bindCanvas(document.getElementById('hex'))
    this.hexGrid = hexGrid

    let linkGrid = new HexGrid(radius, padding, origin)
    linkGrid.bindCanvas(document.getElementById('link'))
    this.linkGrid = linkGrid

    this.initEvents()
    this.initInterface()
  }

  initEvents () {
    let hexGrid = this.hexGrid
    let onMouseDown = (e) => {
      e.preventDefault()
      this.mouseDown = true
    }

    let onMouseMove = (e) => {
      e.preventDefault()
      if (this.mouseDown) {
        let point = hexGrid.getCursorCanvasPoint(e)
        let hex = hexGrid.getHexAtPoint(point)

        if (hex) {
          let hexId = this.skillHexToId(hex)

          if (hexId !== this.prevHexId) {
            this.toggleNode(hexId)
            this.prevHexId = hexId
          }
        }
      }
    }

    let onMouseUp = (e) => {
      e.preventDefault()
      let point = hexGrid.getCursorCanvasPoint(e)
      let hex = hexGrid.getHexAtPoint(point)

      if (hex) {
        let hexId = this.skillHexToId(hex)

        if (e.ctrlKey) {
          let activate = !this.active.has(hexId)
          let nodeGroup = this.groups[this.skills[hexId].group]
          for (let node of nodeGroup) {
            if (activate) {
              this.activateNode(node)
            } else {
              this.deactivateNode(node)
            }
          }
        } else {
          if (hexId !== this.prevHexId) {
            this.toggleNode(hexId)
            this.prevHexId = hexId
          }
        }
      }
      this.prevHexId = null
      this.mouseDown = false
    }

    this.bind('mousedown', onMouseDown, this)
    this.bind('mousemove', onMouseMove, this)
    this.bind('mouseup', onMouseUp, this)
  }

  initInterface () {
    let bindEvent = (element, event, fn) => {
      document.getElementById(element).addEventListener('click', fn, false)
    }

    bindEvent('toggle-firepower', 'click', () => {
      this.category = 'Firepower'
      this.draw()
    })

    bindEvent('toggle-survival', 'click', () => {
      this.category = 'Survival'
      this.draw()
    })

    bindEvent('toggle-mobility', 'click', () => {
      this.category = 'Mobility'
      this.draw()
    })

    bindEvent('toggle-jumpjets', 'click', () => {
      this.category = 'Jump Jets'
      this.draw()
    })

    bindEvent('toggle-operations', 'click', () => {
      this.category = 'Operations'
      this.draw()
    })

    bindEvent('toggle-sensors', 'click', () => {
      this.category = 'Sensors'
      this.draw()
    })

    bindEvent('toggle-auxiliary', 'click', () => {
      this.category = 'Auxiliary'
      this.draw()
    })

    bindEvent('add-section', 'click', () => {
      this.active = _su.union(this.active, this.tree[this.category].nodes)
      this.draw()
    })

    bindEvent('clear-section', 'click', () => {
      this.active = _su.difference(this.active, this.tree[this.category].nodes)
      this.draw()
    })

    bindEvent('clear-all', 'click', () => {
      this.active = new Set()
      this.draw()
    })

    bindEvent('export', 'click', () => {
      window.prompt('Please copy this Session String:', this.exportSession())
    })

    bindEvent('import', 'click', () => {
      let sessionString = window.prompt('Please enter the Session String to import:')
      this.importSession(sessionString)
    })
  }

  loadData (cb) {
    let loadColors = this.getJSON('./data/colors.json')
      .then((colors) => {
        this.colors = colors
      })
      .catch((err) => {
        console.error(err)
      })

    let loadSkills = this.getJSON('./data/latest.json')
      .then((tree) => {
        for (let category in tree) {
          let subtree = tree[category]
          // Load all skill nodes
          Object.assign(this.skills, subtree.nodes)

          this.tree[category] = {
            'root': subtree['root'],
            'nodes': new Set()
          }

          // Hex coord to node lookup
          this.coords[category] = {}
          for (let id in subtree.nodes) {
            let skill = this.skills[id]
            let coord = `(${skill.col}, ${skill.row})`
            this.coords[category][coord] = id

            // Add node to subtree
            this.tree[category]['nodes'].add(id)
          }
        }

        for (let id in this.skills) {
          let skill = this.skills[id]

          // Add node to group
          let group = skill['group']
          if (this.groups[group]) {
            this.groups[group].add(id)
          } else {
            this.groups[group] = new Set([id])
          }

          // Process node links
          this.links[id] = []
          let links = skill.links
          for (let i = 0, len = links.length; i < len; i++) {
            if (links[i]) {
              this.links[id].push(links[i])
            }
          }
        }
      })
      .catch((err) => {
        console.error(err)
      })

    let loadLocale = this.getJSON('./data/locale.json')
      .then((locale) => {
        this.locale = locale
      })
      .catch((err) => {
        console.error(err)
      })

    Promise.all([loadColors, loadSkills, loadLocale])
      .then(() => {
        typeof cb === 'function' && cb()
      })
  }

  bind (action, fn, ctx) {
    document.addEventListener(action, fn.bind(ctx || this), false)
  }

  getJSON (url) {
    return new Promise((resolve, reject) => {
      let req = new window.XMLHttpRequest()
      req.open('GET', url, true)

      req.onload = function () {
        if (this.status >= 200 && this.status < 400) {
          resolve(JSON.parse(this.response))
        } else {
          reject(new Error('Failed to retrieve URL!'))
        }
      }

      req.send()
    })
  }

  skillIdToHex (id) {
    let col = this.skills[id].col
    let row = this.skills[id].row
    return `(${col}, ${row})`
  }

  skillHexToId (hex) {
    let col = hex.col
    let row = hex.row
    return this.coords[this.category][`(${col}, ${row})`]
  }

  toggleNode (nodeId) {
    // Ignore invalid/non-visible nodes
    if (!nodeId || !this.tree[this.category].nodes.has(nodeId)) { return }

    if (this.active.has(nodeId)) {
      this.active.delete(nodeId)
    } else {
      this.active.add(nodeId)
    }
    this.draw()
  }

  activateNode (nodeId) {
    // Ignore invalid/non-visible nodes
    if (!nodeId || !this.tree[this.category].nodes.has(nodeId)) { return }
    this.active.add(nodeId)
    this.draw()
  }

  deactivateNode (nodeId) {
    // Ignore invalid/non-visible nodes
    if (!nodeId || !this.tree[this.category].nodes.has(nodeId)) { return }
    this.active.delete(nodeId)
    this.draw()
  }

  // Draw skill node and label
  drawNode (skill, state) {
    let col = skill.col
    let row = skill.row
    this.hexGrid.drawHexCell(col, row, this.colors['cell'][state])

    let name = skill.name
    let text = (name.length > 12) ? strWrap(name) : name
    this.hexGrid.drawHexLabel(col, row, text, undefined, this.colors['label'][state])
  }

  // Recalculate state using DFS
  update () {
    let updateNodeState = () => {
      let active = new Set(this.active)
      let possible = new Set()
      let visited = new Set()

      let root = this.tree[this.category].root
      let stack = [root]

      if (!active.has(root)) {
        this.orphan = this.active
        this.possible = new Set(stack)
        return
      }

      while (stack.length) {
        let cur = stack.pop()
        active.delete(cur)
        visited.add(cur)
        let neighbors = planner.links[cur]
        for (let i = 0, len = neighbors.length; i < len; i++) {
          let neighbor = neighbors[i]
          // Neighbor not visited nor queued
          if (!visited.has(neighbor) && stack.indexOf(neighbor) < 0) {
            // Neighbor active and reachable
            if (active.has(neighbor)) {
              stack.push(neighbor)
            } else {
              possible.add(neighbor)
            }
          }
        }
      }

      this.orphan = active
      this.possible = possible
    }

    let updateNodeVisibility = () => {
      let categoryNodes = this.tree[this.category].nodes
      this.visibleActive = _su.intersect(this.active, categoryNodes)
      this.visibleOrphan = _su.intersect(this.orphan, categoryNodes)
      this.visiblePossible = _su.intersect(this.possible, categoryNodes)
    }

    let updateNodeEffects = () => {
      this.effects = {}

      for (let id of this.active) {
        let effects = this.skills[id]['effects']

        for (let i = 0, len = effects.length; i < len; i++) {
          let effect = effects[i]
          if (!this.effects[effect]) {
            this.effects[effect] = 0
          }
          this.effects[effect] += 1
        }
      }
    }

    // TODO: make interface updates a separate step
    let updateInterface = () => {
      document.getElementById('active-nodes').value = `${this.active.size} / 91`
      document.getElementById('category-nodes').value = `${this.visibleActive.size} / ${this.tree[this.category].nodes.size}`

      let effectText = ''
      for (let effect in this.effects) {
        // TODO: load proper values later!
        let effectValue = 1
        effectText += `${this.locale[effect]}: ${this.effects[effect] * effectValue}\n`
      }
      document.getElementById('effects-display').value = effectText
    }

    updateNodeState()
    updateNodeVisibility()
    updateNodeEffects()
    updateInterface()
    this.saveSession()
  }

  // Draw planner; calls state change internally
  draw () {
    this.update()
    this.hexGrid.clear()
    this.linkGrid.clear()
    let categoryNodes = this.tree[this.category].nodes

    // Draw nodes of current category
    for (let id of categoryNodes) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'inactive')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['inactive'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['inactive'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, this.colors['label']['inactive'])
        }  // END DEBUG
      }
    }

    // Draw visible active nodes and links
    for (let id of this.visibleActive) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'active')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['active'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['active'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['active'])
        }  // END DEBUG
      }
    }

    // Draw visible possible nodes and links
    for (let id of this.visiblePossible) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'possible')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['possible'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['possible'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['possible'])
        }  // END DEBUG
      }
    }

    // Draw visible orphan nodes and links
    for (let id of this.visibleOrphan) {
      if (id) {
        let node = this.skills[id]
        planner.drawNode(node, 'orphan')

        let links = this.links[id]
        for (let i = 0, len = links.length; i < len; i++) {
          let dest = this.skills[links[i]]
          if (dest) {
            this.linkGrid.drawHexLink(node.col, node.row, dest.col, dest.row, this.colors['link']['orphan'])
          }
        }

        // BEGIN DEBUG
        if (this.DEBUG) {
          this.hexGrid.drawHexCell(node.col, node.row, this.colors['cell']['orphan'])
          this.hexGrid.drawHexLabel(node.col, node.row, id, undefined, this.colors['cell']['orphan'])
        }  // END DEBUG
      }
    }
  }

  exportImage () {
    let image = this.hexGrid.canvas.toDataURL('image/png')
    let file = image.replace('image/png', 'image/octet-stream')
    window.location.href = file
  }

  saveSession () {
    let session = JSON.stringify([...this.active])
    window.sessionStorage.setItem('active', session)
  }

  loadSesssion () {
    let session = window.sessionStorage.getItem('active')
    if (session) {
      this.active = new Set(JSON.parse(session))
    }
  }

  exportSession () {
    return [...this.active].map((id) => parseInt(id, 10)).join('-')
  }

  importSession (sessionString) {
    this.active = new Set(sessionString.split('-')) || new Set()
    this.draw()
  }
}

const planner = new SkillPlanner()
planner.loadData(() => {
  planner.loadSesssion()
  planner.draw()
})
<|endoftext|>


<commit_before>/**
  Copyright 2018 LNFWebsite

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
**/

// * This function is for iteration order changes such as playlistRepeat

function changeIteration(which) {
  var sum = videoIteration + which;
  if (playlistRepeat && sum > videoCounter) {
    return 1;
  }
  else if (playlistRepeat && sum < 0) {
    return videoCounter - 1;
  }
  else {
    return sum;
  }
}

// * This function loads videos from the playlist into the YouTube iFrame

function playVideo() {
  console.log("hereprobe");
  videoPlaying = true;
  highlight(videoIteration, "selected", false);
  videoPreviews();
  addAutoplayVideo();

  document.title = "Streamly - " + decodeURIComponent(videos[videoIteration][0]);

  if (!stationRemote) {
    $("#youtube").css("display", "block");

    if (!videoPaused) {
      player.loadVideoById(videos[videoIteration][2]);
    }
    else {
      player.cueVideoById(videos[videoIteration][2]);
    }
    console.log("Debug: playVideo");
  }
  backRestart = false;
  clearTimeout(backRestartTimer);
  backRestartTimer = window.setTimeout(function() {
    backRestart = true;
  }, 3000);
}

// * This function is run on video ending events, changing the current video to the next

function loopVideo() {
  if (videoIteration < videoCounter || playlistRepeat) {
    if (playlistShuffle && playlistRepeat && videoIteration === videoCounter) {
      shufflePlaylist();
    }
    videoIteration = changeIteration(1);
    playVideo();
  }
  else {
    videoPlaying = false;
    actionTimers.clear();
    $("#youtube").css("display", "none");
    if (videos[0] !== undefined && videos[0] !== null) {
      document.title = "Streamly - " + decodeURIComponent(videos[0]);
    }
    else {
      document.title = "Streamly";
    }
  }
}

// * This object does the extra stuff that occurs on pausing and playing videos
// * Such as pausing the progress bar loop

var VideoFunctions = function() {
  this.play = function() {
    setVideoTime();
    sendStation("videofunctionsplay");
    videoPaused = false;
    document.title = "Streamly - " + decodeURIComponent(videos[videoIteration][0]);
    $("#favicon").attr("href", faviconPlay);
  }
  this.pause = function() {
    sendStation("videofunctionspause");
    videoPaused = true;
    if (videos[0] !== undefined && videos[0] !== null) {
      document.title = "Streamly - " + decodeURIComponent(videos[0]);
    }
    $("#favicon").attr("href", faviconPause);
    actionTimers.pause();
  }
}
var videoFunctions = new VideoFunctions();

// * This function skips to the next video in the playlist

function forwardVideo() {
  sendStation("forwardvideo");
  if (changeIteration(1) <= videoCounter) {
    loopVideo();
  }
}

// * This function skips to the previous video in the playlist (or reloads the current video)

function backVideo() {
  sendStation("backvideo");
  if (!backRestart) {
    if (changeIteration(-2) > -1) {
      videoIteration = changeIteration(-2);
      loopVideo();
    }
  }
  else {
    videoIteration = changeIteration(-1);
    loopVideo();
  }
}

// * This function adds a video to the videos array
// * It also plays the video if it is the first video loaded

function addVideo(name, time, id) {
  console.log("debugAddVideo: " + name + "," + time + "," + id);
  videoCounter++;
  var iteration;
  if (playlistPlayNext) {
    iteration = videoIteration + 1;
  }
  else {
    iteration = videoCounter;
  }
  var video = [];
  video[0] = name;
  video[1] = time;
  video[2] = id;
  if (videos.length > 0) {
    videos.splice(iteration, 0, video);
  }
  else {
    videos[iteration] = video;
  }

  sendStation("addvideo," + video);

  if (playlistShuffle) {
    addedVideosWhileShuffled.push(video);
  }

  var printTime = msConversion(time * 1000);

  addVideoToList(name, printTime, iteration, true);

  //if playlist, loop addAutoplayVideo for YouTube playlist import
  //autoplayListOverride is for utilizing YouTube Mix stations without loading all videos
  if (autoplayList && !autoplayListOverride) {
    console.log(autoplayVideoIteration);
    addAutoplayVideo();
  }

  setPlaylist();
  makeSortable();
  videoPreviews();

  if (videoCounter === 1 || (videoPlaying === false && !videoPaused && videoIteration === videoCounter - 1)) {
    loopVideo();
  }
}

// * This function plays videos if the play button in the playlist viewer is clicked
// * Otherwise, videos are always played with the loopVideo function

function actionPlayVideo(iteration) {
  sendStation("actionplayvideo," + iteration);
  videoIteration = iteration;
  videoPaused = false;
  loopVideo();
  $("#favicon").attr("href", faviconPlay);
}

// * This function removes videos from the playlist, skipping to the next video if it is the one removed
// * It's primary use is the remove video button in the playlist viewer

function actionRemoveVideo(iteration) {
  sendStation("actionremovevideo," + iteration);
  if (iteration === videoIteration) {
    if (videoIteration + 1 <= videoCounter) {
      forwardVideo();
      videoIteration = changeIteration(-1);
    }
    else {
      if (!stationRemote) {
        actionTimers.clear();
        player.stopVideo();
        $("#youtube").css("display", "none");
      }
      document.title = "Streamly";
      videoIteration = changeIteration(-1);
    }
  }
  else if (iteration < videoIteration) {
    videoIteration = changeIteration(-1);
  }
  videoCounter--;
  videos.splice(iteration, 1);
  removeVideoFromList(iteration, false);

  setPlaylist();
  makeSortable();
  videoPreviews();
  //if user removed the last video in playlist with autoplay on, load next one
  if ((iteration - 1) === videoCounter) {
    addAutoplayVideo();
  }
}

// * Congrats! You've made it to the bottom of the code! While you're here I should mention that I'm an amateur
// * web developer with literally zero formal education on the subject. If you see how something could be better,
// * please change it! Fork and submit a pull request. This is your project as much as it is mine!
// *
// * Bonus, I've written an ad-lib 'poem' that is totally unrelated
// *
// * Stirring in the wisps of wind
// * Rings a song of thunder and rain
// * Many have seen the falling leaves
// * Draining summer's warmth in vain
// *
// * It's not in the hills and valleys
// * Not within the breath of doves
// * Shining brightly upon the shadows
// * Grey dim lighting from clouds above
// *
// * Longing for lightning from Heaven's alleys
// * Waiting for calm before a storm
// * Seeing the field reflecting the shield
// * Of winter's white colors, no dark skies of love
// *                      - LNFWebsite (9/23/2017)
<commit_msg>try this out<commit_after>/**
  Copyright 2018 LNFWebsite

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
**/

// * This function is for iteration order changes such as playlistRepeat

function changeIteration(which) {
  var sum = videoIteration + which;
  if (playlistRepeat && sum > videoCounter) {
    return 1;
  }
  else if (playlistRepeat && sum < 0) {
    return videoCounter - 1;
  }
  else {
    return sum;
  }
}

// * This function loads videos from the playlist into the YouTube iFrame

function playVideo() {
  videoPlaying = true;
  highlight(videoIteration, "selected", false);
  videoPreviews();
  //only load next radio video if not loading playlist (interferes)
  if (!autoplayList) {
    addAutoplayVideo();
  }

  document.title = "Streamly - " + decodeURIComponent(videos[videoIteration][0]);

  if (!stationRemote) {
    $("#youtube").css("display", "block");

    if (!videoPaused) {
      player.loadVideoById(videos[videoIteration][2]);
    }
    else {
      player.cueVideoById(videos[videoIteration][2]);
    }
    console.log("Debug: playVideo");
  }
  backRestart = false;
  clearTimeout(backRestartTimer);
  backRestartTimer = window.setTimeout(function() {
    backRestart = true;
  }, 3000);
}

// * This function is run on video ending events, changing the current video to the next

function loopVideo() {
  if (videoIteration < videoCounter || playlistRepeat) {
    if (playlistShuffle && playlistRepeat && videoIteration === videoCounter) {
      shufflePlaylist();
    }
    videoIteration = changeIteration(1);
    playVideo();
  }
  else {
    videoPlaying = false;
    actionTimers.clear();
    $("#youtube").css("display", "none");
    if (videos[0] !== undefined && videos[0] !== null) {
      document.title = "Streamly - " + decodeURIComponent(videos[0]);
    }
    else {
      document.title = "Streamly";
    }
  }
}

// * This object does the extra stuff that occurs on pausing and playing videos
// * Such as pausing the progress bar loop

var VideoFunctions = function() {
  this.play = function() {
    setVideoTime();
    sendStation("videofunctionsplay");
    videoPaused = false;
    document.title = "Streamly - " + decodeURIComponent(videos[videoIteration][0]);
    $("#favicon").attr("href", faviconPlay);
  }
  this.pause = function() {
    sendStation("videofunctionspause");
    videoPaused = true;
    if (videos[0] !== undefined && videos[0] !== null) {
      document.title = "Streamly - " + decodeURIComponent(videos[0]);
    }
    $("#favicon").attr("href", faviconPause);
    actionTimers.pause();
  }
}
var videoFunctions = new VideoFunctions();

// * This function skips to the next video in the playlist

function forwardVideo() {
  sendStation("forwardvideo");
  if (changeIteration(1) <= videoCounter) {
    loopVideo();
  }
}

// * This function skips to the previous video in the playlist (or reloads the current video)

function backVideo() {
  sendStation("backvideo");
  if (!backRestart) {
    if (changeIteration(-2) > -1) {
      videoIteration = changeIteration(-2);
      loopVideo();
    }
  }
  else {
    videoIteration = changeIteration(-1);
    loopVideo();
  }
}

// * This function adds a video to the videos array
// * It also plays the video if it is the first video loaded

function addVideo(name, time, id) {
  console.log("debugAddVideo: " + name + "," + time + "," + id);
  videoCounter++;
  var iteration;
  if (playlistPlayNext) {
    iteration = videoIteration + 1;
  }
  else {
    iteration = videoCounter;
  }
  var video = [];
  video[0] = name;
  video[1] = time;
  video[2] = id;
  if (videos.length > 0) {
    videos.splice(iteration, 0, video);
  }
  else {
    videos[iteration] = video;
  }

  sendStation("addvideo," + video);

  if (playlistShuffle) {
    addedVideosWhileShuffled.push(video);
  }

  var printTime = msConversion(time * 1000);

  addVideoToList(name, printTime, iteration, true);

  //if playlist, loop addAutoplayVideo for YouTube playlist import
  //autoplayListOverride is for utilizing YouTube Mix stations without loading all videos
  if (autoplayList && !autoplayListOverride) {
    console.log(autoplayVideoIteration);
    addAutoplayVideo();
  }

  setPlaylist();
  makeSortable();
  videoPreviews();

  if (videoCounter === 1 || (videoPlaying === false && !videoPaused && videoIteration === videoCounter - 1)) {
    loopVideo();
  }
}

// * This function plays videos if the play button in the playlist viewer is clicked
// * Otherwise, videos are always played with the loopVideo function

function actionPlayVideo(iteration) {
  sendStation("actionplayvideo," + iteration);
  videoIteration = iteration;
  videoPaused = false;
  loopVideo();
  $("#favicon").attr("href", faviconPlay);
}

// * This function removes videos from the playlist, skipping to the next video if it is the one removed
// * It's primary use is the remove video button in the playlist viewer

function actionRemoveVideo(iteration) {
  sendStation("actionremovevideo," + iteration);
  if (iteration === videoIteration) {
    if (videoIteration + 1 <= videoCounter) {
      forwardVideo();
      videoIteration = changeIteration(-1);
    }
    else {
      if (!stationRemote) {
        actionTimers.clear();
        player.stopVideo();
        $("#youtube").css("display", "none");
      }
      document.title = "Streamly";
      videoIteration = changeIteration(-1);
    }
  }
  else if (iteration < videoIteration) {
    videoIteration = changeIteration(-1);
  }
  videoCounter--;
  videos.splice(iteration, 1);
  removeVideoFromList(iteration, false);

  setPlaylist();
  makeSortable();
  videoPreviews();
  //if user removed the last video in playlist with autoplay on, load next one
  if ((iteration - 1) === videoCounter) {
    addAutoplayVideo();
  }
}

// * Congrats! You've made it to the bottom of the code! While you're here I should mention that I'm an amateur
// * web developer with literally zero formal education on the subject. If you see how something could be better,
// * please change it! Fork and submit a pull request. This is your project as much as it is mine!
// *
// * Bonus, I've written an ad-lib 'poem' that is totally unrelated
// *
// * Stirring in the wisps of wind
// * Rings a song of thunder and rain
// * Many have seen the falling leaves
// * Draining summer's warmth in vain
// *
// * It's not in the hills and valleys
// * Not within the breath of doves
// * Shining brightly upon the shadows
// * Grey dim lighting from clouds above
// *
// * Longing for lightning from Heaven's alleys
// * Waiting for calm before a storm
// * Seeing the field reflecting the shield
// * Of winter's white colors, no dark skies of love
// *                      - LNFWebsite (9/23/2017)
<|endoftext|>


<commit_before>// TODO: yeah...this script could use some work.
var newdate = new Date();
if (newdate.getHours() > 5) {
    newdate.setDate(newdate.getDate() + 1);
}
//You can change what time the cookie defaults to expire by changing 5 to another number 1-24.
newdate.setHours(5);
newdate.setMinutes(0);
newdate.setSeconds(0);//Use the same number you used above to replace the 5 in this line as well.

var guardianVisits = GetCookie("guardianVisits~(curschoolid)");
if (!guardianVisits) {
    guardianVisits = 0;
    guardianVisits++;
}

SetCookie("guardianVisits~(curschoolid)", guardianVisits, newdate, "/", null, false);
if (guardianVisits == 1) {
    if (guardianFirstScreen !== '') {
        location.href = guardianFirstScreen;
    }
}
/*
 * Set cookie function
 */
function SetCookie(name, value) {
    var argv = SetCookie.arguments;
    var argc = SetCookie.arguments.length;
    var expires = (2 < argc) ? argv[2] : null;
    var path = (3 < argc) ? argv[3] : null;
    var domain = (4 < argc) ? argv[4] : null;
    var secure = (5 < argc) ? argv[5] : false;
    document.cookie = name + "=" + escape(value) +
        ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
        ((path == null) ? "" : ("; path=" + path)) +
        ((domain == null) ? "" : ("; domain=" + domain)) +
        ((secure == true) ? "; secure" : "");
}

/*
 * Get cookie function
 */
function getCookieVal(offset) {
    var endstr = document.cookie.indexOf(";", offset);
    if (endstr == -1)
        endstr = document.cookie.length;
    return unescape(document.cookie.substring(offset, endstr));
}
function GetCookie(name) {

    var arg = name + "=";
    var alen = arg.length;
    var clen = document.cookie.length;
    var i = 0;
    while (i < clen) {
        var j = i + alen;
        if (document.cookie.substring(i, j) == arg)
            return getCookieVal(j);
        i = document.cookie.indexOf(" ", i) + 1;
        if (i == 0)
            break;
    }
    return null;
}

function getCookie(name) {
    var dc = document.cookie;
    //alert("cookie=" + dc);
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1) {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    }
    else begin += 2;
    var end = document.cookie.indexOf(";", begin);
    if (end == -1) end = dc.length;
    var retval = unescape(dc.substring(begin + prefix.length, end));
    //alert("retval=" + retval);
    return retval;
}

function deleteCookie(name) {
    if (getCookie(name)) {
        document.cookie = "psaid=<-A-><-E->; expires=Thu, 01-Jan-70 00:00:00 GMT";
        //alert("deleted cookie=" + document.cookie);
    }
}<commit_msg>Fixed infinite redirection bug.<commit_after>// TODO: yeah...this script could use some work.
var newdate = new Date();
if (newdate.getHours() > 5) {
    newdate.setDate(newdate.getDate() + 1);
}
//You can change what time the cookie defaults to expire by changing 5 to another number 1-24.
newdate.setHours(5);
newdate.setMinutes(0);
newdate.setSeconds(0);//Use the same number you used above to replace the 5 in this line as well.

var guardianVisits = GetCookie("guardianVisits~(curschoolid)");
if (!guardianVisits) {
    guardianVisits = 0;
}
guardianVisits++;

SetCookie("guardianVisits~(curschoolid)", guardianVisits, newdate, "/", null, false);
if (guardianVisits == 1) {
    if (guardianFirstScreen !== '') {
        location.href = guardianFirstScreen;
    }
}
/*
 * Set cookie function
 */
function SetCookie(name, value) {
    var argv = SetCookie.arguments;
    var argc = SetCookie.arguments.length;
    var expires = (2 < argc) ? argv[2] : null;
    var path = (3 < argc) ? argv[3] : null;
    var domain = (4 < argc) ? argv[4] : null;
    var secure = (5 < argc) ? argv[5] : false;
    document.cookie = name + "=" + escape(value) +
        ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
        ((path == null) ? "" : ("; path=" + path)) +
        ((domain == null) ? "" : ("; domain=" + domain)) +
        ((secure == true) ? "; secure" : "");
}

/*
 * Get cookie function
 */
function getCookieVal(offset) {
    var endstr = document.cookie.indexOf(";", offset);
    if (endstr == -1)
        endstr = document.cookie.length;
    return unescape(document.cookie.substring(offset, endstr));
}
function GetCookie(name) {

    var arg = name + "=";
    var alen = arg.length;
    var clen = document.cookie.length;
    var i = 0;
    while (i < clen) {
        var j = i + alen;
        if (document.cookie.substring(i, j) == arg)
            return getCookieVal(j);
        i = document.cookie.indexOf(" ", i) + 1;
        if (i == 0)
            break;
    }
    return null;
}

function getCookie(name) {
    var dc = document.cookie;
    //alert("cookie=" + dc);
    var prefix = name + "=";
    var begin = dc.indexOf("; " + prefix);
    if (begin == -1) {
        begin = dc.indexOf(prefix);
        if (begin != 0) return null;
    }
    else begin += 2;
    var end = document.cookie.indexOf(";", begin);
    if (end == -1) end = dc.length;
    var retval = unescape(dc.substring(begin + prefix.length, end));
    //alert("retval=" + retval);
    return retval;
}

function deleteCookie(name) {
    if (getCookie(name)) {
        document.cookie = "psaid=<-A-><-E->; expires=Thu, 01-Jan-70 00:00:00 GMT";
        //alert("deleted cookie=" + document.cookie);
    }
}<|endoftext|>