<commit_before>/**
 * @ngdoc service
 * @name $timeline
 * @function
 *
 * @description
 * The timeline service provides support for mapping timeline functions to different values.
 *
 * @example
 * ```html
 * <fa-modifier
 *   fa-rotate-y="rRotation(t.get())"
 *   fa-translate="translation(t.get())"
 * >
 *   ...
 * </fa-modifier>
 * ```
 * ```javascript
 * var Transitionable = $famous['famous/transitions/Transitionable'];
 * var Easing = $famous['famous/transitions/Easing'];
 *
 * $scope.t = new Transitionable(0);
 *
 * $scope.yRotation = $timeline([
 *   [0, 0, Easing.inOutQuad],
 *   [0.8, 1.1 * (Math.PI/2), Easing.inOutQuart],
 *   [1, Math.PI/2]
 * ]);
 *
 * $scope.translation = $timeline([
 *   [0, [100, 100, 0], Easing.inOutQuad],
 *   [1, [400, 200, 0]]
 * ]);
 *
 * ```
 */

angular.module('famous.angular')
  .factory('$timeline', function () {

    // polymorphic add for scalars and vectors
    var add = function(a, b) {
      if (typeof a === "number") {
        return a + b;
      }
      else {
        return a.map(function(x, i) { return x + b[i]; });
      }
    };

    // polymorphic subtract for scalars and vectors
    var subtract = function(a, b) {
      if (typeof a === "number") {
        return a - b;
      }
      else {
        return a.map(function(x, i) { return x - b[i]; });
      }
    };

    // polymorphic multiply for scalar and vectors
    var multiply = function(A, b) {
      // b is a scalar, A is a scalar or a vector
      if (typeof A === "number") {
        return A * b;
      }
      else {
        return A.map(function(x) { return x * b; });
      }
    };

    var scale = function (f, x1, x2, y1, y2) {
      // x1 and x2 must be scalars. y1 and y2 can be scalars or vectors
      return function(x) {
        var yTranslate = y1;
        var xTranslate = -x1;
        var xScale = 1 / (x2 - x1);
        var yScale = subtract(y2, y1);
        return add(multiply(yScale, f(xScale * (x + xTranslate))), yTranslate);
      };
    };

    return function(points) {
        //
        // Takes a list of points, with the curve to follow to the next point.
        // Any curve value on the last point is ignored. If no curve function is
        // provided, a linear (identity) function is used.
        //
        //  e.g., [[0, 100, Easings.inOutQuad], [1, 500]]
        //
        // Returns a piecewise function f:
        //
        //
        //         / y₀,             if x < x₀
        //         |
        //         | scaled(curveᵢ,  if xᵢ ≤ x < xᵢ₊₁
        //         |        xᵢ,
        //         |        xᵢ,
        // f(x) = <         yᵢ₊₁,
        //         |        yᵢ₊₁)(x)
        //         | for i in 0..segments.length - 1
        //         |
        //         \ last x,         otherwise
        //

        var linear = function(x) { return x; };

        return function(x) {
          if (x < points[0][0]) {
            return points[0][1];
          }
          for (var i = 0; i < points.length - 1; i++) {
            if (points[i][0] <= x && x < points[i+1][0]) {
              var f = scale(points[i][2] || linear,
                            points[i][0],
                            points[i+1][0],
                            points[i][1],
                            points[i+1][1]);

              return f(x);
            }
          }
          return points[points.length-1][1];
        };

      };

  });
<commit_msg>Typo on line 12<commit_after>/**
 * @ngdoc service
 * @name $timeline
 * @function
 *
 * @description
 * The timeline service provides support for mapping timeline functions to different values.
 *
 * @example
 * ```html
 * <fa-modifier
 *   fa-rotate-y="yRotation(t.get())"
 *   fa-translate="translation(t.get())"
 * >
 *   ...
 * </fa-modifier>
 * ```
 * ```javascript
 * var Transitionable = $famous['famous/transitions/Transitionable'];
 * var Easing = $famous['famous/transitions/Easing'];
 *
 * $scope.t = new Transitionable(0);
 *
 * $scope.yRotation = $timeline([
 *   [0, 0, Easing.inOutQuad],
 *   [0.8, 1.1 * (Math.PI/2), Easing.inOutQuart],
 *   [1, Math.PI/2]
 * ]);
 *
 * $scope.translation = $timeline([
 *   [0, [100, 100, 0], Easing.inOutQuad],
 *   [1, [400, 200, 0]]
 * ]);
 *
 * ```
 */

angular.module('famous.angular')
  .factory('$timeline', function () {

    // polymorphic add for scalars and vectors
    var add = function(a, b) {
      if (typeof a === "number") {
        return a + b;
      }
      else {
        return a.map(function(x, i) { return x + b[i]; });
      }
    };

    // polymorphic subtract for scalars and vectors
    var subtract = function(a, b) {
      if (typeof a === "number") {
        return a - b;
      }
      else {
        return a.map(function(x, i) { return x - b[i]; });
      }
    };

    // polymorphic multiply for scalar and vectors
    var multiply = function(A, b) {
      // b is a scalar, A is a scalar or a vector
      if (typeof A === "number") {
        return A * b;
      }
      else {
        return A.map(function(x) { return x * b; });
      }
    };

    var scale = function (f, x1, x2, y1, y2) {
      // x1 and x2 must be scalars. y1 and y2 can be scalars or vectors
      return function(x) {
        var yTranslate = y1;
        var xTranslate = -x1;
        var xScale = 1 / (x2 - x1);
        var yScale = subtract(y2, y1);
        return add(multiply(yScale, f(xScale * (x + xTranslate))), yTranslate);
      };
    };

    return function(points) {
        //
        // Takes a list of points, with the curve to follow to the next point.
        // Any curve value on the last point is ignored. If no curve function is
        // provided, a linear (identity) function is used.
        //
        //  e.g., [[0, 100, Easings.inOutQuad], [1, 500]]
        //
        // Returns a piecewise function f:
        //
        //
        //         / y₀,             if x < x₀
        //         |
        //         | scaled(curveᵢ,  if xᵢ ≤ x < xᵢ₊₁
        //         |        xᵢ,
        //         |        xᵢ,
        // f(x) = <         yᵢ₊₁,
        //         |        yᵢ₊₁)(x)
        //         | for i in 0..segments.length - 1
        //         |
        //         \ last x,         otherwise
        //

        var linear = function(x) { return x; };

        return function(x) {
          if (x < points[0][0]) {
            return points[0][1];
          }
          for (var i = 0; i < points.length - 1; i++) {
            if (points[i][0] <= x && x < points[i+1][0]) {
              var f = scale(points[i][2] || linear,
                            points[i][0],
                            points[i+1][0],
                            points[i][1],
                            points[i+1][1]);

              return f(x);
            }
          }
          return points[points.length-1][1];
        };

      };

  });
<|endoftext|>


<commit_before>const authorisedRequest = require('../lib/authorisedrequest')
const config = require('../config')

function mapContacts (contacts) {
  if (contacts && (typeof contacts.map) === 'function') {
    return contacts.map((contact) => {
      return {
        url: `contact/${contact.id}`,
        name: `${contact.first_name} ${contact.last_name}`,
        id: contact.id,
        company: {
          url: `/company/company_company/${contact.company.id}`,
          name: contact.company.name,
          id: contact.company.id
        }
      }
    })
  }

  return contacts
}

function mapInteractions (interactions) {
  if (interactions && (typeof interactions.map) === 'function') {
    return interactions.map((interaction) => {
      return {
        url: `/interaction/${interaction.id}`,
        id: interaction.id,
        subject: interaction.subject
      }
    })
  }

  return interactions
}

module.exports = {
  getHomepageData: (token, days = 15) => {
    return authorisedRequest(token, `${config.apiRoot}/dashboard/homepage/?days=${days}`).then((data) => {
      data.contacts = mapContacts(data.contacts)
      data.interactions = mapInteractions(data.interactions)
      return data
    })
  }
}
<commit_msg>Fix link<commit_after>const authorisedRequest = require('../lib/authorisedrequest')
const config = require('../config')

function mapContacts (contacts) {
  if (contacts && (typeof contacts.map) === 'function') {
    return contacts.map((contact) => {
      return {
        url: `contact/${contact.id}`,
        name: `${contact.first_name} ${contact.last_name}`,
        id: contact.id,
        company: {
          url: `/company/company_company/${contact.company.id}`,
          name: contact.company.name,
          id: contact.company.id
        }
      }
    })
  }

  return contacts
}

function mapInteractions (interactions) {
  if (interactions && (typeof interactions.map) === 'function') {
    return interactions.map((interaction) => {
      return {
        url: `/interaction/${interaction.id}/details`,
        id: interaction.id,
        subject: interaction.subject
      }
    })
  }

  return interactions
}

module.exports = {
  getHomepageData: (token, days = 15) => {
    return authorisedRequest(token, `${config.apiRoot}/dashboard/homepage/?days=${days}`).then((data) => {
      data.contacts = mapContacts(data.contacts)
      data.interactions = mapInteractions(data.interactions)
      return data
    })
  }
}
<|endoftext|>


<commit_before>/** @module */
const { areBoxesOverlapping } = require('box-overlap');

const config = require('../config');
const { ACT_AIM_RANGE_TYPES, ACT_EFFECT_RANGE_TYPES, BOARD_TYPES, FACTION_TYPES, FRIENDSHIP_TYPES, STYLES
  } = require('../immutable/constants');
const { expandReachToRelativeCoordinates } = require('../lib/core');
const bulletMethods = require('./bullet');
const coordinateMethods = require('./coordinate');
const effectMethods = require('./effect');
const effectLogMethods = require('./effect-log');
const geometricApis = require('./geometric-apis');
const {
  coordinateToLocation,
  coordinateToRectangle,
  locationToCoordinate,
  locationToRectangle,
  rectangleToCoordinate,
  rectangleToLocation,
} = geometricApis;  // Expand only useful methods
const locationMethods = require('./location');
const rectangleMethods = require('./rectangle');
const squareMatrixMethods = require('./square-matrix');
const unitMethods = require('./unit');

/**
 * @param {State~Unit} unit
 * @return {State~Location}
 */
const getUnitPositionAsLocation = (unit) => {
  if (unit.location) {
    return unit.location;
  } else if (unit.placement) {
    return coordinateToLocation(unit.placement.coordinate);
  }
  throw new Error(`The unit does not have either \`location\` or \`placement\``);
};

/**
 * @param {State~Unit} actor
 * @param {State~Unit} targetedUnit
 * @param {State~Coordinate} endPointCoordinate
 * @return {?State~Coordinate}
 */
const choiceClosestCoordinateUnderTargetedUnit = (actor, targetedUnit, endPointCoordinate) => {
  const actorLocation = getUnitPositionAsLocation(actor);
  const targetedUnitLocation = getUnitPositionAsLocation(targetedUnit);
  const targetedUnitRectangle = locationToRectangle(targetedUnitLocation);

  const candidates = geometricApis.findCoordinatesWhereRectangleOverlaps(
    targetedUnitRectangle, endPointCoordinate);

  // Since enemies can only move up / down / left / right,
  //   it should not be more than 2 coordinates.
  // Ref) calculateMovementResults
  if (candidates.length !== 1 && candidates.length !== 2) {
    throw new Error('Enemy position is wrong');
  }

  // Sort in order of closeness
  return candidates.sort((a, b) => {
    const aLocation = coordinateToLocation(a);
    const bLocation = coordinateToLocation(b);
    const distanceToA = locationMethods.measureDistance(actorLocation, aLocation);
    const distanceToB = locationMethods.measureDistance(actorLocation, bLocation);

    if (distanceToA < distanceToB) {
      return -1;
    } else if (distanceToA > distanceToB) {
      return 1;
    }
    return 0;
  })[0] || null
};

