<commit_before>(function () {
  'use strict';

  angular
    .module('lorryApp')
    .controller('DocumentCtrl', DocumentCtrl);

  DocumentCtrl.$inject = ['$rootScope', '$scope', '$log', '$http', '$location', 'lodash', 'jsyaml', 'ngDialog',
    'yamlValidator', 'serviceDefTransformer', '$timeout', 'cookiesService', 'keysService'];

  function DocumentCtrl($rootScope, $scope, $log, $http, $location, lodash, jsyaml, ngDialog,
                        yamlValidator, serviceDefTransformer, $timeout, cookiesService, keysService) {

    var self = this;

    $scope.yamlDocument = {};
    $scope.resettable = false;
    $scope.importable = true;
    $scope.editedServiceYamlDocumentJson = {};
    $scope.newServiceBlock = false;
    $rootScope.serviceNameList = [];
    $rootScope.markAsDeletedTracker = {};

    $scope.hasErrors = function () {
      return lodash.any($scope.yamlDocument.errors);
    };

    $scope.resetWorkspace = function () {

      $scope.confirmMessage = 'Are you sure you want to clear the workspace?';
      ngDialog.openConfirm(
        {
          template: '/views/confirm-dialog.html',
          className: 'ngconfirm-theme-lorry',
          showClose: false,
          scope: $scope
        }
      ).then(function () {
          self.reset();
        });
    };

    $scope.deleteService = function (serviceName) {
      if ($scope.yamlDocument.json.hasOwnProperty(serviceName)) {
        delete $scope.yamlDocument.json[serviceName];
        self.validateJson();
      }
    };

    $scope.$watchCollection('yamlDocument.raw', function () {
      var documentDefined = (angular.isDefined($scope.yamlDocument) &&
                             angular.isDefined($scope.yamlDocument.raw) &&
                             !$scope.yamlDocument.loadFailure);
      if (documentDefined) {
        self.failFastOrValidateYaml();
      }
      $scope.resettable = documentDefined || !!$scope.yamlDocument.loadFailure;
      $scope.importable = !$scope.resettable;
    });

    this.reset = function () {
      $scope.yamlDocument = {};
      $scope.editedServiceYamlDocumentJson = {};
      $scope.newServiceBlock = false;
      this.buildServiceDefinitions();
    };

    this.validateJson = function () {
      if (lodash.isEmpty($scope.yamlDocument.json)) {
        this.reset();
      } else {
        $scope.yamlDocument.parseErrors = false;
        $scope.yamlDocument.loadFailure = false;
        $scope.yamlDocument.raw = jsyaml.safeDump($scope.yamlDocument.json);
      }
    };

    this.failFastOrValidateYaml = function () {
      var yaml = $scope.yamlDocument.raw;

      try {
        $scope.yamlDocument.json = jsyaml.safeLoad(yaml);
        self.validateYaml();
      } catch (YamlException) {
        // if jsyaml can't load the document, don't bother calling the server
        $scope.yamlDocument.errors = [{error: {message: YamlException.message}}];
        $scope.yamlDocument.parseErrors = false;
        $scope.yamlDocument.loadFailure = true;
      }
    };

    this.validateYaml = function () {
      var yaml = $scope.yamlDocument.raw;

      yamlValidator.validate(yaml)
        .then(function (response) {
          $scope.yamlDocument.lines = response.data.lines;
          $scope.yamlDocument.errors = response.data.errors;
          $scope.yamlDocument.warnings = response.data.warnings;
          if (lodash.any(response.data.errors)) {
            $scope.yamlDocument.parseErrors = true;
            $scope.yamlDocument.loadFailure = false;
          }
        })
        .catch(function (response) {
          if (response.status === 422 && response.data) {
            $scope.yamlDocument.lines = response.data.lines;
            $scope.yamlDocument.errors = [{error: {message: response.data.error}}];
          } else {
            $scope.yamlDocument.errors = [{error: {message: 'An internal server error has occurred'}}];
          }
          $scope.yamlDocument.parseErrors = false;
          $scope.yamlDocument.loadFailure = true;
        })
        .finally(function () {
          self.buildServiceDefinitions();
          $rootScope.serviceNameList = $scope.serviceNames();
        }
      );
    };

    this.buildServiceDefinitions = function () {
      $scope.serviceDefinitions = serviceDefTransformer.fromYamlDocument($scope.yamlDocument);
    };

    $scope.serviceName = function (srvcDef) {
      return srvcDef[0].text.split(':')[0];
    };

    $scope.editService = function (serviceName) {
      if ($scope.yamlDocument.json.hasOwnProperty(serviceName)) {
        // copy the service json for editing purposes
        $scope.editedServiceYamlDocumentJson = lodash.cloneDeep($scope.yamlDocument.json[serviceName]);

        // turn on edit mode
        $scope.yamlDocument.json[serviceName].editMode = true;
      }
    };

    $scope.$on('saveService', function (e, oldServiceName, newServiceName, updatedSectionData) {
      // Delete the markedForDeletion items
      updatedSectionData = self.deleteItemsMarkedForDeletion(updatedSectionData);

      // Update the json for the service name
      if (oldServiceName && oldServiceName !== newServiceName) {
        delete $scope.yamlDocument.json[oldServiceName];
      }

      if ($scope.yamlDocument.json && $scope.yamlDocument.json[newServiceName]) {
        // editing an existing yaml block in an existing yaml doc
        $scope.yamlDocument.json[newServiceName] = updatedSectionData;
        delete $scope.yamlDocument.json[newServiceName].editMode;
      } else {
        // add a new service block either to an existing yaml doc or an empty one
        if (!$scope.yamlDocument.json) {
          $scope.yamlDocument.json = {};
        }
        $scope.yamlDocument.json[newServiceName] = updatedSectionData;
        $scope.newServiceBlock = false;
      }

      // reset the edited service yaml
      $scope.editedServiceYamlDocumentJson = {};

      self.validateJson();
    });

    $scope.$on('cancelEditing', function (e, serviceName) {
      // reset the delete tracker
      $rootScope.markAsDeletedTracker = {};
      // reset the edited service yaml
      $scope.editedServiceYamlDocumentJson = {};

      if ($scope.yamlDocument.json) {
        if ($scope.newServiceBlock) {
          // hide new service block and show new service button
          $scope.newServiceBlock = false;
        } else if ($scope.yamlDocument.json[serviceName]) {
          // turn off edit mode
          delete $scope.yamlDocument.json[serviceName].editMode;
        }
      }
    });

    this.deleteItemsMarkedForDeletion = function (data) {
      var tracker = $rootScope.markAsDeletedTracker;

      angular.forEach(tracker, function (v, k) {
        if (v[0] === 'delete me') {
          delete data[k];
        } else {
          lodash.pullAt(data[k], v);
          // delete key if no items are left
          if (lodash.size(data[k]) === 0) {
            delete data[k];
          }
        }
      });

      // reset the tracker
      $rootScope.markAsDeletedTracker = {};

      return data;
    };

    $scope.serviceNames = function () {
      return lodash.keys($scope.yamlDocument.json);
    };

    $scope.inEditMode = function () {
      return lodash.some($scope.yamlDocument.json, 'editMode', true);
    };

    $scope.triggerClickForElement = function (elementId) {
      $timeout(function () {
        angular.element(elementId).triggerHandler('click');
      }, 0);
    };

    $scope.isNewSession = function () {
      return cookiesService.get('lorry-started') ? false : true;
    };

    $scope.setNewSession = function () {
      cookiesService.put('lorry-started', 'true');
    };

    $scope.addNewServiceDef = function () {
      if (!$scope.inEditMode()) {
        $scope.newServiceBlock = true;
      }
    };

    $scope.inNewServiceMode = function () {
      return $scope.newServiceBlock;
    };

    $scope.showAddServiceBlockOrBtn = function () {
      // the inverse is: !$scope.serviceDefinitions || $scope.serviceDefinitions.length == 0 || $scope.newServiceBlock
      return $scope.serviceDefinitions && $scope.serviceDefinitions.length > 0 && !$scope.newServiceBlock ? true : false;
    };

    $scope.getValidKeys = function () {
      keysService.keys()
        .then(function (response) {
          var keys = [];
          var keysHelpText = [];
          angular.forEach(response.data, function (v) {
            var key = lodash.keys(v)[0];
            keys.push(key);
            var desc = lodash.values(v)[0].desc;
            var obj = {};
            obj[key] = desc;
            keysHelpText.push(obj);
          });
          $rootScope.validKeys = keys;
          $rootScope.keysHelpText = keysHelpText;
        })
        .catch(function () {
          $rootScope.validKeys = [];
          $rootScope.keysHelpText = [];
        });
    };

    $scope.hasLoadFailure = function () {
      return $scope.yamlDocument.loadFailure;
    };

    this.initialize = function () {
      var gistUri = $location.search().gist;
      if (angular.isDefined(gistUri)) {
        $scope.setNewSession();
        $http.get(gistUri)
          .then(function (response) {
            $scope.yamlDocument.raw = response.data;
          })
          .catch(function (response) {
            $log.error(response);
            $scope.yamlDocument.raw = null;
          });
      }
    };

    self.initialize();

  }
})();
<commit_msg>Add GA event tracking for saving the first scratch block for a document and for adding images on save.<commit_after>(function () {
  'use strict';

  angular
    .module('lorryApp')
    .controller('DocumentCtrl', DocumentCtrl);

  DocumentCtrl.$inject = ['$rootScope', '$scope', '$log', '$http', '$location', 'lodash', 'jsyaml', 'ngDialog',
    'yamlValidator', 'serviceDefTransformer', '$timeout', 'cookiesService', 'keysService', 'analyticsService'];

  function DocumentCtrl($rootScope, $scope, $log, $http, $location, lodash, jsyaml, ngDialog,
                        yamlValidator, serviceDefTransformer, $timeout, cookiesService, keysService, analyticsService) {

    var self = this;

    $scope.yamlDocument = {};
    $scope.resettable = false;
    $scope.importable = true;
    $scope.editedServiceYamlDocumentJson = {};
    $scope.newServiceBlock = false;
    $rootScope.serviceNameList = [];
    $rootScope.markAsDeletedTracker = {};

    $scope.hasErrors = function () {
      return lodash.any($scope.yamlDocument.errors);
    };

    $scope.resetWorkspace = function () {

      $scope.confirmMessage = 'Are you sure you want to clear the workspace?';
      ngDialog.openConfirm(
        {
          template: '/views/confirm-dialog.html',
          className: 'ngconfirm-theme-lorry',
          showClose: false,
          scope: $scope
        }
      ).then(function () {
          self.reset();
        });
    };

    $scope.deleteService = function (serviceName) {
      if ($scope.yamlDocument.json.hasOwnProperty(serviceName)) {
        delete $scope.yamlDocument.json[serviceName];
        self.validateJson();
      }
    };

    $scope.$watchCollection('yamlDocument.raw', function () {
      var documentDefined = (angular.isDefined($scope.yamlDocument) &&
                             angular.isDefined($scope.yamlDocument.raw) &&
                             !$scope.yamlDocument.loadFailure);
      if (documentDefined) {
        self.failFastOrValidateYaml();
      }
      $scope.resettable = documentDefined || !!$scope.yamlDocument.loadFailure;
      $scope.importable = !$scope.resettable;
    });

    this.reset = function () {
      $scope.yamlDocument = {};
      $scope.editedServiceYamlDocumentJson = {};
      $scope.newServiceBlock = false;
      this.buildServiceDefinitions();
    };

    this.validateJson = function () {
      if (lodash.isEmpty($scope.yamlDocument.json)) {
        this.reset();
      } else {
        $scope.yamlDocument.parseErrors = false;
        $scope.yamlDocument.loadFailure = false;
        $scope.yamlDocument.raw = jsyaml.safeDump($scope.yamlDocument.json);
      }
    };

    this.failFastOrValidateYaml = function () {
      var yaml = $scope.yamlDocument.raw;

      try {
        $scope.yamlDocument.json = jsyaml.safeLoad(yaml);
        self.validateYaml();
      } catch (YamlException) {
        // if jsyaml can't load the document, don't bother calling the server
        $scope.yamlDocument.errors = [{error: {message: YamlException.message}}];
        $scope.yamlDocument.parseErrors = false;
        $scope.yamlDocument.loadFailure = true;
      }
    };

    this.validateYaml = function () {
      var yaml = $scope.yamlDocument.raw;

      yamlValidator.validate(yaml)
        .then(function (response) {
          $scope.yamlDocument.lines = response.data.lines;
          $scope.yamlDocument.errors = response.data.errors;
          $scope.yamlDocument.warnings = response.data.warnings;
          if (lodash.any(response.data.errors)) {
            $scope.yamlDocument.parseErrors = true;
            $scope.yamlDocument.loadFailure = false;
          }
        })
        .catch(function (response) {
          if (response.status === 422 && response.data) {
            $scope.yamlDocument.lines = response.data.lines;
            $scope.yamlDocument.errors = [{error: {message: response.data.error}}];
          } else {
            $scope.yamlDocument.errors = [{error: {message: 'An internal server error has occurred'}}];
          }
          $scope.yamlDocument.parseErrors = false;
          $scope.yamlDocument.loadFailure = true;
        })
        .finally(function () {
          self.buildServiceDefinitions();
          $rootScope.serviceNameList = $scope.serviceNames();
        }
      );
    };

    this.buildServiceDefinitions = function () {
      $scope.serviceDefinitions = serviceDefTransformer.fromYamlDocument($scope.yamlDocument);
    };

    $scope.serviceName = function (srvcDef) {
      return srvcDef[0].text.split(':')[0];
    };

    $scope.editService = function (serviceName) {
      if ($scope.yamlDocument.json.hasOwnProperty(serviceName)) {
        // copy the service json for editing purposes
        $scope.editedServiceYamlDocumentJson = lodash.cloneDeep($scope.yamlDocument.json[serviceName]);

        // turn on edit mode
        $scope.yamlDocument.json[serviceName].editMode = true;
      }
    };

    $scope.$on('saveService', function (e, oldServiceName, newServiceName, updatedSectionData) {
      // Delete the markedForDeletion items
      updatedSectionData = self.deleteItemsMarkedForDeletion(updatedSectionData);

      // Update the json for the service name
      if (oldServiceName && oldServiceName !== newServiceName) {
        delete $scope.yamlDocument.json[oldServiceName];
      }

      if ($scope.yamlDocument.json && $scope.yamlDocument.json[newServiceName]) {
        // editing an existing yaml block in an existing yaml doc
        $scope.yamlDocument.json[newServiceName] = updatedSectionData;
        delete $scope.yamlDocument.json[newServiceName].editMode;
      } else {
        var scratchDoc = false;
        // add a new service block either to an existing yaml doc or an empty one
        if (!$scope.yamlDocument.json) {
          scratchDoc = true;
          $scope.yamlDocument.json = {};
        }
        $scope.yamlDocument.json[newServiceName] = updatedSectionData;
        $scope.newServiceBlock = false;

        if (scratchDoc) {
          // GA click tracking for save on first scratch block
          analyticsService.trackEvent('create', 'scratch', '');
        }
      }

      // GA click tracking for image added per service block
      var imageName = updatedSectionData.image;
      analyticsService.trackEvent('image', 'add', imageName);

      // reset the edited service yaml
      $scope.editedServiceYamlDocumentJson = {};

      self.validateJson();
    });

    $scope.$on('cancelEditing', function (e, serviceName) {
      // reset the delete tracker
      $rootScope.markAsDeletedTracker = {};
      // reset the edited service yaml
      $scope.editedServiceYamlDocumentJson = {};

      if ($scope.yamlDocument.json) {
        if ($scope.newServiceBlock) {
          // hide new service block and show new service button
          $scope.newServiceBlock = false;
        } else if ($scope.yamlDocument.json[serviceName]) {
          // turn off edit mode
          delete $scope.yamlDocument.json[serviceName].editMode;
        }
      }
    });

    this.deleteItemsMarkedForDeletion = function (data) {
      var tracker = $rootScope.markAsDeletedTracker;

      angular.forEach(tracker, function (v, k) {
        if (v[0] === 'delete me') {
          delete data[k];
        } else {
          lodash.pullAt(data[k], v);
          // delete key if no items are left
          if (lodash.size(data[k]) === 0) {
            delete data[k];
          }
        }
      });

      // reset the tracker
      $rootScope.markAsDeletedTracker = {};

      return data;
    };

    $scope.serviceNames = function () {
      return lodash.keys($scope.yamlDocument.json);
    };

    $scope.inEditMode = function () {
      return lodash.some($scope.yamlDocument.json, 'editMode', true);
    };

    $scope.triggerClickForElement = function (elementId) {
      $timeout(function () {
        angular.element(elementId).triggerHandler('click');
      }, 0);
    };

    $scope.isNewSession = function () {
      return cookiesService.get('lorry-started') ? false : true;
    };

    $scope.setNewSession = function () {
      cookiesService.put('lorry-started', 'true');
    };

    $scope.addNewServiceDef = function () {
      if (!$scope.inEditMode()) {
        $scope.newServiceBlock = true;
      }
    };

    $scope.inNewServiceMode = function () {
      return $scope.newServiceBlock;
    };

    $scope.showAddServiceBlockOrBtn = function () {
      // the inverse is: !$scope.serviceDefinitions || $scope.serviceDefinitions.length == 0 || $scope.newServiceBlock
      return $scope.serviceDefinitions && $scope.serviceDefinitions.length > 0 && !$scope.newServiceBlock ? true : false;
    };

    $scope.getValidKeys = function () {
      keysService.keys()
        .then(function (response) {
          var keys = [];
          var keysHelpText = [];
          angular.forEach(response.data, function (v) {
            var key = lodash.keys(v)[0];
            keys.push(key);
            var desc = lodash.values(v)[0].desc;
            var obj = {};
            obj[key] = desc;
            keysHelpText.push(obj);
          });
          $rootScope.validKeys = keys;
          $rootScope.keysHelpText = keysHelpText;
        })
        .catch(function () {
          $rootScope.validKeys = [];
          $rootScope.keysHelpText = [];
        });
    };

    $scope.hasLoadFailure = function () {
      return $scope.yamlDocument.loadFailure;
    };

    this.initialize = function () {
      var gistUri = $location.search().gist;
      if (angular.isDefined(gistUri)) {
        $scope.setNewSession();
        $http.get(gistUri)
          .then(function (response) {
            $scope.yamlDocument.raw = response.data;
          })
          .catch(function (response) {
            $log.error(response);
            $scope.yamlDocument.raw = null;
          });
      }
    };

    self.initialize();

  }
})();
<|endoftext|>


