<commit_before>// Copyright 2014 The Oppia Authors. All Rights Reserved.
//
// 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.

/**
 * @fileoverview Controllers for rules corresponding to a state's interaction.
 *
 * @author sll@google.com (Sean Lip)
 */

// A state-specific cache for interaction handlers. It stores handlers
// corresponding to an interaction id so that they can be restored if the
// interaction is changed back while the user is still in this state. This
// cache should be reset each time the state editor is initialized.
oppia.factory('interactionHandlersCache', [function() {
  var _cache = {};
  return {
    reset: function() {
      _cache = {};
    },
    contains: function(interactionId) {
      return _cache.hasOwnProperty(interactionId);
    },
    set: function(interactionId, interactionHandlers) {
      _cache[interactionId] = angular.copy(interactionHandlers);
    },
    get: function(interactionId) {
      if (!_cache.hasOwnProperty(interactionId)) {
        return null;
      }
      return angular.copy(_cache[interactionId]);
    }
  };
}]);

oppia.factory('rulesService', [
    'stateInteractionIdService', 'INTERACTION_SPECS', 'interactionHandlersCache',
    'editorContextService', 'changeListService', 'explorationStatesService', 'graphDataService',
    'warningsData',
    function(
      stateInteractionIdService, INTERACTION_SPECS, interactionHandlersCache,
      editorContextService, changeListService, explorationStatesService, graphDataService,
      warningsData) {

  var _interactionHandlersMemento = null;
  var _activeRuleIndex = null;
  var _interactionHandlers = null;
  var _answerChoices = null;
  var _interactionHandlerSpecs = null;

  var _refreshHandlerSpecs = function() {
    if (!stateInteractionIdService.savedMemento) {
      $log.error('ERROR: Interaction id not specified.');
    }

    _interactionHandlerSpecs = INTERACTION_SPECS[
      stateInteractionIdService.savedMemento].handler_specs;
  };

  var _saveInteractionHandlers = function(newHandlers) {
    var oldHandlers = _interactionHandlersMemento;
    if (newHandlers && oldHandlers && !angular.equals(newHandlers, oldHandlers)) {
      _interactionHandlers = newHandlers;

      changeListService.editStateProperty(
        editorContextService.getActiveStateName(), 'widget_handlers',
        angular.copy(newHandlers), angular.copy(oldHandlers));

      var activeStateName = editorContextService.getActiveStateName();

      var _stateDict = explorationStatesService.getState(activeStateName);
      for (var i = 0; i < _stateDict.interaction.handlers.length; i++) {
        var handlerName = _stateDict.interaction.handlers[i].name;
        _stateDict.interaction.handlers[i].rule_specs = _interactionHandlers[handlerName];
      }
      explorationStatesService.setState(activeStateName, _stateDict);

      graphDataService.recompute();
      _interactionHandlersMemento = angular.copy(newHandlers);
    }
  };

  return {
    // The 'data' arg is a list of interaction handlers for the currently-active state.
    init: function(data) {
      interactionHandlersCache.reset();
      _refreshHandlerSpecs();

      // Stores rules as key-value pairs. For each pair, the key is the
      // corresponding handler name and the value has several keys:
      // - 'definition' (the rule definition)
      // - 'description' (the rule description string)
      // - 'dest' (the destination for this rule)
      // - 'feedback' (list of feedback given for this rule)
      // - 'param_changes' (parameter changes associated with this rule)
      _interactionHandlers = {};
      for (var i = 0; i < data.handlers.length; i++) {
        _interactionHandlers[data.handlers[i].name] = data.handlers[i].rule_specs;
      }
      interactionHandlersCache.set(
        stateInteractionIdService.savedMemento, _interactionHandlers);

      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _activeRuleIndex = 0;
    },
    onInteractionIdChanged: function(newInteractionId, callback) {
      _refreshHandlerSpecs();

      if (interactionHandlersCache.contains(newInteractionId)) {
        _interactionHandlers = interactionHandlersCache.get(newInteractionId);
      } else {
        // Preserve just the default rule, unless the new interaction id is a
        // terminal one (in which case, change its destination to be a
        // self-loop instead).
        _interactionHandlers = {
          'submit': [
            _interactionHandlers['submit'][_interactionHandlers['submit'].length - 1]
          ]
        };
        if (INTERACTION_SPECS[newInteractionId].is_terminal) {
          _interactionHandlers['submit'][0].dest = editorContextService.getActiveStateName();
        }
      }

      _saveInteractionHandlers(_interactionHandlers);
      interactionHandlersCache.set(newInteractionId, _interactionHandlers);

      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _activeRuleIndex = 0;

      if (callback) {
        callback();
      }
    },
    getActiveRuleIndex: function() {
      return _activeRuleIndex;
    },
    getAnswerChoices: function() {
      return angular.copy(_answerChoices);
    },
    changeActiveRuleIndex: function(newIndex) {
      _activeRuleIndex = newIndex;
    },
    getActiveRule: function() {
      if (_interactionHandlers) {
        return _interactionHandlers['submit'][_activeRuleIndex];
      } else {
        return null;
      }
    },
    deleteActiveRule: function() {
      if (_activeRuleIndex === _interactionHandlers.length - 1) {
        warningsData.addWarning('Cannot delete default rule.');
        return;
      }
      if (!window.confirm('Are you sure you want to delete this rule?')) {
        return;
      }
      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _interactionHandlers['submit'].splice(_activeRuleIndex, 1);
      _saveInteractionHandlers(_interactionHandlers);
      _activeRuleIndex = 0;
    },
    saveActiveRule: function(activeRule) {
      _interactionHandlers['submit'][_activeRuleIndex] = activeRule;
      _saveInteractionHandlers(_interactionHandlers);
    },
    getInteractionHandlerSpecs: function() {
      return angular.copy(_interactionHandlerSpecs);
    },
    // Updates answer choices when the interaction requires it -- for example,
    // the rules for multiple choice need to refer to the multiple choice
    // interaction's customization arguments.
    updateAnswerChoices: function(newAnswerChoices) {
      _answerChoices = newAnswerChoices;
    },
    getInteractionHandlers: function() {
      return angular.copy(_interactionHandlers);
    },
    // This registers the change to the handlers in the list of changes, and also
    // updates the states object in explorationStatesService.
    save: function(newHandlers) {
      _saveInteractionHandlers(newHandlers);
    }
  };
}]);

oppia.controller('StateRules', [
    '$scope', '$log', '$rootScope', '$modal', 'stateInteractionIdService', 'editorContextService',
    'warningsData', 'rulesService',
    function(
      $scope, $log, $rootScope, $modal, stateInteractionIdService, editorContextService,
      warningsData, rulesService) {

  $scope.getAnswerChoices = function() {
    return rulesService.getAnswerChoices();
  };
  $scope.editorContextService = editorContextService;

  $scope.changeActiveRuleIndex = function(newIndex) {
    $rootScope.$broadcast('externalSave');
    rulesService.changeActiveRuleIndex(newIndex);
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  };

  $scope.getCurrentInteractionId = function() {
    return stateInteractionIdService.savedMemento;
  };

  $scope.$on('initializeHandlers', function(evt, data) {
    rulesService.init(data);
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  });

  $scope.$on('onInteractionIdChanged', function(evt, newInteractionId) {
    rulesService.onInteractionIdChanged(newInteractionId, function() {
      $scope.interactionHandlers = rulesService.getInteractionHandlers();
      $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
      $rootScope.$broadcast('activeRuleChanged');
    });
  });

  $scope.$on('ruleDeleted', function(evt) {
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  });

  $scope.$on('ruleSaved', function(evt) {
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
  });

  // Updates answer choices when the interaction requires it -- for example,
  // the rules for multiple choice need to refer to the multiple choice
  // interaction's customization arguments.
  // TODO(sll): Remove the need for this watcher, or make it less ad hoc.
  $scope.$on('updateAnswerChoices', function(evt, newAnswerChoices) {
    rulesService.updateAnswerChoices(newAnswerChoices);
  });

  $scope.isDefaultRuleTabShown = function() {
    var defaultRule = $scope.interactionHandlers[$scope.interactionHandlers.length - 1];
    return (
      defaultRule.dest !== stateName ||
      defaultRule.feedback.length > 0);
  };

  $scope.openAddRuleModal = function() {
    warningsData.clear();
    $rootScope.$broadcast('externalSave');

    $modal.open({
      templateUrl: 'modals/addRule',
      backdrop: true,
      resolve: {},
      controller: [
          '$scope', '$modalInstance', 'rulesService',
          function($scope, $modalInstance, rulesService) {
        $scope.currentRuleDescription = null;
        $scope.currentRuleDefinition = {
          rule_type: 'atomic',
          name: null,
          inputs: {},
          subject: 'answer'
        };

        $scope.interactionHandlerSpecs = rulesService.getInteractionHandlerSpecs();
        $scope.answerChoices = rulesService.getAnswerChoices();

        $scope.addNewRule = function() {
          $modalInstance.close({
            description: $scope.currentRuleDescription,
            definition: $scope.currentRuleDefinition
          });
        };

        $scope.cancel = function() {
          $modalInstance.dismiss('cancel');
          warningsData.clear();
        };
      }]
    }).result.then(function(tmpRule) {
      _interactionHandlersMemento = angular.copy($scope.interactionHandlers);

      // Move the tmp rule into the list of 'real' rules.
      var numRules = $scope.interactionHandlers['submit'].length;
      $scope.interactionHandlers['submit'].splice(numRules - 1, 0, {
        description: tmpRule.description,
        definition: tmpRule.definition,
        dest: editorContextService.getActiveStateName(),
        feedback: [],
        param_changes: []
      });

      rulesService.save($scope.interactionHandlers);

      $scope.changeActiveRuleIndex($scope.interactionHandlers['submit'].length - 2);
    });
  };

  $scope.RULE_LIST_SORTABLE_OPTIONS = {
    axis: 'y',
    cursor: 'move',
    handle: '.oppia-rule-sort-handle',
    items: '.oppia-sortable-rule-block',
    tolerance: 'pointer',
    start: function(e, ui) {
      $rootScope.$broadcast('externalSave');
      $scope.$apply();
      ui.placeholder.height(ui.item.height());
    },
    stop: function(e, ui) {
      $scope.$apply();
      rulesService.save($scope.interactionHandlers);
      $scope.changeActiveRuleIndex(ui.item.index());
      $rootScope.$broadcast('activeRuleChanged');
    }
  };
}]);