/**
 * @return {State~Square[]}
 */
const findSquaresFromBoardsByPlacement = (placement, ...boards) => {
  const squares = [];

  boards
    .filter(board => placement.boardType === board.boardType)
    .forEach(board => {
      const square = squareMatrixMethods.findSquareByCoordinate(board.squareMatrix, placement.coordinate);
      if (square) squares.push(square);
    })
  ;

  return squares;
};

/**
 * @param {State~Placement} placement
 * @param {...State~Board} boards
 * @throws {Error} Found multiple squares
 * @return {?State~Square}
 */
const findOneSquareFromBoardsByPlacement = (placement, ...boards) => {
  const squares = findSquaresFromBoardsByPlacement(placement, ...boards);

  if (squares.length > 1) {
    throw new Error(`There are multiple squares found by the placement of ${ JSON.stringify(placement) }`);
  }

  return squares[0] || null;
};

/**
 * @param {State~Unit} unit
 * @return {boolean}
 */
const isUnitInBattle = (unit) => {
  return unit.placement.boardType === BOARD_TYPES.BATTLE_BOARD && unit.hitPoints > 0;
};

/**
 * @param {string} actFriendshipType - One of FRIENDSHIP_TYPES
 * @param {string} actorFactionType - One of FACTION_TYPES
 * @return {string[]} Some of FACTION_TYPES
 */
const judgeAffectableFractionTypes = (actFriendshipType, actorFactionType) => {
  if (actFriendshipType === FRIENDSHIP_TYPES.FRIENDLY) {
    return [actorFactionType];
  } else if (actFriendshipType === FRIENDSHIP_TYPES.UNFRIENDLY) {
    if (actorFactionType === FACTION_TYPES.ALLY) {
      return [FACTION_TYPES.ENEMY];
    } else if (actorFactionType === FACTION_TYPES.ENEMY) {
      return [FACTION_TYPES.ALLY];
    }
  }

  throw new Error(`Invalid actFriendshipType=${ actFriendshipType } or actorFactionType=${ actorFactionType }`);
};

/**
 * @param {State~Unit} actor - Only those in battle
 * @param {Act} act
 * @param {State~Unit} unit - Only those in battle
 * @return {boolean}
 */
const willActorAimActAtUnit = (actor, act, unit) => {
  return act.friendshipType === unitMethods.determineFriendship(actor, unit);
};

/**
 * @param {State~Unit} actor - Only those in battle
 * @param {Act} act
 * @param {State~Unit} target - Only those in battle
 * @return {boolean}
 */
const canActorAimActAtTargetedUnit = (actor, act, target) => {
  if (act.aimRange.type === ACT_AIM_RANGE_TYPES.REACHABLE) {
    // TODO: 暗黙的にユニットのサイズをマス目と同じとしているが、これで問題ないのか不明。
    const actorLocation = getUnitPositionAsLocation(actor);
    const reachableRects = geometricApis.createReachableRectangles(actorLocation, act.aimRange.reach);
    const targetLocation = getUnitPositionAsLocation(target);
    const targetRect = locationToRectangle(targetLocation);
    return reachableRects.some(rect => areBoxesOverlapping(rect, targetRect));
  }

  throw new Error(`Invalid aim-range-type`);
};

/**
 * @param {State~Unit} actor
 * @param {Function} act
 * @param {State~Unit[]} units
 * @return {?State~Unit}
 */
const choiceAimedUnit = (actor, act, units) => {
  const aimableUnits = units
    .filter(unit => willActorAimActAtUnit(actor, act, unit))
    .filter(unit => canActorAimActAtTargetedUnit(actor, act, unit));

  const actorLocation = getUnitPositionAsLocation(actor);

  aimableUnits.sort((a, b) => {
    const aLocation = getUnitPositionAsLocation(a);
    const bLocation = getUnitPositionAsLocation(b);

    // 1st: Sort by closest
    const actorToA = locationMethods.measureDistance(actorLocation, aLocation);
    const actorToB = locationMethods.measureDistance(actorLocation, bLocation);
    if (actorToA < actorToB) {
      return -1;
    } else if (actorToA > actorToB) {
      return 1;
    }

    // 2nd; Sort by clock-wise
    const angleA = locationMethods.measureAngleWithTopAsZero(actorLocation, aLocation);
    const angleB = locationMethods.measureAngleWithTopAsZero(actorLocation, bLocation);
    if (angleA === null || angleA < angleB) {
      return -1;
    } else if (angleB === null || angleA > angleB) {
      return 1;
    }

    return 0;
  });

  return aimableUnits[0] || null;
};

/**
 * Create bullets carrying effects on each
 * @param {State~Unit} actor
 * @param {Function} act
 * @param {State~Unit} aimedUnit
 * @param {State~Coordinate} squareMatrixEndPointCoordinate
 * @param {(Object|undefined)} options
 * @param {?State~Effect} [options.effect] - Pass the effect from the outside. It is mainly for testing.
 * @return {State~Bullet[]}
 */
const fireBullets = (actor, act, aimedUnit, squareMatrixEndPointCoordinate, options = {}) => {
  const defaultedOptions = Object.assign({
    effect: null,
  }, options);

  const bullets = [];

  const actorLocation = getUnitPositionAsLocation(actor);
  const aimedUnitLocation = getUnitPositionAsLocation(aimedUnit);

  const fromLocation = locationMethods.calculateCenterOfSquare(actorLocation);

  let toLocation;
  if (act.effectRange.type === ACT_EFFECT_RANGE_TYPES.UNIT) {
    toLocation = locationMethods.calculateCenterOfSquare(aimedUnitLocation);
  } else {
    toLocation = locationMethods.calculateCenterOfSquare(
      coordinateToLocation(
        choiceClosestCoordinateUnderTargetedUnit(actor, aimedUnit, squareMatrixEndPointCoordinate)
      )
    );
  }

  const direction = locationMethods.measureAngleAsEffectDirection(fromLocation, toLocation);

  let effect;
  if (options.effect) {
    effect = options.effect;
  } else {
    const effectOptions = {
      damagePoints: act.effectParameters.damagePoints,
      healingPoints: act.effectParameters.healingPoints,
    };
    if (act.effectRange.type === ACT_EFFECT_RANGE_TYPES.UNIT) {
      effectOptions.aimedUnitUid = aimedUnit.uid;
    } else {
      effectOptions.relativeCoordinates = act.expandEffectRangeToRelativeCoordinates(direction);
    }

    effect = effectMethods.createNewEffectState(
      judgeAffectableFractionTypes(act.friendshipType, actor.factionType),
      toLocation,
      effectOptions
    );
  }

  bullets.push(bulletMethods.createNewBulletState(fromLocation, toLocation, act.bullet.speed, effect));

  return bullets;
};

/**
 * @param {State~Effect} effect
 * @param {State~Unit} unit
 * @return {{newUnit, effectLogs}}
 */
const applyEffectToUnit = (effect, unit) => {
  let newUnit = Object.assign({}, unit);
  const effectLogs = [];

  const log = (options) => {
    effectLogs.push(effectLogMethods.createNewEffectLogState(unit.uid, options));
  };

  // Healing
  if (effect.healingPoints > 0) {
    const result = unitMethods.calculateHealing(unit, effect.healingPoints);

    newUnit = Object.assign(newUnit, { hitPoints: result.hitPoints });
    log({ healingPoints: result.healingPoints });
  }

  // Damaging
  if (effect.damagePoints > 0) {
    const result = unitMethods.calculateDamage(unit, effect.damagePoints);

    newUnit = Object.assign(newUnit, { hitPoints: result.hitPoints });
    log({ damagePoints: result.damagePoints });
  }

  return {
    newUnit,
    effectLogs,
  };
};

/**
 * Apply effect to units within the effective range
 * @param {State~Effect} effect
 * @param {State~Unit[]} units
 * @return {{units:<State~Unit[]>, effectLogs:<State~EffectLog[]>}}
 */
const effectOccurs = (effect, units) => {
  const effectLogs = [];

  const effectiveRectangles = effectMethods.createEffectiveRectangles(effect);

  const newUnits = units.map(unit => {
    const unitLocation = getUnitPositionAsLocation(unit);
    const unitRectangle = locationToRectangle(unitLocation);

    if (effect.affectableFractionTypes.indexOf(unit.factionType) > -1) {
      if (
        (
          effect.aimedUnitUid &&
          unit.uid === effect.aimedUnitUid &&
          areBoxesOverlapping(
            // TODO: bullet size
            locationToRectangle(effect.impactedLocation, { width: 4, height: 4, asCenterPoint: true }),
            locationToRectangle(unitLocation)
          )
        )
        ||
        (
          effectiveRectangles.some(rect => areBoxesOverlapping(rect, unitRectangle))
        )
      ) {
        const resultApplied = applyEffectToUnit(effect, unit);

        resultApplied.effectLogs.forEach(v => effectLogs.push(v));

        return resultApplied.newUnit;
      }
    }

    return unit;
  });

  return {
    units: newUnits,
    effectLogs,
  };
};

/**
 * Compute the state transition during the "tick"
 * <section>
 *   The "tick" is a coined word, which means so-called "one game loop".
 * </section>
 * @param {Object} state - A plain object generated from `store.getState()`
 * @return {Object}
 */
const computeTick = ({ allies, enemies, bullets, battleBoard, gameStatus }) => {
  const battleBoardEndPointCoordinate = squareMatrixMethods.getEndPointCoordinate(battleBoard.squareMatrix);

  let newBullets = bullets.slice();
  let newAllies = allies.slice();
  let newEnemies = enemies.slice();

  // Send dead enemies to the graveyard
  // TODO: Move to another part of the state instead of delete.
  newEnemies = newEnemies.filter(unitMethods.isAlive);

  // Send dead allies to the sortie board
  // TODO

  // Bullets movement and effect
  newBullets = newBullets
    // Apply effect & Clean effect occured bullets
    .filter(bullet => {
      if (!bulletMethods.isArrivedToDestination(bullet)) {
        return true;
      };

      // TODO: effectLogs
      const effectResult = effectOccurs(bullet.effect, newAllies.concat(newEnemies));
      newAllies = effectResult.units.filter(unit => unit.factionType === FACTION_TYPES.ALLY);
      newEnemies = effectResult.units.filter(unit => unit.factionType === FACTION_TYPES.ENEMY);

      return false;
    })
    // Bullets move
    //   The reason for the movement after the effect calculation is to guarantee landing on the display.
    .map(bullet => {
      return Object.assign({}, bullet, {
        location: bulletMethods.calculateNextLocation(bullet),
      });
    })
  ;

  // Separate dead units in this tick
  let alivedEnemies = newEnemies.filter(unitMethods.isAlive);
  let deadEnemies = newEnemies.filter(unitMethods.isDead);

  // Enemy's movement
  alivedEnemies = alivedEnemies.map(enemy => {
    const { location, destinationIndex } = unitMethods.calculateMovementResults(enemy);

    return Object.assign({}, enemy, {
      location,
      destinationIndex,
    });
  });

  // Ally's act
  newAllies = newAllies.map(ally => {
    const newAlly = Object.assign({}, ally);

    if (!isUnitInBattle(newAlly)) {
      // TODO: 出撃ポイントの回復
      return newAlly;
    }

    const act = unitMethods.getAct(newAlly);

    let didAct = false;

    if (unitMethods.canDoAct(newAlly, act)) {
      const aimedUnit = choiceAimedUnit(newAlly, act, newAllies.concat(alivedEnemies));

      if (aimedUnit) {
        if (config.isEnabledTickLog) {
          console.debug(`${ newAlly.factionType }:${ newAlly.jobId } aims ${ act.id } at ${ aimedUnit.factionType }:${ aimedUnit.jobId }`);
        }

        // Create bullets carrying effect on each
        newBullets = newBullets.concat(fireBullets(newAlly, act, aimedUnit, battleBoardEndPointCoordinate));

        // Comsume APs
        newAlly.actionPoints = unitMethods.calculateActionPointsConsumption(newAlly, act);

        didAct = true;
      }
    }

    if (!didAct) {
      // Recover APs
      newAlly.actionPoints = unitMethods.calculateActionPointsRecovery(newAlly);
    }

    return newAlly;
  });

  // Enemy's act
  // TODO

  return {
    allies: newAllies,
    enemies: alivedEnemies.concat(deadEnemies),
    bullets: newBullets,
  };
};