<commit_before>
/*!
 * Module dependencies.
 */

var markdown = require('github-flavored-markdown').parse
  , escape = require('./utils').escape;

/**
 * Library version.
 */

exports.version = '0.3.1';

/**
 * Parse comments in the given string of `js`.
 *
 * @param {String} js
 * @param {Object} options
 * @return {Array}
 * @see exports.parseComment
 * @api public
 */

exports.parseComments = function(js, options){
  options = options || {};

  var comments = []
    , raw = options.raw
    , comment
    , buf = ''
    , ignore
    , within
    , code;

  for (var i = 0, len = js.length; i < len; ++i) {
    // start comment
    if (!within && '/' == js[i] && '*' == js[i+1]) {
      // code following previous comment
      if (buf.trim().length) {
        comment = comments[comments.length - 1];
        if(comment) {
          comment.code = code = buf.trim();
          comment.ctx = exports.parseCodeContext(code);
        }
        buf = '';
      }
      i += 2;
      within = true;
      ignore = '!' == js[i];
    // end comment
    } else if (within && '*' == js[i] && '/' == js[i+1]) {
      i += 2;
      buf = buf.replace(/^ *\* ?/gm, '');
      var comment = exports.parseComment(buf, options);
      comment.ignore = ignore;
      comments.push(comment);
      within = ignore = false;
      buf = '';
    // buffer comment or code
    } else {
      buf += js[i];
    }
  }

  // trailing code
  if (buf.trim().length) {
    comment = comments[comments.length - 1];
    code = buf.trim();
    comment.code = code;
    comment.ctx = exports.parseCodeContext(code);
  }

  return comments;
};

/**
 * Parse the given comment `str`.
 *
 * The comment object returned contains the following
 *
 *  - `tags`  array of tag objects
 *  - `description` the first line of the comment
 *  - `body` lines following the description
 *  - `content` both the description and the body
 *  - `isPrivate` true when "@api private" is used
 *
 * @param {String} str
 * @param {Object} options
 * @return {Object}
 * @see exports.parseTag
 * @api public
 */

exports.parseComment = function(str, options) {
  str = str.trim();
  options = options || {};

  var comment = { tags: [] }
    , raw = options.raw
    , description = {};

  // parse comment body
  description.full = str.split('\n@')[0].replace(/^([A-Z][\w ]+):$/gm, '## $1');
  description.summary = description.full.split('\n\n')[0];
  description.body = description.full.split('\n\n').slice(1).join('\n\n');
  comment.description = description;

  // parse tags
  if (~str.indexOf('\n@')) {
    var tags = '@' + str.split('\n@').slice(1).join('\n@');
    comment.tags = tags.split('\n').map(exports.parseTag);
    comment.isPrivate = comment.tags.some(function(tag){
      return 'api' == tag.type && 'private' == tag.visibility;
    })
  }

  // markdown
  if (!raw) {
    description.full = markdown(description.full);
    description.summary = markdown(description.summary);
    description.body = markdown(description.body);
  }

  return comment;
}

/**
 * Parse tag string "@param {Array} name description" etc.
 *
 * @param {String}
 * @return {Object}
 * @api public
 */

exports.parseTag = function(str) {
  var tag = {}
    , parts = str.split(/ +/)
    , type = tag.type = parts.shift().replace('@', '');

  switch (type) {
    case 'param':
      tag.types = exports.parseTagTypes(parts.shift());
      tag.name = parts.shift() || '';
      tag.description = parts.join(' ');
      break;
    case 'return':
      tag.types = exports.parseTagTypes(parts.shift());
      tag.description = parts.join(' ');
      break;
    case 'see':
      if (~str.indexOf('http')) {
        tag.title = parts.length > 1
          ? parts.shift()
          : '';
        tag.url = parts.join(' ');
      } else {
        tag.local = parts.join(' ');
      }
    case 'api':
      tag.visibility = parts.shift();
      break;
    case 'type':
      tag.types = exports.parseTagTypes(parts.shift());
      break;
    case 'memberOf':
      tag.parent = parts.shift();
      break;
    case 'augments':
      tag.otherClass = parts.shift();
      break;
    case 'borrows':
      tag.otherMemberName = parts.join(' ').split(' as ')[0];
      tag.thisMemberName = parts.join(' ').split(' as ')[1];
      break;
    default:
      tag.string = parts.join(' ');
      break;
  }

  return tag;
}

/**
 * Parse tag type string "{Array|Object}" etc.
 *
 * @param {String} str
 * @return {Array}
 * @api public
 */

exports.parseTagTypes = function(str) {
  return str
    .replace(/[{}]/g, '')
    .split(/ *[|,\/] */);
};

/**
 * Parse the context from the given `str` of js.
 *
 * This method attempts to discover the context
 * for the comment based on it's code. Currently
 * supports:
 *
 *   - function statements
 *   - function expressions
 *   - prototype methods
 *   - prototype properties
 *   - methods
 *   - properties
 *   - declarations
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parseCodeContext = function(str){
  var str = str.split('\n')[0];

  // function statement
  if (/^function (\w+) *\(/.exec(str)) {
    return {
        type: 'function'
      , name: RegExp.$1
      , string: RegExp.$1 + '()'
    };
  // function expression
  } else if (/^var *(\w+) *= *function/.exec(str)) {
    return {
        type: 'function'
      , name: RegExp.$1
      , string: RegExp.$1 + '()'
    };
  // prototype method
  } else if (/^(\w+)\.prototype\.(\w+) *= *function/.exec(str)) {
    return {
        type: 'method'
      , constructor: RegExp.$1
      , name: RegExp.$2
      , string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()'
    };
  // prototype property
  } else if (/^(\w+)\.prototype\.(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'property'
      , constructor: RegExp.$1
      , name: RegExp.$2
      , value: RegExp.$3
      , string: RegExp.$1 + '.prototype' + RegExp.$2
    };
  // method
  } else if (/^(\w+)\.(\w+) *= *function/.exec(str)) {
    return {
        type: 'method'
      , receiver: RegExp.$1
      , name: RegExp.$2
      , string: RegExp.$1 + '.' + RegExp.$2 + '()'
    };
  // property
  } else if (/^(\w+)\.(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'property'
      , receiver: RegExp.$1
      , name: RegExp.$2
      , value: RegExp.$3
      , string: RegExp.$1 + '.' + RegExp.$2
    };
  // declaration
  } else if (/^var +(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'declaration'
      , name: RegExp.$1
      , value: RegExp.$2
      , string: RegExp.$1
    };
  }
};
<commit_msg>Normalize line endings to work better on windows<commit_after>/*!
 * Module dependencies.
 */