oppia.controller('StateEditorActiveRule', [
    '$scope', '$rootScope', 'rulesService', 'editorContextService',
    function($scope, $rootScope, rulesService, editorContextService) {

  $scope.interactionHandlers = rulesService.getInteractionHandlers();
  $scope.ruleIsShown = false;

  $scope.$on('activeRuleChanged', function() {
    $scope.activeRule = rulesService.getActiveRule();
    $scope.currentStateName = editorContextService.getActiveStateName();

    $scope.ruleIsShown = (
      $scope.activeRule.definition.rule_type !== 'default' ||
      $scope.activeRule.dest !== stateName ||
      $scope.activeRule.feedback.length > 0);
  });

  $scope.deleteActiveRule = function() {
    rulesService.deleteActiveRule();
    $rootScope.$broadcast('ruleDeleted');
  };

  $scope.saveRule = function() {
    rulesService.saveActiveRule($scope.activeRule);
    $rootScope.$broadcast('ruleSaved');
  };
}]);
<commit_msg>Fix some errors in previous commit.<commit_after>// Copyright 2014 The Oppia Authors. All Rights Reserved.
//
// 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.

/**
 * @fileoverview Controllers for rules corresponding to a state's interaction.
 *
 * @author sll@google.com (Sean Lip)
 */

// A state-specific cache for interaction handlers. It stores handlers
// corresponding to an interaction id so that they can be restored if the
// interaction is changed back while the user is still in this state. This
// cache should be reset each time the state editor is initialized.
oppia.factory('interactionHandlersCache', [function() {
  var _cache = {};
  return {
    reset: function() {
      _cache = {};
    },
    contains: function(interactionId) {
      return _cache.hasOwnProperty(interactionId);
    },
    set: function(interactionId, interactionHandlers) {
      _cache[interactionId] = angular.copy(interactionHandlers);
    },
    get: function(interactionId) {
      if (!_cache.hasOwnProperty(interactionId)) {
        return null;
      }
      return angular.copy(_cache[interactionId]);
    }
  };
}]);

oppia.factory('rulesService', [
    'stateInteractionIdService', 'INTERACTION_SPECS', 'interactionHandlersCache',
    'editorContextService', 'changeListService', 'explorationStatesService', 'graphDataService',
    'warningsData',
    function(
      stateInteractionIdService, INTERACTION_SPECS, interactionHandlersCache,
      editorContextService, changeListService, explorationStatesService, graphDataService,
      warningsData) {

  var _interactionHandlersMemento = null;
  var _activeRuleIndex = null;
  var _interactionHandlers = null;
  var _answerChoices = null;
  var _interactionHandlerSpecs = null;

  var _refreshHandlerSpecs = function() {
    if (!stateInteractionIdService.savedMemento) {
      $log.error('ERROR: Interaction id not specified.');
    }

    _interactionHandlerSpecs = INTERACTION_SPECS[
      stateInteractionIdService.savedMemento].handler_specs;
  };

  var _saveInteractionHandlers = function(newHandlers) {
    var oldHandlers = _interactionHandlersMemento;
    if (newHandlers && oldHandlers && !angular.equals(newHandlers, oldHandlers)) {
      _interactionHandlers = newHandlers;

      changeListService.editStateProperty(
        editorContextService.getActiveStateName(), 'widget_handlers',
        angular.copy(newHandlers), angular.copy(oldHandlers));

      var activeStateName = editorContextService.getActiveStateName();

      var _stateDict = explorationStatesService.getState(activeStateName);
      for (var i = 0; i < _stateDict.interaction.handlers.length; i++) {
        var handlerName = _stateDict.interaction.handlers[i].name;
        _stateDict.interaction.handlers[i].rule_specs = _interactionHandlers[handlerName];
      }
      explorationStatesService.setState(activeStateName, _stateDict);

      graphDataService.recompute();
      _interactionHandlersMemento = angular.copy(newHandlers);
    }
  };

  return {
    // The 'data' arg is a list of interaction handlers for the currently-active state.
    init: function(data) {
      interactionHandlersCache.reset();
      _refreshHandlerSpecs();

      // Stores rules as key-value pairs. For each pair, the key is the
      // corresponding handler name and the value has several keys:
      // - 'definition' (the rule definition)
      // - 'description' (the rule description string)
      // - 'dest' (the destination for this rule)
      // - 'feedback' (list of feedback given for this rule)
      // - 'param_changes' (parameter changes associated with this rule)
      _interactionHandlers = {};
      for (var i = 0; i < data.handlers.length; i++) {
        _interactionHandlers[data.handlers[i].name] = data.handlers[i].rule_specs;
      }
      interactionHandlersCache.set(
        stateInteractionIdService.savedMemento, _interactionHandlers);

      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _activeRuleIndex = 0;
    },
    onInteractionIdChanged: function(newInteractionId, callback) {
      _refreshHandlerSpecs();

      if (interactionHandlersCache.contains(newInteractionId)) {
        _interactionHandlers = interactionHandlersCache.get(newInteractionId);
      } else {
        // Preserve just the default rule, unless the new interaction id is a
        // terminal one (in which case, change its destination to be a
        // self-loop instead).
        _interactionHandlers = {
          'submit': [
            _interactionHandlers['submit'][_interactionHandlers['submit'].length - 1]
          ]
        };
        if (INTERACTION_SPECS[newInteractionId].is_terminal) {
          _interactionHandlers['submit'][0].dest = editorContextService.getActiveStateName();
        }
      }

      _saveInteractionHandlers(_interactionHandlers);
      interactionHandlersCache.set(newInteractionId, _interactionHandlers);

      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _activeRuleIndex = 0;

      if (callback) {
        callback();
      }
    },
    getActiveRuleIndex: function() {
      return _activeRuleIndex;
    },
    getAnswerChoices: function() {
      return angular.copy(_answerChoices);
    },
    changeActiveRuleIndex: function(newIndex) {
      _activeRuleIndex = newIndex;
    },
    getActiveRule: function() {
      if (_interactionHandlers) {
        return _interactionHandlers['submit'][_activeRuleIndex];
      } else {
        return null;
      }
    },
    deleteActiveRule: function() {
      if (_activeRuleIndex === _interactionHandlers.length - 1) {
        warningsData.addWarning('Cannot delete default rule.');
        return;
      }
      if (!window.confirm('Are you sure you want to delete this rule?')) {
        return;
      }
      _interactionHandlersMemento = angular.copy(_interactionHandlers);
      _interactionHandlers['submit'].splice(_activeRuleIndex, 1);
      _saveInteractionHandlers(_interactionHandlers);
      _activeRuleIndex = 0;
    },
    saveActiveRule: function(activeRule) {
      _interactionHandlers['submit'][_activeRuleIndex] = activeRule;
      _saveInteractionHandlers(_interactionHandlers);
    },
    getInteractionHandlerSpecs: function() {
      return angular.copy(_interactionHandlerSpecs);
    },
    // Updates answer choices when the interaction requires it -- for example,
    // the rules for multiple choice need to refer to the multiple choice
    // interaction's customization arguments.
    updateAnswerChoices: function(newAnswerChoices) {
      _answerChoices = newAnswerChoices;
    },
    getInteractionHandlers: function() {
      return angular.copy(_interactionHandlers);
    },
    // This registers the change to the handlers in the list of changes, and also
    // updates the states object in explorationStatesService.
    save: function(newHandlers) {
      _saveInteractionHandlers(newHandlers);
    }
  };
}]);