module.exports = {
  applyEffectToUnit,
  canActorAimActAtTargetedUnit,
  choiceAimedUnit,
  choiceClosestCoordinateUnderTargetedUnit,
  computeTick,
  effectOccurs,
  findOneSquareFromBoardsByPlacement,
  fireBullets,
  judgeAffectableFractionTypes,
  willActorAimActAtUnit,
};
<commit_msg>Fix a bug<commit_after>/** @module */
const { areBoxesOverlapping } = require('box-overlap');

const config = require('../config');
const { ACT_AIM_RANGE_TYPES, ACT_EFFECT_RANGE_TYPES, BOARD_TYPES, FACTION_TYPES, FRIENDSHIP_TYPES, STYLES
  } = require('../immutable/constants');
const { expandReachToRelativeCoordinates } = require('../lib/core');
const bulletMethods = require('./bullet');
const coordinateMethods = require('./coordinate');
const effectMethods = require('./effect');
const effectLogMethods = require('./effect-log');
const geometricApis = require('./geometric-apis');
const {
  coordinateToLocation,
  coordinateToRectangle,
  locationToCoordinate,
  locationToRectangle,
  rectangleToCoordinate,
  rectangleToLocation,
} = geometricApis;  // Expand only useful methods
const locationMethods = require('./location');
const rectangleMethods = require('./rectangle');
const squareMatrixMethods = require('./square-matrix');
const unitMethods = require('./unit');

/**
 * @param {State~Unit} unit
 * @return {State~Location}
 */
const getUnitPositionAsLocation = (unit) => {
  if (unit.location) {
    return unit.location;
  } else if (unit.placement.boardType === BOARD_TYPES.BATTLE_BOARD && unit.placement.coordinate) {
    return coordinateToLocation(unit.placement.coordinate);
  }
  throw new Error(`The unit does not have either \`location\` or \`placement\``);
};

/**
 * @param {State~Unit} actor
 * @param {State~Unit} targetedUnit
 * @param {State~Coordinate} endPointCoordinate
 * @return {?State~Coordinate}
 */
const choiceClosestCoordinateUnderTargetedUnit = (actor, targetedUnit, endPointCoordinate) => {
  const actorLocation = getUnitPositionAsLocation(actor);
  const targetedUnitLocation = getUnitPositionAsLocation(targetedUnit);
  const targetedUnitRectangle = locationToRectangle(targetedUnitLocation);

  const candidates = geometricApis.findCoordinatesWhereRectangleOverlaps(
    targetedUnitRectangle, endPointCoordinate);

  // Since enemies can only move up / down / left / right,
  //   it should not be more than 2 coordinates.
  // Ref) calculateMovementResults
  if (candidates.length !== 1 && candidates.length !== 2) {
    throw new Error('Enemy position is wrong');
  }

  // Sort in order of closeness
  return candidates.sort((a, b) => {
    const aLocation = coordinateToLocation(a);
    const bLocation = coordinateToLocation(b);
    const distanceToA = locationMethods.measureDistance(actorLocation, aLocation);
    const distanceToB = locationMethods.measureDistance(actorLocation, bLocation);

    if (distanceToA < distanceToB) {
      return -1;
    } else if (distanceToA > distanceToB) {
      return 1;
    }
    return 0;
  })[0] || null
};

/**
 * @return {State~Square[]}
 */
const findSquaresFromBoardsByPlacement = (placement, ...boards) => {
  const squares = [];

  boards
    .filter(board => placement.boardType === board.boardType)
    .forEach(board => {
      const square = squareMatrixMethods.findSquareByCoordinate(board.squareMatrix, placement.coordinate);
      if (square) squares.push(square);
    })
  ;

  return squares;
};

/**
 * @param {State~Placement} placement
 * @param {...State~Board} boards
 * @throws {Error} Found multiple squares
 * @return {?State~Square}
 */
const findOneSquareFromBoardsByPlacement = (placement, ...boards) => {
  const squares = findSquaresFromBoardsByPlacement(placement, ...boards);

  if (squares.length > 1) {
    throw new Error(`There are multiple squares found by the placement of ${ JSON.stringify(placement) }`);
  }

  return squares[0] || null;
};

/**
 * @param {State~Unit} unit
 * @return {boolean}
 */
const isUnitInBattle = (unit) => {
  return (unit.location || unit.placement.boardType === BOARD_TYPES.BATTLE_BOARD) && unit.hitPoints > 0;
};

/**
 * @param {string} actFriendshipType - One of FRIENDSHIP_TYPES
 * @param {string} actorFactionType - One of FACTION_TYPES
 * @return {string[]} Some of FACTION_TYPES
 */
const judgeAffectableFractionTypes = (actFriendshipType, actorFactionType) => {
  if (actFriendshipType === FRIENDSHIP_TYPES.FRIENDLY) {
    return [actorFactionType];
  } else if (actFriendshipType === FRIENDSHIP_TYPES.UNFRIENDLY) {
    if (actorFactionType === FACTION_TYPES.ALLY) {
      return [FACTION_TYPES.ENEMY];
    } else if (actorFactionType === FACTION_TYPES.ENEMY) {
      return [FACTION_TYPES.ALLY];
    }
  }

  throw new Error(`Invalid actFriendshipType=${ actFriendshipType } or actorFactionType=${ actorFactionType }`);
};

/**
 * @param {State~Unit} actor - Only those in battle
 * @param {Act} act
 * @param {State~Unit} unit - Only those in battle
 * @return {boolean}
 */
const willActorAimActAtUnit = (actor, act, unit) => {
  return act.friendshipType === unitMethods.determineFriendship(actor, unit);
};

/**
 * @param {State~Unit} actor - Only those in battle
 * @param {Act} act
 * @param {State~Unit} target - Only those in battle
 * @return {boolean}
 */
const canActorAimActAtTargetedUnit = (actor, act, target) => {
  if (act.aimRange.type === ACT_AIM_RANGE_TYPES.REACHABLE) {
    // TODO: 暗黙的にユニットのサイズをマス目と同じとしているが、これで問題ないのか不明。
    const actorLocation = getUnitPositionAsLocation(actor);
    const reachableRects = geometricApis.createReachableRectangles(actorLocation, act.aimRange.reach);
    const targetLocation = getUnitPositionAsLocation(target);
    const targetRect = locationToRectangle(targetLocation);
    return reachableRects.some(rect => areBoxesOverlapping(rect, targetRect));
  }

  throw new Error(`Invalid aim-range-type`);
};

/**
 * @param {State~Unit} actor
 * @param {Function} act
 * @param {State~Unit[]} units
 * @return {?State~Unit}
 */
const choiceAimedUnit = (actor, act, units) => {
  const aimableUnits = units
    .filter(unit => willActorAimActAtUnit(actor, act, unit))
    .filter(unit => canActorAimActAtTargetedUnit(actor, act, unit));

  const actorLocation = getUnitPositionAsLocation(actor);

  aimableUnits.sort((a, b) => {
    const aLocation = getUnitPositionAsLocation(a);
    const bLocation = getUnitPositionAsLocation(b);

    // 1st: Sort by closest
    const actorToA = locationMethods.measureDistance(actorLocation, aLocation);
    const actorToB = locationMethods.measureDistance(actorLocation, bLocation);
    if (actorToA < actorToB) {
      return -1;
    } else if (actorToA > actorToB) {
      return 1;
    }

    // 2nd; Sort by clock-wise
    const angleA = locationMethods.measureAngleWithTopAsZero(actorLocation, aLocation);
    const angleB = locationMethods.measureAngleWithTopAsZero(actorLocation, bLocation);
    if (angleA === null || angleA < angleB) {
      return -1;
    } else if (angleB === null || angleA > angleB) {
      return 1;
    }

    return 0;
  });

  return aimableUnits[0] || null;
};

/**
 * Create bullets carrying effects on each
 * @param {State~Unit} actor
 * @param {Function} act
 * @param {State~Unit} aimedUnit
 * @param {State~Coordinate} squareMatrixEndPointCoordinate
 * @param {(Object|undefined)} options
 * @param {?State~Effect} [options.effect] - Pass the effect from the outside. It is mainly for testing.
 * @return {State~Bullet[]}
 */
const fireBullets = (actor, act, aimedUnit, squareMatrixEndPointCoordinate, options = {}) => {
  const defaultedOptions = Object.assign({
    effect: null,
  }, options);

  const bullets = [];

  const actorLocation = getUnitPositionAsLocation(actor);
  const aimedUnitLocation = getUnitPositionAsLocation(aimedUnit);

  const fromLocation = locationMethods.calculateCenterOfSquare(actorLocation);

  let toLocation;
  if (act.effectRange.type === ACT_EFFECT_RANGE_TYPES.UNIT) {
    toLocation = locationMethods.calculateCenterOfSquare(aimedUnitLocation);
  } else {
    toLocation = locationMethods.calculateCenterOfSquare(
      coordinateToLocation(
        choiceClosestCoordinateUnderTargetedUnit(actor, aimedUnit, squareMatrixEndPointCoordinate)
      )
    );
  }

  const direction = locationMethods.measureAngleAsEffectDirection(fromLocation, toLocation);

  let effect;
  if (options.effect) {
    effect = options.effect;
  } else {
    const effectOptions = {
      damagePoints: act.effectParameters.damagePoints,
      healingPoints: act.effectParameters.healingPoints,
    };
    if (act.effectRange.type === ACT_EFFECT_RANGE_TYPES.UNIT) {
      effectOptions.aimedUnitUid = aimedUnit.uid;
    } else {
      effectOptions.relativeCoordinates = act.expandEffectRangeToRelativeCoordinates(direction);
    }

    effect = effectMethods.createNewEffectState(
      judgeAffectableFractionTypes(act.friendshipType, actor.factionType),
      toLocation,
      effectOptions
    );
  }

  bullets.push(bulletMethods.createNewBulletState(fromLocation, toLocation, act.bullet.speed, effect));

  return bullets;
};