var markdown = require('github-flavored-markdown').parse
  , escape = require('./utils').escape;

/**
 * Library version.
 */

exports.version = '0.3.1';

/**
 * Parse comments in the given string of `js`.
 *
 * @param {String} js
 * @param {Object} options
 * @return {Array}
 * @see exports.parseComment
 * @api public
 */

exports.parseComments = function(js, options){
  options = options || {};
  //normalize line endings
  js = js.replace(/\r\n/gm, '\n');

  var comments = []
    , raw = options.raw
    , comment
    , buf = ''
    , ignore
    , within
    , code;

  for (var i = 0, len = js.length; i < len; ++i) {
    // start comment
    if (!within && '/' == js[i] && '*' == js[i+1]) {
      // code following previous comment
      if (buf.trim().length) {
        comment = comments[comments.length - 1];
        if(comment) {
          comment.code = code = buf.trim();
          comment.ctx = exports.parseCodeContext(code);
        }
        buf = '';
      }
      i += 2;
      within = true;
      ignore = '!' == js[i];
    // end comment
    } else if (within && '*' == js[i] && '/' == js[i+1]) {
      i += 2;
      buf = buf.replace(/^ *\* ?/gm, '');
      var comment = exports.parseComment(buf, options);
      comment.ignore = ignore;
      comments.push(comment);
      within = ignore = false;
      buf = '';
    // buffer comment or code
    } else {
      buf += js[i];
    }
  }

  // trailing code
  if (buf.trim().length) {
    comment = comments[comments.length - 1];
    code = buf.trim();
    comment.code = code;
    comment.ctx = exports.parseCodeContext(code);
  }

  return comments;
};

/**
 * Parse the given comment `str`.
 *
 * The comment object returned contains the following
 *
 *  - `tags`  array of tag objects
 *  - `description` the first line of the comment
 *  - `body` lines following the description
 *  - `content` both the description and the body
 *  - `isPrivate` true when "@api private" is used
 *
 * @param {String} str
 * @param {Object} options
 * @return {Object}
 * @see exports.parseTag
 * @api public
 */

exports.parseComment = function(str, options) {
  str = str.trim();
  options = options || {};

  var comment = { tags: [] }
    , raw = options.raw
    , description = {};

  // parse comment body
  description.full = str.split('\n@')[0].replace(/^([A-Z][\w ]+):$/gm, '## $1');
  description.summary = description.full.split('\n\n')[0];
  description.body = description.full.split('\n\n').slice(1).join('\n\n');
  comment.description = description;

  // parse tags
  if (~str.indexOf('\n@')) {
    var tags = '@' + str.split('\n@').slice(1).join('\n@');
    comment.tags = tags.split('\n').map(exports.parseTag);
    comment.isPrivate = comment.tags.some(function(tag){
      return 'api' == tag.type && 'private' == tag.visibility;
    })
  }

  // markdown
  if (!raw) {
    description.full = markdown(description.full);
    description.summary = markdown(description.summary);
    description.body = markdown(description.body);
  }

  return comment;
}

/**
 * Parse tag string "@param {Array} name description" etc.
 *
 * @param {String}
 * @return {Object}
 * @api public
 */

exports.parseTag = function(str) {
  var tag = {}
    , parts = str.split(/ +/)
    , type = tag.type = parts.shift().replace('@', '');

  switch (type) {
    case 'param':
      tag.types = exports.parseTagTypes(parts.shift());
      tag.name = parts.shift() || '';
      tag.description = parts.join(' ');
      break;
    case 'return':
      tag.types = exports.parseTagTypes(parts.shift());
      tag.description = parts.join(' ');
      break;
    case 'see':
      if (~str.indexOf('http')) {
        tag.title = parts.length > 1
          ? parts.shift()
          : '';
        tag.url = parts.join(' ');
      } else {
        tag.local = parts.join(' ');
      }
    case 'api':
      tag.visibility = parts.shift();
      break;
    case 'type':
      tag.types = exports.parseTagTypes(parts.shift());
      break;
    case 'memberOf':
      tag.parent = parts.shift();
      break;
    case 'augments':
      tag.otherClass = parts.shift();
      break;
    case 'borrows':
      tag.otherMemberName = parts.join(' ').split(' as ')[0];
      tag.thisMemberName = parts.join(' ').split(' as ')[1];
      break;
    default:
      tag.string = parts.join(' ');
      break;
  }

  return tag;
}

/**
 * Parse tag type string "{Array|Object}" etc.
 *
 * @param {String} str
 * @return {Array}
 * @api public
 */

exports.parseTagTypes = function(str) {
  return str
    .replace(/[{}]/g, '')
    .split(/ *[|,\/] */);
};

/**
 * Parse the context from the given `str` of js.
 *
 * This method attempts to discover the context
 * for the comment based on it's code. Currently
 * supports:
 *
 *   - function statements
 *   - function expressions
 *   - prototype methods
 *   - prototype properties
 *   - methods
 *   - properties
 *   - declarations
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parseCodeContext = function(str){
  var str = str.split('\n')[0];

  // function statement
  if (/^function (\w+) *\(/.exec(str)) {
    return {
        type: 'function'
      , name: RegExp.$1
      , string: RegExp.$1 + '()'
    };
  // function expression
  } else if (/^var *(\w+) *= *function/.exec(str)) {
    return {
        type: 'function'
      , name: RegExp.$1
      , string: RegExp.$1 + '()'
    };
  // prototype method
  } else if (/^(\w+)\.prototype\.(\w+) *= *function/.exec(str)) {
    return {
        type: 'method'
      , constructor: RegExp.$1
      , name: RegExp.$2
      , string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()'
    };
  // prototype property
  } else if (/^(\w+)\.prototype\.(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'property'
      , constructor: RegExp.$1
      , name: RegExp.$2
      , value: RegExp.$3
      , string: RegExp.$1 + '.prototype' + RegExp.$2
    };
  // method
  } else if (/^(\w+)\.(\w+) *= *function/.exec(str)) {
    return {
        type: 'method'
      , receiver: RegExp.$1
      , name: RegExp.$2
      , string: RegExp.$1 + '.' + RegExp.$2 + '()'
    };
  // property
  } else if (/^(\w+)\.(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'property'
      , receiver: RegExp.$1
      , name: RegExp.$2
      , value: RegExp.$3
      , string: RegExp.$1 + '.' + RegExp.$2
    };
  // declaration
  } else if (/^var +(\w+) *= *([^\n;]+)/.exec(str)) {
    return {
        type: 'declaration'
      , name: RegExp.$1
      , value: RegExp.$2
      , string: RegExp.$1
    };
  }
};
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Type for loading a playing sounds, works on multiple platforms and supports
 * embedded base64 data.  This uses Web Audio when available, primarily
 * because the webkit platforms were failing with cross-domain errors when
 * attempting to load audio data from embedded data URIs.  This was occurring
 * in mid-September 2013.  Simplification may be possible if the cross-domain
 * issue goes away at some point in the future.
 *
 * TODO: Do we want to specify just the stem of the file name and have this
 * code figure out whether to use mp3 or ogg?
 */
define( function( require ) {
  'use strict';

  // Imports
  var base64Binary = require( 'SHERPA/base64Binary' );
  var platform = require( 'PHET_CORE/platform' );
  var empty = require( 'audio!VIBE/empty.mp3' );

  // Set up a single audio context that will be used by all sounds when
  // using Web Audio API.
  var audioContext;
  if ( 'AudioContext' in window ) {
    /*global AudioContext*/ // Disable jshint warning.
    audioContext = new AudioContext();
  }
  else if ( 'webkitAudioContext' in window ) {
    /*global webkitAudioContext*/
    /*jshint newcap:false*/ // Disable jshint warning.
    audioContext = new webkitAudioContext();
  }

  /**
   * @param {Object} soundInfo - Object containing information
   * that defines the sound, either a URL or a base64 definition.
   * @constructor
   */
  function Sound( soundInfo ) {
    debugger;

    // Parameter checking.
    if ( typeof( soundInfo ) !== 'object' || ( typeof( soundInfo.base64 ) === 'undefined' && typeof( soundInfo.url ) === 'undefined' ) ) {
      throw new Error( 'Error with soundInfo object: Does not contain a necessary value.' );
    }

    var self = this;

    // Load the sound.
    if ( audioContext ) {
      var arrayBuff;

      if ( soundInfo.base64 ) {
        // We're working with base64 data, so we need to decode it.
        // The regular expression removes the mime header
        var soundData = ( soundInfo.base64 ? soundInfo.base64 : this.sound.getAttribute( 'src' )).replace( new RegExp( '^.*,' ), '' );
        arrayBuff = base64Binary.decodeArrayBuffer( soundData );
        audioContext.decodeAudioData( arrayBuff,
          function( audioData ) {
            self.audioBuffer = audioData;
          },
          function() {
            console.log( "Error: Unable to decode audio data." );
          } );
      }
      else {
        // Load sound via URL.
        var request = new XMLHttpRequest();
        request.open( 'GET', soundInfo.url, true );
        request.responseType = 'arraybuffer';
        request.onload = function() {
          // Decode the audio data asynchronously
          audioContext.decodeAudioData( request.response,
            function( audioData ) {
              self.audioBuffer = audioData;
            },
            function() { console.log( "Error loading and decoding sound, sound name: " + soundInfo.url ); }
          );
        };
        request.onerror = function() {
          console.log( "Error occurred on attempt to load sound data." );
        };
        request.send();
      }
    }
    else {
      // Web Audio API is not available, so insert the sound into the DOM and
      // use HTML5 audio.
      this.sound = document.createElement( 'audio' );
      this.sound.setAttribute( 'src', soundInfo.base64 ? soundInfo.base64 : soundInfo.url );
      this.sound.load();
    }
  }

  /**
   * Plays the sound using the Web Audio API if available or HTML5 audio if not.
   */
  Sound.prototype.play = function() {
    if ( audioContext ) {
      // Use the Web Audio API.
      this.soundSource = audioContext.createBufferSource();
      this.soundSource.buffer = this.audioBuffer;
      this.soundSource.connect( audioContext.destination );

      if ( 'AudioContext' in window ) {
        this.soundSource.start( 0 );
      }
      else if ( 'webkitAudioContext' in window ) {
        this.soundSource.noteOn( 0 );
      }
    }
    else {
      // Use the HTML5 API.
      this.sound.play();
    }
  };

  /**
   * Stop the sound if it is currently playing.
   */
  Sound.prototype.stop = function() {
    if ( 'AudioContext' in window ) {
      // TODO: TBD
    }
    else if ( 'webkitAudioContext' in window ) {
      // TODO: TBD
    }
    else {
      this.sound.pause();
      this.sound.currentTime = 0;
    }
  };

  // Workaround for iOS+Safari: In this situation, we must play an audio file
  // from a thread initiated by a user event such as touchstart before any
  // sounds will play. This is impossible with scenery, since all scenery
  // events are batched and dispatched from the animation loop.
  // See http://stackoverflow.com/questions/12517000/no-sound-on-ios-6-web-audio-api
  // Note: This requires the user to touch the screen before audio can be played
  if ( platform.mobileSafari ) {

    var silence = new Sound( empty );
    var playSilence = function() {
      silence.play();
      window.removeEventListener( 'touchstart', playSilence, false );
    };
    window.addEventListener( 'touchstart', playSilence, false );
  }

  return Sound;
} );
<commit_msg>addressed jshint issues<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Type for loading a playing sounds, works on multiple platforms and supports
 * embedded base64 data.  This uses Web Audio when available, primarily
 * because the webkit platforms were failing with cross-domain errors when
 * attempting to load audio data from embedded data URIs.  This was occurring
 * in mid-September 2013.  Simplification may be possible if the cross-domain
 * issue goes away at some point in the future.
 *
 * TODO: Do we want to specify just the stem of the file name and have this
 * code figure out whether to use mp3 or ogg?
 */