oppia.controller('StateRules', [
    '$scope', '$log', '$rootScope', '$modal', 'stateInteractionIdService', 'editorContextService',
    'warningsData', 'rulesService',
    function(
      $scope, $log, $rootScope, $modal, stateInteractionIdService, editorContextService,
      warningsData, rulesService) {

  $scope.getAnswerChoices = function() {
    return rulesService.getAnswerChoices();
  };
  $scope.editorContextService = editorContextService;

  $scope.changeActiveRuleIndex = function(newIndex) {
    $rootScope.$broadcast('externalSave');
    rulesService.changeActiveRuleIndex(newIndex);
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  };

  $scope.getCurrentInteractionId = function() {
    return stateInteractionIdService.savedMemento;
  };

  $scope.$on('initializeHandlers', function(evt, data) {
    rulesService.init(data);
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  });

  $scope.$on('onInteractionIdChanged', function(evt, newInteractionId) {
    rulesService.onInteractionIdChanged(newInteractionId, function() {
      $scope.interactionHandlers = rulesService.getInteractionHandlers();
      $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
      $rootScope.$broadcast('activeRuleChanged');
    });
  });

  $scope.$on('ruleDeleted', function(evt) {
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
    $rootScope.$broadcast('activeRuleChanged');
  });

  $scope.$on('ruleSaved', function(evt) {
    $scope.interactionHandlers = rulesService.getInteractionHandlers();
    $scope.activeRuleIndex = rulesService.getActiveRuleIndex();
  });

  // Updates answer choices when the interaction requires it -- for example,
  // the rules for multiple choice need to refer to the multiple choice
  // interaction's customization arguments.
  // TODO(sll): Remove the need for this watcher, or make it less ad hoc.
  $scope.$on('updateAnswerChoices', function(evt, newAnswerChoices) {
    rulesService.updateAnswerChoices(newAnswerChoices);
  });

  $scope.isDefaultRuleTabShown = function() {
    var defaultRule = $scope.interactionHandlers.submit[$scope.interactionHandlers.submit.length - 1];
    return (
      defaultRule.dest !== editorContextService.getActiveStateName() ||
      defaultRule.feedback.length > 0);
  };

  $scope.openAddRuleModal = function() {
    warningsData.clear();
    $rootScope.$broadcast('externalSave');

    $modal.open({
      templateUrl: 'modals/addRule',
      backdrop: true,
      resolve: {},
      controller: [
          '$scope', '$modalInstance', 'rulesService',
          function($scope, $modalInstance, rulesService) {
        $scope.currentRuleDescription = null;
        $scope.currentRuleDefinition = {
          rule_type: 'atomic',
          name: null,
          inputs: {},
          subject: 'answer'
        };

        $scope.interactionHandlerSpecs = rulesService.getInteractionHandlerSpecs();
        $scope.answerChoices = rulesService.getAnswerChoices();

        $scope.addNewRule = function() {
          $modalInstance.close({
            description: $scope.currentRuleDescription,
            definition: $scope.currentRuleDefinition
          });
        };

        $scope.cancel = function() {
          $modalInstance.dismiss('cancel');
          warningsData.clear();
        };
      }]
    }).result.then(function(tmpRule) {
      _interactionHandlersMemento = angular.copy($scope.interactionHandlers);

      // Move the tmp rule into the list of 'real' rules.
      var numRules = $scope.interactionHandlers['submit'].length;
      $scope.interactionHandlers['submit'].splice(numRules - 1, 0, {
        description: tmpRule.description,
        definition: tmpRule.definition,
        dest: editorContextService.getActiveStateName(),
        feedback: [],
        param_changes: []
      });

      rulesService.save($scope.interactionHandlers);

      $scope.changeActiveRuleIndex($scope.interactionHandlers['submit'].length - 2);
    });
  };

  $scope.RULE_LIST_SORTABLE_OPTIONS = {
    axis: 'y',
    cursor: 'move',
    handle: '.oppia-rule-sort-handle',
    items: '.oppia-sortable-rule-block',
    tolerance: 'pointer',
    start: function(e, ui) {
      $rootScope.$broadcast('externalSave');
      $scope.$apply();
      ui.placeholder.height(ui.item.height());
    },
    stop: function(e, ui) {
      $scope.$apply();
      rulesService.save($scope.interactionHandlers);
      $scope.changeActiveRuleIndex(ui.item.index());
      $rootScope.$broadcast('activeRuleChanged');
    }
  };
}]);

oppia.controller('StateEditorActiveRule', [
    '$scope', '$rootScope', 'rulesService', 'editorContextService',
    function($scope, $rootScope, rulesService, editorContextService) {

  $scope.interactionHandlers = rulesService.getInteractionHandlers();
  $scope.ruleIsShown = false;

  $scope.$on('activeRuleChanged', function() {
    $scope.activeRule = rulesService.getActiveRule();
    $scope.currentStateName = editorContextService.getActiveStateName();

    $scope.ruleIsShown = (
      $scope.activeRule.definition.rule_type !== 'default' ||
      $scope.activeRule.dest !== editorContextService.getActiveStateName() ||
      $scope.activeRule.feedback.length > 0);
  });

  $scope.deleteActiveRule = function() {
    rulesService.deleteActiveRule();
    $rootScope.$broadcast('ruleDeleted');
  };

  $scope.saveRule = function() {
    rulesService.saveActiveRule($scope.activeRule);
    $rootScope.$broadcast('ruleSaved');
  };
}]);
<|endoftext|>


<commit_before>/*
 * ------------------------------------------
 * 模块基类实现文件
 * @version  1.0
 * @author   genify(caijf@corp.netease.com)
 * ------------------------------------------
 */
var f = function(){
    // variable declaration
    var _  = NEJ.P,
        _e = _('nej.e'),
        _v = _('nej.v');
    // aop regist api
    _e._$regist =
    _e._$regist._$aop(function(_event){
        _event.stopped = !0;
        var _klass = _event.args[1],
            _inst = _klass._$allocate();
        _inst.count = 0;
        _v._$addEvent(
            location,'urlchange',
            function(){
                _inst.count++;
                var _options = location.parse(location.hash.substr(1));
                _options.param = _options.query;
                _options.parent = document.mbody||document.body;
                _inst._$dispatchEvent(_inst.count==1?'onshow':'onrefresh',_options);
            }
        );
        location.active();
    });
    /**
     * 使用模版测试
     * @param  {String} 模版ID
     * @return {Void}
     */
    _e._$testByTemplate = function(_tid){
        _e._$parseTemplate(_tid||'template-box');
    };
};
NEJ.define('{lib}util/dispatcher/test.js',
          ['{lib}util/dispatcher/module.2.js'
          ,'{lib}util/history/history.js'
          ,'{lib}util/template/tpl.js'],f);<commit_msg>module test<commit_after>/*
 * ------------------------------------------
 * 模块基类实现文件
 * @version  1.0
 * @author   genify(caijf@corp.netease.com)
 * ------------------------------------------
 */
NEJ.define([
	'base/event',
	'util/template/tpl',
	'util/history/history',
	'./module.js'
],function(_v,_t,_h,_m,_p,_o,_f,_r){

    // aop regist api
    _m._$regist =
    _m._$regist._$aop(function(_event){
        _event.stopped = !0;
        var _klass = _event.args[1],
            _inst = _klass._$allocate();
        _inst.count = 0;
        _v._$addEvent(
            location,'urlchange',
            function(){
                _inst.count++;
                var _options = location.parse(location.hash.substr(1));
                _options.param = _options.query;
                _options.parent = document.mbody||document.body;
                _inst._$dispatchEvent(_inst.count==1?'onshow':'onrefresh',_options);
            }
        );
        location.active();
    });
    /**
     * 使用模版测试
     * @param  {String} 模版ID
     * @return {Void}
     */
    _p._$testByTemplate = function(_tid){
        _t._$parseTemplate(_tid||'template-box');
    };

	return _p;
});
<|endoftext|>


<commit_before>import React from 'react';
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import { checkA11y } from 'storybook-addon-a11y';
import { withInfo } from '@storybook/addon-info';
import { Box, DataGrid, IconMenu, MenuItem, StatusBullet, TextTiny, Tooltip } from '../components';

const rows = [
  {
    column1: "ruby",
    column2: "Epic Agency",
    column3: 412.00,
    column4: "30 Jul 2017",
    column5: "2017 / 350",
  },
  {
    column1: "ruby",
    column2: "Bekaert and Sons",
    column3: 999.99,
    column4: "5 Aug 2017",
    column5: "2017 / 351",
  },
  {
    column1: "gold",
    column2: "Davitamon",
    column3: 2500.00,
    column4: "28 July 2017",
    column5: "Not booked",
  },
  {
    column1: "gold",
    column2: "Dog Studio",
    column3: 199.95,
    column4: "1 Aug 2017",
    column5: "Not booked",
  },
  {
    column1: "neutral",
    column2: "Standard de Liège",
    column3: 1299.67,
    column4: "29 Jul 2017",
    column5: "2017 / 349",
  },
  {
    column1: "neutral",
    column2: "Invisible Puppy",
    column3: 3749.00,
    column4: "05 Feb 2017",
    column5: "2017 / 980",
  },
  {
    column1: "violet",
    column2: "The Big Simon",
    column3: 999.99,
    column4: "08 Feb 2017",
    column5: "2017 / 986",
  },
  {
    column1: "violet",
    column2: "SN Brussels Airlines",
    column3: 4285.95,
    column4: "06 Apr 2017",
    column5: "2017 / 1028",
  },
];

const TooltippedStatusBullet = Tooltip(StatusBullet);

storiesOf('DataGrids', module)
  .addDecorator((story, context) => withInfo('common info')(story)(context))
  .addDecorator(checkA11y)
  .add('Basic', () => (
    <Box paddingVertical={5}>
      <DataGrid selectable>
        <DataGrid.HeaderRow>
          <DataGrid.HeaderCell flex="min-width" />
          <DataGrid.HeaderCell border="right" flex="2" onClick={action('onClick: column sort')}>Customer</DataGrid.HeaderCell>
          <DataGrid.HeaderCell align="right">Amount</DataGrid.HeaderCell>
          <DataGrid.HeaderCell>Due date</DataGrid.HeaderCell>
          <DataGrid.HeaderCell>Reference</DataGrid.HeaderCell>
          <DataGrid.HeaderCell flex="min-width"/>
        </DataGrid.HeaderRow>
        {
          rows.map((row, index) => {
            return (
              <DataGrid.BodyRow key={index}>
                <DataGrid.Cell align="center" flex="min-width">
                  <TooltippedStatusBullet
                    color={row.column1}
                    tooltip={<TextTiny>Overdue</TextTiny>}
                    tooltipColor={row.column1}
                    tooltipSize="small"
                    size="large"
                  />
                </DataGrid.Cell>
                <DataGrid.Cell border="right" flex="2"> {row.column2}</DataGrid.Cell>
                <DataGrid.Cell align="right" strong> {`€ ${row.column3}`}</DataGrid.Cell>
                <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
                <DataGrid.Cell soft> {row.column5} </DataGrid.Cell>
                <DataGrid.Cell align="right" flex="min-width">
                  <IconMenu position="top-right">
                    <MenuItem>Remove row</MenuItem>
                  </IconMenu>
                </DataGrid.Cell>
              </DataGrid.BodyRow>
            );
          })
        }
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2">
            Total Excl Btw
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong>
            € 13460.52
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2">
            + VAT
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong>
            € 2826.71
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2">
            Total Incl Btw
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong backgroundColor="white" border="around">
            € 16287.23
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
      </DataGrid>
    </Box>
  ));
<commit_msg>Use Heading4 components for the footer total labels<commit_after>import React from 'react';
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';
import { checkA11y } from 'storybook-addon-a11y';
import { withInfo } from '@storybook/addon-info';
import { Box, DataGrid, Heading4, IconMenu, MenuItem, StatusBullet, TextTiny, Tooltip } from '../components';