/**
 * @param {State~Effect} effect
 * @param {State~Unit} unit
 * @return {{newUnit, effectLogs}}
 */
const applyEffectToUnit = (effect, unit) => {
  let newUnit = Object.assign({}, unit);
  const effectLogs = [];

  const log = (options) => {
    effectLogs.push(effectLogMethods.createNewEffectLogState(unit.uid, options));
  };

  // Healing
  if (effect.healingPoints > 0) {
    const result = unitMethods.calculateHealing(unit, effect.healingPoints);

    newUnit = Object.assign(newUnit, { hitPoints: result.hitPoints });
    log({ healingPoints: result.healingPoints });
  }

  // Damaging
  if (effect.damagePoints > 0) {
    const result = unitMethods.calculateDamage(unit, effect.damagePoints);

    newUnit = Object.assign(newUnit, { hitPoints: result.hitPoints });
    log({ damagePoints: result.damagePoints });
  }

  return {
    newUnit,
    effectLogs,
  };
};

/**
 * Apply effect to units within the effective range
 * @param {State~Effect} effect
 * @param {State~Unit[]} units
 * @return {{units:<State~Unit[]>, effectLogs:<State~EffectLog[]>}}
 */
const effectOccurs = (effect, units) => {
  const effectLogs = [];

  const effectiveRectangles = effectMethods.createEffectiveRectangles(effect);

  const newUnits = units.map(unit => {
    if (!isUnitInBattle(unit)) return unit;

    const unitLocation = getUnitPositionAsLocation(unit);
    const unitRectangle = locationToRectangle(unitLocation);

    if (effect.affectableFractionTypes.indexOf(unit.factionType) > -1) {
      if (
        (
          effect.aimedUnitUid &&
          unit.uid === effect.aimedUnitUid &&
          areBoxesOverlapping(
            // TODO: bullet size
            locationToRectangle(effect.impactedLocation, { width: 4, height: 4, asCenterPoint: true }),
            locationToRectangle(unitLocation)
          )
        )
        ||
        (
          effectiveRectangles.some(rect => areBoxesOverlapping(rect, unitRectangle))
        )
      ) {
        const resultApplied = applyEffectToUnit(effect, unit);

        resultApplied.effectLogs.forEach(v => effectLogs.push(v));

        return resultApplied.newUnit;
      }
    }

    return unit;
  });

  return {
    units: newUnits,
    effectLogs,
  };
};

/**
 * Compute the state transition during the "tick"
 * <section>
 *   The "tick" is a coined word, which means so-called "one game loop".
 * </section>
 * @param {Object} state - A plain object generated from `store.getState()`
 * @return {Object}
 */
const computeTick = ({ allies, enemies, bullets, battleBoard, gameStatus }) => {
  const battleBoardEndPointCoordinate = squareMatrixMethods.getEndPointCoordinate(battleBoard.squareMatrix);

  let newBullets = bullets.slice();
  let newAllies = allies.slice();
  let newEnemies = enemies.slice();

  // Send dead enemies to the graveyard
  // TODO: Move to another part of the state instead of delete.
  newEnemies = newEnemies.filter(unitMethods.isAlive);

  // Send dead allies to the sortie board
  // TODO

  // Bullets movement and effect
  newBullets = newBullets
    // Apply effect & Clean effect occured bullets
    .filter(bullet => {
      if (!bulletMethods.isArrivedToDestination(bullet)) {
        return true;
      };

      // TODO: effectLogs
      const effectResult = effectOccurs(bullet.effect, newAllies.concat(newEnemies));
      newAllies = effectResult.units.filter(unit => unit.factionType === FACTION_TYPES.ALLY);
      newEnemies = effectResult.units.filter(unit => unit.factionType === FACTION_TYPES.ENEMY);

      return false;
    })
    // Bullets move
    //   The reason for the movement after the effect calculation is to guarantee landing on the display.
    .map(bullet => {
      return Object.assign({}, bullet, {
        location: bulletMethods.calculateNextLocation(bullet),
      });
    })
  ;

  // Separate dead units in this tick
  let alivedEnemies = newEnemies.filter(unitMethods.isAlive);
  let deadEnemies = newEnemies.filter(unitMethods.isDead);

  // Enemy's movement
  alivedEnemies = alivedEnemies.map(enemy => {
    const { location, destinationIndex } = unitMethods.calculateMovementResults(enemy);

    return Object.assign({}, enemy, {
      location,
      destinationIndex,
    });
  });

  // Ally's act
  newAllies = newAllies.map(ally => {
    const newAlly = Object.assign({}, ally);

    if (!isUnitInBattle(newAlly)) {
      // TODO: 出撃ポイントの回復
      return newAlly;
    }

    const act = unitMethods.getAct(newAlly);

    let didAct = false;

    if (unitMethods.canDoAct(newAlly, act)) {
      const aimedUnit = choiceAimedUnit(newAlly, act, newAllies.concat(alivedEnemies));

      if (aimedUnit) {
        if (config.isEnabledTickLog) {
          console.debug(`${ newAlly.factionType }:${ newAlly.jobId } aims ${ act.id } at ${ aimedUnit.factionType }:${ aimedUnit.jobId }`);
        }

        // Create bullets carrying effect on each
        newBullets = newBullets.concat(fireBullets(newAlly, act, aimedUnit, battleBoardEndPointCoordinate));

        // Comsume APs
        newAlly.actionPoints = unitMethods.calculateActionPointsConsumption(newAlly, act);

        didAct = true;
      }
    }

    if (!didAct) {
      // Recover APs
      newAlly.actionPoints = unitMethods.calculateActionPointsRecovery(newAlly);
    }

    return newAlly;
  });

  // Enemy's act
  // TODO

  return {
    allies: newAllies,
    enemies: alivedEnemies.concat(deadEnemies),
    bullets: newBullets,
  };
};

module.exports = {
  applyEffectToUnit,
  canActorAimActAtTargetedUnit,
  choiceAimedUnit,
  choiceClosestCoordinateUnderTargetedUnit,
  computeTick,
  effectOccurs,
  findOneSquareFromBoardsByPlacement,
  fireBullets,
  judgeAffectableFractionTypes,
  willActorAimActAtUnit,
};
<|endoftext|>


<commit_before>/**
* XPM specific functions
*/
(function($) {
	// initialize global variable
	SDL_ENV.isInXpm = false;

	// check and set global variable
	$.fn.checkIfInXpm = function() {
		// check via availability of Tridion objects
		if (typeof Tridion != "undefined" && typeof Tridion.Web.UI.SiteEdit != "undefined") {
			console.log("in XPM");
			SDL_ENV.isInXpm = true;

			// display xpm buttons
			$(".xpm-button").show();

			// pause automatic scrolling of carousels
			$(".carousel").carousel("pause");
		} else {
			console.log("not in XPM");
			SDL_ENV.isInXpm = false;

			// hide xpm buttons
			$(".xpm-button").hide();
		}
	};
}(jQuery));

$(document).ready(function() {
	// wait 5 seconds after document ready (to complete XPM load) and check if we are in XPM (setting global variable)
	setTimeout(function() {
		$().checkIfInXpm();
	}, 5000);
});
<commit_msg>TSF-557 added check on constant for SFXPM (which should also be available in 8.1), so this should now work for both 7.1, 8.0 and 8.1 already.<commit_after>/**
* XPM specific functions
*/
(function($) {
	// initialize global variable
	SDL_ENV.isInXpm = false;

	// check and set global variable
	$.fn.checkIfInXpm = function() {
		// check via availability of Tridion objects
		if (typeof Tridion != "undefined" && (typeof Tridion.Web.UI.SiteEdit != "undefined" || Tridion.Constants.IsInXPM)) {
			console.log("in XPM");
			SDL_ENV.isInXpm = true;

			// display xpm buttons
			$(".xpm-button").show();

			// pause automatic scrolling of carousels
			$(".carousel").carousel("pause");
		} else {
			console.log("not in XPM");
			SDL_ENV.isInXpm = false;

			// hide xpm buttons
			$(".xpm-button").hide();
		}
	};
}(jQuery));

$(document).ready(function() {
	// wait 5 seconds after document ready (to complete XPM load) and check if we are in XPM (setting global variable)
	setTimeout(function() {
		$().checkIfInXpm();
	}, 5000);
});
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import ResizeHandle from 'shared/components/ResizeHandle'
import classnames from 'classnames'

const {node, number, string} = PropTypes

const maximumNumChildren = 2

const ResizeContainer = React.createClass({
  propTypes: {
    children: node.isRequired,
    containerClass: string.isRequired,
    minTopHeight: number,
    minBottomHeight: number,
  },

  getDefaultProps() {
    return {
      minTopHeight: 200,
      minBottomHeight: 200,
    }
  },

  getInitialState() {
    return {
      topHeight: '60%',
      bottomHeight: '40%',
      isDragging: false,
    }
  },

  handleStopDrag() {
    this.setState({isDragging: false})
  },

  handleStartDrag() {
    this.setState({isDragging: true})
  },

  handleMouseLeave() {
    this.setState({isDragging: false})
  },

  handleDrag(e) {
    if (!this.state.isDragging) {
      return
    }

    const {minTopHeight, minBottomHeight} = this.props
    const oneHundred = 100
    const containerHeight = parseInt(
      getComputedStyle(this.refs.resizeContainer).height,
      10
    )
    // verticalOffset moves the resize handle as many pixels as the page-heading is taking up.
    const verticalOffset = window.innerHeight - containerHeight
    const newTopPanelPercent = Math.ceil(
      (e.pageY - verticalOffset) / containerHeight * oneHundred
    )
    const newBottomPanelPercent = oneHundred - newTopPanelPercent

    // Don't trigger a resize unless the change in size is greater than minResizePercentage
    const minResizePercentage = 0.5
    if (
      Math.abs(newTopPanelPercent - parseFloat(this.state.topHeight)) <
      minResizePercentage
    ) {
      return
    }

    const topHeightPixels = newTopPanelPercent / oneHundred * containerHeight
    const bottomHeightPixels = newBottomPanelPercent / oneHundred * containerHeight

    // Don't trigger a resize if the new sizes are too small
    if (
      topHeightPixels < minTopHeight ||
      bottomHeightPixels < minBottomHeight
    ) {
      return
    }

    this.setState({
      topHeight: `${newTopPanelPercent}%`,
      bottomHeight: `${newBottomPanelPercent}%`,
    })
  },

  renderHandle() {
    const {isDragging, topHeight} = this.state
    return (
      <ResizeHandle
        isDragging={isDragging}
        onHandleStartDrag={this.handleStartDrag}
        top={topHeight}
      />
    )
  },

  render() {
    const {topHeight, bottomHeight, isDragging} = this.state
    const {containerClass, children} = this.props

    if (children.length > maximumNumChildren) {
      console.error(`There cannot be more than ${maximumNumChildren}' children in ResizeContainer`)
      return
    }

    return (
      <div
        className={classnames(`resize--container ${containerClass}`, {'resize--dragging': isDragging})}
        onMouseLeave={this.handleMouseLeave}
        onMouseUp={this.handleStopDrag}
        onMouseMove={this.handleDrag}
        ref="resizeContainer"
      >

          {React.cloneElement(children[0])}

        {this.renderHandle()}

          {React.cloneElement(children[1])}

    )
  },
})

