<commit_before>'use strict';

window.Darwinator.GeneticAlgorithm = window.Darwinator.GeneticAlgorithm || {

  /*
  * Make sure that:
  * NUMBER_OF_GENES is divisible by NUMBER_OF_VARIABLES
  */
  POPULATION_SIZE:          10,
  CROSSOVER_PROBABILITY:    0.8,
  MUTATION_PROBABILITY:     0.025,
  TOURNAMENT_PARAMETER:     0.75,
  NUMBER_OF_GENERATIONS:    100,
  NUMBER_OF_VARIABLES:      3,
  TOURNAMENT_SIZE:          4,
  ELITISM_DEGREE:           5,

  // depends on player attributes
  NUMBER_OF_GENES:          undefined,
  MAX_ATTRIBUTE_SUM:        undefined, // force the sum of enemy attributes depend on the sum of the player attributes
  VARIABLE_RANGE:           undefined,
  //PLAYER_ADVANTAGE:         5, // used to set range

  // depends on population success
  MUTATION_RATE:            undefined,
  POOR_MAX_FITNESS:         0.1,  // used to set mutation rate

  /**
  * Generates a population of individuals from a given population. The new population is likely to be
  * more adapted to find a solution to the enemyScore() function.
  *
  * @method Darwinator.GeneticAlgorithm#generatePopulation
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Phaser.Group} [enemyGroup] - Optional. If not provided, a group with default values will be set.
  * @param {Boolean} [singleGeneration] - If true, only one iteration will be run, else, this.NUMBER_OF_GENERATIONS is used.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Array} The last population generated.
  */
  generatePopulation: function(game, target, enemyGroup, singleGeneration, spawnPositions) {
    // some of the constants depend on the current attributes of the player and are set here
    this.initRanges(target.attributes);

    if(!enemyGroup){
      enemyGroup = game.add.group();
      console.log('GA: Enemy group not provided. Initializing with default values.');
      enemyGroup = this.initPopulation(enemyGroup, game, target, spawnPositions);
      return enemyGroup;
    }
    console.log('GA: Starting GA run..');
    var translatedEnemies = this.translateEnemyWave(enemyGroup);
    var population        = translatedEnemies[0];
    var fitnessLevels     = translatedEnemies[1];
    var bestIndex         = translatedEnemies[2];
    var bestIndividual    = population[bestIndex];
    console.log('Best fitness: ' + fitnessLevels[bestIndex]);
    // to eliminate weak populations
    if(fitnessLevels[bestIndex] <= this.POOR_MAX_FITNESS){
      this.MUTATION_RATE = 20;
    }else{
      this.MUTATION_RATE = 1;
    }

    console.log('Mutation probability: ' + (this.MUTATION_PROBABILITY*this.MUTATION_RATE));

    // algorithm main loop
    for (var i = 0; i < (singleGeneration ? 1 : this.NUMBER_OF_GENERATIONS); i++) {

      // clone population
      var tmpPopulation = population.slice(0);

      // selection
      for(var l = 0; l < population.length; l += 2) {
        var ind1 = this.selection(fitnessLevels);
        var ind2 = this.selection(fitnessLevels);
        // crossover
        if (Math.random() < this.CROSSOVER_PROBABILITY) {
          var chromePair        = this.cross(population[ind1], population[ind2]);
          tmpPopulation[l]      = chromePair[0];
          tmpPopulation[l + 1]  = chromePair[1];
        } else {
          tmpPopulation[l]      = population[ind1];
          tmpPopulation[l + 1]  = population[ind2];
        }
        // mutation
        tmpPopulation[l]    = this.mutate(tmpPopulation[l]);
        tmpPopulation[l+1]  = this.mutate(tmpPopulation[l+1]);
      }

      // elitism
      for(l = 0; l < this.ELITISM_DEGREE; l++) {
        tmpPopulation[l] = bestIndividual;
      }

      // replace old population
      population = tmpPopulation;
    } //endof algorithm main loop

    var nextGeneration = this.translatePopulation(population, enemyGroup, game, target, spawnPositions);
    console.log('GA: Returning the new generation!');
    return nextGeneration;
  },

  initRanges: function(attributes){
    this.MAX_ATTRIBUTE_SUM  = attributes.strength + attributes.agility + attributes.intellect;
    this.VARIABLE_RANGE     = this.maxOf(attributes.strength, attributes.agility, attributes.intellect);
    this.NUMBER_OF_GENES    = new Number(this.VARIABLE_RANGE).toString(2).length * this.NUMBER_OF_VARIABLES;
  },

  maxOf: function(x, y, z){
    var max = x;
    if(y > max)
      max = y;
    if(z > max)
      max = z;
    return max;
  },

  /**
  * Generates a group of enemy sprites with some default values.
  *
  * @method Darwinator.GeneticAlgorithm#initPopulation
  * @param {Phaser.Group} [enemyGroup] - The enemy group to fill with enemies.
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Array} An enemy population with default attribute values.
  */
  initPopulation: function(enemyGroup, game, target, spawnPositions) {
    // 4 different enemy initial set of attributes - just an example, not permanent!
    var enemiesPerType  = this.POPULATION_SIZE / 5;
    var pos = [];
    var strength, agility, intellect;
    for(var i = 0; i < this.POPULATION_SIZE; i++){
      pos = spawnPositions[i];
      if(i < enemiesPerType){
        // smart but slow and weak
        strength  = 0;
        agility   = 0;
        intellect = this.VARIABLE_RANGE;
      }else if(i < enemiesPerType*2){
        // strong but slow and stupid
        strength  = this.VARIABLE_RANGE;
        agility   = 0;
        intellect = 0;
      }else if(i < enemiesPerType*3){
        // fast but weak and stupid
        strength  = 0;
        agility   = this.VARIABLE_RANGE;
        intellect = 0;
      }else{
        // hybrid
        strength  = agility = intellect = this.VARIABLE_RANGE / 3;
      }
      enemyGroup.add(new Darwinator.Enemy(game, target, pos.x, pos.y, undefined, strength, agility, intellect));
    }
    return enemyGroup;
  },

  /**
  * Decodes a individual from binary encoding to real numbers. The values of each
  * variable will lie in the range [0, this.VARIABLE_RANGE].
  *
  * @method Darwinator.GeneticAlgorithm#decodeIndividual
  * @param {Array} [individual] - The binary encoded individual to be decoded
  * @return {Array} The decoded individual. Will have a length of this.NUMBER_OF_VARIABLES
  */
  decodeIndividual: function(individual) {
    var pointsToSpend = this.MAX_ATTRIBUTE_SUM;
    var bitsPerVar    = this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES;
    var decoded       = new Array(this.NUMBER_OF_VARIABLES);

    for(var i = 0; i < this.NUMBER_OF_VARIABLES; i++) {
      decoded[i] = 0;
      for(var l = 0; l < bitsPerVar; l++) {

        var startVar = i * bitsPerVar;
        decoded[i] += individual[startVar + l] * Math.pow(2, -(l+1));
      }
      decoded[i] = pointsToSpend * decoded[i]/(1 - Math.pow(2,-bitsPerVar));
      if(pointsToSpend > 0){
        pointsToSpend -= decoded[i];
      }
    }
    console.log('decoded: ' + decoded);
    return decoded;
  },

  /**
  * Cross two individuals to create two new ones. Will select a crossing point at random,
  * and swap the binary encoded values between the individuals from the selected point.
  *
  * @method Darwinator.GeneticAlgorithm#cross
  * @param {Array} [firstInd] - The first individual to be crossed
  * @param {Array} [secondInd] - The second individual to be crossed
  * @return {Array} - A touple containing the two new individuals.
  */
  cross: function(firstInd, secondInd) {
    var crossPoint = Math.round(Math.random()*this.NUMBER_OF_GENES - 1);
    var newInd1 = new Array(this.NUMBER_OF_GENES);
    var newInd2 = new Array(this.NUMBER_OF_GENES);

    for(var i = 0; i < this.NUMBER_OF_GENES; i++) {
      if (i < crossPoint) {
        newInd1[i] = firstInd[i];
        newInd2[i] = secondInd[i];
      } else {
        newInd2[i] = firstInd[i];
        newInd1[i] = secondInd[i];
      }
    }

    return [newInd1, newInd2];
  },

  /**
  * Select a individual based on the fitness levels of the entire population.
  * this.TOURNAMENT_SIZE many individuals will be selected at random, and one of them will
  * be returned. The tournament works in such a way that a individual with a high fitness level
  * is more likely to be returned from the tournament, but is not more likely to participate.
  *
  * @method Darwinator.GeneticAlgorithm#selection
  * @param {Array} - The fitness levels of the entire population
  * @return {Number} - The index of the selected individual.
  */
  selection: function(fitnessLevels) {
    var tournamentParticipants = new Array(this.TOURNAMENT_SIZE);

    /* Select individuals at random, and represent them as a touple containing their indexes and their
    * fitness levels. */
    for (var i = 0; i < this.TOURNAMENT_SIZE; i++) {
      tournamentParticipants[i]    = new Array(2);
      tournamentParticipants[i][0] = Math.round(Math.random() * (this.POPULATION_SIZE - 1));
      tournamentParticipants[i][1] = fitnessLevels[tournamentParticipants[i][0]];
    }

    /* Sort by fitness levels */
    tournamentParticipants.sort(
      function(a,b) {return b[1]-a[1];}
    );

    for(i = 0; i < this.TOURNAMENT_SIZE; i++) {
      if (Math.random() < this.TOURNAMENT_PARAMETER) {
        return tournamentParticipants[i][0];
      }
    }

    return tournamentParticipants[i - 1][0];
  },

  /**
  * Mutate a given individual by swapping some of the binary encoded values at random.
  * The chance of a value swapping is set by this.MUTATION_PROBABILITY.
  *
  * @method Darwinator.GeneticAlgorithm#mutate
  * @param {Array} - The individual to be mutated
  * @return {Array} - The mutated individual.
  */
  mutate: function(individual) {
    for (var i = 0; i < this.NUMBER_OF_GENES; i++) {
      if (Math.random() < this.MUTATION_PROBABILITY * this.MUTATION_RATE) {
        individual[i] = 1 - individual[i];
      }
    }
    return individual;
  },

  /**
  * Evaluates an enemy sprite based on its score.
  *
  * @method Darwinator.GeneticAlgorithm#evaluateInd
  * @param {Phaser.Sprite} - The individual to be evaluated
  * @return {Number} - The fitness level of the individual
  */
  evaluateInd: function(enemy) {
    var fitness = 1 - (1 / this.enemyScore(enemy));
    return fitness == -Infinity ? 0 : fitness;// maximize fitness based on enemy score
  },

  /**
  * Calculates the score of an enemy based on statistical attributes.
  *
  * @method Darwinator.GeneticAlgorithm#enemyScore
  * @param {Phaser.Sprite} - An enemy sprite
  * @return {Number} - The score of the enemy for a given game round.
  */
  enemyScore: function(enemy) {
    var score = enemy.alive ? enemy.damageDone*2 : enemy.damageDone;
    return score;
  },

  /**
  *
  * Tranlates the attributes of an enemy to a binary chromosome.
  *
  * @method Darwinator.GeneticAlgorithm#enemyToChromosome
  * @param {Phaser.Sprite} - An enemy sprite
  * @return {Number} - The enemy represented as a binary chromosome
  */
  enemyToChromosome: function (enemy){
    // concatenate the translation of all attributes - NOTE: order matters!
    var chrom = this.attrToGenes(enemy.attributes.strength)
                  .concat(this.attrToGenes(enemy.attributes.agility))
                  .concat(this.attrToGenes(enemy.attributes.intellect));

    if(chrom.length !== this.NUMBER_OF_GENES){
      console.log('Illegal length of chromosome: ' + chrom.length);
    }else{
      return chrom;
    }
  },

  /**
  * Translates an enemy group of sprites to chromosomes and fitness levels.
  * The index of the most fit individual is also provided.
  *
  * @method Darwinator.GeneticAlgorithm#translateEnemyWave
  * @param {Phaser.Group} - A group of enemy sprites
  * @return {Array} - Chromosomes, fitnessLevels and the index of the fittest individual.
  *                     as [chromosomes, fitnessLevels, fittestIndex]
  */
  translateEnemyWave: function(enemyGroup){
    var currentSize = enemyGroup.length; //could add an extra param for this.
    var population  = new Array(currentSize);
    var fitness     = new Array(currentSize);
    var bestIndex   = 0;
    for(var i = 0; i < currentSize; i++){
      var enemy     = enemyGroup.getAt(i);
      population[i] = this.enemyToChromosome(enemy);
      fitness[i]    = this.evaluateInd(enemy);
      if(fitness[i] > fitness[bestIndex]){
        bestIndex = i;
      }
    }
    return [population, fitness, bestIndex];
  },

  /**
  * Replaces the given group of enemy sprites to the next generation of sprites.
  *
  * @method Darwinator.GeneticAlgorithm#translatePopulation
  * @param {Array} [population] - The chromosomes of the new generation.
  * @param {Phaser.Group} [enemyGroup] - The enemy sprite group to replace.
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Phaser.Group} The new group of enemies.
  */
  translatePopulation: function(population, enemyGroup, game, target, spawnPositions){
    // FIXME possible memory leak, should call destroy but it crashes for some reason..
    //game.enemies.destroy(true);
    enemyGroup = game.add.group();
    for(var i = 0; i < population.length; i++){
      var pos         = spawnPositions[i];
      var attributes  = this.decodeIndividual(population[i]);
      var strength    = attributes[0];
      var agility     = attributes[1];
      var intellect   = attributes[2];
      var enemy       = new Darwinator.Enemy(game, target, pos.x, pos.y, undefined, strength, agility, intellect);
      enemyGroup.add(enemy);
    }
    return enemyGroup;
  },

  /**
  * Translates an enemy attribute to a binary string of length this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES.
  *
  * @method Darwinator.GeneticAlgorithm#attrToGenes
  * @param {Number} - An enemy attribute.
  * @return {Array} - A binary string representation of the enemy attribute.
  */
  attrToGenes: function(attr) {
      // binary conversion
      var base = 2;
      var binaryString = Number(attr).toString(base).split('').
                map(function(str){return parseInt(str, base);});

      var bitsPerVar = this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES;
      if (binaryString.length < bitsPerVar){
        // add zeros to beginning if the binary string was too short
        var nbrInitialZeros = bitsPerVar - binaryString.length;
        var zeros           = new Array(nbrInitialZeros);
        for(var i = 0; i < nbrInitialZeros; i++){
          zeros[i] = 0;
        }

        binaryString = zeros.concat(binaryString);
      }else if(binaryString.length > bitsPerVar){
        // keep the last bitsPerVar bits if the binary string was too long
        // happens if 2^bitsPerVar - 1 < this.VARIABLE_RANGE
        binaryString = binaryString.slice(-bitsPerVar);
      }
      return binaryString;
  }

};
<commit_msg>removed some debug messages<commit_after>'use strict';