const rows = [
  {
    column1: "ruby",
    column2: "Epic Agency",
    column3: 412.00,
    column4: "30 Jul 2017",
    column5: "2017 / 350",
  },
  {
    column1: "ruby",
    column2: "Bekaert and Sons",
    column3: 999.99,
    column4: "5 Aug 2017",
    column5: "2017 / 351",
  },
  {
    column1: "gold",
    column2: "Davitamon",
    column3: 2500.00,
    column4: "28 July 2017",
    column5: "Not booked",
  },
  {
    column1: "gold",
    column2: "Dog Studio",
    column3: 199.95,
    column4: "1 Aug 2017",
    column5: "Not booked",
  },
  {
    column1: "neutral",
    column2: "Standard de Liège",
    column3: 1299.67,
    column4: "29 Jul 2017",
    column5: "2017 / 349",
  },
  {
    column1: "neutral",
    column2: "Invisible Puppy",
    column3: 3749.00,
    column4: "05 Feb 2017",
    column5: "2017 / 980",
  },
  {
    column1: "violet",
    column2: "The Big Simon",
    column3: 999.99,
    column4: "08 Feb 2017",
    column5: "2017 / 986",
  },
  {
    column1: "violet",
    column2: "SN Brussels Airlines",
    column3: 4285.95,
    column4: "06 Apr 2017",
    column5: "2017 / 1028",
  },
];

const TooltippedStatusBullet = Tooltip(StatusBullet);

storiesOf('DataGrids', module)
  .addDecorator((story, context) => withInfo('common info')(story)(context))
  .addDecorator(checkA11y)
  .add('Basic', () => (
    <Box paddingVertical={5}>
      <DataGrid selectable>
        <DataGrid.HeaderRow>
          <DataGrid.HeaderCell flex="min-width" />
          <DataGrid.HeaderCell border="right" flex="2" onClick={action('onClick: column sort')}>Customer</DataGrid.HeaderCell>
          <DataGrid.HeaderCell align="right">Amount</DataGrid.HeaderCell>
          <DataGrid.HeaderCell>Due date</DataGrid.HeaderCell>
          <DataGrid.HeaderCell>Reference</DataGrid.HeaderCell>
          <DataGrid.HeaderCell flex="min-width"/>
        </DataGrid.HeaderRow>
        {
          rows.map((row, index) => {
            return (
              <DataGrid.BodyRow key={index}>
                <DataGrid.Cell align="center" flex="min-width">
                  <TooltippedStatusBullet
                    color={row.column1}
                    tooltip={<TextTiny>Overdue</TextTiny>}
                    tooltipColor={row.column1}
                    tooltipSize="small"
                    size="large"
                  />
                </DataGrid.Cell>
                <DataGrid.Cell border="right" flex="2"> {row.column2}</DataGrid.Cell>
                <DataGrid.Cell align="right" strong> {`€ ${row.column3}`}</DataGrid.Cell>
                <DataGrid.Cell soft>{row.column4}</DataGrid.Cell>
                <DataGrid.Cell soft> {row.column5} </DataGrid.Cell>
                <DataGrid.Cell align="right" flex="min-width">
                  <IconMenu position="top-right">
                    <MenuItem>Remove row</MenuItem>
                  </IconMenu>
                </DataGrid.Cell>
              </DataGrid.BodyRow>
            );
          })
        }
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2" soft>
            <Heading4>Total Excl Btw</Heading4>
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong>
            € 13460.52
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2" soft>
            <Heading4>+ VAT</Heading4>
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong>
            € 2826.71
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
        <DataGrid.FooterRow backgroundColor="neutral">
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell flex="min-width"/>
          <DataGrid.Cell align="right" border="right" flex="2" soft>
            <Heading4>Total Incl Btw</Heading4>
          </DataGrid.Cell>
          <DataGrid.Cell align="right" strong backgroundColor="white" border="around">
            € 16287.23
          </DataGrid.Cell>
          <DataGrid.Cell/>
          <DataGrid.Cell/>
          <DataGrid.Cell flex="min-width"/>
        </DataGrid.FooterRow>
      </DataGrid>
    </Box>
  ));
<|endoftext|>


<commit_before>/*
Copyright 2019 New Vector Ltd

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.
*/

import Matrix from 'matrix-js-sdk';
import Analytics from '../Analytics';

const localStorage = window.localStorage;

// just *accessing* indexedDB throws an exception in firefox with
// indexeddb disabled.
let indexedDB;
try {
    indexedDB = window.indexedDB;
} catch (e) {}

// The JS SDK will add a prefix of "matrix-js-sdk:" to the sync store name.
const SYNC_STORE_NAME = "riot-web-sync";
const CRYPTO_STORE_NAME = "matrix-js-sdk:crypto";

function log(msg) {
    console.log(`StorageManager: ${msg}`);
}

function error(msg) {
    console.error(`StorageManager: ${msg}`);
}

function track(action) {
    Analytics.trackEvent("StorageManager", action);
}

export async function checkConsistency() {
    log("Checking storage consistency");
    log(`Local storage supported? ${!!localStorage}`);
    log(`IndexedDB supported? ${!!indexedDB}`);

    let dataInLocalStorage = false;
    let dataInCryptoStore = false;
    let healthy = true;

    if (localStorage) {
        dataInLocalStorage = localStorage.length > 0;
        log(`Local storage contains data? ${dataInLocalStorage}`);
    } else {
        healthy = false;
        error("Local storage cannot be used on this browser");
        track("Local storage disabled");
    }

    if (indexedDB && localStorage) {
        const dataInSyncStore = await Matrix.IndexedDBStore.exists(
            indexedDB, SYNC_STORE_NAME,
        );
        log(`Sync store contains data? ${dataInSyncStore}`);
    } else {
        healthy = false;
        error("Sync store cannot be used on this browser");
        track("Sync store disabled");
    }

    if (indexedDB) {
        dataInCryptoStore = await Matrix.IndexedDBCryptoStore.exists(
            indexedDB, CRYPTO_STORE_NAME,
        );
        log(`Crypto store contains data? ${dataInCryptoStore}`);
    } else {
        healthy = false;
        error("Crypto store cannot be used on this browser");
        track("Crypto store disabled");
    }

    if (dataInLocalStorage && !dataInCryptoStore) {
        healthy = false;
        error(
            "Data exists in local storage but not in crypto store. " +
            "IndexedDB storage has likely been evicted by the browser!",
        );
        track("Crypto store evicted");
    }

    if (healthy) {
        log("Storage consistency checks passed");
        track("Consistency checks passed");
    } else {
        error("Storage consistency checks failed");
        track("Consistency checks failed");
    }
}
<commit_msg>Catch errors when checking IndexedDB<commit_after>/*
Copyright 2019 New Vector Ltd

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.
*/

import Matrix from 'matrix-js-sdk';
import Analytics from '../Analytics';

const localStorage = window.localStorage;

// just *accessing* indexedDB throws an exception in firefox with
// indexeddb disabled.
let indexedDB;
try {
    indexedDB = window.indexedDB;
} catch (e) {}

// The JS SDK will add a prefix of "matrix-js-sdk:" to the sync store name.
const SYNC_STORE_NAME = "riot-web-sync";
const CRYPTO_STORE_NAME = "matrix-js-sdk:crypto";

function log(msg) {
    console.log(`StorageManager: ${msg}`);
}

function error(msg) {
    console.error(`StorageManager: ${msg}`);
}

function track(action) {
    Analytics.trackEvent("StorageManager", action);
}

export async function checkConsistency() {
    log("Checking storage consistency");
    log(`Local storage supported? ${!!localStorage}`);
    log(`IndexedDB supported? ${!!indexedDB}`);

    let dataInLocalStorage = false;
    let dataInCryptoStore = false;
    let healthy = true;

    if (localStorage) {
        dataInLocalStorage = localStorage.length > 0;
        log(`Local storage contains data? ${dataInLocalStorage}`);
    } else {
        healthy = false;
        error("Local storage cannot be used on this browser");
        track("Local storage disabled");
    }

    if (indexedDB && localStorage) {
        try {
            const dataInSyncStore = await Matrix.IndexedDBStore.exists(
                indexedDB, SYNC_STORE_NAME,
            );
            log(`Sync store contains data? ${dataInSyncStore}`);
        } catch (e) {
            healthy = false;
            error("Sync store inaccessible", e);
            track("Sync store inaccessible");
        }
    } else {
        healthy = false;
        error("Sync store cannot be used on this browser");
        track("Sync store disabled");
    }

    if (indexedDB) {
        try {
            dataInCryptoStore = await Matrix.IndexedDBCryptoStore.exists(
                indexedDB, CRYPTO_STORE_NAME,
            );
            log(`Crypto store contains data? ${dataInCryptoStore}`);
        } catch (e) {
            healthy = false;
            error("Crypto store inaccessible", e);
            track("Crypto store inaccessible");
        }
    } else {
        healthy = false;
        error("Crypto store cannot be used on this browser");
        track("Crypto store disabled");
    }

    if (dataInLocalStorage && !dataInCryptoStore) {
        healthy = false;
        error(
            "Data exists in local storage but not in crypto store. " +
            "IndexedDB storage has likely been evicted by the browser!",
        );
        track("Crypto store evicted");
    }

    if (healthy) {
        log("Storage consistency checks passed");
        track("Consistency checks passed");
    } else {
        error("Storage consistency checks failed");
        track("Consistency checks failed");
    }
}
<|endoftext|>


<commit_before>import {read_vk_api_parameters} from './vk_api'
import vk_strategy from 'passport-vkontakte'
import {user_model} from '../models/user_model'
import {logger} from './logger'
import passport from 'passport'
import mongoose from 'mongoose'