export default ResizeContainer
<commit_msg>User proper means of calculating number of children<commit_after>import React, {PropTypes} from 'react'
import ResizeHandle from 'shared/components/ResizeHandle'
import classnames from 'classnames'

const {node, number, string} = PropTypes

const maximumNumChildren = 2

const ResizeContainer = React.createClass({
  propTypes: {
    children: node.isRequired,
    containerClass: string.isRequired,
    minTopHeight: number,
    minBottomHeight: number,
  },

  getDefaultProps() {
    return {
      minTopHeight: 200,
      minBottomHeight: 200,
    }
  },

  getInitialState() {
    return {
      topHeight: '60%',
      bottomHeight: '40%',
      isDragging: false,
    }
  },

  handleStopDrag() {
    this.setState({isDragging: false})
  },

  handleStartDrag() {
    this.setState({isDragging: true})
  },

  handleMouseLeave() {
    this.setState({isDragging: false})
  },

  handleDrag(e) {
    if (!this.state.isDragging) {
      return
    }

    const {minTopHeight, minBottomHeight} = this.props
    const oneHundred = 100
    const containerHeight = parseInt(
      getComputedStyle(this.refs.resizeContainer).height,
      10
    )
    // verticalOffset moves the resize handle as many pixels as the page-heading is taking up.
    const verticalOffset = window.innerHeight - containerHeight
    const newTopPanelPercent = Math.ceil(
      (e.pageY - verticalOffset) / containerHeight * oneHundred
    )
    const newBottomPanelPercent = oneHundred - newTopPanelPercent

    // Don't trigger a resize unless the change in size is greater than minResizePercentage
    const minResizePercentage = 0.5
    if (
      Math.abs(newTopPanelPercent - parseFloat(this.state.topHeight)) <
      minResizePercentage
    ) {
      return
    }

    const topHeightPixels = newTopPanelPercent / oneHundred * containerHeight
    const bottomHeightPixels = newBottomPanelPercent / oneHundred * containerHeight

    // Don't trigger a resize if the new sizes are too small
    if (
      topHeightPixels < minTopHeight ||
      bottomHeightPixels < minBottomHeight
    ) {
      return
    }

    this.setState({
      topHeight: `${newTopPanelPercent}%`,
      bottomHeight: `${newBottomPanelPercent}%`,
    })
  },

  renderHandle() {
    const {isDragging, topHeight} = this.state
    return (
      <ResizeHandle
        isDragging={isDragging}
        onHandleStartDrag={this.handleStartDrag}
        top={topHeight}
      />
    )
  },

  render() {
    const {topHeight, bottomHeight, isDragging} = this.state
    const {containerClass, children} = this.props

    if (React.Children.count(children) > maximumNumChildren) {
      console.error(`There cannot be more than ${maximumNumChildren}' children in ResizeContainer`)
      return
    }

    return (
      <div
        className={classnames(`resize--container ${containerClass}`, {'resize--dragging': isDragging})}
        onMouseLeave={this.handleMouseLeave}
        onMouseUp={this.handleStopDrag}
        onMouseMove={this.handleDrag}
        ref="resizeContainer"
      >

          {React.cloneElement(children[0])}

        {this.renderHandle()}

          {React.cloneElement(children[1])}

    )
  },
})

export default ResizeContainer
<|endoftext|>


<commit_before>export const LINEAR = '(linear)'
export const NONE = '(none)'
export const NULL = '(null)'
export const NUMBER = '(number)'
export const PREVIOUS = '(previous)'

export const QUERY_FILL_OPTIONS = [NULL, PREVIOUS, NUMBER, NONE, LINEAR]
<commit_msg>Remove parens from queryFill options<commit_after>export const LINEAR = 'linear'
export const NONE = 'none'
export const NULL = 'null'
export const NUMBER = 'number'
export const PREVIOUS = 'previous'

export const QUERY_FILL_OPTIONS = [NULL, PREVIOUS, NUMBER, NONE, LINEAR]
<|endoftext|>


<commit_before>// Unit test for a.route

module('core/route.js', {
    setup: function() {
        hashtag('');
    },
    teardown: function() {
        hashtag('');
    }
});

// Test entering route system
asyncTest('a.route.enter', function() {
    expect(1);

    // Dummy function to test entering route element
    function checkRoute(hash) {
        strictEqual(hash, 'unittest-route1', 'Test entering');
    };

    // Binding function to route
    a.route.bind('unittest-route1', checkRoute);

    chain('unittest-route1', function() {
        hashtag('unittest-noroute1');
    });

    chain('unittest-noroute1', function() {
        a.route.unbind('unittest-route1', checkRoute);
        start();
    }, 100);

    hashtag('unittest-route1');
});

// Test leaving route system
asyncTest('a.route.leave', function() {
    expect(1);

    // Dummy function to test leaving route element
    function checkRoute(hash) {
        strictEqual(hash, 'unittest-route2', 'Test leaving');
    };

    // Binding function to route
    a.route.bind('unittest-route2', checkRoute, 'leave');

    chain('unittest-route2', function() {
        hashtag('unittest-noroute2');
    });

    chain('unittest-noroute2', function() {
        a.route.unbind('unittest-route2', checkRoute, 'leave');
        start();
    }, 100);

    // Starting system
    hashtag('unittest-route2');
});

// Test entering - otherwise system
asyncTest('a.route.enter-otherwise', function() {
    expect(2);

    // Dummy function to test entering route element
    function checkOtherwise() {
        strictEqual(1, 1, 'Test otherwise enter');
    };

    // Binding function to route
    a.route.otherwise(checkOtherwise);

    chain('unittest-route-otherwise1', function() {
        hashtag('unittest-route-nootherwise1');
    });

    chain('unittest-route-nootherwise1', function() {
       a.route.otherwise(null);
        start();
    }, 100);

    hashtag('unittest-route-otherwise1');
});

// Test leaving - otherwise system
asyncTest('a.route.leaving-otherwise', function() {
    expect(2);

    // Dummy function to test leaving route element
    function checkOtherwise() {
        strictEqual(1, 1, 'Test otherwise leaving');
    };

    // Binding function to route
    a.route.otherwise(checkOtherwise, 'leave');

    chain('unittest-route-otherwise2', function() {
        hashtag('unittest-route-nootherwise2');
    });

    chain('unittest-route-nootherwise2', function() {
        a.route.otherwise(null, 'leave');
        start();
    }, 100);

    hashtag('unittest-route-otherwise2');
});

asyncTest('a.route.fake', function() {
    expect(1);

    // Dummy function to test entering route element
    function checkRoute(hash) {
        strictEqual(hash, 'unittest-route3', 'Test entering');
        a.route.unbind('unittest-route3', checkRoute);
        start();
    };

    // Binding function to route
    a.route.bind('unittest-route3', checkRoute);
    a.route.fake('unittest-route3');
});<commit_msg>Making route.js now compatible with new QUnit rules.<commit_after>// Unit test for a.route

QUnit.module('core/route.js', {
    setup: function() {
        hashtag('');
        QAppStorm.clear();
    },
    teardown: function() {
        hashtag('');
        QAppStorm.clear();
    }
});

// Test entering route system
QUnit.asyncTest('a.route.enter', function(assert) {
    assert.expect(1);

    // Dummy function to test entering route element
    function checkRoute(hash) {
        QAppStorm.pop();
        assert.strictEqual(hash, 'unittest-route1', 'Test entering');
    };

    // Binding function to route
    a.route.bind('unittest-route1', checkRoute);

    QAppStorm.chain({
        hash: 'unittest-route1',
        expect: 0
    }, {
        hash: 'unittest-noroute1',
        expect: 1,
        callback: function(chain) {
            a.route.unbind('unittest-route1', checkRoute);
            chain.next();
        }
    });
});

// Test leaving route system
QUnit.asyncTest('a.route.leave', function(assert) {
    assert.expect(1);

    // Dummy function to test leaving route element
    function checkRoute(hash) {
        QAppStorm.pop();
        assert.strictEqual(hash, 'unittest-route2', 'Test leaving');
    };

    // Binding function to route
    a.route.bind('unittest-route2', checkRoute, 'leave');

    QAppStorm.chain({
        hash: 'unittest-route2',
        expect: 0
    }, {
        hash: 'unittest-noroute2',
        expect: 1,
        callback: function(chain) {
            a.route.unbind('unittest-route2', checkRoute);
            chain.next();
        }
    });
});

// Test entering - otherwise system
QUnit.asyncTest('a.route.enter-otherwise', function(assert) {
    assert.expect(1);

    // Dummy function to test entering route element
    function checkOtherwise() {
        QAppStorm.pop();
        assert.strictEqual(1, 1, 'Test otherwise enter');
    };

    // Binding function to route
    a.route.otherwise(checkOtherwise);

    QAppStorm.chain({
        hash: 'unittest-route-otherwise1',
        expect: 0
    }, {
        hash: 'unittest-route-nootherwise1',
        expect: 1,
        callback: function(chain) {
            a.route.otherwise(null);
            chain.next();
        }
    });
});

// Test leaving - otherwise system
QUnit.asyncTest('a.route.leaving-otherwise', function(assert) {
    assert.expect(1);

    // Dummy function to test leaving route element
    function checkOtherwise() {
        QAppStorm.pop();
        assert.strictEqual(1, 1, 'Test otherwise leaving');
    };

    // Binding function to route
    a.route.otherwise(checkOtherwise, 'leave');

    QAppStorm.chain({
        hash: 'unittest-route-otherwise2',
        expect: 0
    }, {
        hash: 'unittest-route-nootherwise2',
        expect: 1,
        callback: function(chain) {
            a.route.otherwise(null, 'leave');
            chain.next();
        }
    });
});

QUnit.asyncTest('a.route.fake', function(assert) {
    assert.expect(1);

    // Dummy function to test entering route element
    function checkRoute(hash) {
        assert.strictEqual(hash, 'unittest-route3', 'Test entering');
        a.route.unbind('unittest-route3', checkRoute);
        QUnit.start();
    };

    // Binding function to route
    a.route.bind('unittest-route3', checkRoute);
    a.route.fake('unittest-route3');
});<|endoftext|>


<commit_before>TestCase("Croaker.OrFilter.Tests", {
  testEmptyOrFilterReturnsEmpty: function() {
    var orFilter = new croaker.OrFilter([]);

    assertThat(orFilter.filter(''), true);

  },

   setUp: function () {
    JsHamcrest.Integration.JsTestDriver();
    JsMockito.Integration.JsTestDriver();
  }
});<commit_msg>added test for OrFilter w/true element<commit_after>TestCase("Croaker.OrFilter.Tests", {
  testEmptyOrFilterReturnsEmpty: function() {
    var orFilter = new croaker.OrFilter([]);

    assertThat(orFilter.filter(''), true);
  },

  testSingleOrFilterElementReturnsTrue: function() {
    var filter = mock(croaker.StrainFilter),
      orFilter = new croaker.OrFilter([filter]);

      when(filter).filter('').thenReturn(true);
      assertThat(orFilter.filter(''), true);
  },

   setUp: function () {
    JsHamcrest.Integration.JsTestDriver();
    JsMockito.Integration.JsTestDriver();
  }
});<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview Code for the viewport.
 */
base.require('base.events');