define( function( require ) {
  'use strict';

  // Imports
  var base64Binary = require( 'SHERPA/base64Binary' );
  var platform = require( 'PHET_CORE/platform' );
  var empty = require( 'audio!VIBE/empty.mp3' );

  // Set up a single audio context that will be used by all sounds when
  // using Web Audio API.
  var audioContext;
  if ( 'AudioContext' in window ) {
    /*global AudioContext*/ // Disable jshint warning.
    audioContext = new AudioContext();
  }
  else if ( 'webkitAudioContext' in window ) {
    /*global webkitAudioContext*/
    /*jshint newcap:false*/ // Disable jshint warning.
    audioContext = new webkitAudioContext();
  }

  /**
   * @param {Object} soundInfo - Object containing information
   * that defines the sound, either a URL or a base64 definition.
   * @constructor
   */
  function Sound( soundInfo ) {

    // Parameter checking.
    if ( typeof( soundInfo ) !== 'object' || ( typeof( soundInfo.base64 ) === 'undefined' && typeof( soundInfo.url ) === 'undefined' ) ) {
      throw new Error( 'Error with soundInfo object: Does not contain a necessary value.' );
    }

    var self = this;

    // Load the sound.
    if ( audioContext ) {
      var arrayBuff;

      if ( soundInfo.base64 ) {
        // We're working with base64 data, so we need to decode it.
        // The regular expression removes the mime header
        var soundData = ( soundInfo.base64 ? soundInfo.base64 : this.sound.getAttribute( 'src' )).replace( new RegExp( '^.*,' ), '' );
        arrayBuff = base64Binary.decodeArrayBuffer( soundData );
        audioContext.decodeAudioData( arrayBuff,
          function( audioData ) {
            self.audioBuffer = audioData;
          },
          function() {
            console.log( "Error: Unable to decode audio data." );
          } );
      }
      else {
        // Load sound via URL.
        var request = new XMLHttpRequest();
        request.open( 'GET', soundInfo.url, true );
        request.responseType = 'arraybuffer';
        request.onload = function() {
          // Decode the audio data asynchronously
          audioContext.decodeAudioData( request.response,
            function( audioData ) {
              self.audioBuffer = audioData;
            },
            function() { console.log( "Error loading and decoding sound, sound name: " + soundInfo.url ); }
          );
        };
        request.onerror = function() {
          console.log( "Error occurred on attempt to load sound data." );
        };
        request.send();
      }
    }
    else {
      // Web Audio API is not available, so insert the sound into the DOM and
      // use HTML5 audio.
      this.sound = document.createElement( 'audio' );
      this.sound.setAttribute( 'src', soundInfo.base64 ? soundInfo.base64 : soundInfo.url );
      this.sound.load();
    }
  }

  /**
   * Plays the sound using the Web Audio API if available or HTML5 audio if not.
   */
  Sound.prototype.play = function() {
    if ( audioContext ) {
      // Use the Web Audio API.
      this.soundSource = audioContext.createBufferSource();
      this.soundSource.buffer = this.audioBuffer;
      this.soundSource.connect( audioContext.destination );

      if ( 'AudioContext' in window ) {
        this.soundSource.start( 0 );
      }
      else if ( 'webkitAudioContext' in window ) {
        this.soundSource.noteOn( 0 );
      }
    }
    else {
      // Use the HTML5 API.
      this.sound.play();
    }
  };

  /**
   * Stop the sound if it is currently playing.
   */
  Sound.prototype.stop = function() {
    if ( 'AudioContext' in window ) {
      // TODO: TBD
    }
    else if ( 'webkitAudioContext' in window ) {
      // TODO: TBD
    }
    else {
      this.sound.pause();
      this.sound.currentTime = 0;
    }
  };

  // Workaround for iOS+Safari: In this situation, we must play an audio file
  // from a thread initiated by a user event such as touchstart before any
  // sounds will play. This is impossible with scenery, since all scenery
  // events are batched and dispatched from the animation loop.
  // See http://stackoverflow.com/questions/12517000/no-sound-on-ios-6-web-audio-api
  // Note: This requires the user to touch the screen before audio can be played
  if ( platform.mobileSafari ) {

    var silence = new Sound( empty );
    var playSilence = function() {
      silence.play();
      window.removeEventListener( 'touchstart', playSilence, false );
    };
    window.addEventListener( 'touchstart', playSilence, false );
  }

  return Sound;
} );
<|endoftext|>


<commit_before>angular.module("protonet.platform").controller("InstallUpdateCtrl", function($scope, $state, $timeout, API, Notification) {

  function toggleDetailedUpdateStatus() {
    $scope.showDetailedUpdateStatus = !$scope.showDetailedUpdateStatus;
  }
  $scope.toggleDetailedUpdateStatus = toggleDetailedUpdateStatus;

  function updateDetailedStatus(images) {
    $scope.status = {};
    for (var property in images) {
      if (images.hasOwnProperty(property)) {
        var image = images[property];
        $scope.status[property] = {
          local: image.local,
          remote: image.remote,
          upToDate: image.local.indexOf(image.remote) === 0
        }
      }
    }
    $scope.statusAvailable = !$.isEmptyObject($scope.status);
  }

  function check() {
    $timeout(function() {
      API.get("/admin/api/system/update").then(function(data) {
        if (data.up_to_date) {
          Notification.notice("Update successfully installed");
          $state.go("dashboard.index");
        } else {
          updateDetailedStatus(data.images);
          check();
        }
      }).catch(check);
    }, 10000);
  }

  check();
});<commit_msg>Better local/remote comparison<commit_after>angular.module("protonet.platform").controller("InstallUpdateCtrl", function($scope, $state, $timeout, API, Notification) {

  function toggleDetailedUpdateStatus() {
    $scope.showDetailedUpdateStatus = !$scope.showDetailedUpdateStatus;
  }
  $scope.toggleDetailedUpdateStatus = toggleDetailedUpdateStatus;

  function updateDetailedStatus(images) {
    $scope.status = {};
    for (var property in images) {
      if (images.hasOwnProperty(property)) {
        var image = images[property];
        var local = image.local.substr(0, 8);
        var remote = image.remote.substr(0, 8);
        $scope.status[property] = {
          local: local,
          remote: remote,
          upToDate: local === remote
        };
      }
    }
    $scope.statusAvailable = !$.isEmptyObject($scope.status);
  }

  function check() {
    $timeout(function() {
      API.get("/admin/api/system/update").then(function(data) {
        if (data.up_to_date) {
          Notification.notice("Update successfully installed");
          $state.go("dashboard.index");
        } else {
          updateDetailedStatus(data.images);
          check();
        }
      }).catch(check);
    }, 10000);
  }

  check();
});<|endoftext|>


<commit_before>'use strict';

angular.module('countingDown.controllers')
.controller('MainCtrl', function ($scope, $timeout, $moment, $location) {

  $scope.timeDateInput = '';
  $scope.title = 'hgf';
  $scope.years = 0;
  $scope.days = 0;
  $scope.hours = 0;
  $scope.minutes = 0;
  $scope.seconds = 0;

  $scope.hasValidDate = false;
  $scope.hasValidTitle = false;

  var secondsPerYear = 31557600;
  var secondsPerDay = 86400;
  var secondsPerHour = 3600;
  var secondsPerMinute = 60;

  $scope.handleStart = function () {
    $scope.hasValidDate = true;
  };

  var validateTitle = function (title) {
    if (title === 'undefined' || title === 'null' || title === '') {
      $scope.hasValidTitle = false;
      $scope.title = '';
    } else {
      $scope.title = title;
    }
  };

});
<commit_msg>Set query strings<commit_after>'use strict';

angular.module('countingDown.controllers')
.controller('MainCtrl', function ($scope, $timeout, $moment, $location) {

  $scope.timeDateInput = '';
  $scope.title = 'hgf';
  $scope.years = 0;
  $scope.days = 0;
  $scope.hours = 0;
  $scope.minutes = 0;
  $scope.seconds = 0;

  $scope.hasValidDate = false;
  $scope.hasValidTitle = false;

  var secondsPerYear = 31557600;
  var secondsPerDay = 86400;
  var secondsPerHour = 3600;
  var secondsPerMinute = 60;
  var setQueryStringParams = function (title, unixTime) {
    var encodedTitle = encodeURIComponent(title);

    $location.search({
      title: encodedTitle,
      t: unixTime
    });
  };

  $scope.handleStart = function () {
    $scope.hasValidDate = true;
  };

  var validateTitle = function (title) {
    if (title === 'undefined' || title === 'null' || title === '') {
      $scope.hasValidTitle = false;
      $scope.title = '';
    } else {
      $scope.title = title;
    }
  };

});
<|endoftext|>


<commit_before>/* eslint-disable import/imports-first */

// FIXME: replace globals with central (non-global) config
global.__SERVER__ = true

const Promise = require('bluebird')

const updateProjectCycleStats = require('src/server/actions/updateProjectStats')
const {findPlayers, getPlayerById} = require('src/server/db/player')
const {findChapters} = require('src/server/db/chapter')
const {getCyclesForChapter} = require('src/server/db/cycle')
const {getProjectsForChapterInCycle} = require('src/server/db/project')
const {COMPLETE} = require('src/common/models/cycle')
const {finish} = require('./util')

const LOG_PREFIX = '[runStats]'

// FIXME: hardcoded, yuck
const PRO_PLAYER_RATING = 1300
const PRO_PLAYER_IDS = {
  '070b3063-0ff7-40c6-b3d1-321fa49b6c94': 'bluemihai',
  '75dbe257-a701-4725-ba74-4341376f540d': 'jrob8577',
  'dcf14075-6fbe-44ab-89bf-cba2511f0278': 'deadlyicon',
  '3760fbe8-2c2e-46d9-bca7-a9610dc0d417': 'prattsj',
  'ed958f6f-1870-4ba9-8de9-e1092c9fa758': 'deonna',
}

run()
  .then(() => finish())
  .catch(err => finish(err))

async function run() {
  const errors = []

  const players = await findPlayers()
  await Promise.each(players, player => {
    return clearPlayerStats(player)
  })

  const proPlayerStats = {elo: {rating: PRO_PLAYER_RATING}}
  const proPlayers = players.filter(player => PRO_PLAYER_IDS[player.id])
  await Promise.each(proPlayers, proPlayer => {
    return setPlayerStats(proPlayer, proPlayerStats)
  })

  const chapters = await findChapters()
  await Promise.each(chapters, chapter => {
    return updateChapterStats(chapter).catch(err => {
      errors.push(err)
    })
  })

  if (errors.length) {
    console.error(LOG_PREFIX, 'Errors:')
    errors.forEach(err => console.error('\n', err))
    throw new Error('Stats computation failed')
  }

  const playersFinal = await findPlayers()

  // log final/current ratings
  playersFinal
    .filter(player => player.stats && player.stats.elo)
    .map(player => ({
      id: player.id,
      elo: ((player.stats || {}).elo || {}).rating || null
    }))
    .sort((a, b) => a.elo - b.elo)
    .forEach(player => console.log(player.id.slice(0, 8), player.elo))
}

async function clearPlayerStats(player) {
  console.log(LOG_PREFIX, `Clearing stats for player ${player.id}`)

  await getPlayerById(player.id)
    .replace(player => player.without('stats'))
    .run()
}

async function setPlayerStats(player, stats) {
  console.log(LOG_PREFIX, `Setting stats for player ${player.id}`)

  await getPlayerById(player.id)
    .update({stats})
    .run()
}