export function init_authentication() {
  const {app_id, app_secret, app_redirect_uri} = read_vk_api_parameters()
  passport.use(new vk_strategy.Strategy({
    clientID: app_id,
    clientSecret: app_secret,
    callbackURL: app_redirect_uri,
  }, (access_token, refresh_token, parameters, profile, done_handler) => {
    new user_model({
      vk_id: profile.id,
    })
      .update((error, user) => {
        if (error !== null) {
          next_handler(error)
          return
        }

        logger.info(`user ${user.vk_id} logged into the app`)
        done_handler(null, user)
      })
  }))
  passport.serializeUser((user, done_handler) => {
    done_handler(null, user.id)
  })
  passport.deserializeUser((user_id, done_handler) => {
    user_model
      .findById(user_id)
      .then(user => done_handler(null, user))
      .catch(done_handler)
  })
}

const fake_user = new user_model({
  _id: new mongoose.Types.ObjectId('507f1f77bcf86cd799439011'),
  vk_id: 0,
})
export function fake_authentication(request, response, next_handler) {
  request.user = fake_user
  next_handler()
}
<commit_msg>Issue #34: correct the fake user<commit_after>import {read_vk_api_parameters} from './vk_api'
import vk_strategy from 'passport-vkontakte'
import {user_model} from '../models/user_model'
import {logger} from './logger'
import passport from 'passport'
import mongoose from 'mongoose'

export function init_authentication() {
  const {app_id, app_secret, app_redirect_uri} = read_vk_api_parameters()
  passport.use(new vk_strategy.Strategy({
    clientID: app_id,
    clientSecret: app_secret,
    callbackURL: app_redirect_uri,
  }, (access_token, refresh_token, parameters, profile, done_handler) => {
    new user_model({
      vk_id: profile.id,
    })
      .update((error, user) => {
        if (error !== null) {
          next_handler(error)
          return
        }

        logger.info(`user ${user.vk_id} logged into the app`)
        done_handler(null, user)
      })
  }))
  passport.serializeUser((user, done_handler) => {
    done_handler(null, user.id)
  })
  passport.deserializeUser((user_id, done_handler) => {
    user_model
      .findById(user_id)
      .then(user => done_handler(null, user))
      .catch(done_handler)
  })
}

const fake_user = new user_model({
  _id: new mongoose.Types.ObjectId('507f1f77bcf86cd799439011'),
  vk_id: 13605040,
})
export function fake_authentication(request, response, next_handler) {
  request.user = fake_user
  next_handler()
}
<|endoftext|>


<commit_before>/*JavaScript interface class file*/

/**
 * Event
 * @class
 * @constructor
 * @description The class that defines the generic events. This is a helper class to abstract the viewer specific event types.
 * @author Meine Toonen
 * @author Roy Braam
 */
Ext.define("viewer.viewercontroller.controller.Event",{
    statics:{
        //Events:
        //MapComponent events:                        0 - 10
        /** @field */
        ON_CONFIG_COMPLETE                    : "ON_CONFIG_COMPLETE",
        /** @field */
        ON_SET_TOOL                           : "ON_SET_TOOL",

        // Map events:                              11 - 50
        /** @field
         * @param map the map object
         * @param options the options returned
         * @param options.nr number of layers that are identified
         * @param options.total number of total layers that needs to be done.
         **/
        ON_GET_FEATURE_INFO_PROGRESS          : "ON_GET_FEATURE_INFO_PROGRESS",
        /**
         * @field
         * Occures when the map wants a maptip.
         * @param map the map where this event occured
         * @param options.x x in pixels on the screen
         * @param options.y y in pixels on the screen
         * @param options.coord.x the x coord in world coords
         * @param options.coord.y the y coord in world coords
         */
        ON_GET_FEATURE_INFO                   : "ON_GET_FEATURE_INFO",
        /** @field
         * Occures when a maptip returns data
         * @param layer the layer where this event occured
         * @param options a object with options
         * @param options.data the data as a multi array
         * @param options.x the x pixel (screen location)
         * @param options.y the y pixel (screen location)
         * @param options.coord.x the x world coord
         * @param options.coord.y the y world coord
         * @param options.extent (not always available) the place where this maptip for is done.
         * @param options.extent.minx (not always available) the minx world coord (world location)
         * @param options.extent.miny (not always available) the miny world coord (world location)
         * @param options.extent.maxx (not always available) the maxx world coord (world location)
         * @param options.extent.maxy (not always available) the maxy world coord (world location)
         * @param options.nr nr of layer that is done
         * @param options.total total identifies that needs to be done
         **/
        ON_GET_FEATURE_INFO_DATA              : "ON_GET_FEATURE_INFO_DATA",
        /** @field */
        ON_ALL_LAYERS_LOADING_COMPLETE        : "ON_ALL_LAYERS_LOADING_COMPLETE",
        /** @field */
        ON_CHANGE_EXTENT                      : "ON_CHANGE_EXTENT",
        /** @field */
        ON_FINISHED_CHANGE_EXTENT             : "ON_FINISHED_CHANGE_EXTENT",
        /** @field
         * Occures when a layer is added to this map
         * @param map the map object
         * @param options.layer the layer that is added.
         **/
        ON_LAYER_ADDED                        : "ON_LAYER_ADDED",
        ON_LAYER_REMOVED                        : "ON_LAYER_REMOVED",
        /**
         *@field
         *occures when a mouse is moved and the maptip must be cancelled
         *@param map the map object that called this event.
         */
        ON_MAPTIP_CANCEL                      : "ON_MAPTIP_CANCEL",

        // Layer events:                            50 - 100
        /** @field */
        ON_GET_CAPABILITIES                   : "ON_REQUEST",
        /** @field */
        ON_FEATURE_ADDED                      : "ON_FEATURE_ADDED",
        /** @field */
        ON_REQUEST                            : "ON_REQUEST",
        /** @field */
        ON_LOADING_START                      : "ON_LOADING_START",
        /** @field */
        ON_LOADING_END                        : "ON_LOADING_END",
        // Maptips
        /**
         * @field
         * Occures when the map wants a maptip.
         * @param map the map where this event occured
         * @param options.x x in pixels on the screen
         * @param options.y y in pixels on the screen
         * @param options.coord.x the x coord in world coords
         * @param options.coord.y the y coord in world coords
         */
        ON_MAPTIP                             : "ON_MAPTIP",
        /** @field
         * Occures when a maptip returns data
         * @param layer the layer where this event occured
         * @param options a object with options
         * @param options.data the data as a multi array
         * @param options.x the x pixel (screen location)
         * @param options.y the y pixel (screen location)
         * @param options.coord.x the x world coord
         * @param options.coord.y the y world coord
         * @param options.extent (not always available) the place where this maptip for is done.
         * @param options.extent.minx (not always available) the minx world coord (world location)
         * @param options.extent.miny (not always available) the miny world coord (world location)
         * @param options.extent.maxx (not always available) the maxx world coord (world location)
         * @param options.extent.maxy (not always available) the maxy world coord (world location)
         **/
        ON_MAPTIP_DATA                        : "ON_MAPTIP_DATA",

        // Tool events:                             100 - 150
        ON_EVENT_DOWN                         : "ON_EVENT_DOWN",
        /** @field */
        ON_EVENT_UP                           : "ON_EVENT_UP",
        /** @field
         * Occures when a hover event is thrown on a buuton
         */
        ON_EVENT_OVER                           : "ON_EVENT_OVER ",
        /** @field */
        ON_CLICK                              : "ON_CLICK",
        /** @field */
        ON_MEASURE                            : "ON_MEASURE",

        // Shared evens:                            150 - ...
        /** @field */
        ON_ONIT                               : "ON_ONIT",  // Shared by

        ON_LAYER_VISIBILITY_CHANGED           : "ON_LAYER_VISIBILITY_CHANGED",

        ON_LAYERSELECTOR_CHANGE                 : "ON_LAYERSELECTOR_CHANGE",
        ON_COMPONENTS_FINISHED_LOADING           : "ON_COMPONENTS_FINISHED_LOADING",
        ON_SELECTEDCONTENT_CHANGE             : "ON_SELECTEDCONTENT_CHANGE",
        ON_MAP_CLICKED                        :  "ON_MAP_CLICKED",
        /** @field
         *  Occurs when the active feature is changed. Note: Alse fired when the first point of a polygon is set, so the WKT may be incorrect
         */
        ON_ACTIVE_FEATURE_CHANGED             :  "ON_ACTIVE_FEATURE_CHANGED",

        /**
         * @field
         * Occurs when a filter has been created. Fired by the ViewerController. The parameters are the filter and the layer.
         */
        ON_FILTER_ACTIVATED                     : "ON_FILTER_ACTIVATED"

    }
});<commit_msg>Added comment for event onlayerremoved<commit_after>/*JavaScript interface class file*/

/**
 * Event
 * @class
 * @constructor
 * @description The class that defines the generic events. This is a helper class to abstract the viewer specific event types.
 * @author Meine Toonen
 * @author Roy Braam
 */