window.Darwinator.GeneticAlgorithm = window.Darwinator.GeneticAlgorithm || {

  /*
  * Make sure that:
  * NUMBER_OF_GENES is divisible by NUMBER_OF_VARIABLES
  */
  POPULATION_SIZE:          10,
  CROSSOVER_PROBABILITY:    0.8,
  MUTATION_PROBABILITY:     0.025,
  TOURNAMENT_PARAMETER:     0.75,
  NUMBER_OF_GENERATIONS:    100,
  NUMBER_OF_VARIABLES:      3,
  TOURNAMENT_SIZE:          4,
  ELITISM_DEGREE:           5,

  // depends on player attributes
  NUMBER_OF_GENES:          undefined,
  MAX_ATTRIBUTE_SUM:        undefined, // force the sum of enemy attributes depend on the sum of the player attributes
  VARIABLE_RANGE:           undefined,
  //PLAYER_ADVANTAGE:         5, // used to set range

  // depends on population success
  MUTATION_RATE:            undefined,
  POOR_MAX_FITNESS:         0.1,  // used to set mutation rate

  /**
  * Generates a population of individuals from a given population. The new population is likely to be
  * more adapted to find a solution to the enemyScore() function.
  *
  * @method Darwinator.GeneticAlgorithm#generatePopulation
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Phaser.Group} [enemyGroup] - Optional. If not provided, a group with default values will be set.
  * @param {Boolean} [singleGeneration] - If true, only one iteration will be run, else, this.NUMBER_OF_GENERATIONS is used.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Array} The last population generated.
  */
  generatePopulation: function(game, target, enemyGroup, singleGeneration, spawnPositions) {
    // some of the constants depend on the current attributes of the player and are set here
    this.initRanges(target.attributes);

    if(!enemyGroup){
      enemyGroup = game.add.group();
      console.log('GA: Enemy group not provided. Initializing with default values.');
      enemyGroup = this.initPopulation(enemyGroup, game, target, spawnPositions);
      return enemyGroup;
    }
    console.log('GA: Starting GA run..');
    var translatedEnemies = this.translateEnemyWave(enemyGroup);
    var population        = translatedEnemies[0];
    var fitnessLevels     = translatedEnemies[1];
    var bestIndex         = translatedEnemies[2];
    var bestIndividual    = population[bestIndex];

    // to eliminate weak populations
    if(fitnessLevels[bestIndex] <= this.POOR_MAX_FITNESS){
      this.MUTATION_RATE = 20;
    }else{
      this.MUTATION_RATE = 1;
    }

    // algorithm main loop
    for (var i = 0; i < (singleGeneration ? 1 : this.NUMBER_OF_GENERATIONS); i++) {

      // clone population
      var tmpPopulation = population.slice(0);

      // selection
      for(var l = 0; l < population.length; l += 2) {
        var ind1 = this.selection(fitnessLevels);
        var ind2 = this.selection(fitnessLevels);
        // crossover
        if (Math.random() < this.CROSSOVER_PROBABILITY) {
          var chromePair        = this.cross(population[ind1], population[ind2]);
          tmpPopulation[l]      = chromePair[0];
          tmpPopulation[l + 1]  = chromePair[1];
        } else {
          tmpPopulation[l]      = population[ind1];
          tmpPopulation[l + 1]  = population[ind2];
        }
        // mutation
        tmpPopulation[l]    = this.mutate(tmpPopulation[l]);
        tmpPopulation[l+1]  = this.mutate(tmpPopulation[l+1]);
      }

      // elitism
      for(l = 0; l < this.ELITISM_DEGREE; l++) {
        tmpPopulation[l] = bestIndividual;
      }

      // replace old population
      population = tmpPopulation;
    } //endof algorithm main loop

    var nextGeneration = this.translatePopulation(population, enemyGroup, game, target, spawnPositions);
    console.log('GA: Returning the new generation!');
    return nextGeneration;
  },

  initRanges: function(attributes){
    this.MAX_ATTRIBUTE_SUM  = attributes.strength + attributes.agility + attributes.intellect;
    this.VARIABLE_RANGE     = this.maxOf(attributes.strength, attributes.agility, attributes.intellect);
    this.NUMBER_OF_GENES    = new Number(this.VARIABLE_RANGE).toString(2).length * this.NUMBER_OF_VARIABLES;
  },

  maxOf: function(x, y, z){
    var max = x;
    if(y > max)
      max = y;
    if(z > max)
      max = z;
    return max;
  },

  /**
  * Generates a group of enemy sprites with some default values.
  *
  * @method Darwinator.GeneticAlgorithm#initPopulation
  * @param {Phaser.Group} [enemyGroup] - The enemy group to fill with enemies.
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Array} An enemy population with default attribute values.
  */
  initPopulation: function(enemyGroup, game, target, spawnPositions) {
    // 4 different enemy initial set of attributes - just an example, not permanent!
    var enemiesPerType  = this.POPULATION_SIZE / 5;
    var pos = [];
    var strength, agility, intellect;
    for(var i = 0; i < this.POPULATION_SIZE; i++){
      pos = spawnPositions[i];
      if(i < enemiesPerType){
        // smart but slow and weak
        strength  = 0;
        agility   = 0;
        intellect = this.VARIABLE_RANGE;
      }else if(i < enemiesPerType*2){
        // strong but slow and stupid
        strength  = this.VARIABLE_RANGE;
        agility   = 0;
        intellect = 0;
      }else if(i < enemiesPerType*3){
        // fast but weak and stupid
        strength  = 0;
        agility   = this.VARIABLE_RANGE;
        intellect = 0;
      }else{
        // hybrid
        strength  = agility = intellect = this.VARIABLE_RANGE / 3;
      }
      enemyGroup.add(new Darwinator.Enemy(game, target, pos.x, pos.y, undefined, strength, agility, intellect));
    }
    return enemyGroup;
  },

  /**
  * Decodes a individual from binary encoding to real numbers. The values of each
  * variable will lie in the range [0, this.VARIABLE_RANGE].
  *
  * @method Darwinator.GeneticAlgorithm#decodeIndividual
  * @param {Array} [individual] - The binary encoded individual to be decoded
  * @return {Array} The decoded individual. Will have a length of this.NUMBER_OF_VARIABLES
  */
  decodeIndividual: function(individual) {
    var pointsToSpend = this.MAX_ATTRIBUTE_SUM;
    var bitsPerVar    = this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES;
    var decoded       = new Array(this.NUMBER_OF_VARIABLES);

    for(var i = 0; i < this.NUMBER_OF_VARIABLES; i++) {
      decoded[i] = 0;
      for(var l = 0; l < bitsPerVar; l++) {

        var startVar = i * bitsPerVar;
        decoded[i] += individual[startVar + l] * Math.pow(2, -(l+1));
      }
      decoded[i] = pointsToSpend * decoded[i]/(1 - Math.pow(2,-bitsPerVar));
      if(pointsToSpend > 0){
        pointsToSpend -= decoded[i];
      }
    }
    return decoded;
  },

  /**
  * Cross two individuals to create two new ones. Will select a crossing point at random,
  * and swap the binary encoded values between the individuals from the selected point.
  *
  * @method Darwinator.GeneticAlgorithm#cross
  * @param {Array} [firstInd] - The first individual to be crossed
  * @param {Array} [secondInd] - The second individual to be crossed
  * @return {Array} - A touple containing the two new individuals.
  */
  cross: function(firstInd, secondInd) {
    var crossPoint = Math.round(Math.random()*this.NUMBER_OF_GENES - 1);
    var newInd1 = new Array(this.NUMBER_OF_GENES);
    var newInd2 = new Array(this.NUMBER_OF_GENES);

    for(var i = 0; i < this.NUMBER_OF_GENES; i++) {
      if (i < crossPoint) {
        newInd1[i] = firstInd[i];
        newInd2[i] = secondInd[i];
      } else {
        newInd2[i] = firstInd[i];
        newInd1[i] = secondInd[i];
      }
    }

    return [newInd1, newInd2];
  },

  /**
  * Select a individual based on the fitness levels of the entire population.
  * this.TOURNAMENT_SIZE many individuals will be selected at random, and one of them will
  * be returned. The tournament works in such a way that a individual with a high fitness level
  * is more likely to be returned from the tournament, but is not more likely to participate.
  *
  * @method Darwinator.GeneticAlgorithm#selection
  * @param {Array} - The fitness levels of the entire population
  * @return {Number} - The index of the selected individual.
  */
  selection: function(fitnessLevels) {
    var tournamentParticipants = new Array(this.TOURNAMENT_SIZE);

    /* Select individuals at random, and represent them as a touple containing their indexes and their
    * fitness levels. */
    for (var i = 0; i < this.TOURNAMENT_SIZE; i++) {
      tournamentParticipants[i]    = new Array(2);
      tournamentParticipants[i][0] = Math.round(Math.random() * (this.POPULATION_SIZE - 1));
      tournamentParticipants[i][1] = fitnessLevels[tournamentParticipants[i][0]];
    }

    /* Sort by fitness levels */
    tournamentParticipants.sort(
      function(a,b) {return b[1]-a[1];}
    );

    for(i = 0; i < this.TOURNAMENT_SIZE; i++) {
      if (Math.random() < this.TOURNAMENT_PARAMETER) {
        return tournamentParticipants[i][0];
      }
    }

    return tournamentParticipants[i - 1][0];
  },

  /**
  * Mutate a given individual by swapping some of the binary encoded values at random.
  * The chance of a value swapping is set by this.MUTATION_PROBABILITY.
  *
  * @method Darwinator.GeneticAlgorithm#mutate
  * @param {Array} - The individual to be mutated
  * @return {Array} - The mutated individual.
  */
  mutate: function(individual) {
    for (var i = 0; i < this.NUMBER_OF_GENES; i++) {
      if (Math.random() < this.MUTATION_PROBABILITY * this.MUTATION_RATE) {
        individual[i] = 1 - individual[i];
      }
    }
    return individual;
  },

  /**
  * Evaluates an enemy sprite based on its score.
  *
  * @method Darwinator.GeneticAlgorithm#evaluateInd
  * @param {Phaser.Sprite} - The individual to be evaluated
  * @return {Number} - The fitness level of the individual
  */
  evaluateInd: function(enemy) {
    var fitness = 1 - (1 / this.enemyScore(enemy));
    return fitness == -Infinity ? 0 : fitness;// maximize fitness based on enemy score
  },

  /**
  * Calculates the score of an enemy based on statistical attributes.
  *
  * @method Darwinator.GeneticAlgorithm#enemyScore
  * @param {Phaser.Sprite} - An enemy sprite
  * @return {Number} - The score of the enemy for a given game round.
  */
  enemyScore: function(enemy) {
    var score = enemy.alive ? enemy.damageDone*2 : enemy.damageDone;
    return score;
  },

  /**
  *
  * Tranlates the attributes of an enemy to a binary chromosome.
  *
  * @method Darwinator.GeneticAlgorithm#enemyToChromosome
  * @param {Phaser.Sprite} - An enemy sprite
  * @return {Number} - The enemy represented as a binary chromosome
  */
  enemyToChromosome: function (enemy){
    // concatenate the translation of all attributes - NOTE: order matters!
    var chrom = this.attrToGenes(enemy.attributes.strength)
                  .concat(this.attrToGenes(enemy.attributes.agility))
                  .concat(this.attrToGenes(enemy.attributes.intellect));

    if(chrom.length !== this.NUMBER_OF_GENES){
      console.log('Illegal length of chromosome: ' + chrom.length);
    }else{
      return chrom;
    }
  },

  /**
  * Translates an enemy group of sprites to chromosomes and fitness levels.
  * The index of the most fit individual is also provided.
  *
  * @method Darwinator.GeneticAlgorithm#translateEnemyWave
  * @param {Phaser.Group} - A group of enemy sprites
  * @return {Array} - Chromosomes, fitnessLevels and the index of the fittest individual.
  *                     as [chromosomes, fitnessLevels, fittestIndex]
  */
  translateEnemyWave: function(enemyGroup){
    var currentSize = enemyGroup.length; //could add an extra param for this.
    var population  = new Array(currentSize);
    var fitness     = new Array(currentSize);
    var bestIndex   = 0;
    for(var i = 0; i < currentSize; i++){
      var enemy     = enemyGroup.getAt(i);
      population[i] = this.enemyToChromosome(enemy);
      fitness[i]    = this.evaluateInd(enemy);
      if(fitness[i] > fitness[bestIndex]){
        bestIndex = i;
      }
    }
    return [population, fitness, bestIndex];
  },

  /**
  * Replaces the given group of enemy sprites to the next generation of sprites.
  *
  * @method Darwinator.GeneticAlgorithm#translatePopulation
  * @param {Array} [population] - The chromosomes of the new generation.
  * @param {Phaser.Group} [enemyGroup] - The enemy sprite group to replace.
  * @param {Phaser.Game} [game] - The game that uses the algorithm.
  * @param {Phaser.Sprite} [target] - The sprite to be attacked by the enemies.
  * @param {Array} [spawnPositions] - The positions on which the enemies are allowed to spawn.
  * @return {Phaser.Group} The new group of enemies.
  */
  translatePopulation: function(population, enemyGroup, game, target, spawnPositions){
    // FIXME possible memory leak, should call destroy but it crashes for some reason..
    //game.enemies.destroy(true);
    enemyGroup = game.add.group();
    for(var i = 0; i < population.length; i++){
      var pos         = spawnPositions[i];
      var attributes  = this.decodeIndividual(population[i]);
      var strength    = attributes[0];
      var agility     = attributes[1];
      var intellect   = attributes[2];
      var enemy       = new Darwinator.Enemy(game, target, pos.x, pos.y, undefined, strength, agility, intellect);
      enemyGroup.add(enemy);
    }
    return enemyGroup;
  },

  /**
  * Translates an enemy attribute to a binary string of length this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES.
  *
  * @method Darwinator.GeneticAlgorithm#attrToGenes
  * @param {Number} - An enemy attribute.
  * @return {Array} - A binary string representation of the enemy attribute.
  */
  attrToGenes: function(attr) {
      // binary conversion
      var base = 2;
      var binaryString = Number(attr).toString(base).split('').
                map(function(str){return parseInt(str, base);});

      var bitsPerVar = this.NUMBER_OF_GENES / this.NUMBER_OF_VARIABLES;
      if (binaryString.length < bitsPerVar){
        // add zeros to beginning if the binary string was too short
        var nbrInitialZeros = bitsPerVar - binaryString.length;
        var zeros           = new Array(nbrInitialZeros);
        for(var i = 0; i < nbrInitialZeros; i++){
          zeros[i] = 0;
        }

        binaryString = zeros.concat(binaryString);
      }else if(binaryString.length > bitsPerVar){
        // keep the last bitsPerVar bits if the binary string was too long
        // happens if 2^bitsPerVar - 1 < this.VARIABLE_RANGE
        binaryString = binaryString.slice(-bitsPerVar);
      }
      return binaryString;
  }

};
<|endoftext|>