base.exportTo('tracing', function() {

  /**
   * The TimelineViewport manages the transform used for navigating
   * within the timeline. It is a simple transform:
   *   x' = (x+pan) * scale
   *
   * The timeline code tries to avoid directly accessing this transform,
   * instead using this class to do conversion between world and viewspace,
   * as well as the math for centering the viewport in various interesting
   * ways.
   *
   * @constructor
   * @extends {base.EventTarget}
   */
  function TimelineViewport(parentEl) {
    this.parentEl_ = parentEl;
    this.modelTrackContainer_ = null;
    this.scaleX_ = 1;
    this.panX_ = 0;
    this.panY_ = 0;
    this.gridTimebase_ = 0;
    this.gridStep_ = 1000 / 60;
    this.gridEnabled_ = false;
    this.hasCalledSetupFunction_ = false;

    this.onResize_ = this.onResize_.bind(this);

    // The following code uses an interval to detect when the parent element
    // is attached to the document. That is a trigger to run the setup function
    // and install a resize listener.
    this.checkForAttachInterval_ = setInterval(
        this.checkForAttach_.bind(this), 250);

    this.markers = [];
  }

  TimelineViewport.prototype = {
    __proto__: base.EventTarget.prototype,

    /**
     * Allows initialization of the viewport when the viewport's parent element
     * has been attached to the document and given a size.
     * @param {Function} fn Function to call when the viewport can be safely
     * initialized.
     */
    setWhenPossible: function(fn) {
      this.pendingSetFunction_ = fn;
    },

    /**
     * @return {boolean} Whether the current timeline is attached to the
     * document.
     */
    get isAttachedToDocument_() {
      var cur = this.parentEl_;
      // Allow not providing a parent element, used by tests.
      if (cur === undefined)
        return;
      while (cur.parentNode)
        cur = cur.parentNode;
      return cur == this.parentEl_.ownerDocument;
    },

    onResize_: function() {
      this.dispatchChangeEvent();
    },

    /**
     * Checks whether the parentNode is attached to the document.
     * When it is, it installs the iframe-based resize detection hook
     * and then runs the pendingSetFunction_, if present.
     */
    checkForAttach_: function() {
      if (!this.isAttachedToDocument_ || this.clientWidth == 0)
        return;

      if (!this.iframe_) {
        this.iframe_ = document.createElement('iframe');
        this.iframe_.style.cssText =
            'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
        this.parentEl_.appendChild(this.iframe_);

        this.iframe_.contentWindow.addEventListener('resize', this.onResize_);
      }

      var curSize = this.parentEl_.clientWidth + 'x' +
          this.parentEl_.clientHeight;
      if (this.pendingSetFunction_) {
        this.lastSize_ = curSize;
        try {
          this.pendingSetFunction_();
        } catch (ex) {
          console.log('While running setWhenPossible:',
              ex.message ? ex.message + '\n' + ex.stack : ex.stack);
        }
        this.pendingSetFunction_ = undefined;
      }

      window.clearInterval(this.checkForAttachInterval_);
      this.checkForAttachInterval_ = undefined;
    },

    /**
     * Fires the change event on this viewport. Used to notify listeners
     * to redraw when the underlying model has been mutated.
     */
    dispatchChangeEvent: function() {
      base.dispatchSimpleEvent(this, 'change');
    },

    dispatchMarkersChangeEvent_: function() {
      base.dispatchSimpleEvent(this, 'markersChange');
    },

    detach: function() {
      if (this.checkForAttachInterval_) {
        window.clearInterval(this.checkForAttachInterval_);
        this.checkForAttachInterval_ = undefined;
      }
      if (this.iframe_) {
        this.iframe_.removeEventListener('resize', this.onResize_);
        this.parentEl_.removeChild(this.iframe_);
      }
    },

    getStateInViewCoordinates: function() {
      return {
        panX: this.xWorldVectorToView(this.panX),
        panY: this.panY,
        scaleX: this.scaleX
      };
    },

    setStateInViewCoordinates: function(state) {
      this.panX = this.xViewVectorToWorld(state.panX);
      this.panY = state.panY;
    },

    set modelTrackContainer(m) {
      this.modelTrackContainer_ = m;
    },

    get scaleX() {
      return this.scaleX_;
    },
    set scaleX(s) {
      var changed = this.scaleX_ != s;
      if (changed) {
        this.scaleX_ = s;
        this.dispatchChangeEvent();
      }
    },

    get panX() {
      return this.panX_;
    },
    set panX(p) {
      var changed = this.panX_ != p;
      if (changed) {
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    get panY() {
      return this.panY_;
    },
    set panY(p) {
      this.panY_ = p;
      this.modelTrackContainer_.scrollTop = p;
    },

    setPanAndScale: function(p, s) {
      var changed = this.scaleX_ != s || this.panX_ != p;
      if (changed) {
        this.scaleX_ = s;
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    xWorldToView: function(x) {
      return (x + this.panX_) * this.scaleX_;
    },

    xWorldVectorToView: function(x) {
      return x * this.scaleX_;
    },

    xViewToWorld: function(x) {
      return (x / this.scaleX_) - this.panX_;
    },

    xViewVectorToWorld: function(x) {
      return x / this.scaleX_;
    },

    xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
      if (typeof viewX == 'string') {
        if (viewX == 'left') {
          viewX = 0;
        } else if (viewX == 'center') {
          viewX = viewWidth / 2;
        } else if (viewX == 'right') {
          viewX = viewWidth - 1;
        } else {
          throw new Error('unrecognized string for viewPos. left|center|right');
        }
      }
      this.panX = (viewX / this.scaleX_) - worldX;
    },

    xPanWorldBoundsIntoView: function(worldMin, worldMax, viewWidth) {
      if (this.xWorldToView(worldMin) < 0)
        this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
      else if (this.xWorldToView(worldMax) > viewWidth)
        this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
    },

    xSetWorldBounds: function(worldMin, worldMax, viewWidth) {
      var worldWidth = worldMax - worldMin;
      var scaleX = viewWidth / worldWidth;
      var panX = -worldMin;
      this.setPanAndScale(panX, scaleX);
    },

    get gridEnabled() {
      return this.gridEnabled_;
    },

    set gridEnabled(enabled) {
      if (this.gridEnabled_ == enabled)
        return;

      this.gridEnabled_ = enabled && true;
      this.dispatchChangeEvent();
    },

    get gridTimebase() {
      return this.gridTimebase_;
    },

    set gridTimebase(timebase) {
      if (this.gridTimebase_ == timebase)
        return;
      this.gridTimebase_ = timebase;
      this.dispatchChangeEvent();
    },

    get gridStep() {
      return this.gridStep_;
    },

    applyTransformToCanvas: function(ctx) {
      ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
    },

    addMarker: function(positionWorld) {
      var marker = new ViewportMarker(this, positionWorld);
      this.markers.push(marker);
      this.dispatchChangeEvent();
      this.dispatchMarkersChangeEvent_();
      return marker;
    },

    removeMarker: function(marker) {
      for (var i = 0; i < this.markers.length; ++i) {
        if (this.markers[i] === marker) {
          this.markers.splice(i, 1);
          this.dispatchChangeEvent();
          this.dispatchMarkersChangeEvent_();
          return true;
        }
      }
    },

    findMarkerNear: function(positionWorld, nearnessInViewPixels) {
      // Converts pixels into distance in world.
      var nearnessThresholdWorld = this.xViewVectorToWorld(
          nearnessInViewPixels);
      for (var i = 0; i < this.markers.length; ++i) {
        if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
            nearnessThresholdWorld) {
          var marker = this.markers[i];
          return marker;
        }
      }
      return undefined;
    },

    drawGridLines: function(ctx, viewLWorld, viewRWorld) {
      if (!this.gridEnabled)
        return;

      var x = this.gridTimebase;

      ctx.beginPath();
      while (x < viewRWorld) {
        if (x >= viewLWorld) {
          // Do conversion to viewspace here rather than on
          // x to avoid precision issues.
          var vx = this.xWorldToView(x);
          ctx.moveTo(vx, 0);
          ctx.lineTo(vx, ctx.canvas.height);
        }
        x += this.gridStep;
      }
      ctx.strokeStyle = 'rgba(255,0,0,0.25)';
      ctx.stroke();
    },

    drawMarkerArrows: function(ctx, viewLWorld, viewRWorld, drawHeight) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
                                      ctx.canvas.height, drawHeight, this);
      }
    },

    drawMarkerLines: function(ctx, viewLWorld, viewRWorld) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawLine(ctx, viewLWorld, viewRWorld,
            ctx.canvas.height, this);
      }
    }
  };

  /**
   * Represents a marked position in the world, at a viewport level.
   * @constructor
   */
  function ViewportMarker(vp, positionWorld) {
    this.viewport_ = vp;
    this.positionWorld_ = positionWorld;
    this.selected_ = false;
  }

  ViewportMarker.prototype = {
    get positionWorld() {
      return this.positionWorld_;
    },

    set positionWorld(positionWorld) {
      this.positionWorld_ = positionWorld;
      this.viewport_.dispatchChangeEvent();
    },

    set selected(selected) {
      this.selected_ = selected;
      this.viewport_.dispatchChangeEvent();
    },

    get selected() {
      return this.selected_;
    },

    get color() {
      if (this.selected)
        return 'rgb(255,0,0)';
      return 'rgb(0,0,0)';
    },

    drawTriangle_: function(ctx, viewLWorld, viewRWorld,
                            canvasH, rulerHeight, vp) {
      ctx.beginPath();

      var ts = this.positionWorld_;
      if (ts < viewLWorld || ts > viewRWorld)
        return;

      var viewX = vp.xWorldToView(ts);
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX - 3, rulerHeight / 2);
      ctx.lineTo(viewX + 3, rulerHeight / 2);
      ctx.lineTo(viewX, rulerHeight);
      ctx.closePath();
      ctx.fillStyle = this.color;
      ctx.fill();

      if (rulerHeight === canvasH)
        return;

      // Draw line from bottom of triangle to the bottom of our canvas.
      ctx.beginPath();
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX, canvasH);
      ctx.closePath();
      ctx.strokeStyle = this.color;
      ctx.stroke();
    },

    drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
      ctx.beginPath();
      var ts = this.positionWorld_;
      if (ts >= viewLWorld && ts < viewRWorld) {
        var viewX = vp.xWorldToView(ts);
        ctx.moveTo(viewX, 0);
        ctx.lineTo(viewX, canvasH);
      }
      ctx.strokeStyle = this.color;
      ctx.stroke();
    }
  };

  return {
    TimelineViewport: TimelineViewport,
    ViewportMarker: ViewportMarker
  };
});
<commit_msg>If you scroll the model track container with your mouse wheel the panY value doesn't update to match. When panning / scanning the view jumps to the last known panY value.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview Code for the viewport.
 */
base.require('base.events');