Ext.define("viewer.viewercontroller.controller.Event",{
    statics:{
        //Events:
        //MapComponent events:                        0 - 10
        /** @field */
        ON_CONFIG_COMPLETE                    : "ON_CONFIG_COMPLETE",
        /** @field */
        ON_SET_TOOL                           : "ON_SET_TOOL",

        // Map events:                              11 - 50
        /** @field
         * @param map the map object
         * @param options the options returned
         * @param options.nr number of layers that are identified
         * @param options.total number of total layers that needs to be done.
         **/
        ON_GET_FEATURE_INFO_PROGRESS          : "ON_GET_FEATURE_INFO_PROGRESS",
        /**
         * @field
         * Occures when the map wants a maptip.
         * @param map the map where this event occured
         * @param options.x x in pixels on the screen
         * @param options.y y in pixels on the screen
         * @param options.coord.x the x coord in world coords
         * @param options.coord.y the y coord in world coords
         */
        ON_GET_FEATURE_INFO                   : "ON_GET_FEATURE_INFO",
        /** @field
         * Occures when a maptip returns data
         * @param layer the layer where this event occured
         * @param options a object with options
         * @param options.data the data as a multi array
         * @param options.x the x pixel (screen location)
         * @param options.y the y pixel (screen location)
         * @param options.coord.x the x world coord
         * @param options.coord.y the y world coord
         * @param options.extent (not always available) the place where this maptip for is done.
         * @param options.extent.minx (not always available) the minx world coord (world location)
         * @param options.extent.miny (not always available) the miny world coord (world location)
         * @param options.extent.maxx (not always available) the maxx world coord (world location)
         * @param options.extent.maxy (not always available) the maxy world coord (world location)
         * @param options.nr nr of layer that is done
         * @param options.total total identifies that needs to be done
         **/
        ON_GET_FEATURE_INFO_DATA              : "ON_GET_FEATURE_INFO_DATA",
        /** @field */
        ON_ALL_LAYERS_LOADING_COMPLETE        : "ON_ALL_LAYERS_LOADING_COMPLETE",
        /** @field */
        ON_CHANGE_EXTENT                      : "ON_CHANGE_EXTENT",
        /** @field */
        ON_FINISHED_CHANGE_EXTENT             : "ON_FINISHED_CHANGE_EXTENT",
        /** @field
         * Occures when a layer is added to this map
         * @param map the map object
         * @param options.layer the layer that is added.
         **/
        ON_LAYER_ADDED                        : "ON_LAYER_ADDED",
        /**
         * @field
         * Occures when a layer is removed from the map.
         * @param map the map object
         * @param options.layer the layer that is removed.
         */
        ON_LAYER_REMOVED                        : "ON_LAYER_REMOVED",
        /**
         *@field
         *occures when a mouse is moved and the maptip must be cancelled
         *@param map the map object that called this event.
         */
        ON_MAPTIP_CANCEL                      : "ON_MAPTIP_CANCEL",

        // Layer events:                            50 - 100
        /** @field */
        ON_GET_CAPABILITIES                   : "ON_REQUEST",
        /** @field */
        ON_FEATURE_ADDED                      : "ON_FEATURE_ADDED",
        /** @field */
        ON_REQUEST                            : "ON_REQUEST",
        /** @field */
        ON_LOADING_START                      : "ON_LOADING_START",
        /** @field */
        ON_LOADING_END                        : "ON_LOADING_END",
        // Maptips
        /**
         * @field
         * Occures when the map wants a maptip.
         * @param map the map where this event occured
         * @param options.x x in pixels on the screen
         * @param options.y y in pixels on the screen
         * @param options.coord.x the x coord in world coords
         * @param options.coord.y the y coord in world coords
         */
        ON_MAPTIP                             : "ON_MAPTIP",
        /** @field
         * Occures when a maptip returns data
         * @param layer the layer where this event occured
         * @param options a object with options
         * @param options.data the data as a multi array
         * @param options.x the x pixel (screen location)
         * @param options.y the y pixel (screen location)
         * @param options.coord.x the x world coord
         * @param options.coord.y the y world coord
         * @param options.extent (not always available) the place where this maptip for is done.
         * @param options.extent.minx (not always available) the minx world coord (world location)
         * @param options.extent.miny (not always available) the miny world coord (world location)
         * @param options.extent.maxx (not always available) the maxx world coord (world location)
         * @param options.extent.maxy (not always available) the maxy world coord (world location)
         **/
        ON_MAPTIP_DATA                        : "ON_MAPTIP_DATA",

        // Tool events:                             100 - 150
        ON_EVENT_DOWN                         : "ON_EVENT_DOWN",
        /** @field */
        ON_EVENT_UP                           : "ON_EVENT_UP",
        /** @field
         * Occures when a hover event is thrown on a buuton
         */
        ON_EVENT_OVER                           : "ON_EVENT_OVER ",
        /** @field */
        ON_CLICK                              : "ON_CLICK",
        /** @field */
        ON_MEASURE                            : "ON_MEASURE",

        // Shared evens:                            150 - ...
        /** @field */
        ON_ONIT                               : "ON_ONIT",  // Shared by

        ON_LAYER_VISIBILITY_CHANGED           : "ON_LAYER_VISIBILITY_CHANGED",

        ON_LAYERSELECTOR_CHANGE                 : "ON_LAYERSELECTOR_CHANGE",
        ON_COMPONENTS_FINISHED_LOADING           : "ON_COMPONENTS_FINISHED_LOADING",
        ON_SELECTEDCONTENT_CHANGE             : "ON_SELECTEDCONTENT_CHANGE",
        ON_MAP_CLICKED                        :  "ON_MAP_CLICKED",
        /** @field
         *  Occurs when the active feature is changed. Note: Alse fired when the first point of a polygon is set, so the WKT may be incorrect
         */
        ON_ACTIVE_FEATURE_CHANGED             :  "ON_ACTIVE_FEATURE_CHANGED",

        /**
         * @field
         * Occurs when a filter has been created. Fired by the ViewerController. The parameters are the filter and the layer.
         */
        ON_FILTER_ACTIVATED                     : "ON_FILTER_ACTIVATED"

    }
});<|endoftext|>


<commit_before>var util = require('../core/util');
var _ = require('lodash');
var fs = require('fs');

var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + '/log');
var CandleBatcher = require(dirs.core + 'candleBatcher');

var moment = require('moment');

var Actor = function(done) {
  _.bindAll(this);

  this.done = done;

  this.batcher = new CandleBatcher(config.tradingAdvisor.candleSize);

  var mode = util.gekkoMode();

  if(mode === 'realtime')
    this.prepareHistoricalData(this.init);
  else if(mode === 'backtest')
    this.init();
}

util.makeEventEmitter(Actor);

Actor.prototype.init = function(done) {
  var methodName = config.tradingAdvisor.method;

  if(!fs.existsSync(dirs.methods + methodName + '.js'))
    util.die('Gekko doesn\'t know the method ' + methodName);

  log.info('\t', 'Using the trading method: ' + methodName);

  var method = require(dirs.methods + methodName);

  // bind all trading method specific functions
  // to the Consultant.
  var Consultant = require(dirs.core + 'baseTradingMethod');

  _.each(method, function(fn, name) {
    Consultant.prototype[name] = fn;
  });

  this.method = new Consultant;

  this.batcher
    .on('candle', this.processCustomCandle)

  this.method
    .on('advice', this.relayAdvice);

  this.done();
}

Actor.prototype.prepareHistoricalData = function(done) {
  // - step 1: check oldest trade reachable by API
  // - step 2: check most recent stored candle window
  // - step 3: see if overlap
  // - step 4: feed candle stream into CandleBatcher

  if(config.tradingAdvisor.historySize === 0)
    return done();

  var requiredHistory = config.tradingAdvisor.candleSize * config.tradingAdvisor.historySize * 60;
  var Reader = require(dirs.plugins + config.tradingAdvisor.adapter + '/reader');

  var reader = new Reader;

  log.debug(
    'The trading method requests',
    requiredHistory,
    'seconds of historic data. Checking availablity..'
  );

  // TODO: refactor cb hell
  this.checkExchangeTrades(requiredHistory, function(err, window) {
    log.debug(
      'Exchange has data spanning',
      window.to - window.from,
      'seconds, we need at least',
      requiredHistory,
      'seconds.'
    );

    if(window.to - window.from > requiredHistory) {
      // calc new required history
      var dif = window.to - window.from;
      requiredHistory = Math.floor(dif / 60 / config.tradingAdvisor.candleSize);

      log.debug(
        'Exchange returns more data than we need,',
        'shift required history to',
        requiredHistory,
        '.'
      );

      util.setConfigProperty(
        'tradingAdvisor',
        'historySize',
        requiredHistory
      );

      return done();
    }

    // We need more data

    var exchangeTo = moment.unix(window.to).startOf('minute').unix();
    var exchangeFrom = moment.unix(window.from).subtract(1, 'm').startOf('minute').unix();
    var optimalFrom = exchangeTo - requiredHistory;

    reader.mostRecentWindow(exchangeFrom, optimalFrom, function(result) {
      if(!result) {
        log.info(
          '\t',
          'Unable to seed the trading method',
          'with locally stored historical candles',
          '(Gekko needs more time before it can give advice).'
        );
        return done();
      }

      if(result === optimalFrom) {
        log.debug(
          'Full history locally available.',
          'Seeding the trading method with all required historical candles.'
        );
      } else {
        log.debug(
          'Partial history locally available. But',
          result - optimalFrom,
          'seconds are missing. Seeding the trading method with',
          'partial historical data.'
        );
      }

      reader.get(result, exchangeFrom, function(rows) {
        // todo: do this in proper place

        rows = rows.map(row => {
          row.start = moment.unix(row.start);
          return row;
        });

        this.batcher.write(rows);
        reader.close();
        done();

      }.bind(this));
    }.bind(this));
  }.bind(this));
}

Actor.prototype.checkExchangeTrades = function(requiredHistory, next) {
  var provider = config.watch.exchange.toLowerCase();
  var DataProvider = require(util.dirs().gekko + 'exchanges/' + provider);

  var exchangeChecker = require(util.dirs().core + 'exchangeChecker');
  var exchangeSettings = exchangeChecker.settings(config.watch)

  var watcher = new DataProvider(config.watch);

  if(exchangeSettings.maxHistoryFetch)
    var since = exchangeSettings.maxHistoryFetch;
  else if(exchangeSettings.providesHistory === 'date')
    // NOTE: uses current time
    var since = moment()
      .subtract(requiredHistory, 'seconds')
      .subtract(config.tradingAdvisor.candleSize, 'minutes');
  else if(exchangeSettings.providesHistory)
    // NOTE: specific to btc-e atm
    var since = exchangeSettings.providesHistory;

  util.setConfigProperty(
    'tradingAdvisor',
    'firstFetchSince',
    since
  );

  watcher.getTrades(since, function(e, d) {
    if(_.isEmpty(d))
      return util.die(
        `Gekko tried to retrieve data since ${since.format('YYYY-MM-DD HH:mm:ss')}, however
        ${provider} did not return any trades. Try to raise the tradingAdviser.historySize.`
      );

    next(e, {
      from: _.first(d).date,
      to: _.last(d).date
    })
  });
}