<commit_before>define([
    'js-whatever/js/base-page',
    'text!about-page/templates/about-page.html',
    'datatables.net-bs'
], function(BasePage, template) {

    return BasePage.extend({
        template: _.template(template),

        initialize: function(options) {
            this.options = options;
        },

        render: function() {
            this.$el.html(this.template(this.options));

            this.$('table.table').dataTable({
                autoWidth: false,
                language: {
                    search: ''
                }
            });

            this.$('.dataTables_filter input')
                .prop('placeholder', this.options.strings.search)
                .addClass('search-query');
        },

        getTemplateParameters: $.noop
    });
});
<commit_msg>Remove unnecessary class.<commit_after>define([
    'js-whatever/js/base-page',
    'text!about-page/templates/about-page.html',
    'datatables.net-bs'
], function(BasePage, template) {

    return BasePage.extend({
        template: _.template(template),

        initialize: function(options) {
            this.options = options;
        },

        render: function() {
            this.$el.html(this.template(this.options));

            this.$('table.table').dataTable({
                autoWidth: false,
                language: {
                    search: ''
                }
            });

            this.$('.dataTables_filter input')
                .prop('placeholder', this.options.strings.search);
        },

        getTemplateParameters: $.noop
    });
});
<|endoftext|>


<commit_before>import _msg from './modules/msg';
import * as contributions from './modules/git-contribution';
import * as store from './modules/store';
import * as theme from './modules/theme';