async function updateChapterStats(chapter) {
  console.log(LOG_PREFIX, `Updating stats for chapter ${chapter.name} (${chapter.id})`)

  const chapterCycles = await getCyclesForChapter(chapter.id)
  const chapterCyclesSorted = chapterCycles.sort((a, b) => a.cycleNumber - b.cycleNumber)

  return Promise.each(chapterCyclesSorted, cycle => {
    if (cycle.state !== COMPLETE) {
      console.log(LOG_PREFIX, `Skipping cycle ${cycle.id} in state ${cycle.state}`)
      return
    }
    return updateChapterCycleStats(chapter, cycle)
  })
}

async function updateChapterCycleStats(chapter, cycle) {
  console.log(LOG_PREFIX, `Updating stats for cycle ${cycle.cycleNumber} (${cycle.id})`)

  const cycleProjects = await getProjectsForChapterInCycle(chapter.id, cycle.id)
  return Promise.each(cycleProjects, project => {
    return updateCycleProjectStats(cycle, project)
  })
}

function updateCycleProjectStats(cycle, project) {
  console.log(LOG_PREFIX, `Updating stats for project ${project.name} (${project.id})`)

  const projectCycleHistory = (project.cycleHistory || []).find(ch => ch.cycleId === cycle.id)
  if (!projectCycleHistory) {
    console.warn(LOG_PREFIX, `Cycle history not found for project ${project.name} (${project.id})`)
    return
  }

  return updateProjectCycleStats(project, cycle.id)
}
<commit_msg>adding tanner to runStats script; setting each pro players elo individually<commit_after>/* eslint-disable import/imports-first */

// FIXME: replace globals with central (non-global) config
global.__SERVER__ = true

const Promise = require('bluebird')

const updateProjectCycleStats = require('src/server/actions/updateProjectStats')
const {findPlayers, getPlayerById} = require('src/server/db/player')
const {findChapters} = require('src/server/db/chapter')
const {getCyclesForChapter} = require('src/server/db/cycle')
const {getProjectsForChapterInCycle} = require('src/server/db/project')
const {COMPLETE} = require('src/common/models/cycle')
const {finish} = require('./util')

const LOG_PREFIX = '[runStats]'

// FIXME: hardcoded, yuck
const PRO_PLAYERS = {
  '070b3063-0ff7-40c6-b3d1-321fa49b6c94': {handle: 'bluemihai', initialEloRating: 1300},
  '75dbe257-a701-4725-ba74-4341376f540d': {handle: 'jrob8577', initialEloRating: 1300},
  'dcf14075-6fbe-44ab-89bf-cba2511f0278': {handle: 'deadlyicon', initialEloRating: 1300},
  '3760fbe8-2c2e-46d9-bca7-a9610dc0d417': {handle: 'prattsj', initialEloRating: 1300},
  'ed958f6f-1870-4ba9-8de9-e1092c9fa758': {handle: 'deonna', initialEloRating: 1300},
  'f490c8ee-e609-4774-bcf5-9ed7f938676d': {handle: 'tannerwelsh', initialEloRating: 1150},
}

run()
  .then(() => finish())
  .catch(err => finish(err))

async function run() {
  const errors = []

  const players = await findPlayers()
  await Promise.each(players, player => {
    return clearPlayerStats(player)
  })

  const proPlayers = players.filter(player => PRO_PLAYERS[player.id])
  await Promise.each(proPlayers, proPlayer => {
    return setPlayerStats(proPlayer, {elo: {rating: PRO_PLAYERS[proPlayer.id].initialEloRating}})
  })

  const chapters = await findChapters()
  await Promise.each(chapters, chapter => {
    return updateChapterStats(chapter).catch(err => {
      errors.push(err)
    })
  })

  if (errors.length) {
    console.error(LOG_PREFIX, 'Errors:')
    errors.forEach(err => console.error('\n', err))
    throw new Error('Stats computation failed')
  }

  const playersFinal = await findPlayers()

  // log final/current ratings
  playersFinal
    .filter(player => player.stats && player.stats.elo)
    .map(player => ({
      id: player.id,
      elo: ((player.stats || {}).elo || {}).rating || null
    }))
    .sort((a, b) => a.elo - b.elo)
    .forEach(player => console.log(player.id.slice(0, 8), player.elo))
}

async function clearPlayerStats(player) {
  console.log(LOG_PREFIX, `Clearing stats for player ${player.id}`)

  await getPlayerById(player.id)
    .replace(player => player.without('stats'))
    .run()
}

async function setPlayerStats(player, stats) {
  console.log(LOG_PREFIX, `Setting stats for player ${player.id}`)

  await getPlayerById(player.id)
    .update({stats})
    .run()
}

async function updateChapterStats(chapter) {
  console.log(LOG_PREFIX, `Updating stats for chapter ${chapter.name} (${chapter.id})`)

  const chapterCycles = await getCyclesForChapter(chapter.id)
  const chapterCyclesSorted = chapterCycles.sort((a, b) => a.cycleNumber - b.cycleNumber)

  return Promise.each(chapterCyclesSorted, cycle => {
    if (cycle.state !== COMPLETE) {
      console.log(LOG_PREFIX, `Skipping cycle ${cycle.id} in state ${cycle.state}`)
      return
    }
    return updateChapterCycleStats(chapter, cycle)
  })
}

async function updateChapterCycleStats(chapter, cycle) {
  console.log(LOG_PREFIX, `Updating stats for cycle ${cycle.cycleNumber} (${cycle.id})`)

  const cycleProjects = await getProjectsForChapterInCycle(chapter.id, cycle.id)
  return Promise.each(cycleProjects, project => {
    return updateCycleProjectStats(cycle, project)
  })
}