// HANDLERS
// process the 1m candles
Actor.prototype.processCandle = function(candle, done) {
  this.batcher.write([candle]);
  done();
}

Actor.prototype.finalize = _.noop;

// propogate a custom sized candle to the trading method
Actor.prototype.processCustomCandle = function(candle) {
  this.method.tick(candle);
}

// EMITTERS
Actor.prototype.relayAdvice = function(advice) {
  this.emit('advice', advice);
}

module.exports = Actor;
<commit_msg>typos<commit_after>var util = require('../core/util');
var _ = require('lodash');
var fs = require('fs');

var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + '/log');
var CandleBatcher = require(dirs.core + 'candleBatcher');

var moment = require('moment');

var Actor = function(done) {
  _.bindAll(this);

  this.done = done;

  this.batcher = new CandleBatcher(config.tradingAdvisor.candleSize);

  var mode = util.gekkoMode();

  if(mode === 'realtime')
    this.prepareHistoricalData(this.init);
  else if(mode === 'backtest')
    this.init();
}

util.makeEventEmitter(Actor);

Actor.prototype.init = function(done) {
  var methodName = config.tradingAdvisor.method;

  if(!fs.existsSync(dirs.methods + methodName + '.js'))
    util.die('Gekko doesn\'t know the method ' + methodName);

  log.info('\t', 'Using the trading method: ' + methodName);

  var method = require(dirs.methods + methodName);

  // bind all trading method specific functions
  // to the Consultant.
  var Consultant = require(dirs.core + 'baseTradingMethod');

  _.each(method, function(fn, name) {
    Consultant.prototype[name] = fn;
  });

  this.method = new Consultant;

  this.batcher
    .on('candle', this.processCustomCandle)

  this.method
    .on('advice', this.relayAdvice);

  this.done();
}

Actor.prototype.prepareHistoricalData = function(done) {
  // - step 1: check oldest trade reachable by API
  // - step 2: check most recent stored candle window
  // - step 3: see if overlap
  // - step 4: feed candle stream into CandleBatcher

  if(config.tradingAdvisor.historySize === 0)
    return done();

  var requiredHistory = config.tradingAdvisor.candleSize * config.tradingAdvisor.historySize * 60;
  var Reader = require(dirs.plugins + config.tradingAdvisor.adapter + '/reader');

  var reader = new Reader;

  log.debug(
    'The trading method requests',
    requiredHistory,
    'seconds of historic data. Checking availablity..'
  );

  // TODO: refactor cb hell
  this.checkExchangeTrades(requiredHistory, function(err, window) {
    log.debug(
      'Exchange has data spanning',
      window.to - window.from,
      'seconds, we need at least',
      requiredHistory,
      'seconds.'
    );

    if(window.to - window.from > requiredHistory) {
      // calc new required history
      var dif = window.to - window.from;
      requiredHistory = Math.floor(dif / 60 / config.tradingAdvisor.candleSize);

      log.debug(
        'Exchange returns more data than we need,',
        'shift required history to',
        requiredHistory,
        '.'
      );

      util.setConfigProperty(
        'tradingAdvisor',
        'historySize',
        requiredHistory
      );

      return done();
    }

    // We need more data

    var exchangeTo = moment.unix(window.to).startOf('minute').unix();
    var exchangeFrom = moment.unix(window.from).subtract(1, 'm').startOf('minute').unix();
    var optimalFrom = exchangeTo - requiredHistory;

    reader.mostRecentWindow(exchangeFrom, optimalFrom, function(result) {
      if(!result) {
        log.info(
          '\t',
          'Unable to seed the trading method',
          'with locally stored historical candles',
          '(Gekko needs more time before it can give advice).'
        );
        return done();
      }

      if(result === optimalFrom) {
        log.debug(
          'Full history locally available.',
          'Seeding the trading method with all required historical candles.'
        );
      } else {
        log.debug(
          'Partial history locally available. But',
          result - optimalFrom,
          'seconds are missing. Seeding the trading method with',
          'partial historical data (Gekko needs more time before',
          'it can give advice).'
        );
      }

      reader.get(result, exchangeFrom, function(rows) {
        // todo: do this in proper place

        rows = rows.map(row => {
          row.start = moment.unix(row.start);
          return row;
        });

        this.batcher.write(rows);
        reader.close();
        done();

      }.bind(this));
    }.bind(this));
  }.bind(this));
}

Actor.prototype.checkExchangeTrades = function(requiredHistory, next) {
  var provider = config.watch.exchange.toLowerCase();
  var DataProvider = require(util.dirs().gekko + 'exchanges/' + provider);

  var exchangeChecker = require(util.dirs().core + 'exchangeChecker');
  var exchangeSettings = exchangeChecker.settings(config.watch)

  var watcher = new DataProvider(config.watch);

  if(exchangeSettings.maxHistoryFetch)
    var since = exchangeSettings.maxHistoryFetch;
  else if(exchangeSettings.providesHistory === 'date')
    // NOTE: uses current time
    var since = moment()
      .subtract(requiredHistory, 'seconds')
      .subtract(config.tradingAdvisor.candleSize, 'minutes');
  else if(exchangeSettings.providesHistory)
    // NOTE: specific to btc-e atm
    var since = exchangeSettings.providesHistory;

  util.setConfigProperty(
    'tradingAdvisor',
    'firstFetchSince',
    since
  );

  watcher.getTrades(since, function(e, d) {
    if(_.isEmpty(d))
      return util.die(
        `Gekko tried to retrieve data since ${since.format('YYYY-MM-DD HH:mm:ss')}, however
        ${provider} did not return any trades. Try to raise the tradingAdviser.historySize.`
      );

    next(e, {
      from: _.first(d).date,
      to: _.last(d).date
    })
  });
}

// HANDLERS
// process the 1m candles
Actor.prototype.processCandle = function(candle, done) {
  this.batcher.write([candle]);
  done();
}

Actor.prototype.finalize = _.noop;

// propogate a custom sized candle to the trading method
Actor.prototype.processCustomCandle = function(candle) {
  this.method.tick(candle);
}

// EMITTERS
Actor.prototype.relayAdvice = function(advice) {
  this.emit('advice', advice);
}

module.exports = Actor;
<|endoftext|>


<commit_before>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * 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.
 */

import compiler from '@ampproject/rollup-plugin-closure-compiler';

export default {
  input: 'exports/config.js',
  output: {
    file: 'build/rollup.js',
    format: 'es',
  },
  plugins: [
    compiler(),
  ],
};

// export default commandLineArgs => {
//   return {
//     input: commandLineArgs.input,
//     output: {
//       file: 'build/rollup.js',
//       format: 'esm',
//     },
//     // plugins: [
//     //   compiler(),
//     // ],
//   };
// };

// // rollup.config.js
// import defaultConfig from './rollup.default.config.js';
// import debugConfig from './rollup.debug.config.js';

//   if (commandLineArgs.configDebug === true) {
//     return debugConfig;
//   }
//   return defaultConfig;<commit_msg>lint fix<commit_after>/**
 * Copyright 2018 The Subscribe with Google Authors. All Rights Reserved.
 *
 * 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.
 */

import compiler from '@ampproject/rollup-plugin-closure-compiler';

export default {
  input: 'exports/config.js',
  output: {
    file: 'build/rollup.js',
    format: 'es',
  },
  plugins: [
    compiler(),
  ],
};
<|endoftext|>


<commit_before>var standingsController = app.controller('standingsController', ['$scope', '$http', '$filter',  'Auth', 'Toast', 'loadPlayers', 'loadFixtures', 'loadPredictions', function ($scope, $http, $filter, Auth, Toast, loadPlayers, loadFixtures, loadPredictions) {
  scrollToTheTop();

  /**
   * RULE
   *
   * SO   spot-on 3 PTS
   * GD   gold difference 2 PTS
   * W    side 1 PTS
   * L    wrong 0 PTS
   * F    forfeit -1 PTS
   */

  $scope.players = loadPlayers;
  $scope.fixtures = loadFixtures; // the fixtures that are stored here are the ones that are "completed"
  $scope.predictions = loadPredictions;
  $scope.currentPlayer = Auth.info();

  // initiating players' stat
  $scope.playersStat = {}; // {'username': {SO: 0, GD: 0, W: 0, L: 0, F: 0, PTS: 0}}
  angular.forEach($scope.players, function (value, key) {
    $scope.playersStat[value.player_username] = {$username: value.player_username, SO: 0, GD: 0, W: 0, L: 0, F: 0, PTS: 0};
  });

  // we'll loop through fixtures and associate em' with predictions
  angular.forEach($scope.fixtures, function (valueF, keyF) {
    $scope.fixtures[keyF].predictions = {};

    angular.forEach($scope.predictions, function (valueP, keyP) {
      if (valueF.fixture_id === valueP.prediction_fixture.fixture_id) {
        $scope.fixtures[keyF].predictions[valueP.prediction_player.player_username] = valueP;
      }
    });
  });

  // yes more loop
  angular.forEach($scope.playersStat, function (stat, username) {
    angular.forEach($scope.fixtures, function (valueF, keyF) {
      // show me what you got little mama
      if (valueF.predictions.hasOwnProperty(username) === true) {
        // spot-on
        if ((valueF.fixture_team_home_score === valueF.predictions[username].prediction_home_team) && (valueF.fixture_team_away_score === valueF.predictions[username].prediction_away_team)) {
          $scope.playersStat[username].SO ++;
          $scope.playersStat[username].PTS += 3;
          $scope.fixtures[keyF].predictions[username].PTS = 3;
        }

        // goal difference
        else if ((valueF.fixture_team_home_score - valueF.fixture_team_away_score) === (valueF.predictions[username].prediction_home_team - valueF.predictions[username].prediction_away_team)) {
          $scope.playersStat[username].GD ++;
          $scope.playersStat[username].PTS += 2;
          $scope.fixtures[keyF].predictions[username].PTS = 2;
        }

        // side
        else if ((valueF.fixture_team_home_score > valueF.fixture_team_away_score) === (valueF.predictions[username].prediction_home_team > valueF.predictions[username].prediction_away_team)) {
          $scope.playersStat[username].W ++;
          $scope.playersStat[username].PTS += 1;
          $scope.fixtures[keyF].predictions[username].PTS = 1;
        }

        // miiiiiiiiiiiiiiith
        else {
          $scope.playersStat[username].L ++;
          $scope.fixtures[keyF].predictions[username].PTS = 0;
        }
      }

      // forfeit
      else {
        $scope.playersStat[username].F ++;
        $scope.playersStat[username].PTS -= 1;
        $scope.fixtures[keyF].predictions[username] = {PTS: -1};
      }
    });
  });

  $scope.fixtures = $filter('orderBy')($scope.fixtures, 'unixEpoch', true);

  /**
   * there's no turning back now
   * i was wrong, so wrong! (Prometheus)
   */
  $scope.statOrdered = [];
  angular.forEach($scope.playersStat, function (value, key) {
    $scope.statOrdered.push(value);
  });

  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'F', false);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'L', false);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'W', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'GD', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'SO', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'PTS', true);

  $scope.standingsController = this;
}]);