/* --------------------------------------
 GLOBAL CONSTANTS & VARS (bad practice T_T)
-------------------------------------- */
const UPDATE_INTERVAL = 2 * 60 * 1000;
let TARGET_CONTRIBUTION_COUNT;
let GITHUB_USERNAME;
let BACKGROUND_WORKER;

/* --------------------------------------
 MAIN FUNCTIONS
-------------------------------------- */
// Defined as non-arrow function so that we can update the Global variables
function SYNC_USERDATA() {
  const temp = store.load();
  if (temp) {
    const { targetContributionCount, githubId } = temp;
    TARGET_CONTRIBUTION_COUNT = targetContributionCount;
    GITHUB_USERNAME = githubId;
    return true;
  }

  return false;
}

/* updates extension's badge */
const UPDATE_BADGE = (githubId, value) => {
  if (value) {
    chrome.browserAction.setBadgeBackgroundColor({ color: value.color });
    chrome.browserAction.setBadgeText({ text: value.text });
  } else {
    contributions.getContributionsOfTheDay(githubId)
      .then((commitCount) => {
        chrome.browserAction.setBadgeBackgroundColor({
          color: theme.getColor(Math.round((commitCount / TARGET_CONTRIBUTION_COUNT) * 100))
        });
        chrome.browserAction.setBadgeText({
          text: commitCount.toString()
        });
      })
      .catch(() => {
        console.log('[UPDATE_BADGE, contributions.getContributionsOfTheDay] ERROR!');
      });
  }
};

/* scrap & update commit count periodically + configure it to repeat it periodically */
const INIT_WORKER = (githubId, updateInterval) =>
  setInterval(() => { UPDATE_BADGE(githubId); }, updateInterval);

/* --------------------------------------
 START OF APPLICATION
-------------------------------------- */
/* initialize message-passing module to listen for popup events */
_msg.init('bg', {
  updateData: () => {
    if (SYNC_USERDATA() === true) {
      UPDATE_BADGE(GITHUB_USERNAME);
      clearInterval(BACKGROUND_WORKER);
      BACKGROUND_WORKER = INIT_WORKER(GITHUB_USERNAME, UPDATE_INTERVAL);
    }
  },
  updateBadgeText: (done, { text, color }) => {
    UPDATE_BADGE(GITHUB_USERNAME, { text, color });
  }
});

if (SYNC_USERDATA() === true) {
  UPDATE_BADGE(GITHUB_USERNAME);
  clearInterval(BACKGROUND_WORKER);
  BACKGROUND_WORKER = INIT_WORKER(GITHUB_USERNAME, UPDATE_INTERVAL);
}
<commit_msg>Extension badge now shows '?' if githubID is unconfigured<commit_after>import _msg from './modules/msg';
import * as contributions from './modules/git-contribution';
import * as store from './modules/store';
import * as theme from './modules/theme';

/* --------------------------------------
 GLOBAL CONSTANTS & VARS (bad practice T_T)
-------------------------------------- */
const UPDATE_INTERVAL = 2 * 60 * 1000; // 2 mins
let TARGET_CONTRIBUTION_COUNT;
let GITHUB_USERNAME;
let BACKGROUND_WORKER;

/* --------------------------------------
 MAIN FUNCTIONS
-------------------------------------- */
// Defined as non-arrow function so that we can update the Global variables
function SYNC_USERDATA() {
  const temp = store.load();
  if (temp) {
    const { targetContributionCount, githubId } = temp;
    TARGET_CONTRIBUTION_COUNT = targetContributionCount;
    GITHUB_USERNAME = githubId;
    return true;
  }

  return false;
}

/* updates extension's badge */
const UPDATE_BADGE = (githubId, value) => {
  if (value) {
    chrome.browserAction.setBadgeBackgroundColor({ color: value.color });
    chrome.browserAction.setBadgeText({ text: value.text });
  } else {
    contributions.getContributionsOfTheDay(githubId)
      .then((commitCount) => {
        if (commitCount) {
          chrome.browserAction.setBadgeBackgroundColor({
            color: theme.getColor(Math.round((commitCount / TARGET_CONTRIBUTION_COUNT) * 100))
          });
          chrome.browserAction.setBadgeText({
            text: commitCount.toString()
          });
        } else {
          chrome.browserAction.setBadgeBackgroundColor({ color: theme.getColor(0) });
          chrome.browserAction.setBadgeText({ text: '?' });
        }
      })
      .catch(() => {
        console.log('[UPDATE_BADGE, contributions.getContributionsOfTheDay] ERROR!');
      });
  }
};