base.exportTo('tracing', function() {

  /**
   * The TimelineViewport manages the transform used for navigating
   * within the timeline. It is a simple transform:
   *   x' = (x+pan) * scale
   *
   * The timeline code tries to avoid directly accessing this transform,
   * instead using this class to do conversion between world and viewspace,
   * as well as the math for centering the viewport in various interesting
   * ways.
   *
   * @constructor
   * @extends {base.EventTarget}
   */
  function TimelineViewport(parentEl) {
    this.parentEl_ = parentEl;
    this.modelTrackContainer_ = null;
    this.scaleX_ = 1;
    this.panX_ = 0;
    this.panY_ = 0;
    this.gridTimebase_ = 0;
    this.gridStep_ = 1000 / 60;
    this.gridEnabled_ = false;
    this.hasCalledSetupFunction_ = false;

    this.onResize_ = this.onResize_.bind(this);
    this.onModelTrackControllerScroll_ =
        this.onModelTrackControllerScroll_.bind(this);

    // The following code uses an interval to detect when the parent element
    // is attached to the document. That is a trigger to run the setup function
    // and install a resize listener.
    this.checkForAttachInterval_ = setInterval(
        this.checkForAttach_.bind(this), 250);

    this.markers = [];
  }

  TimelineViewport.prototype = {
    __proto__: base.EventTarget.prototype,

    /**
     * Allows initialization of the viewport when the viewport's parent element
     * has been attached to the document and given a size.
     * @param {Function} fn Function to call when the viewport can be safely
     * initialized.
     */
    setWhenPossible: function(fn) {
      this.pendingSetFunction_ = fn;
    },

    /**
     * @return {boolean} Whether the current timeline is attached to the
     * document.
     */
    get isAttachedToDocument_() {
      var cur = this.parentEl_;
      // Allow not providing a parent element, used by tests.
      if (cur === undefined)
        return;
      while (cur.parentNode)
        cur = cur.parentNode;
      return cur == this.parentEl_.ownerDocument;
    },

    onResize_: function() {
      this.dispatchChangeEvent();
    },

    /**
     * Checks whether the parentNode is attached to the document.
     * When it is, it installs the iframe-based resize detection hook
     * and then runs the pendingSetFunction_, if present.
     */
    checkForAttach_: function() {
      if (!this.isAttachedToDocument_ || this.clientWidth == 0)
        return;

      if (!this.iframe_) {
        this.iframe_ = document.createElement('iframe');
        this.iframe_.style.cssText =
            'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
        this.parentEl_.appendChild(this.iframe_);

        this.iframe_.contentWindow.addEventListener('resize', this.onResize_);
      }

      var curSize = this.parentEl_.clientWidth + 'x' +
          this.parentEl_.clientHeight;
      if (this.pendingSetFunction_) {
        this.lastSize_ = curSize;
        try {
          this.pendingSetFunction_();
        } catch (ex) {
          console.log('While running setWhenPossible:',
              ex.message ? ex.message + '\n' + ex.stack : ex.stack);
        }
        this.pendingSetFunction_ = undefined;
      }

      window.clearInterval(this.checkForAttachInterval_);
      this.checkForAttachInterval_ = undefined;
    },

    /**
     * Fires the change event on this viewport. Used to notify listeners
     * to redraw when the underlying model has been mutated.
     */
    dispatchChangeEvent: function() {
      base.dispatchSimpleEvent(this, 'change');
    },

    dispatchMarkersChangeEvent_: function() {
      base.dispatchSimpleEvent(this, 'markersChange');
    },

    detach: function() {
      if (this.checkForAttachInterval_) {
        window.clearInterval(this.checkForAttachInterval_);
        this.checkForAttachInterval_ = undefined;
      }
      if (this.iframe_) {
        this.iframe_.removeEventListener('resize', this.onResize_);
        this.parentEl_.removeChild(this.iframe_);
      }
    },

    getStateInViewCoordinates: function() {
      return {
        panX: this.xWorldVectorToView(this.panX),
        panY: this.panY,
        scaleX: this.scaleX
      };
    },

    setStateInViewCoordinates: function(state) {
      this.panX = this.xViewVectorToWorld(state.panX);
      this.panY = state.panY;
    },

    onModelTrackControllerScroll_: function(e) {
      this.panY_ = this.modelTrackContainer_.scrollTop;
    },

    set modelTrackContainer(m) {

      if (this.modelTrackContainer_)
        this.modelTrackContainer_.removeEventListener('scroll',
            this.onModelTrackControllerScroll_);

      this.modelTrackContainer_ = m;
      this.modelTrackContainer_.addEventListener('scroll',
          this.onModelTrackControllerScroll_);
    },

    get scaleX() {
      return this.scaleX_;
    },
    set scaleX(s) {
      var changed = this.scaleX_ != s;
      if (changed) {
        this.scaleX_ = s;
        this.dispatchChangeEvent();
      }
    },

    get panX() {
      return this.panX_;
    },
    set panX(p) {
      var changed = this.panX_ != p;
      if (changed) {
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    get panY() {
      return this.panY_;
    },
    set panY(p) {
      this.panY_ = p;
      this.modelTrackContainer_.scrollTop = p;
    },

    setPanAndScale: function(p, s) {
      var changed = this.scaleX_ != s || this.panX_ != p;
      if (changed) {
        this.scaleX_ = s;
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    xWorldToView: function(x) {
      return (x + this.panX_) * this.scaleX_;
    },

    xWorldVectorToView: function(x) {
      return x * this.scaleX_;
    },

    xViewToWorld: function(x) {
      return (x / this.scaleX_) - this.panX_;
    },

    xViewVectorToWorld: function(x) {
      return x / this.scaleX_;
    },

    xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
      if (typeof viewX == 'string') {
        if (viewX == 'left') {
          viewX = 0;
        } else if (viewX == 'center') {
          viewX = viewWidth / 2;
        } else if (viewX == 'right') {
          viewX = viewWidth - 1;
        } else {
          throw new Error('unrecognized string for viewPos. left|center|right');
        }
      }
      this.panX = (viewX / this.scaleX_) - worldX;
    },

    xPanWorldBoundsIntoView: function(worldMin, worldMax, viewWidth) {
      if (this.xWorldToView(worldMin) < 0)
        this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
      else if (this.xWorldToView(worldMax) > viewWidth)
        this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
    },

    xSetWorldBounds: function(worldMin, worldMax, viewWidth) {
      var worldWidth = worldMax - worldMin;
      var scaleX = viewWidth / worldWidth;
      var panX = -worldMin;
      this.setPanAndScale(panX, scaleX);
    },

    get gridEnabled() {
      return this.gridEnabled_;
    },

    set gridEnabled(enabled) {
      if (this.gridEnabled_ == enabled)
        return;

      this.gridEnabled_ = enabled && true;
      this.dispatchChangeEvent();
    },

    get gridTimebase() {
      return this.gridTimebase_;
    },

    set gridTimebase(timebase) {
      if (this.gridTimebase_ == timebase)
        return;
      this.gridTimebase_ = timebase;
      this.dispatchChangeEvent();
    },

    get gridStep() {
      return this.gridStep_;
    },

    applyTransformToCanvas: function(ctx) {
      ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
    },

    addMarker: function(positionWorld) {
      var marker = new ViewportMarker(this, positionWorld);
      this.markers.push(marker);
      this.dispatchChangeEvent();
      this.dispatchMarkersChangeEvent_();
      return marker;
    },

    removeMarker: function(marker) {
      for (var i = 0; i < this.markers.length; ++i) {
        if (this.markers[i] === marker) {
          this.markers.splice(i, 1);
          this.dispatchChangeEvent();
          this.dispatchMarkersChangeEvent_();
          return true;
        }
      }
    },

    findMarkerNear: function(positionWorld, nearnessInViewPixels) {
      // Converts pixels into distance in world.
      var nearnessThresholdWorld = this.xViewVectorToWorld(
          nearnessInViewPixels);
      for (var i = 0; i < this.markers.length; ++i) {
        if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
            nearnessThresholdWorld) {
          var marker = this.markers[i];
          return marker;
        }
      }
      return undefined;
    },

    drawGridLines: function(ctx, viewLWorld, viewRWorld) {
      if (!this.gridEnabled)
        return;

      var x = this.gridTimebase;

      ctx.beginPath();
      while (x < viewRWorld) {
        if (x >= viewLWorld) {
          // Do conversion to viewspace here rather than on
          // x to avoid precision issues.
          var vx = this.xWorldToView(x);
          ctx.moveTo(vx, 0);
          ctx.lineTo(vx, ctx.canvas.height);
        }
        x += this.gridStep;
      }
      ctx.strokeStyle = 'rgba(255,0,0,0.25)';
      ctx.stroke();
    },

    drawMarkerArrows: function(ctx, viewLWorld, viewRWorld, drawHeight) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
                                      ctx.canvas.height, drawHeight, this);
      }
    },

    drawMarkerLines: function(ctx, viewLWorld, viewRWorld) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawLine(ctx, viewLWorld, viewRWorld,
            ctx.canvas.height, this);
      }
    }
  };

  /**
   * Represents a marked position in the world, at a viewport level.
   * @constructor
   */
  function ViewportMarker(vp, positionWorld) {
    this.viewport_ = vp;
    this.positionWorld_ = positionWorld;
    this.selected_ = false;
  }

  ViewportMarker.prototype = {
    get positionWorld() {
      return this.positionWorld_;
    },

    set positionWorld(positionWorld) {
      this.positionWorld_ = positionWorld;
      this.viewport_.dispatchChangeEvent();
    },

    set selected(selected) {
      this.selected_ = selected;
      this.viewport_.dispatchChangeEvent();
    },

    get selected() {
      return this.selected_;
    },

    get color() {
      if (this.selected)
        return 'rgb(255,0,0)';
      return 'rgb(0,0,0)';
    },

    drawTriangle_: function(ctx, viewLWorld, viewRWorld,
                            canvasH, rulerHeight, vp) {
      ctx.beginPath();

      var ts = this.positionWorld_;
      if (ts < viewLWorld || ts > viewRWorld)
        return;

      var viewX = vp.xWorldToView(ts);
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX - 3, rulerHeight / 2);
      ctx.lineTo(viewX + 3, rulerHeight / 2);
      ctx.lineTo(viewX, rulerHeight);
      ctx.closePath();
      ctx.fillStyle = this.color;
      ctx.fill();

      if (rulerHeight === canvasH)
        return;

      // Draw line from bottom of triangle to the bottom of our canvas.
      ctx.beginPath();
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX, canvasH);
      ctx.closePath();
      ctx.strokeStyle = this.color;
      ctx.stroke();
    },

    drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
      ctx.beginPath();
      var ts = this.positionWorld_;
      if (ts >= viewLWorld && ts < viewRWorld) {
        var viewX = vp.xWorldToView(ts);
        ctx.moveTo(viewX, 0);
        ctx.lineTo(viewX, canvasH);
      }
      ctx.strokeStyle = this.color;
      ctx.stroke();
    }
  };

  return {
    TimelineViewport: TimelineViewport,
    ViewportMarker: ViewportMarker
  };
});
<|endoftext|>


<commit_before>'use strict';