function updateCycleProjectStats(cycle, project) {
  console.log(LOG_PREFIX, `Updating stats for project ${project.name} (${project.id})`)

  const projectCycleHistory = (project.cycleHistory || []).find(ch => ch.cycleId === cycle.id)
  if (!projectCycleHistory) {
    console.warn(LOG_PREFIX, `Cycle history not found for project ${project.name} (${project.id})`)
    return
  }

  return updateProjectCycleStats(project, cycle.id)
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name chessmateApp.controller:NotationCtrl
 * @description
 * # NotationCtrl
 * Controller of the chessmateApp
 */
angular.module('chessmateApp')
  .controller('NotationCtrl', function ($scope, $rootScope) {

    $scope.upload = function () {
      var f = document.getElementById('file').files[0];

      var r = new FileReader();
      r.onloadend = function(e){
        var text = r.result;
        $scope.buildGame(text);
        //send you binary data via $http or $resource or do anything else with it
      }
      r.readAsText(f, 'UTF-8');

    };

    $scope.buildGame = function (notation) {

      //initial game info array
      $rootScope.gameInfo = [];
      var game = {
        "boards": [$scope.buildInitialBoard()]
      };

      $scope.generateBoardFromNotation(game.boards, notation);
      $rootScope.$broadcast('game-updated', game);
    };

    $scope.addComment = function(comment) {
      $rootScope.$broadcast('add-comment', comment);
    }

    $scope.buildInitialBoard = function () {
      var board = {
        "turn": 0,
        "move": undefined,
        "source": undefined,
        "destination": undefined,
        "position": {
          "A1": $scope.buildPiece('R', "white"),
          "B1": $scope.buildPiece('N', "white"),
          "C1": $scope.buildPiece('C', "white"),
          "D1": $scope.buildPiece('K', "white"),
          "E1": $scope.buildPiece('M', "white"),
          "F1": $scope.buildPiece('C', "white"),
          "G1": $scope.buildPiece('N', "white"),
          "H1": $scope.buildPiece('R', "white"),
          "A3": $scope.buildPiece('B', "white"),
          "B3": $scope.buildPiece('B', "white"),
          "C3": $scope.buildPiece('B', "white"),
          "D3": $scope.buildPiece('B', "white"),
          "E3": $scope.buildPiece('B', "white"),
          "F3": $scope.buildPiece('B', "white"),
          "G3": $scope.buildPiece('B', "white"),
          "H3": $scope.buildPiece('B', "white"),
          "A6": $scope.buildPiece('B', "black"),
          "B6": $scope.buildPiece('B', "black"),
          "C6": $scope.buildPiece('B', "black"),
          "D6": $scope.buildPiece('B', "black"),
          "E6": $scope.buildPiece('B', "black"),
          "F6": $scope.buildPiece('B', "black"),
          "G6": $scope.buildPiece('B', "black"),
          "H6": $scope.buildPiece('B', "black"),
          "A8": $scope.buildPiece('R', "black"),
          "B8": $scope.buildPiece('N', "black"),
          "C8": $scope.buildPiece('C', "black"),
          "D8": $scope.buildPiece('M', "black"),
          "E8": $scope.buildPiece('K', "black"),
          "F8": $scope.buildPiece('C', "black"),
          "G8": $scope.buildPiece('N', "black"),
          "H8": $scope.buildPiece('R', "black")
        },
        next : function (game) {
          var nextBoardIndex = this.turn + 1;
          return game.boards[nextBoardIndex];
        },
        previous : function (game) {
          var nextBoardIndex = this.turn - 1;
          return game.boards[nextBoardIndex];
        },
        switch : function (game) {
          var nextBoardIndex = this.turn;
          return game.boards[nextBoardIndex];
        }
      };

      return board;
    };

    $scope.generateBoardFromNotation = function (boardsArray, notation) {

            //extract header and moves out of notation string
            var header = notation.substring(0,notation.lastIndexOf("]"));
            //do something with header

      var moves = $scope.getMoves(notation);

      //while still have moves
      var turn = 1;
      var dotPos;

      while ((dotPos = moves.indexOf(".")) != -1) {
        //find first 6 and send with white colour

        var whiteMove = moves.substring(dotPos + 1, moves.indexOf(' ',dotPos));
        var whiteBoard = $scope.buildBoard(boardsArray[boardsArray.length - 1], whiteMove, 'white');
        boardsArray.push(whiteBoard);
        //find last 6 and send with black colour
          var indexOfFirstBlack = dotPos+1+whiteMove.length+1;
          var indexOfSecondSpace = moves.indexOf(' ', indexOfFirstBlack);

        if(indexOfSecondSpace == -1){
          indexOfSecondSpace = moves.lastIndex;
        }

        var blackMove = moves.substring(indexOfFirstBlack ,indexOfSecondSpace);
        var blackBoard = $scope.buildBoard(boardsArray[boardsArray.length - 1], blackMove, 'black');
          boardsArray.push(blackBoard);

        moves = moves.substring(moves.indexOf(blackMove)+blackMove.length+1);

        //build game info objective
        $scope.buildAndPushGameInfoObject(turn, whiteMove, blackMove, whiteBoard, blackBoard);
        turn++;
      }
    };

    $scope.buildAndPushGameInfoObject = function(turn, whiteMove, blackMove, whiteBoard, blackBoard){

      var gameInfoObject = {};
      gameInfoObject.turn = turn;
      gameInfoObject.whiteMove = whiteMove;
      gameInfoObject.blackMove = blackMove;
      gameInfoObject.whiteBoard = whiteBoard;
      gameInfoObject.blackBoard = blackBoard;
      $rootScope.gameInfo.push(gameInfoObject);
    };

    $scope.getMoves = function (notation) {
      var lastIndexOfBracket = notation.lastIndexOf("]");
      return notation.substring(lastIndexOfBracket + 1);
    };
    $scope.buildBoard = function (currentBoard, move, color) {
      var board = angular.copy(currentBoard);
      var char = (move.indexOf("=") == -1) ? move.substring(0, 1) : "A";
      var source = move.substring(1, 3);
      var destination = move.substring(4, 6);

      board.turn = currentBoard.turn + 1;
      board.move = move;
      board.source = source;
      board.destination = destination;
      board.position[destination] = $scope.buildPiece(char, color);
      delete board.position[source];

      return board;
    };

    $scope.buildPiece = function (char, color) {
      var piece = {
        type: $scope.getType(char),
        color: color
      };

      piece.value = buildValue(piece);

      return piece;
    };

    $scope.getType = function (char) {
      var type;

      if (char == 'B') {
        type = "pawn";
      } else if (char == 'R') {
        type = "rook";
      } else if (char == 'N') {
        type = "knight";
      } else if (char == 'C') {
        type = "bishop";
      } else if (char == 'K') {
        type = "king";
      } else if (char == 'M') {
        type = "queen";
      } else if (char == 'A') {
        type = "queen";
      }

      return type;
    };

    var buildValue = function (piece) {
      var map = Immutable.Map({
        "rook-black": "♜",
        "knight-black": "♞",
        "bishop-black": "♝",
        "king-black": "♚",
        "queen-black": "♛",
        "pawn-black": "♟",

        "rook-white": "♖",
        "knight-white": "♘",
        "bishop-white": "♗",
        "king-white": "♔",
        "queen-white": "♕",
        "pawn-white": "♙"
      });
      var key = piece.type + "-" + piece.color;
      return map.get(key);
    };

    $scope.notationString = "[White AMA][Black 500miles] [Tournament Thai Chess League][Date -1-0] [Result 1/2-1/2]" +
      "1.BF3-F4 BC6-C5 2.NG1-F3 BD6-D5 3.BE3-E4 NB8-C6 4.ME1-F2 MD8-C7 5.MF2-E3 MC7-D6 6.CC1-C2 CF8-F7 7.NB1-D2 NG8-E7 8.BH3-H4 BH6-H5 9.CF1-F2 BA6-A5 10.KD1-E2 CC8-C7"+
    "11.RH1-D1 KE8-D7 12.BD3-D4 BA5-A4 "+
    "13.BE4xD5 BE6xD5 14.BC3-C4 BA4xB3=ง "+
    "15.CC2xB3 RH8-E8 16.KE2-F1 CF7-E6 17.BD4xC5 BB6xC5 18.BC4xD5 CE6xD5 19.CB3-C4 RE8-B8 20.CC4xD5 NE7xD5 "+
    "21.ND2-C4 NC6-E7 22.NF3-D2 KD7-E6 23.CF2-F3 BF6-F5 24.CF3-E2 CC7-C6 25.ND2-F3 CC6-B5 26.NF3-G5 KE6-D7 "+
    "27.NC4-E5 MD6xE5 28.BF4xE5 RA8-A6 29.ME3-F4 CB5-C4 30.RA1-C1 RA6-A4 31.BE5-E6=ง KD7-D6 32.AE6xD5 CC4xD5 "+
    "33.CE2-D3 RA4xA3 34.CD3-C4 RB8-C8 35.RC1-C2 RA3-A4 36.CC4xD5 NE7xD5 37.RC2-D2 RA4-D4 38.RD2xD4 BC5xD4 "+
    "39.RD1xD4 RC8-C1 40.KF1-E2 RC1-C2 41.KE2-D3 RC2-C3 42.KD3-D2 RC3-A3 43.NG5-F7 KD6-C5 44.RD4-D3 RA3-A2 "+
    "45.KD2-E1 ND5-B4 46.RD3-D2 RA2xD2 47.KE1xD2 NB4-C6 48.KD2-D3 KC5-D5 49.NF7-H8 NC6-E7 50.NH8-F7 NE7-C6 "+
    "51.NF7-G5 NC6-D4 52.MF4-E3 ND4-E6 53.NG5xE6 KD5xE6 54.ME3-F4 KE6-D5 55.KD3-E3 KD5-C4 56.MF4-E5 KC4-D5 "+
    "57.KE3-F4 KD5-E6 58.ME5-D4 KE6-F6 59.MD4-E3 BG6-G5 60.BH4xG5 KF6-G6 61.ME3-F2 BH5-H4 62.BG3xH4 KG6-H5 "+
    "63.MF2-G3 KH5-G6 64.KF4-E3 KG6-H5 65.KE3-F3 KH5-G6 66.MG3-F4 KG6-H5 67.KF3-G3 KH5-G6 68.KG3-F3 KG6-H5 "+
    "69.MF4-G3 KH5-G6 70.KF3-F4 KG6-H5 71.KF4-F3 KH5-G6";

  });
<commit_msg>fix default notation<commit_after>'use strict';

/**
 * @ngdoc function
 * @name chessmateApp.controller:NotationCtrl
 * @description
 * # NotationCtrl
 * Controller of the chessmateApp
 */
angular.module('chessmateApp')
  .controller('NotationCtrl', function ($scope, $rootScope) {

    $scope.upload = function () {
      var f = document.getElementById('file').files[0];

      var r = new FileReader();
      r.onloadend = function(e){
        var text = r.result;
        $scope.buildGame(text);
        //send you binary data via $http or $resource or do anything else with it
      }
      r.readAsText(f, 'UTF-8');

    };

    $scope.buildGame = function (notation) {

      //initial game info array
      $rootScope.gameInfo = [];
      var game = {
        "boards": [$scope.buildInitialBoard()]
      };

      $scope.generateBoardFromNotation(game.boards, notation);
      $rootScope.$broadcast('game-updated', game);
    };

    $scope.addComment = function(comment) {
      $rootScope.$broadcast('add-comment', comment);
    }

    $scope.buildInitialBoard = function () {
      var board = {
        "turn": 0,
        "move": undefined,
        "source": undefined,
        "destination": undefined,
        "position": {
          "A1": $scope.buildPiece('R', "white"),
          "B1": $scope.buildPiece('N', "white"),
          "C1": $scope.buildPiece('C', "white"),
          "D1": $scope.buildPiece('K', "white"),
          "E1": $scope.buildPiece('M', "white"),
          "F1": $scope.buildPiece('C', "white"),
          "G1": $scope.buildPiece('N', "white"),
          "H1": $scope.buildPiece('R', "white"),
          "A3": $scope.buildPiece('B', "white"),
          "B3": $scope.buildPiece('B', "white"),
          "C3": $scope.buildPiece('B', "white"),
          "D3": $scope.buildPiece('B', "white"),
          "E3": $scope.buildPiece('B', "white"),
          "F3": $scope.buildPiece('B', "white"),
          "G3": $scope.buildPiece('B', "white"),
          "H3": $scope.buildPiece('B', "white"),
          "A6": $scope.buildPiece('B', "black"),
          "B6": $scope.buildPiece('B', "black"),
          "C6": $scope.buildPiece('B', "black"),
          "D6": $scope.buildPiece('B', "black"),
          "E6": $scope.buildPiece('B', "black"),
          "F6": $scope.buildPiece('B', "black"),
          "G6": $scope.buildPiece('B', "black"),
          "H6": $scope.buildPiece('B', "black"),
          "A8": $scope.buildPiece('R', "black"),
          "B8": $scope.buildPiece('N', "black"),
          "C8": $scope.buildPiece('C', "black"),
          "D8": $scope.buildPiece('M', "black"),
          "E8": $scope.buildPiece('K', "black"),
          "F8": $scope.buildPiece('C', "black"),
          "G8": $scope.buildPiece('N', "black"),
          "H8": $scope.buildPiece('R', "black")
        },
        next : function (game) {
          var nextBoardIndex = this.turn + 1;
          return game.boards[nextBoardIndex];
        },
        previous : function (game) {
          var nextBoardIndex = this.turn - 1;
          return game.boards[nextBoardIndex];
        },
        switch : function (game) {
          var nextBoardIndex = this.turn;
          return game.boards[nextBoardIndex];
        }
      };

      return board;
    };

    $scope.generateBoardFromNotation = function (boardsArray, notation) {

            //extract header and moves out of notation string
            var header = notation.substring(0,notation.lastIndexOf("]"));
            //do something with header

      var moves = $scope.getMoves(notation);

      //while still have moves
      var turn = 1;
      var dotPos;

      while ((dotPos = moves.indexOf(".")) != -1) {
        //find first 6 and send with white colour

        var whiteMove = moves.substring(dotPos + 1, moves.indexOf(' ',dotPos));
        var whiteBoard = $scope.buildBoard(boardsArray[boardsArray.length - 1], whiteMove, 'white');
        boardsArray.push(whiteBoard);
        //find last 6 and send with black colour
          var indexOfFirstBlack = dotPos+1+whiteMove.length+1;
          var indexOfSecondSpace = moves.indexOf(' ', indexOfFirstBlack);

        if(indexOfSecondSpace == -1){
          indexOfSecondSpace = moves.lastIndex;
        }

        var blackMove = moves.substring(indexOfFirstBlack ,indexOfSecondSpace);
        var blackBoard = $scope.buildBoard(boardsArray[boardsArray.length - 1], blackMove, 'black');
          boardsArray.push(blackBoard);

        moves = moves.substring(moves.indexOf(blackMove)+blackMove.length+1);

        //build game info objective
        $scope.buildAndPushGameInfoObject(turn, whiteMove, blackMove, whiteBoard, blackBoard);
        turn++;
      }
    };

    $scope.buildAndPushGameInfoObject = function(turn, whiteMove, blackMove, whiteBoard, blackBoard){

      var gameInfoObject = {};
      gameInfoObject.turn = turn;
      gameInfoObject.whiteMove = whiteMove;
      gameInfoObject.blackMove = blackMove;
      gameInfoObject.whiteBoard = whiteBoard;
      gameInfoObject.blackBoard = blackBoard;
      $rootScope.gameInfo.push(gameInfoObject);
    };

    $scope.getMoves = function (notation) {
      var lastIndexOfBracket = notation.lastIndexOf("]");
      return notation.substring(lastIndexOfBracket + 1);
    };
    $scope.buildBoard = function (currentBoard, move, color) {
      var board = angular.copy(currentBoard);
      var char = (move.indexOf("=") == -1) ? move.substring(0, 1) : "A";
      var source = move.substring(1, 3);
      var destination = move.substring(4, 6);

      board.turn = currentBoard.turn + 1;
      board.move = move;
      board.source = source;
      board.destination = destination;
      board.position[destination] = $scope.buildPiece(char, color);
      delete board.position[source];

      return board;
    };

    $scope.buildPiece = function (char, color) {
      var piece = {
        type: $scope.getType(char),
        color: color
      };

      piece.value = buildValue(piece);

      return piece;
    };

    $scope.getType = function (char) {
      var type;

      if (char == 'B') {
        type = "pawn";
      } else if (char == 'R') {
        type = "rook";
      } else if (char == 'N') {
        type = "knight";
      } else if (char == 'C') {
        type = "bishop";
      } else if (char == 'K') {
        type = "king";
      } else if (char == 'M') {
        type = "queen";
      } else if (char == 'A') {
        type = "queen";
      }

      return type;
    };

    var buildValue = function (piece) {
      var map = Immutable.Map({
        "rook-black": "♜",
        "knight-black": "♞",
        "bishop-black": "♝",
        "king-black": "♚",
        "queen-black": "♛",
        "pawn-black": "♟",

        "rook-white": "♖",
        "knight-white": "♘",
        "bishop-white": "♗",
        "king-white": "♔",
        "queen-white": "♕",
        "pawn-white": "♙"
      });
      var key = piece.type + "-" + piece.color;
      return map.get(key);
    };

    $scope.notationString = "[White AMA][Black 500miles] [Tournament Thai Chess League][Date -1-0] [Result 1/2-1/2]" +
      "1.BF3-F4 BC6-C5 2.NG1-F3 BD6-D5 3.BE3-E4 NB8-C6 4.ME1-F2 MD8-C7 5.MF2-E3 MC7-D6 6.CC1-C2 CF8-F7 7.NB1-D2 NG8-E7 8.BH3-H4 BH6-H5 9.CF1-F2 BA6-A5 10.KD1-E2 CC8-C7 "+
    "11.RH1-D1 KE8-D7 12.BD3-D4 BA5-A4 "+
    "13.BE4xD5 BE6xD5 14.BC3-C4 BA4xB3=ง "+
    "15.CC2xB3 RH8-E8 16.KE2-F1 CF7-E6 17.BD4xC5 BB6xC5 18.BC4xD5 CE6xD5 19.CB3-C4 RE8-B8 20.CC4xD5 NE7xD5 "+
    "21.ND2-C4 NC6-E7 22.NF3-D2 KD7-E6 23.CF2-F3 BF6-F5 24.CF3-E2 CC7-C6 25.ND2-F3 CC6-B5 26.NF3-G5 KE6-D7 "+
    "27.NC4-E5 MD6xE5 28.BF4xE5 RA8-A6 29.ME3-F4 CB5-C4 30.RA1-C1 RA6-A4 31.BE5-E6=ง KD7-D6 32.AE6xD5 CC4xD5 "+
    "33.CE2-D3 RA4xA3 34.CD3-C4 RB8-C8 35.RC1-C2 RA3-A4 36.CC4xD5 NE7xD5 37.RC2-D2 RA4-D4 38.RD2xD4 BC5xD4 "+
    "39.RD1xD4 RC8-C1 40.KF1-E2 RC1-C2 41.KE2-D3 RC2-C3 42.KD3-D2 RC3-A3 43.NG5-F7 KD6-C5 44.RD4-D3 RA3-A2 "+
    "45.KD2-E1 ND5-B4 46.RD3-D2 RA2xD2 47.KE1xD2 NB4-C6 48.KD2-D3 KC5-D5 49.NF7-H8 NC6-E7 50.NH8-F7 NE7-C6 "+
    "51.NF7-G5 NC6-D4 52.MF4-E3 ND4-E6 53.NG5xE6 KD5xE6 54.ME3-F4 KE6-D5 55.KD3-E3 KD5-C4 56.MF4-E5 KC4-D5 "+
    "57.KE3-F4 KD5-E6 58.ME5-D4 KE6-F6 59.MD4-E3 BG6-G5 60.BH4xG5 KF6-G6 61.ME3-F2 BH5-H4 62.BG3xH4 KG6-H5 "+
    "63.MF2-G3 KH5-G6 64.KF4-E3 KG6-H5 65.KE3-F3 KH5-G6 66.MG3-F4 KG6-H5 67.KF3-G3 KH5-G6 68.KG3-F3 KG6-H5 "+
    "69.MF4-G3 KH5-G6 70.KF3-F4 KG6-H5 71.KF4-F3 KH5-G6";

  });
<|endoftext|>


<commit_before>'use strict';

var co = require('collections-online');

// This allows loading of environment variables from a .env file
require('dotenv').config({silent: true});
// Loading the configuration
var state = {};
var config = require('./config');
co.indexing(state, config).then(function() {
  console.log('Good bye!');
  process.exit(0);
}, function(err) {
  console.error('An error occured!');
  console.error(err.stack || err);
  process.exit(1);
});
<commit_msg>Better good bye :)<commit_after>'use strict';