/* scrap & update commit count periodically + configure it to repeat it periodically */
const INIT_WORKER = (githubId, updateInterval) =>
  setInterval(() => { UPDATE_BADGE(githubId); }, updateInterval);

/* --------------------------------------
 START OF APPLICATION
-------------------------------------- */
/* initialize message-passing module to listen for popup events */
_msg.init('bg', {
  updateData: () => {
    if (SYNC_USERDATA() === true) {
      UPDATE_BADGE(GITHUB_USERNAME);
      clearInterval(BACKGROUND_WORKER);
      BACKGROUND_WORKER = INIT_WORKER(GITHUB_USERNAME, UPDATE_INTERVAL);
    }
  },
  updateBadgeText: (done, { text, color }) => {
    UPDATE_BADGE(GITHUB_USERNAME, { text, color });
  }
});

if (SYNC_USERDATA() === true) {
  UPDATE_BADGE(GITHUB_USERNAME);
  clearInterval(BACKGROUND_WORKER);
  BACKGROUND_WORKER = INIT_WORKER(GITHUB_USERNAME, UPDATE_INTERVAL);
}
<|endoftext|>


<commit_before>export default {
	"10/8": [
		{
			horario: "10:15",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "11:30",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		}
	],
	"11/8": [
		{
			horario: "10:15",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "11:30",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "12:50",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		}
	],
	"12/8": [
		{
			horario: "10:15",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "11:30",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "12:50",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		},
		{
			horario: "13:10",
			titulo: "Lorem Ipsum",
			palestrante: "Samuel Novaes",
			descricao: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu lorem vel nibh posuere placerat."
		}
	]
}
<commit_msg>js/cronograma.js deletado<commit_after><|endoftext|>


<commit_before>/**
* Is a UI Widget for picking dates.
* @name DatePicker
* @class DatePicker
* @augments ch.Controls
* @requires ch.Calendar
* @memberOf ch
* @param {Object} [conf] Object with configuration properties.
* @param {String} [conf.format] Sets the date format. By default is "DD/MM/YYYY".
* @param {String} [conf.selected] Sets a date that should be selected by default. By default is the date of today.
* @param {String} [conf.from] Set a maximum selectable date.
* @param {String} [conf.to] Set a minimum selectable date.
* @param {String} [conf.points] Points to be positioned. See Positioner component. By default is "ct cb".
* @param {Array} [conf.monthsNames] By default is ["Enero", ... , "Diciembre"].
* @param {Array} [conf.weekdays] By default is ["Dom", ... , "Sab"].
* @param {Boolean} [conf.closable] Defines if floated component will be closed when a date is selected or not. By default it's "true".
* @returns itself
* @example
* // Create a new Date Picker with configuration.
* var me = $(".example").datePicker({
*	 "format": "MM/DD/YYYY",
*	 "selected": "2011/12/25",
*	 "from": "2010/12/25",
*	 "to": "2012/12/25",
*	 "monthsNames": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
*	 "weekdays": ["Su", "Mo", "Tu", "We", "Thu", "Fr", "Sa"]
* });
* @example
* // Create a new datePicker with a class name 'example'.
* var me = $(".example").datePicker();
*/