standingsController.loadPlayers = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/players')
    .success(function (data, staus) {
      deferred.resolve(data);
    })
    .error(function (data, staus) {
      deferred.reject();
    });

  return deferred.promise;
};

standingsController.loadFixtures = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/fixtures')
    .success(function (data, staus) {
      var fixtures = [];
      angular.forEach(data, function (value, key) {
        if (value.fixture_team_home_score > -1) {
          value.humanFormat = moment(value.fixture_time).format('MMMM DD, YYYY @ hh:mm A');
          value.unixEpoch = moment(value.fixture_time).valueOf();
          fixtures.push(value);
        }
      });

      deferred.resolve(fixtures);
    })
    .error(function (data, staus) {
      deferred.reject();
    })

  return deferred.promise;
};

standingsController.loadPredictions = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/predictions')
    .success(function (data, staus) {
      deferred.resolve(data);
    })
    .error(function (data, staus) {
      deferred.reject();
    })

  return deferred.promise;
};
<commit_msg>side fix<commit_after>var standingsController = app.controller('standingsController', ['$scope', '$http', '$filter',  'Auth', 'Toast', 'loadPlayers', 'loadFixtures', 'loadPredictions', function ($scope, $http, $filter, Auth, Toast, loadPlayers, loadFixtures, loadPredictions) {
  scrollToTheTop();

  /**
   * RULE
   *
   * SO   spot-on 3 PTS
   * GD   gold difference 2 PTS
   * W    side 1 PTS
   * L    wrong 0 PTS
   * F    forfeit -1 PTS
   */

  $scope.players = loadPlayers;
  $scope.fixtures = loadFixtures; // the fixtures that are stored here are the ones that are "completed"
  $scope.predictions = loadPredictions;
  $scope.currentPlayer = Auth.info();

  // initiating players' stat
  $scope.playersStat = {}; // {'username': {SO: 0, GD: 0, W: 0, L: 0, F: 0, PTS: 0}}
  angular.forEach($scope.players, function (value, key) {
    $scope.playersStat[value.player_username] = {$username: value.player_username, SO: 0, GD: 0, W: 0, L: 0, F: 0, PTS: 0};
  });

  // we'll loop through fixtures and associate em' with predictions
  angular.forEach($scope.fixtures, function (valueF, keyF) {
    $scope.fixtures[keyF].predictions = {};

    angular.forEach($scope.predictions, function (valueP, keyP) {
      if (valueF.fixture_id === valueP.prediction_fixture.fixture_id) {
        $scope.fixtures[keyF].predictions[valueP.prediction_player.player_username] = valueP;
      }
    });
  });

  // yes more loop
  angular.forEach($scope.playersStat, function (stat, username) {
    angular.forEach($scope.fixtures, function (valueF, keyF) {
      // show me what you got little mama
      if (valueF.predictions.hasOwnProperty(username) === true) {
        // spot-on
        if ((valueF.fixture_team_home_score === valueF.predictions[username].prediction_home_team) && (valueF.fixture_team_away_score === valueF.predictions[username].prediction_away_team)) {
          if (valueF.fixture_team_home_score > -1) {
            $scope.playersStat[username].SO ++;
            $scope.playersStat[username].PTS += 3;
            $scope.fixtures[keyF].predictions[username].PTS = 3;
          }
        }

        // goal difference
        else if ((valueF.fixture_team_home_score - valueF.fixture_team_away_score) === (valueF.predictions[username].prediction_home_team - valueF.predictions[username].prediction_away_team)) {
          if (valueF.fixture_team_home_score > -1) {
            $scope.playersStat[username].GD ++;
            $scope.playersStat[username].PTS += 2;
            $scope.fixtures[keyF].predictions[username].PTS = 2;
          }
        }

        // side
        else if ((valueF.fixture_team_home_score > valueF.fixture_team_away_score) === (valueF.predictions[username].prediction_home_team > valueF.predictions[username].prediction_away_team) &&
                 (valueF.fixture_team_home_score < valueF.fixture_team_away_score) === (valueF.predictions[username].prediction_home_team < valueF.predictions[username].prediction_away_team)) {
          if (valueF.fixture_team_home_score > -1) {
            $scope.playersStat[username].W ++;
            $scope.playersStat[username].PTS += 1;
            $scope.fixtures[keyF].predictions[username].PTS = 1;
          }
        }

        // miiiiiiiiiiiiiiith
        else {
          if (valueF.fixture_team_home_score > -1) {
            $scope.playersStat[username].L ++;
            $scope.fixtures[keyF].predictions[username].PTS = 0;
          }
        }
      }

      // forfeit
      else {
        if (valueF.fixture_team_home_score > -1) {
          $scope.playersStat[username].F ++;
          $scope.playersStat[username].PTS -= 1;
          $scope.fixtures[keyF].predictions[username] = {PTS: -1};
        }
      }
    });
  });

  $scope.fixtures = $filter('orderBy')($scope.fixtures, 'unixEpoch', true);

  /**
   * there's no turning back now
   * i was wrong, so wrong! (Prometheus)
   */
  $scope.statOrdered = [];
  angular.forEach($scope.playersStat, function (value, key) {
    $scope.statOrdered.push(value);
  });

  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'F', false);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'L', false);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'W', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'GD', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'SO', true);
  $scope.statOrdered = $filter('orderBy')($scope.statOrdered, 'PTS', true);

  $scope.standingsController = this;
}]);

standingsController.loadPlayers = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/players')
    .success(function (data, staus) {
      deferred.resolve(data);
    })
    .error(function (data, staus) {
      deferred.reject();
    });

  return deferred.promise;
};

standingsController.loadFixtures = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/fixtures')
    .success(function (data, staus) {
      var fixtures = [];
      angular.forEach(data, function (value, key) {
        if (moment(value.fixture_time).isSame(moment(), 'day') || value.fixture_team_home_score > -1) {
          value.humanFormat = moment(value.fixture_time).format('MMMM DD, YYYY @ hh:mm A');
          value.unixEpoch = moment(value.fixture_time).valueOf();
          fixtures.push(value);
        }
      });

      deferred.resolve(fixtures);
    })
    .error(function (data, staus) {
      deferred.reject();
    })

  return deferred.promise;
};

standingsController.loadPredictions = function ($q, $http) {
  var deferred = $q.defer();

  $http.get('api/predictions')
    .success(function (data, staus) {
      deferred.resolve(data);
    })
    .error(function (data, staus) {
      deferred.reject();
    })

  return deferred.promise;
};
<|endoftext|>


<commit_before>import babel from 'rollup-plugin-babel'
import { uglify } from 'rollup-plugin-uglify'
import license from 'rollup-plugin-license'

const date = new Date()
const banner = `/*!
*   Gerador e Validador de CPF v<%= pkg.version %>
*   http://tiagoporto.github.io/gerador-validador-cpf
*   Copyright (c) 2014-${date.getFullYear()} Tiago Porto (http://tiagoporto.com)
*   Released under the MIT license
*/
`

export default [
  {
    input: 'src/js/CPF.ts',
    output: [
      {
        file: 'dist/CPF.umd.js',
        format: 'umd',
        name: 'CPF'
      },
      {
        file: 'dist/CPF.es.js',
        format: 'es'
      }
    ],
    plugins: [
      babel({ extensions: ['.js', '.ts'] }),
      license({
        banner
      })
    ]
  },
  {
    input: 'src/js/CPF.ts',
    output: [
      {
        file: 'dist/CPF.umd.min.js',
        format: 'umd',
        name: 'CPF'
      }
    ],
    plugins: [
      babel({ extensions: ['.js', '.ts'] }),
      uglify(),
      license({
        banner
      })
    ]
  }
]
<commit_msg>Fix import terser<commit_after>import babel from 'rollup-plugin-babel'
import { terser } from 'rollup-plugin-terser'
import license from 'rollup-plugin-license'

const date = new Date()
const banner = `/*!
*   Gerador e Validador de CPF v<%= pkg.version %>
*   http://tiagoporto.github.io/gerador-validador-cpf
*   Copyright (c) 2014-${date.getFullYear()} Tiago Porto (http://tiagoporto.com)
*   Released under the MIT license
*/
`

export default [
  {
    input: 'src/js/CPF.ts',
    output: [
      {
        file: 'dist/CPF.umd.js',
        format: 'umd',
        name: 'CPF'
      },
      {
        file: 'dist/CPF.es.js',
        format: 'es'
      }
    ],
    plugins: [
      babel({ extensions: ['.js', '.ts'] }),
      license({
        banner
      })
    ]
  },
  {
    input: 'src/js/CPF.ts',
    output: [
      {
        file: 'dist/CPF.umd.min.js',
        format: 'umd',
        name: 'CPF'
      }
    ],
    plugins: [
      babel({ extensions: ['.js', '.ts'] }),
      terser(),
      license({
        banner
      })
    ]
  }
]
<|endoftext|>