var co = require('collections-online');

// This allows loading of environment variables from a .env file
require('dotenv').config({silent: true});
// Loading the configuration
var state = {};
var config = require('./config');
co.indexing(state, config).then(function() {
  console.log('\nAll done - good bye!');
  process.exit(0);
}, function(err) {
  console.error('An error occured!');
  console.error(err.stack || err);
  process.exit(1);
});
<|endoftext|>


<commit_before>// This will eventually have much more information, including movement costs and such
function Cell(x, y, spriteName, type) {
    this.position = {x: x, y: y};
    this.spriteName = spriteName;
    this.type = type;
}

// Units may deserve their own file eventually, as they will have to track
// attack and defense information for the various weapon types
function Unit(spriteName, pos, distance, movementType, movement,
              health, nation, name, canMove, attacks, armor) {
    this.spriteName = spriteName;
    this.pos = pos;
    // The total distance the unit can move
    this.distance = distance;
    // treads, feet, wheels, that sort of thing
    this.movementType = movementType;
    // map of cell types to movement costs, ie plains -> 1.0
    this.movement = movement;
    this.health = health;
    this.nation = nation;
    this.name = name;
    this.canMove = canMove;
    this.attacks = attacks;
    this.armor = armor;
}

function Player(id, nation, team) {
    this.id = id;
    this.nation = nation;
    this.team = team;
}

function addWizard(player, pos) {
    var mtype = "feet";
    var movement = {
        0: 1.0, // Plains
    };
    var distance = 4;
    game.world.addUnit(player, "wizard", pos, distance, mtype, movement);
}

function World(width, height) {

    function getWidth() {
        return w;
    }

    function getHeight() {
        return h;
    }

    function getCell(x, y) {
        return cells[x][y];
    }

    function setCell(x, y, cell) {
        cells[x][y] = cell;
    }

    function withinWorld(x, y) {
        return x >= 0 && x < w && y >= 0 && y < h;
    }

    function initialize(entryCells) {
        if (entryCells.length == w && entryCells[0].length == h)
        {
            for (var x = 0; x < w; x++) {
                for (var y = 0; y < h; y++) {
                    cells[x][y] = entryCells[x][y];
                }
            }
        } else {
            console.log("World.initialize called with incorrect size");
        }
    }

    function addOrUpdatePlayer(id, nation, team) {
        var player = Player(id, nation, team);
        players[id] = player;
    }

    // unit sprite's have to be cloned, so we have to wrap their creation
    function addUnit(player, srcSpriteName, pos, distance, movementType,
                     movement, health, nation, name, canMove) {
        if (units[player] === undefined) {
            units[player] = [];
        }
        var newSpriteName = player + srcSpriteName + unitCounter;
        unitCounter += 1;
        var spritePos = jQuery.extend(true, {}, pos);
        assets.spriteManager.cloneSprite(srcSpriteName, newSpriteName, spritePos);
        var newUnit = new Unit(newSpriteName, pos, distance, movementType, movement,
                               health, nation, name, canMove);
        units[player].push(newUnit);
    }

    // If player is null, this will return all units as a list
    function getUnits() {
        var output = [];
        for (var key in units) {
            if (units.hasOwnProperty(key)) {
                if (player === undefined || key == player) {
                    for (var i in units[key]) {
                        output.push(units[key][i]);
                    }
                }
            }
        }
        return output;
    }

    function findUnit(wx, wy) {
        var units = getUnits();
        for (var i = units.length - 1; i >= 0; i--) {
            if (units[i].pos.x == wx && units[i].pos.y == wy) {
                return units[i];
            }
        }
        return null;
    }

    function findAvailableMoves(unit) {
        if (unit.canMove) {
            var moves = findAvailableCells(unit);
            game.movesAvailableCallback(moves);
            return moves;
        } else {
            console.log("That unit moved already");
        }
    }

    function findAvailableAttacks(unit) {
        // need to go through a different process for melee/range units
        // ranged units need to get their attack range from somewhere, min and max
        var moves = findAvailableCells(unit);
        var attacks = processMeleeAttack(unit, moves);
        game.attacksAvailableCallback(attacks);
        return attacks;
    }

    function findAvailableCells(unit) {
        function cellEqual(cell1, cell2) {
            return cell1.cell.position.x == cell2.cell.position.x &&
                   cell1.cell.position.y == cell2.cell.position.y;
        }

        var visited = new BinaryHeap(function() { return 1; }, cellEqual);
        var source = cells[unit.pos.x][unit.pos.y];

        function processCell(cell, remainingmoves) {
            visited.push({cell: cell, remainingmoves: remainingmoves});
            if (remainingmoves > 0) {
                var neighbors = getMatrixNeighbors(cells, cell.position);
                for (var i = neighbors.length - 1; i >= 0; i--) {
                    var neighbor = {cell: neighbors[i],
                                    remainingmoves: neighbors[i].remainingmoves};
                    var neighborUnit = game.world.findUnit(neighbors[i].position.x,
                                                           neighbors[i].position.y);
                    if (visited.contains(neighbor)) {
                        neighbor.remainingmoves = visited.get(neighbor).remainingmoves;
                    } else {
                        neighbor.remainingmoves = 0;
                    }
                    if (!visited.contains(neighbor) ||
                        remainingmoves > neighbor.remainingmoves) {
                        var moveCost = unit.movement[neighbor.cell.type];
                        if (moveCost > 0 && neighborUnit === null &&
                            remainingmoves - moveCost > 0) {
                            processCell(neighbors[i], remainingmoves - moveCost);
                        }
                    }
                }
            }
        }

        processCell(source, unit.distance);

        var moves = new BinaryHeap(function() { return 1; }, cellEqual);
        for (var i = visited.content.length - 1; i >= 0; i--) {
            if (!moves.contains(visited.content[i])) {
                moves.push(visited.content[i]);
            }
        }

        return moves.content;
    }

    function processRangeAttack(unit, moves) {
        // TODO this is all wrong
        var attacks = [];
        for (var i = moves.length - 1; i >= 0; i--) {
            if (ManhattanDistance(moves[i].cell.position, unit.pos) >= unit.minRange) {
                attacks.push(moves[i]);
            }
        }

        return attacks;
    }

    function processMeleeAttack(unit, moves) {
        // sorry!
        function cellEqual(cell1, cell2) {
            return cell1.cell.position.x == cell2.cell.position.x &&
                   cell1.cell.position.y == cell2.cell.position.y;
        }

        var attacks = new BinaryHeap(function() { return 1; }, cellEqual);
        for (var i = moves.length - 1; i >= 0; i--) {
            var neighbors = getMatrixNeighbors(cells, moves[i].cell.position);
            for (var j = neighbors.length - 1; j >= 0; j--) {
                var neighbor = {cell: neighbors[j]};
                if (!attacks.contains(neighbor)) {
                    attacks.push(neighbor);
                }
            }
        }

        return attacks.content;
    }

    function serialize() {
        var outUnits = serializeUnits();
        var terrain = serializeTerrain();

        var serialObject = {"units": outUnits,
                            "terrain": terrain};

        return JSON.stringify(serialObject);
    }

    function serializeTerrain() {
        var terrain = new Array(w);
        for (var x = 0; x < w; x++) {
            terrain[x] = new Array(h);
            for (var y = 0; y < h; y++) {
                terrain[x][y] = cells[x][y].type;
            }
        }

        return terrain;
    }

    function serializeUnits() {
        var outUnits = {};
        for (var player in units) {
            if (units.hasOwnProperty(player)) {
                outUnits[player] = [];
                for (var i = 0; i < units[player].length; i++) {
                    var unit = jQuery.extend(true, {}, units[player][i]);
                    unit.loc = unit.pos;
                    delete unit.pos;
                    outUnits[player].push(unit);
                }
            }
        }

        return outUnits;
    }

    var w = width;
    var h = height;

    var cells = new Array(width);

    var currentPlayerId = null;
    var players = {};
    var unitCounter = 0;
    var units = {};

    for (var x = 0; x < width; x++) {
        cells[x] = new Array(height);
    }

    this.getWidth = function() {
        return getWidth();
    };

    this.getHeight = function() {
        return getHeight();
    };

    this.getCell = function(x, y) {
        return getCell(x, y);
    };

    this.setCell = function(x, y, cell) {
        setCell(x, y, cell);
    };

    this.initialize = function(entryCells) {
        initialize(entryCells);
    };

    this.addUnit = function(player, srcSpriteName, pos, distance, movementType,
                            movement, health, nation, name, canMove) {
        addUnit(player, srcSpriteName, pos, distance, movementType, movement,
                health, nation, name, canMove);
    };

    this.getUnits = function() {
        return getUnits();
    };

    this.findUnit = function(wx, wy) {
        return findUnit(wx, wy);
    };

    this.withinWorld = function(x, y) {
        return withinWorld(x, y);
    };

    this.findAvailableMoves = function(unit) {
        return findAvailableMoves(unit);
    };

    this.findAvailableAttacks = function(unit) {
        return findAvailableAttacks(unit);
    };

    this.serialize = function() {
        return serialize();
    };

    this.addOrUpdatePlayer = addOrUpdatePlayer;
}

function plainsWorld(width, height) {
    var terrain = new Array(width);

    for (var x = 0; x < width; x++) {
        terrain[x] = new Array(height);
        for (var y = 0; y < height; y++) {
            terrain[x][y] = terrainLookup(terrainTypes.plains, x, y);
        }
    }

    var w = new World(width, height);
    w.initialize(terrain);

    return w;
}

function routePath(unit, goal) {
    var moves = game.world.findAvailableMoves(unit);
    var pathworld = convertWorldToPathNodes(game.world, unit, moves);
    var pathfinder = new PathFinder(pathworld, pathworld[unit.pos.x][unit.pos.y]);
    var path = pathfinder.findPath(pathworld[goal.x][goal.y]);
    if (path) {
        game.pathCallback(path);
    } else {
        console.log("You can't get there!");
    }
    return path;
}

var terrainTable;
var terrainTypes = {};

function terrainLookup(id, x, y) {
    // TODO This data should come from some JSON source on the server
    return new terrainTable[id](x, y);
}