ch.datePicker = function (conf) {

	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.DatePicker#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);

	// Configuration by default
	conf.format = conf.format || "DD/MM/YYYY";
	conf.points = conf.points || "ct cb";
	conf.closable = ch.utils.hasOwn(conf, "closable") ? conf.closable : true;

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.controls.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/

/**
*	Protected Members
*/

	/**
	* Pick a date in the Calendar and updates the input data.
	* @protected
	* @function
	* @name ch.DatePicker#process
	*/
	that.process = function (event) {

		// Day selection
		if (event.target.nodeName !== "TD" || event.target.className.indexOf("ch-disabled") !== -1 || event.target.className.indexOf("ch-calendar-other") !== -1) { return; }

		// Select the day and update input value with selected date
		that.element.value = that.calendar.selectDay(event.target.innerHTML);

		// Hide float
		if (conf.closable) { that["float"].innerHide(); }

	};

	/**
	* Reference to the Calendar component instance.
	* @protected
	* @type Object
	* @name ch.DatePicker#calendar
	*/
	that.calendar = $("
")
		// Add functionality for date selection
		.bind("click", function (event) { that.process(event); })
		// Instance Calendar component
		.calendar({
			"format": conf.format,
			"from": conf.from,
			"to": conf.to,
			"selected": conf.selected,
			"monthsNames": conf.monthsNames,
			"weekdays": conf.weekdays
		});

	/**
	* Reference to the Float component instanced.
	* @protected
	* @type Object
	* @name ch.DatePicker#float
	*/
	that["float"] = that.createFloat({
		"$element": $("
Date Picker
").insertAfter(that.element),
		"content": that.calendar.element,
		"points": conf.points,
		"offset": "0 10",
		"aria": {
			"role": "tooltip",
			"identifier": "aria-describedby"
		},
		"closeButton": false,
		"cone": true
	});

/**
*  Public Members
*/

	/**
	* The 'uid' is the Chico's unique instance identifier. Every instance has a different 'uid' property. You can see its value by reading the 'uid' property on any public instance.
	* @public
	* @name ch.DatePicker#uid
	* @type Number
	*/

	/**
	* Reference to a DOM Element. This binding between the component and the HTMLElement, defines context where the component will be executed. Also is usual that this element triggers the component default behavior.
	* @public
	* @name ch.DatePicker#element
	* @type HTMLElement
	*/

	/**
	* This public property defines the component type. All instances are saved into a 'map', grouped by its type. You can reach for any or all of the components from a specific type with 'ch.instances'.
	* @public
	* @name ch.DatePicker#type
	* @type String
	*/

	/**
	* Triggers the innerShow method and returns the public scope to keep method chaining.
	* @public
	* @function
	* @name ch.DatePicker#show
	* @returns itself
	* @example
	* // Following the first example, using 'me' as modal's instance controller:
	* me.show();
	*/
	that["public"].show = function () {
		that["float"].innerShow();

		return that["public"];
	};

	/**
	* Triggers the innerHide method and returns the public scope to keep method chaining.
	* @public
	* @function
	* @name ch.DatePicker#hide
	* @returns itself
	* @example
	* // Following the first example, using 'me' as modal's instance controller:
	* me.hide();
	*/
	that["public"].hide = function () {
		that["float"].innerHide();

		return that["public"];
	};

	/**
	* Select a specific date or returns the selected date.
	* @public
	* @since 0.9
	* @function
	* @name ch.DatePicker#select
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].select = function (date) {
		// Select the day and update input value with selected date
		that.element.value = that.calendar.select(date);

		return that["public"];
	};

	/**
	* Returns date of today
	* @public
	* @since 0.9
	* @function
	* @name ch.DatePicker#today
	* @return date
	*/
	that["public"].today = function () {
		return that.calendar.today();
	};

	/**
	* Move to the next month or year. If it isn't specified, it will be moved to next month.
	* @public
	* @name ch.DatePicker#next
	* @function
	* @param {String} time A string that allows specify if it should move to next month or year.
	* @return {itself}
	* @default Next month
	*/
	that["public"].next = function (time) {
		that.calendar.next(time);

		return that["public"];
	};

	/**
	* Move to the previous month or year. If it isn't specified, it will be moved to previous month.
	* @public
	* @function
	* @param {String} time A string that allows specify if it should move to previous month or year.
	* @return {itself}
	* @default Previous month
	*/
	that["public"].prev = function (time) {
		that.calendar.prev(time);

		return that["public"];
	};

	/**
	* Reset the Date Picker to date of today
	* @public
	* @function
	* @name ch.DatePicker#reset
	* @return itself
	*/
	that["public"].reset = function () {

		// Delete input value
		that.element.value = "";

		that.calendar.reset();

		return that["public"];
	};

	/**
	* Set a minimum selectable date.
	* @public
	* @function
	* @name ch.DatePicker#from
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].from = function (date) {
		that.calendar.from(date);

		return that["public"];
	};

	/**
	* Set a maximum selectable date.
	* @public
	* @function
	* @name ch.DatePicker#to
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].to = function (date) {
		that.calendar.to(date);

		return that["public"];
	};

/**
*	Default event delegation
*/

	// Change type of input to "text"
	that.element.type = "text";

	// Change value of input if there are a selected date
	that.element.value = (conf.selected) ? that.calendar.select() : that.element.value;

	// Add show behaivor to float's trigger.
	that["float"].$element
		.css("cursor", "pointer")
		.bind("click", function (event) { that["float"].innerShow(event); });

	/**
	* Triggers when the component is ready to use (Since 0.8.0).
	* @name ch.DatePicker#ready
	* @event
	* @public
	* @example
	* // Following the first example, using 'me' as Date Picker's instance controller:
	* me.on("ready", function () {
	* 	this.show();
	* });
	*/
	setTimeout(function () { that.trigger("ready"); }, 50);

	return that;
};

ch.factory("datePicker");<commit_msg>#564 DatePicker: select method returns an object instead of date selected<commit_after>/**
* Is a UI Widget for picking dates.
* @name DatePicker
* @class DatePicker
* @augments ch.Controls
* @requires ch.Calendar
* @memberOf ch
* @param {Object} [conf] Object with configuration properties.
* @param {String} [conf.format] Sets the date format. By default is "DD/MM/YYYY".
* @param {String} [conf.selected] Sets a date that should be selected by default. By default is the date of today.
* @param {String} [conf.from] Set a maximum selectable date.
* @param {String} [conf.to] Set a minimum selectable date.
* @param {String} [conf.points] Points to be positioned. See Positioner component. By default is "ct cb".
* @param {Array} [conf.monthsNames] By default is ["Enero", ... , "Diciembre"].
* @param {Array} [conf.weekdays] By default is ["Dom", ... , "Sab"].
* @param {Boolean} [conf.closable] Defines if floated component will be closed when a date is selected or not. By default it's "true".
* @returns itself
* @example
* // Create a new Date Picker with configuration.
* var me = $(".example").datePicker({
*	 "format": "MM/DD/YYYY",
*	 "selected": "2011/12/25",
*	 "from": "2010/12/25",
*	 "to": "2012/12/25",
*	 "monthsNames": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
*	 "weekdays": ["Su", "Mo", "Tu", "We", "Thu", "Fr", "Sa"]
* });
* @example
* // Create a new datePicker with a class name 'example'.
* var me = $(".example").datePicker();
*/

ch.datePicker = function (conf) {

	/**
	* Reference to a internal component instance, saves all the information and configuration properties.
	* @private
	* @name ch.DatePicker#that
	* @type object
	*/
	var that = this;

	conf = ch.clon(conf);

	// Configuration by default
	conf.format = conf.format || "DD/MM/YYYY";
	conf.points = conf.points || "ct cb";
	conf.closable = ch.utils.hasOwn(conf, "closable") ? conf.closable : true;

	that.conf = conf;

/**
*	Inheritance
*/

	that = ch.controls.call(that);
	that.parent = ch.clon(that);

/**
*	Private Members
*/

/**
*	Protected Members
*/

	/**
	* Pick a date in the Calendar and updates the input data.
	* @protected
	* @function
	* @name ch.DatePicker#process
	*/
	that.process = function (event) {

		// Day selection
		if (event.target.nodeName !== "TD" || event.target.className.indexOf("ch-disabled") !== -1 || event.target.className.indexOf("ch-calendar-other") !== -1) { return; }

		// Select the day and update input value with selected date
		that.element.value = that.calendar.selectDay(event.target.innerHTML);

		// Hide float
		if (conf.closable) { that["float"].innerHide(); }

	};

	/**
	* Reference to the Calendar component instance.
	* @protected
	* @type Object
	* @name ch.DatePicker#calendar
	*/
	that.calendar = $("
")
		// Add functionality for date selection
		.bind("click", function (event) { that.process(event); })
		// Instance Calendar component
		.calendar({
			"format": conf.format,
			"from": conf.from,
			"to": conf.to,
			"selected": conf.selected,
			"monthsNames": conf.monthsNames,
			"weekdays": conf.weekdays
		});

	/**
	* Reference to the Float component instanced.
	* @protected
	* @type Object
	* @name ch.DatePicker#float
	*/
	that["float"] = that.createFloat({
		"$element": $("
Date Picker
").insertAfter(that.element),
		"content": that.calendar.element,
		"points": conf.points,
		"offset": "0 10",
		"aria": {
			"role": "tooltip",
			"identifier": "aria-describedby"
		},
		"closeButton": false,
		"cone": true
	});

/**
*  Public Members
*/

	/**
	* The 'uid' is the Chico's unique instance identifier. Every instance has a different 'uid' property. You can see its value by reading the 'uid' property on any public instance.
	* @public
	* @name ch.DatePicker#uid
	* @type Number
	*/

	/**
	* Reference to a DOM Element. This binding between the component and the HTMLElement, defines context where the component will be executed. Also is usual that this element triggers the component default behavior.
	* @public
	* @name ch.DatePicker#element
	* @type HTMLElement
	*/

	/**
	* This public property defines the component type. All instances are saved into a 'map', grouped by its type. You can reach for any or all of the components from a specific type with 'ch.instances'.
	* @public
	* @name ch.DatePicker#type
	* @type String
	*/

	/**
	* Triggers the innerShow method and returns the public scope to keep method chaining.
	* @public
	* @function
	* @name ch.DatePicker#show
	* @returns itself
	* @example
	* // Following the first example, using 'me' as modal's instance controller:
	* me.show();
	*/
	that["public"].show = function () {
		that["float"].innerShow();

		return that["public"];
	};

	/**
	* Triggers the innerHide method and returns the public scope to keep method chaining.
	* @public
	* @function
	* @name ch.DatePicker#hide
	* @returns itself
	* @example
	* // Following the first example, using 'me' as modal's instance controller:
	* me.hide();
	*/
	that["public"].hide = function () {
		that["float"].innerHide();

		return that["public"];
	};

	/**
	* Select a specific date or returns the selected date.
	* @public
	* @since 0.9
	* @function
	* @name ch.DatePicker#select
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].select = function (date) {
		// Select the day and update input value with selected date
		if (date) {
			that.calendar.select(date);
			that.element.value = that.calendar.select();

			return that["public"];
		}

		return that.calendar.select();
	};

	/**
	* Returns date of today
	* @public
	* @since 0.9
	* @function
	* @name ch.DatePicker#today
	* @return date
	*/
	that["public"].today = function () {
		return that.calendar.today();
	};

	/**
	* Move to the next month or year. If it isn't specified, it will be moved to next month.
	* @public
	* @name ch.DatePicker#next
	* @function
	* @param {String} time A string that allows specify if it should move to next month or year.
	* @return {itself}
	* @default Next month
	*/
	that["public"].next = function (time) {
		that.calendar.next(time);

		return that["public"];
	};

	/**
	* Move to the previous month or year. If it isn't specified, it will be moved to previous month.
	* @public
	* @function
	* @param {String} time A string that allows specify if it should move to previous month or year.
	* @return {itself}
	* @default Previous month
	*/
	that["public"].prev = function (time) {
		that.calendar.prev(time);

		return that["public"];
	};

	/**
	* Reset the Date Picker to date of today
	* @public
	* @function
	* @name ch.DatePicker#reset
	* @return itself
	*/
	that["public"].reset = function () {

		// Delete input value
		that.element.value = "";

		that.calendar.reset();

		return that["public"];
	};

	/**
	* Set a minimum selectable date.
	* @public
	* @function
	* @name ch.DatePicker#from
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].from = function (date) {
		that.calendar.from(date);

		return that["public"];
	};

	/**
	* Set a maximum selectable date.
	* @public
	* @function
	* @name ch.DatePicker#to
	* @param {string} "YYYY/MM/DD".
	* @return itself
	*/
	that["public"].to = function (date) {
		that.calendar.to(date);

		return that["public"];
	};

/**
*	Default event delegation
*/

	// Change type of input to "text"
	that.element.type = "text";

	// Change value of input if there are a selected date
	that.element.value = (conf.selected) ? that.calendar.select() : that.element.value;

	// Add show behaivor to float's trigger.
	that["float"].$element
		.css("cursor", "pointer")
		.bind("click", function (event) { that["float"].innerShow(event); });

	/**
	* Triggers when the component is ready to use (Since 0.8.0).
	* @name ch.DatePicker#ready
	* @event
	* @public
	* @example
	* // Following the first example, using 'me' as Date Picker's instance controller:
	* me.on("ready", function () {
	* 	this.show();
	* });
	*/
	setTimeout(function () { that.trigger("ready"); }, 50);

	return that;
};

ch.factory("datePicker");<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

/* More information about these options at jshint.com/docs/options */
/* exported GumHandler */
'use strict';

function GumHandler() {
  this.gumPendingDialog_ = document.getElementById('gum-pending-dialog');
  this.gumErrorDialog_ = document.getElementById('gum-error-dialog');
  this.gumNotSupportedDialog_ =
      document.getElementById('gum-not-supported-dialog');
  this.gumErrorMessage_ = document.getElementById('gum-error-message');
  this.firstUserCheck_ = null;
  this.gumStreamSuccessCallback_ = null;
}

GumHandler.prototype = {
  start: function(callback) {
    this.gumStreamSuccessCallback_ = callback;
    if (typeof navigator.getUserMedia === 'undefined') {
      this.gumNotSupportedDialog_.open();
    } else {
      this.getUserMedia_(callback);
      this.firstUserCheck_ = setTimeout(this.firstTimeUser_.bind(this), 300);
    }
  },

  firstTimeUser_: function() {
    this.gumPendingDialog_.open();
  },

  getUserMedia_: function() {
    doGetUserMedia({audio: true, video: true}, this.gotStream_.bind(this),
        this.gotError_.bind(this));
  },

  gotStream_: function(stream) {
    clearTimeout(this.firstUserCheck_);

    // Stop all tracks to ensure the camera and audio devices are shutdown
    // directly.
    for (var i = 0; i < stream.getVideoTracks().length; i++) {
      stream.getVideoTracks()[i].stop();
    }
    for (var j = 0; j < stream.getAudioTracks().length; j++) {
      stream.getAudioTracks()[j].stop();
    }
    this.gumPendingDialog_.close();
    this.gumErrorDialog_.close();
    this.gumStreamSuccessCallback_();
  },

  gotError_: function(error) {
    clearTimeout(this.firstUserCheck_);
    this.gumPendingDialog_.close();
    this.gumErrorMessage_.innerHTML = error.name;
    this.gumErrorDialog_.open();
    setTimeout(this.getUserMedia_.bind(this), 1000);
  }
};
<commit_msg>Used the no device error<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */

/* More information about these options at jshint.com/docs/options */
/* exported GumHandler */
'use strict';

function GumHandler() {
  this.gumPendingDialog_ = document.getElementById('gum-pending-dialog');
  this.gumErrorDialog_ = document.getElementById('gum-error-dialog');
  this.gumNotSupportedDialog_ =
      document.getElementById('gum-not-supported-dialog');
  this.gumNoDeviceDialog_ =
      document.getElementById('gum-no-device-dialog');
  this.gumErrorMessage_ = document.getElementById('gum-error-message');
  this.firstUserCheck_ = null;
  this.gumStreamSuccessCallback_ = null;
}

GumHandler.prototype = {
  start: function(callback) {
    this.gumStreamSuccessCallback_ = callback;
    if (typeof navigator.getUserMedia === 'undefined') {
      this.gumNotSupportedDialog_.open();
    } else {
      this.getUserMedia_(callback);
      this.firstUserCheck_ = setTimeout(this.firstTimeUser_.bind(this), 300);
    }
  },

  firstTimeUser_: function() {
    this.gumPendingDialog_.open();
  },

  getUserMedia_: function() {
    doGetUserMedia({audio: true, video: true}, this.gotStream_.bind(this),
        this.gotError_.bind(this));
  },

  gotStream_: function(stream) {
    clearTimeout(this.firstUserCheck_);

    // Stop all tracks to ensure the camera and audio devices are shutdown
    // directly.
    for (var i = 0; i < stream.getVideoTracks().length; i++) {
      stream.getVideoTracks()[i].stop();
    }
    for (var j = 0; j < stream.getAudioTracks().length; j++) {
      stream.getAudioTracks()[j].stop();
    }
    this.gumPendingDialog_.close();
    this.gumErrorDialog_.close();
    this.gumStreamSuccessCallback_();
  },

  gotError_: function(error) {
    clearTimeout(this.firstUserCheck_);
    this.gumPendingDialog_.close();
    if (error.name === "DevicesNotFoundError") {
      this.gumNoDeviceDialog_.open();
    }
    else {
      this.gumErrorMessage_.innerHTML = error.name;
      this.gumErrorDialog_.open();
    }
    setTimeout(this.getUserMedia_.bind(this), 1000);
  }
};
<|endoftext|>


<commit_before>(function() {
  angular.module('ui.grid').config(['$provide', function($provide) {
    $provide.decorator('i18nService', ['$delegate', function($delegate) {
      $delegate.add('zh-cn', {
        aggregate: {
          label: '行'
        },
        groupPanel: {
          description: '拖曳表头到此处进行分组'
        },
        search: {
          placeholder: '查找',
          showingItems: '已显示行数：',
          selectedItems: '已选择行数：',
          totalItems: '总行数：',
          size: '每页显示行数：',
          first: '首页',
          next: '下一页',
          previous: '上一页',
          last: '末页'
        },
        menu: {
          text: '选择列：'
        },
        sort: {
          ascending: '升序',
          descending: '降序',
          remove: '取消排序'
        },
        column: {
          hide: '隐藏列'
        },
        aggregation: {
          count: '计数：',
          sum: '求和：',
          avg: '均值：',
          min: '最小值：',
          max: '最大值：'
        },
        pinning: {
          pinLeft: '左侧固定',
          pinRight: '右侧固定',
          unpin: '取消固定'
        },
        gridMenu: {
          columns: '列：',
          importerTitle: '导入文件',
          exporterAllAsCsv: '导出全部数据到CSV',
          exporterVisibleAsCsv: '导出可见数据到CSV',
          exporterSelectedAsCsv: '导出已选数据到CSV',
          exporterAllAsPdf: '导出全部数据到PDF',
          exporterVisibleAsPdf: '导出可见数据到PDF',
          exporterSelectedAsPdf: '导出已选数据到PDF',
          clearAllFilters: '清除所有过滤器'
        },
        importer: {
          noHeaders: '无法获取列名，确定文件包含表头？',
          noObjects: '无法获取数据，确定文件包含数据？',
          invalidCsv: '无法处理文件，确定是合法的CSV文件？',
          invalidJson: '无法处理文件，确定是合法的JSON文件？',
          jsonNotArray: '导入的文件不是JSON数组！'
        },
        pagination: {
          sizes: '行每页',
          totalItems: '行'
        }
      });
      return $delegate;
    }]);
  }]);
})();
<commit_msg>feat(i18n): Completes zh-cn translation.<commit_after>(function() {
  angular.module('ui.grid').config(['$provide', function($provide) {
    $provide.decorator('i18nService', ['$delegate', function($delegate) {
      $delegate.add('zh-cn', {
        headerCell: {
          aria: {
            defaultFilterLabel: '列过滤器',
            removeFilter: '移除过滤器',
            columnMenuButtonLabel: '列菜单'
          },
          priority: '优先级:',
          filterLabel: "列过滤器: "
        },
        aggregate: {
          label: '行'
        },
        groupPanel: {
          description: '拖曳表头到此处进行分组'
        },
        search: {
          placeholder: '查找',
          showingItems: '已显示行数：',
          selectedItems: '已选择行数：',
          totalItems: '总行数：',
          size: '每页显示行数：',
          first: '首页',
          next: '下一页',
          previous: '上一页',
          last: '末页'
        },
        menu: {
          text: '选择列：'
        },
        sort: {
          ascending: '升序',
          descending: '降序',
          none: '无序',
          remove: '取消排序'
        },
        column: {
          hide: '隐藏列'
        },
        aggregation: {
          count: '计数：',
          sum: '求和：',
          avg: '均值：',
          min: '最小值：',
          max: '最大值：'
        },
        pinning: {
          pinLeft: '左侧固定',
          pinRight: '右侧固定',
          unpin: '取消固定'
        },
        columnMenu: {
          close: '关闭'
        },
        gridMenu: {
          aria: {
            buttonLabel: '表格菜单'
          },
          columns: '列：',
          importerTitle: '导入文件',
          exporterAllAsCsv: '导出全部数据到CSV',
          exporterVisibleAsCsv: '导出可见数据到CSV',
          exporterSelectedAsCsv: '导出已选数据到CSV',
          exporterAllAsPdf: '导出全部数据到PDF',
          exporterVisibleAsPdf: '导出可见数据到PDF',
          exporterSelectedAsPdf: '导出已选数据到PDF',
          clearAllFilters: '清除所有过滤器'
        },
        importer: {
          noHeaders: '无法获取列名，确定文件包含表头？',
          noObjects: '无法获取数据，确定文件包含数据？',
          invalidCsv: '无法处理文件，确定是合法的CSV文件？',
          invalidJson: '无法处理文件，确定是合法的JSON文件？',
          jsonNotArray: '导入的文件不是JSON数组！'
        },
        pagination: {
          aria: {
            pageToFirst: '第一页',
            pageBack: '上一页',
            pageSelected: '当前页',
            pageForward: '下一页',
            pageToLast: '最后一页'
          },
          sizes: '行每页',
          totalItems: '行',
          through: '至',
          of: '共'
        },
        grouping: {
          group: '分组',
          ungroup: '取消分组',
          aggregate_count: '合计: 计数',
          aggregate_sum: '合计: 求和',
          aggregate_max: '合计: 最大',
          aggregate_min: '合计: 最小',
          aggregate_avg: '合计: 平均',
          aggregate_remove: '合计: 移除'
        }
      });
      return $delegate;
    }]);
  }]);
})();
<|endoftext|>


<commit_before>/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.12
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( typeof define === 'function' && define.amd ) {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS style for Browserify
        module.exports = factory;
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
        slice  = Array.prototype.slice,
        nullLowestDeltaTimeout, lowestDelta;

    if ( $.event.fixHooks ) {
        for ( var i = toFix.length; i; ) {
            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
        }
    }

    var special = $.event.special.mousewheel = {
        version: '3.1.12',

        setup: function() {
            if ( this.addEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.addEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = handler;
            }
            // Store the line height and page height for this particular element
            $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
            $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
        },

        teardown: function() {
            if ( this.removeEventListener ) {
                for ( var i = toBind.length; i; ) {
                    this.removeEventListener( toBind[--i], handler, false );
                }
            } else {
                this.onmousewheel = null;
            }
            // Clean up the data we added to the element
            $.removeData(this, 'mousewheel-line-height');
            $.removeData(this, 'mousewheel-page-height');
        },

        getLineHeight: function(elem) {
            var $elem = $(elem),
                $parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
            if (!$parent.length) {
                $parent = $('body');
            }
            return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
        },

        getPageHeight: function(elem) {
            return $(elem).height();
        },

        settings: {
            adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
            normalizeOffset: true  // calls getBoundingClientRect for each event
        }
    };

    $.fn.extend({
        mousewheel: function(fn) {
            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
        },

        unmousewheel: function(fn) {
            return this.unbind('mousewheel', fn);
        }
    });

    function handler(event) {
        var orgEvent   = event || window.event,
            args       = slice.call(arguments, 1),
            delta      = 0,
            deltaX     = 0,
            deltaY     = 0,
            absDelta   = 0,
            offsetX    = 0,
            offsetY    = 0;
        event = $.event.fix(orgEvent);
        event.type = 'mousewheel';

        // Old school scrollwheel delta
        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }

        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            deltaX = deltaY * -1;
            deltaY = 0;
        }

        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        delta = deltaY === 0 ? deltaX : deltaY;

        // New school wheel delta (wheel event)
        if ( 'deltaY' in orgEvent ) {
            deltaY = orgEvent.deltaY * -1;
            delta  = deltaY;
        }
        if ( 'deltaX' in orgEvent ) {
            deltaX = orgEvent.deltaX;
            if ( deltaY === 0 ) { delta  = deltaX * -1; }
        }

        // No change actually happened, no reason to go any further
        if ( deltaY === 0 && deltaX === 0 ) { return; }

        // Need to convert lines and pages to pixels if we aren't already in pixels
        // There are three delta modes:
        //   * deltaMode 0 is by pixels, nothing to do
        //   * deltaMode 1 is by lines
        //   * deltaMode 2 is by pages
        if ( orgEvent.deltaMode === 1 ) {
            var lineHeight = $.data(this, 'mousewheel-line-height');
            delta  *= lineHeight;
            deltaY *= lineHeight;
            deltaX *= lineHeight;
        } else if ( orgEvent.deltaMode === 2 ) {
            var pageHeight = $.data(this, 'mousewheel-page-height');
            delta  *= pageHeight;
            deltaY *= pageHeight;
            deltaX *= pageHeight;
        }

        // Store lowest absolute delta to normalize the delta values
        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );

        if ( !lowestDelta || absDelta < lowestDelta ) {
            lowestDelta = absDelta;

            // Adjust older deltas if necessary
            if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
                lowestDelta /= 40;
            }
        }

        // Adjust older deltas if necessary
        if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
            // Divide all the things by 40!
            delta  /= 40;
            deltaX /= 40;
            deltaY /= 40;
        }

        // Get a whole, normalized value for the deltas
        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);

        // Normalise offsetX and offsetY properties
        if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
            var boundingRect = this.getBoundingClientRect();
            offsetX = event.clientX - boundingRect.left;
            offsetY = event.clientY - boundingRect.top;
        }

        // Add information to the event object
        event.deltaX = deltaX;
        event.deltaY = deltaY;
        event.deltaFactor = lowestDelta;
        event.offsetX = offsetX;
        event.offsetY = offsetY;
        // Go ahead and set deltaMode to 0 since we converted to pixels
        // Although this is a little odd since we overwrite the deltaX/Y
        // properties with normalized deltas.
        event.deltaMode = 0;

        // Add event and delta to the front of the arguments
        args.unshift(event, delta, deltaX, deltaY);

        // Clearout lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
        nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);

        return ($.event.dispatch || $.event.handle).apply(this, args);
    }

    function nullLowestDelta() {
        lowestDelta = null;
    }

    function shouldAdjustOldDeltas(orgEvent, absDelta) {
        // If this is an older event and the delta is divisable by 120,
        // then we are assuming that the browser is treating this as an
        // older mouse wheel event and that we should divide the deltas
        // by 40 to try and get a more usable deltaFactor.
        // Side note, this actually impacts the reported scroll distance
        // in older browsers and can cause scrolling to be slower than native.
        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
        return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
    }

}));
<commit_msg> - removed mousewheel.js.<commit_after><|endoftext|>