define(['angular'], function(angular) {

  var app = angular.module('portal.misc.services', []);

  app.factory('PortalGroupService', function($http, miscService, SERVICE_LOC){

    var getGroups = function(){
      var groupPromise = $http.get(SERVICE_LOC.groupURL, {cache : true}).then (
                                    function(result){
                                      return result.data.groups;
                                    },function(reason){
                                      miscService.redirectUser(reason.status, 'group json feed call');
                                    });
      return groupPromise;
    }

    var groupsServiceEnabled = function() {
      if(SERVICE_LOC.groupURL) {
        return true;
      } else {
        return false;
      }
    }

    return {
      getGroups : getGroups,
      groupsServiceEnabled : groupsServiceEnabled
    }
  });

  app.factory('PortalAddToHomeService', function($http, filterFilter, NAMES, MISC_URLS){

    var canAddToHome = function(){
      if (MISC_URLS.addToHomeURLS && MISC_URLS.addToHomeURLS.addToHomeActionURL) {
            return true;
      } else {
        return false;
      }
    };

    var inHome = function(fname){
      return $http.get(MISC_URLS.addToHomeURLS.layoutURL).then(function(result){
        var layout = result.data.layout;
        if(layout) {
          var theFname = (fname ? fname : NAMES.fname);
          var filteredLayout = filterFilter(layout, {fname : theFname});
          return filteredLayout && filteredLayout.length > 0;
        } else {
          return false;
        }
      }, function(){
        //failed
        console.warn('could not reach portal server to get layout, portal down?');
        return true; //returning it is in the layout by default since portal is down
      });
    };

    var addToHome = function(fname){
      return $http.post(MISC_URLS.addToHomeURLS.addToHomeActionURL + (fname ? fname : NAMES.fname));
    };

    return {
      canAddToHome : canAddToHome,
      inHome : inHome,
      addToHome : addToHome
    };
  });

  app.factory('miscService', function($analytics, $http, $window, $location, MISC_URLS) {

    /**
     Used to redirect users to login screen iff result code is 0 (yay shib) or 302

     Setup MISC_URLS.loginURL in js/app-config.js to have redirects happen
    **/
    var redirectUser = function(status, caller) {
      if(status === 0 || status === 302) {
        console.log("redirect happening due to " + status);
        if(MISC_URLS.loginURL) {
          window.location.replace(MISC_URLS.loginURL);
        } else {
          console.warn("MISC_URLS.loginURL was not set, cannot redirect");
        }
      } else {
        console.warn("Strange behavior from " + caller +". Returned status code : " + status);
      }
    };

    /**
     Google Analytics page view
     - searchTerm : Optional parameter to say "this is a search page".
                    This is the actual search term used.
    **/
    var pushPageview = function (searchTerm) {
      var path = $location.path();
      if(searchTerm) {
        path += "?q=" + searchTerm;
      }
      console.warn('this method is deprecated in favor of automatic page views in angulartics. Will be removed in 3.0.0');
      console.log('ga pageview logged ' + path);
      $analytics.pageTrack(path);
    };

    /**
     Google Analytics event push
     - category : e.g.: beta header
     - action   : e.g.: beta buttons
     - label    : e.g.: back to classic

     See https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide for more info
    **/
    var pushGAEvent = function(category, action, label) {
      console.log('ga event logged c:' + category + " a:" + action + " l:" + label);
      $analytics.eventTrack(action, {  category: category, label: label });
    };

    return {
      redirectUser: redirectUser,
      pushPageview: pushPageview,
      pushGAEvent : pushGAEvent
    };

  });

  return app;

});
<commit_msg>add depricated flag on pushPageview<commit_after>'use strict';

define(['angular'], function(angular) {

  var app = angular.module('portal.misc.services', []);

  app.factory('PortalGroupService', function($http, miscService, SERVICE_LOC){

    var getGroups = function(){
      var groupPromise = $http.get(SERVICE_LOC.groupURL, {cache : true}).then (
                                    function(result){
                                      return result.data.groups;
                                    },function(reason){
                                      miscService.redirectUser(reason.status, 'group json feed call');
                                    });
      return groupPromise;
    }

    var groupsServiceEnabled = function() {
      if(SERVICE_LOC.groupURL) {
        return true;
      } else {
        return false;
      }
    }

    return {
      getGroups : getGroups,
      groupsServiceEnabled : groupsServiceEnabled
    }
  });

  app.factory('PortalAddToHomeService', function($http, filterFilter, NAMES, MISC_URLS){

    var canAddToHome = function(){
      if (MISC_URLS.addToHomeURLS && MISC_URLS.addToHomeURLS.addToHomeActionURL) {
            return true;
      } else {
        return false;
      }
    };

    var inHome = function(fname){
      return $http.get(MISC_URLS.addToHomeURLS.layoutURL).then(function(result){
        var layout = result.data.layout;
        if(layout) {
          var theFname = (fname ? fname : NAMES.fname);
          var filteredLayout = filterFilter(layout, {fname : theFname});
          return filteredLayout && filteredLayout.length > 0;
        } else {
          return false;
        }
      }, function(){
        //failed
        console.warn('could not reach portal server to get layout, portal down?');
        return true; //returning it is in the layout by default since portal is down
      });
    };

    var addToHome = function(fname){
      return $http.post(MISC_URLS.addToHomeURLS.addToHomeActionURL + (fname ? fname : NAMES.fname));
    };

    return {
      canAddToHome : canAddToHome,
      inHome : inHome,
      addToHome : addToHome
    };
  });

  app.factory('miscService', function($analytics, $http, $window, $location, MISC_URLS) {

    /**
     Used to redirect users to login screen iff result code is 0 (yay shib) or 302

     Setup MISC_URLS.loginURL in js/app-config.js to have redirects happen
    **/
    var redirectUser = function(status, caller) {
      if(status === 0 || status === 302) {
        console.log("redirect happening due to " + status);
        if(MISC_URLS.loginURL) {
          window.location.replace(MISC_URLS.loginURL);
        } else {
          console.warn("MISC_URLS.loginURL was not set, cannot redirect");
        }
      } else {
        console.warn("Strange behavior from " + caller +". Returned status code : " + status);
      }
    };

    /**
     Google Analytics page view
     - searchTerm : Optional parameter to say "this is a search page".
                    This is the actual search term used.
     @depricated
    **/
    var pushPageview = function (searchTerm) {
      var path = $location.path();
      if(searchTerm) {
        path += "?q=" + searchTerm;
      }
      console.warn('this method is deprecated in favor of automatic page views in angulartics. Will be removed in 3.0.0');
      console.log('ga pageview logged ' + path);
      $analytics.pageTrack(path);
    };

    /**
     Google Analytics event push
     - category : e.g.: beta header
     - action   : e.g.: beta buttons
     - label    : e.g.: back to classic

     See https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide for more info
    **/
    var pushGAEvent = function(category, action, label) {
      console.log('ga event logged c:' + category + " a:" + action + " l:" + label);
      $analytics.eventTrack(action, {  category: category, label: label });
    };

    return {
      redirectUser: redirectUser,
      pushPageview: pushPageview,
      pushGAEvent : pushGAEvent
    };

  });

  return app;

});
<|endoftext|>


<commit_before>import React from "react"

import CoderEstimateHidden from "./coder_estimate_hidden"
import CoderEstimateCompleted from "./coder_estimate_completed"
import CoderEstimatePending from "./coder_estimate_pending"

// TODO: pass in the state as estimates hash and pointOptions array
// TODO: get the ticket data from the server
// TODO: push new ticket data to the server

var CoderEstimates = React.createClass({
  propTypes: {
    estimates: React.PropTypes.object,
    pointOptions: React.PropTypes.array
  },

  render() {
    var estimateComplete = this.isEstimateComplete(this.props.estimates)

    var estimateRows = Object.keys(this.props.estimates).map((key) => {
      return this.estimateRow(estimateComplete, key, this.props.estimates[key])
    })

    return(

        {estimateRows}

    )
  },

  isEstimateComplete(estimates) {
    return Object.values(estimates).indexOf(0) === -1
  },

  estimateRow(estimateComplete, coder, points) {
    var rtn

    if (points === 0) {
      rtn = <CoderEstimatePending key={coder} coder={coder} pointOptions={this.props.pointOptions} />
    } else {
      rtn = (estimateComplete)
            ? <CoderEstimateCompleted key={coder} coder={coder} points={points} />
            : <CoderEstimateHidden key={coder} coder={coder} />
    }

    return rtn
  }
})

module.exports = CoderEstimates

<commit_msg>move estimates from props to state<commit_after>import React from "react"

import CoderEstimateHidden from "./coder_estimate_hidden"
import CoderEstimateCompleted from "./coder_estimate_completed"
import CoderEstimatePending from "./coder_estimate_pending"

// TODO: pass in the state as estimates hash and pointOptions array
// TODO: get the ticket data from the server
// TODO: push new ticket data to the server

var CoderEstimates = React.createClass({
  propTypes: {
    estimates: React.PropTypes.object,
    pointOptions: React.PropTypes.array
  },

  getInitialState() {
    return {
      estimates: this.props.estimates
    }
  },

  render() {
    var estimateComplete = this.isEstimateComplete(this.state.estimates)

    var estimateRows = Object.keys(this.state.estimates).map((key) => {
      return this.estimateRow(estimateComplete, key, this.state.estimates[key])
    })

    return(

        {estimateRows}

    )
  },

  isEstimateComplete(estimates) {
    return Object.values(estimates).indexOf(0) === -1
  },

  estimateRow(estimateComplete, coder, points) {
    var rtn

    if (points === 0) {
      rtn = <CoderEstimatePending key={coder} coder={coder} pointOptions={this.props.pointOptions} />
    } else {
      rtn = (estimateComplete)
            ? <CoderEstimateCompleted key={coder} coder={coder} points={points} />
            : <CoderEstimateHidden key={coder} coder={coder} />
    }

    return rtn
  }
})

module.exports = CoderEstimates

<|endoftext|>


<commit_before>Application.Services.factory("mcFlow", mcFlowService);
function mcFlowService() {
    var self = this;
    self.flow = new Flow(
        {
            target: "/upload"
        }
    );

    function each(obj, callback, context) {
        if (!obj) {
            return ;
        }
        var key;
        // Is Array?
        if (typeof(obj.length) !== 'undefined') {
            for (key = 0; key < obj.length; key++) {
                if (callback.call(context, obj[key], key) === false) {
                    return ;
                }
            }
        } else {
            for (key in obj) {
                if (obj.hasOwnProperty(key) && callback.call(context, obj[key], key) === false) {
                    return ;
                }
            }
        }
    }

    function extend(dst, src) {
        each(arguments, function(obj) {
            if (obj !== dst) {
                each(obj, function(value, key){
                    dst[key] = value;
                });
            }
        });
        return dst;
    }

    self.service = {
        get: function() {
            return self.flow;
        }
    };

    return self.service;
}
<commit_msg>Don't test for chunks. The way that flow does this is not efficient.<commit_after>Application.Services.factory("mcFlow", mcFlowService);
function mcFlowService() {
    var self = this;
    self.flow = new Flow(
        {
            target: "/upload",
            testChunks: false
        }
    );

    function each(obj, callback, context) {
        if (!obj) {
            return ;
        }
        var key;
        // Is Array?
        if (typeof(obj.length) !== 'undefined') {
            for (key = 0; key < obj.length; key++) {
                if (callback.call(context, obj[key], key) === false) {
                    return ;
                }
            }
        } else {
            for (key in obj) {
                if (obj.hasOwnProperty(key) && callback.call(context, obj[key], key) === false) {
                    return ;
                }
            }
        }
    }

    function extend(dst, src) {
        each(arguments, function(obj) {
            if (obj !== dst) {
                each(obj, function(value, key){
                    dst[key] = value;
                });
            }
        });
        return dst;
    }

    self.service = {
        get: function() {
            return self.flow;
        }
    };

    return self.service;
}
<|endoftext|>