function cellEqual(cell1, cell2) {
    return cell1.cell.position.x == cell2.position.x &&
           cell1.cell.position.y == cell2.position.y;
}

function convertWorldToPathNodes(world, unit, moves) {
    var pathworld = new Array(world.getWidth());
    for (var x = 0; x < world.getWidth(); x++) {
        pathworld[x] = new Array(world.getHeight());
        for (var y = 0; y < world.getHeight(); y++) {
            // The cost modifier could/should come from a combination of
            // the cell type and the unit's movement modifiers. Likewise for
            // passable
            var cost = unit.movement[world.getCell(x, y).type];
            var passable = false;
            if (cost > 0 && arrayContains(moves, world.getCell(x, y), cellEqual)) {
                passable = true;
            }
            pathworld[x][y] = new pathNode({x: x, y: y}, cost, passable);
        }
    }
    return pathworld;
}
<commit_msg>Index units on position instead of player id<commit_after>// This will eventually have much more information, including movement costs and such
function Cell(x, y, spriteName, type) {
    this.position = {x: x, y: y};
    this.spriteName = spriteName;
    this.type = type;
}

// Units may deserve their own file eventually, as they will have to track
// attack and defense information for the various weapon types
function Unit(spriteName, pos, distance, movementType, movement,
              health, nation, name, canMove, attacks, armor) {
    this.spriteName = spriteName;
    this.pos = pos;
    // The total distance the unit can move
    this.distance = distance;
    // treads, feet, wheels, that sort of thing
    this.movementType = movementType;
    // map of cell types to movement costs, ie plains -> 1.0
    this.movement = movement;
    this.health = health;
    this.nation = nation;
    this.name = name;
    this.canMove = canMove;
    this.attacks = attacks;
    this.armor = armor;
}

function Player(id, nation, team) {
    this.id = id;
    this.nation = nation;
    this.team = team;
}

function addWizard(player, pos) {
    var mtype = "feet";
    var movement = {
        0: 1.0, // Plains
    };
    var distance = 4;
    game.world.addUnit(player, "wizard", pos, distance, mtype, movement);
}

function World(width, height) {

    function getWidth() {
        return w;
    }

    function getHeight() {
        return h;
    }

    function getCell(x, y) {
        return cells[x][y];
    }

    function setCell(x, y, cell) {
        cells[x][y] = cell;
    }

    function withinWorld(x, y) {
        return x >= 0 && x < w && y >= 0 && y < h;
    }

    function initialize(entryCells) {
        if (entryCells.length == w && entryCells[0].length == h)
        {
            for (var x = 0; x < w; x++) {
                for (var y = 0; y < h; y++) {
                    cells[x][y] = entryCells[x][y];
                }
            }
        } else {
            console.log("World.initialize called with incorrect size");
        }
    }

    function addOrUpdatePlayer(id, nation, team) {
        var player = Player(id, nation, team);
        players[id] = player;
    }

    // unit sprite's have to be cloned, so we have to wrap their creation
    function addUnit(player, srcSpriteName, pos, distance, movementType,
                     movement, health, nation, name, canMove) {
        var newSpriteName = player + srcSpriteName + unitCounter;
        unitCounter += 1;
        var spritePos = jQuery.extend(true, {}, pos);
        assets.spriteManager.cloneSprite(srcSpriteName, newSpriteName, spritePos);
        var newUnit = new Unit(newSpriteName, pos, distance, movementType, movement,
                               health, nation, name, canMove);
        if (units[newUnit.pos.x] === undefined) {
            units[newUnit.pos.x] = {};
        }
        units[newUnit.pos.x][newUnit.pos.y] = newUnit;
    }

    function getUnits() {
        var output = [];
        for (var x in units) {
            for (var y in units[x]) {
                output.push(units[x][y]);
            }
        }
        return output;
    }

    function findUnit(wx, wy) {
        var xunits = units[wx];
        if (xunits) {
            var xyunits = xunits[wy];
            return xyunits === undefined ? null : xyunits;
        }
        return null;
    }

    function findAvailableMoves(unit) {
        if (unit.canMove) {
            var moves = findAvailableCells(unit);
            game.movesAvailableCallback(moves);
            return moves;
        } else {
            console.log("That unit moved already");
        }
    }

    function findAvailableAttacks(unit) {
        // need to go through a different process for melee/range units
        // ranged units need to get their attack range from somewhere, min and max
        var moves = findAvailableCells(unit);
        var attacks = processMeleeAttack(unit, moves);
        game.attacksAvailableCallback(attacks);
        return attacks;
    }

    function findAvailableCells(unit) {
        function cellEqual(cell1, cell2) {
            return cell1.cell.position.x == cell2.cell.position.x &&
                   cell1.cell.position.y == cell2.cell.position.y;
        }

        var visited = new BinaryHeap(function() { return 1; }, cellEqual);
        var source = cells[unit.pos.x][unit.pos.y];

        function processCell(cell, remainingmoves) {
            visited.push({cell: cell, remainingmoves: remainingmoves});
            if (remainingmoves > 0) {
                var neighbors = getMatrixNeighbors(cells, cell.position);
                for (var i = neighbors.length - 1; i >= 0; i--) {
                    var neighbor = {cell: neighbors[i],
                                    remainingmoves: neighbors[i].remainingmoves};
                    var neighborUnit = game.world.findUnit(neighbors[i].position.x,
                                                           neighbors[i].position.y);
                    if (visited.contains(neighbor)) {
                        neighbor.remainingmoves = visited.get(neighbor).remainingmoves;
                    } else {
                        neighbor.remainingmoves = 0;
                    }
                    if (!visited.contains(neighbor) ||
                        remainingmoves > neighbor.remainingmoves) {
                        var moveCost = unit.movement[neighbor.cell.type];
                        if (moveCost > 0 && neighborUnit === null &&
                            remainingmoves - moveCost > 0) {
                            processCell(neighbors[i], remainingmoves - moveCost);
                        }
                    }
                }
            }
        }

        processCell(source, unit.distance);

        var moves = new BinaryHeap(function() { return 1; }, cellEqual);
        for (var i = visited.content.length - 1; i >= 0; i--) {
            if (!moves.contains(visited.content[i])) {
                moves.push(visited.content[i]);
            }
        }

        return moves.content;
    }

    function processRangeAttack(unit, moves) {
        // TODO this is all wrong
        var attacks = [];
        for (var i = moves.length - 1; i >= 0; i--) {
            if (ManhattanDistance(moves[i].cell.position, unit.pos) >= unit.minRange) {
                attacks.push(moves[i]);
            }
        }

        return attacks;
    }

    function processMeleeAttack(unit, moves) {
        // sorry!
        function cellEqual(cell1, cell2) {
            return cell1.cell.position.x == cell2.cell.position.x &&
                   cell1.cell.position.y == cell2.cell.position.y;
        }

        var attacks = new BinaryHeap(function() { return 1; }, cellEqual);
        for (var i = moves.length - 1; i >= 0; i--) {
            var neighbors = getMatrixNeighbors(cells, moves[i].cell.position);
            for (var j = neighbors.length - 1; j >= 0; j--) {
                var neighbor = {cell: neighbors[j]};
                if (!attacks.contains(neighbor)) {
                    attacks.push(neighbor);
                }
            }
        }

        return attacks.content;
    }

    function serialize() {
        var outUnits = serializeUnits();
        var terrain = serializeTerrain();

        var serialObject = {"units": outUnits,
                            "terrain": terrain};

        return JSON.stringify(serialObject);
    }

    function serializeTerrain() {
        var terrain = new Array(w);
        for (var x = 0; x < w; x++) {
            terrain[x] = new Array(h);
            for (var y = 0; y < h; y++) {
                terrain[x][y] = cells[x][y].type;
            }
        }

        return terrain;
    }

    function serializeUnits() {
        var outUnits = {};
        for (var player in units) {
            if (units.hasOwnProperty(player)) {
                outUnits[player] = [];
                for (var i = 0; i < units[player].length; i++) {
                    var unit = jQuery.extend(true, {}, units[player][i]);
                    unit.loc = unit.pos;
                    delete unit.pos;
                    outUnits[player].push(unit);
                }
            }
        }

        return outUnits;
    }

    var w = width;
    var h = height;

    var cells = new Array(width);

    var currentPlayerId = null;
    var players = {};
    var unitCounter = 0;
    var units = {};

    for (var x = 0; x < width; x++) {
        cells[x] = new Array(height);
    }

    this.getWidth = function() {
        return getWidth();
    };

    this.getHeight = function() {
        return getHeight();
    };

    this.getCell = function(x, y) {
        return getCell(x, y);
    };

    this.setCell = function(x, y, cell) {
        setCell(x, y, cell);
    };

    this.initialize = function(entryCells) {
        initialize(entryCells);
    };

    this.addUnit = function(player, srcSpriteName, pos, distance, movementType,
                            movement, health, nation, name, canMove) {
        addUnit(player, srcSpriteName, pos, distance, movementType, movement,
                health, nation, name, canMove);
    };

    this.getUnits = function() {
        return getUnits();
    };

    this.findUnit = function(wx, wy) {
        return findUnit(wx, wy);
    };

    this.withinWorld = function(x, y) {
        return withinWorld(x, y);
    };

    this.findAvailableMoves = function(unit) {
        return findAvailableMoves(unit);
    };

    this.findAvailableAttacks = function(unit) {
        return findAvailableAttacks(unit);
    };

    this.serialize = function() {
        return serialize();
    };

    this.addOrUpdatePlayer = addOrUpdatePlayer;
}

function plainsWorld(width, height) {
    var terrain = new Array(width);

    for (var x = 0; x < width; x++) {
        terrain[x] = new Array(height);
        for (var y = 0; y < height; y++) {
            terrain[x][y] = terrainLookup(terrainTypes.plains, x, y);
        }
    }

    var w = new World(width, height);
    w.initialize(terrain);

    return w;
}

function routePath(unit, goal) {
    var moves = game.world.findAvailableMoves(unit);
    var pathworld = convertWorldToPathNodes(game.world, unit, moves);
    var pathfinder = new PathFinder(pathworld, pathworld[unit.pos.x][unit.pos.y]);
    var path = pathfinder.findPath(pathworld[goal.x][goal.y]);
    if (path) {
        game.pathCallback(path);
    } else {
        console.log("You can't get there!");
    }
    return path;
}

var terrainTable;
var terrainTypes = {};

function terrainLookup(id, x, y) {
    // TODO This data should come from some JSON source on the server
    return new terrainTable[id](x, y);
}

function cellEqual(cell1, cell2) {
    return cell1.cell.position.x == cell2.position.x &&
           cell1.cell.position.y == cell2.position.y;
}

function convertWorldToPathNodes(world, unit, moves) {
    var pathworld = new Array(world.getWidth());
    for (var x = 0; x < world.getWidth(); x++) {
        pathworld[x] = new Array(world.getHeight());
        for (var y = 0; y < world.getHeight(); y++) {
            // The cost modifier could/should come from a combination of
            // the cell type and the unit's movement modifiers. Likewise for
            // passable
            var cost = unit.movement[world.getCell(x, y).type];
            var passable = false;
            if (cost > 0 && arrayContains(moves, world.getCell(x, y), cellEqual)) {
                passable = true;
            }
            pathworld[x][y] = new pathNode({x: x, y: y}, cost, passable);
        }
    }
    return pathworld;
}
<|endoftext|>


<commit_before>'use strict';

// Common directive for Focus

angular.module('openhimWebui2App').directive('focus', function($timeout) {
    return {
      scope : {
        trigger : '@focus'
      },
      link : function(scope, element) {
        scope.$watch('trigger', function(value) {

          console.log( value )

          if (value === "true") {
            $timeout(function() {
              element[0].focus();
            });
          }
        });
      }
    };
  }); <commit_msg>removed console log<commit_after>'use strict';

// Common directive for Focus

angular.module('openhimWebui2App').directive('focus', function($timeout) {
    return {
      scope : {
        trigger : '@focus'
      },
      link : function(scope, element) {
        scope.$watch('trigger', function(value) {
          if (value === "true") {
            $timeout(function() {
              element[0].focus();
            });
          }
        });
      }
    };
  }); <|endoftext|>