<commit_before>export const domReady = new Promise(resolve => {
  function checkState() {
    if (document.readyState != 'loading') resolve();
  }
  document.addEventListener('readystatechange', checkState);
  checkState();
});

const range = document.createRange();
range.selectNode(document.documentElement);

export function strToEl(str) {
  const frag = range.createContextualFragment(str);
  return frag.children[0];
}

const entityMap = {
  "&": "&",
  "<": "<",
  ">": ">",
  '"': '"',
  "'": ''',
  "/": '/'
};

export function escapeHTML(str) {
  return String(str).replace(/[&<>"'/]/g, s => entityMap[s]);
}

export function escapeHtmlTag(strings, ...values) {
  values = values.map(s => escapeHTML(s));
  return strings.reduce((str, val, i) => str += val + (values[i] || ''), '');
}

export function readFileAsText(file) {
  return new Response(file).text();
}

function transitionClassFunc({removeClass = false}={}) {
  return function(el, className = 'active', transitionClass = 'transition') {
    if (removeClass) {
      if (!el.classList.contains(className)) return Promise.resolve();
    }
    else {
      if (el.classList.contains(className)) return Promise.resolve();
    }

    const transitionEnd = new Promise(resolve => {
      const listener = event => {
        if (event.target != el) return;
        el.removeEventListener('transitionend', listener);
        el.classList.remove(transitionClass);
        resolve();
      };

      el.classList.add(transitionClass);

      requestAnimationFrame(() => {
        el.addEventListener('transitionend', listener);
        el.classList[removeClass ? 'remove' : 'add'](className);
      });
    });

    const transitionTimeout = new Promise(resolve => setTimeout(resolve, 1000));

    return Promise.race([transitionEnd, transitionTimeout]);
  };
}

export const transitionToClass = transitionClassFunc();
export const transitionFromClass = transitionClassFunc({removeClass: true});

export function trackFocusMethod() {
  var focusMethod = 'mouse';

  document.body.addEventListener('focus', event => {
    event.target.classList.add(focusMethod == 'key' ? 'key-focused' : 'mouse-focused');
  }, true);

  document.body.addEventListener('blur', event => {
    event.target.classList.remove('key-focused');
    event.target.classList.remove('mouse-focused');
  }, true);

  document.body.addEventListener('keydown', () => {
    focusMethod = 'key';
  }, true);

  document.body.addEventListener('mousedown', () => {
    focusMethod = 'mouse';
  }, true);
}
<commit_msg>utils: Combine multiple `classList.remove` calls (#267)<commit_after>export const domReady = new Promise(resolve => {
  function checkState() {
    if (document.readyState != 'loading') resolve();
  }
  document.addEventListener('readystatechange', checkState);
  checkState();
});

const range = document.createRange();
range.selectNode(document.documentElement);

export function strToEl(str) {
  const frag = range.createContextualFragment(str);
  return frag.children[0];
}

const entityMap = {
  "&": "&",
  "<": "<",
  ">": ">",
  '"': '"',
  "'": ''',
  "/": '/'
};

export function escapeHTML(str) {
  return String(str).replace(/[&<>"'/]/g, s => entityMap[s]);
}

export function escapeHtmlTag(strings, ...values) {
  values = values.map(s => escapeHTML(s));
  return strings.reduce((str, val, i) => str += val + (values[i] || ''), '');
}

export function readFileAsText(file) {
  return new Response(file).text();
}

function transitionClassFunc({removeClass = false}={}) {
  return function(el, className = 'active', transitionClass = 'transition') {
    if (removeClass) {
      if (!el.classList.contains(className)) return Promise.resolve();
    }
    else {
      if (el.classList.contains(className)) return Promise.resolve();
    }

    const transitionEnd = new Promise(resolve => {
      const listener = event => {
        if (event.target != el) return;
        el.removeEventListener('transitionend', listener);
        el.classList.remove(transitionClass);
        resolve();
      };

      el.classList.add(transitionClass);

      requestAnimationFrame(() => {
        el.addEventListener('transitionend', listener);
        el.classList[removeClass ? 'remove' : 'add'](className);
      });
    });

    const transitionTimeout = new Promise(resolve => setTimeout(resolve, 1000));

    return Promise.race([transitionEnd, transitionTimeout]);
  };
}

export const transitionToClass = transitionClassFunc();
export const transitionFromClass = transitionClassFunc({removeClass: true});

export function trackFocusMethod() {
  var focusMethod = 'mouse';

  document.body.addEventListener('focus', event => {
    event.target.classList.add(focusMethod == 'key' ? 'key-focused' : 'mouse-focused');
  }, true);

  document.body.addEventListener('blur', event => {
    event.target.classList.remove('key-focused', 'mouse-focused');
  }, true);

  document.body.addEventListener('keydown', () => {
    focusMethod = 'key';
  }, true);

  document.body.addEventListener('mousedown', () => {
    focusMethod = 'mouse';
  }, true);
}
<|endoftext|>