<commit_before>/**
 * Copyright 2012 Google, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author benvanik@google.com (Ben Vanik)
 */

goog.provide('gf.sim.Variable');
goog.provide('gf.sim.VariableFlag');

goog.require('goog.math');
goog.require('goog.vec.Quaternion');
goog.require('goog.vec.Vec3');

/**
 * Abstract base variable type.
 * Along with its subclasses describes information about a varaible that is used
 * to build fast network lookup and serialization/deserialization tables.
 *
 * Variables must be declared identically both on the client and the server
 * and cannot vary between the two based on state/options/etc.
 *
 * @constructor
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 */
gf.sim.Variable = function(tag, flags) {
  /**
   * Runtime tag.
   * This is used to enable entity state implementations to rendezvous with the
   * variable ordinals. A compiler could obviate the need for these, but that's
   * a lot more work.
   * @type {number}
   */
  this.tag = tag;

  /**
   * Ordinal in the variable table.
   * This is populated by the {@see gf.sim.VariableTable} upon creation.
   * @type {number}
   */
  this.ordinal = 0;

  /**
   * Bitmask of {@see gf.sim.VariableFlag} values describing the behavior of
   * the variable.
   * @type {number}
   */
  this.flags = flags;
};

/**
 * Deep clones the variable.
 * @return {!gf.sim.Variable} A cloned instance.
 */
gf.sim.Variable.prototype.clone = goog.abstractMethod;

/**
 * Reads the variable.
 * @param {!Object} target Target object.
 * @param {!gf.net.PacketReader} reader Packet reader.
 */
gf.sim.Variable.prototype.read = goog.abstractMethod;

/**
 * Writes the variable.
 * @param {!Object} target Target object.
 * @param {!gf.net.PacketWriter} writer Packet writer.
 */
gf.sim.Variable.prototype.write = goog.abstractMethod;

/**
 * Copies the value from one object to another.
 * @param {!Object} source Source object.
 * @param {!Object} target Target object.
 */
gf.sim.Variable.prototype.copy = goog.abstractMethod;

/**
 * Interpolates the value between the given two states.
 * @param {!Object} source Interpolation source object.
 * @param {!Object} target Interpolation target object.
 * @param {number} t Interpolation coefficient, [0-1].
 * @param {!Object} result Storage object.
 */
gf.sim.Variable.prototype.interpolate = goog.abstractMethod;

/**
 * Next tag ID to assign.
 * @private
 * @type {number}
 */
gf.sim.Variable.nextTagId_ = 0;

/**
 * Gets a unique tag ID that can be used when creating variables.
 * These IDs are build specific and should not be used for anything but runtime
 * assignment.
 * @return {number} A tag ID.
 */
gf.sim.Variable.getUniqueTag = function() {
  return gf.sim.Variable.nextTagId_++;
};

/**
 * Stable sort comparison function for priority.
 * Requires that ordinal be set to the array order.
 * @param {!gf.sim.Variable} a Variable.
 * @param {!gf.sim.Variable} b Variable.
 * @return {number} Comparison <0, 0, >0.
 */
gf.sim.Variable.sortByPriority = function(a, b) {
  var frequentA = a.flags & gf.sim.VariableFlag.UPDATED_FREQUENTLY;
  var frequentB = b.flags & gf.sim.VariableFlag.UPDATED_FREQUENTLY;
  return (frequentB - frequentA) || (a.ordinal - b.ordinal);
};

/**
 * Bitmask values describing the behavior of variables.
 * @enum {number}
 */
gf.sim.VariableFlag = {
  /**
   * Variable is never replicated.
   * Values that exist on both client and server but can be inferred on the
   * client should set this bit.
   * Examples: sky color (inferred from weather).
   */
  NOT_REPLICATED: 1 << 0,

  /**
   * Variable will update frequently (perhaps every tick) and should get
   * optimized placement in the lookup table.
   * Examples: actor position.
   */
  UPDATED_FREQUENTLY: 1 << 1,

  /**
   * Variable is predicted on the client.
   * Signals that the variable will be modified during the prediction process.
   * Entities must also have prediction enabled on them.
   * Examples: local player position.
   */
  PREDICTED: 1 << 2,

  /**
   * Variable is interpolated on the client.
   * Any variable that must be interpolated between server sync updates can
   * set this bit.
   * Examples: remote/AI actor position, animation state.
   */
  INTERPOLATED: 1 << 3
};

/**
 * Variable containing a floating-point number.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():number} getter Prototype function that gets the value.
 * @param {!function(number)} setter Prototype function that sets the value.
 */
gf.sim.Variable.Float = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():number}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(number)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.Float, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Float.prototype.clone = function() {
  return new gf.sim.Variable.Float(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.read = function(target, reader) {
  this.setter_.call(target, reader.readFloat32());
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.write = function(target, writer) {
  writer.writeFloat32(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.interpolate = function(source, target, t,
    result) {
  this.setter_.call(result, goog.math.lerp(
      this.getter_.call(source), this.getter_.call(target), t));
};

/**
 * Variable containing a 3-element floating-point vector.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():!goog.vec.Vec3.Float32} getter Function that returns the
 *     value directly (no copy).
 * @param {!function(!goog.vec.Vec3.Float32)} setter Prototype function that
 *     sets the value.
 */
gf.sim.Variable.Vec3 = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():!goog.vec.Vec3.Float32}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(!goog.vec.Vec3.Float32)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.Vec3, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.clone = function() {
  return new gf.sim.Variable.Vec3(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.read = function(target, reader) {
  var v = gf.sim.Variable.Vec3.tmp_;
  reader.readVec3(v);
  this.setter_.call(target, v);
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.write = function(target, writer) {
  writer.writeVec3(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.interpolate = function(source, target, t,
    result) {
  var v = gf.sim.Variable.Vec3.tmp_;
  goog.vec.Vec3.lerp(
      this.getter_.call(source),
      this.getter_.call(target),
      t,
      v);
  this.setter_.call(result, v);
};

/**
 * Scratch Vec3 for math.
 * @private
 * @type {!goog.vec.Vec3.Float32}
 */
gf.sim.Variable.Vec3.tmp_ = goog.vec.Vec3.createFloat32();

/**
 * Variable containing a floating-point quaternion.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():!goog.vec.Quaternion.Float32} getter Function that
 *     returns the value directly (no copy).
 * @param {!function(!goog.vec.Quaternion.Float32)} setter Prototype function
 *     that sets the value.
 * @param {boolean} normalized True if the quaternion is normalized.
 */
gf.sim.Variable.Quaternion = function(tag, flags, getter, setter, normalized) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():!goog.vec.Quaternion.Float32}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(!goog.vec.Quaternion.Float32)}
   */
  this.setter_ = setter;

  /**
   * True if the quaternion values are normalized.
   * If so, an optimization can be used that allows for the sending of only
   * 3 floats instead of 4 by inferring w.
   * @private
   * @type {boolean}
   */
  this.normalized_ = normalized;
};
goog.inherits(gf.sim.Variable.Quaternion, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.clone = function() {
  return new gf.sim.Variable.Quaternion(this.tag, this.flags,
      this.getter_, this.setter_, this.normalized_);
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.read = function(target, reader) {
  var q = gf.sim.Variable.Quaternion.tmp_;
  if (this.normalized_) {
    // Reconstruct w
    reader.readVec3(q);
    // Trick is from http://www.gamedev.net/topic/461253-compressed-quaternions/
    // Known to have issues - may not be worth it
    q[3] = Math.sqrt(1 - q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
  } else {
    reader.readVec4(q);
  }
  this.setter_.call(target, q);
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.write = function(target, writer) {
  if (this.normalized_) {
    // Just ignore w
    writer.writeVec3(this.getter_.call(target));
  } else {
    writer.writeVec4(this.getter_.call(target));
  }
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.interpolate = function(source, target, t,
    result) {
  var q = gf.sim.Variable.Quaternion.tmp_;
  goog.vec.Quaternion.slerp(
      this.getter_.call(source),
      this.getter_.call(target),
      t,
      q);
  this.setter_.call(result, q);
};

/**
 * Scratch Quaternion for math.
 * @private
 * @type {!goog.vec.Quaternion.Float32}
 */
gf.sim.Variable.Quaternion.tmp_ = goog.vec.Quaternion.createFloat32();
<commit_msg>Adding string and entityid variables<commit_after>/**
 * Copyright 2012 Google, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author benvanik@google.com (Ben Vanik)
 */

goog.provide('gf.sim.Variable');
goog.provide('gf.sim.VariableFlag');

goog.require('goog.math');
goog.require('goog.vec.Quaternion');
goog.require('goog.vec.Vec3');

/**
 * Abstract base variable type.
 * Along with its subclasses describes information about a varaible that is used
 * to build fast network lookup and serialization/deserialization tables.
 *
 * Variables must be declared identically both on the client and the server
 * and cannot vary between the two based on state/options/etc.
 *
 * @constructor
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 */
gf.sim.Variable = function(tag, flags) {
  /**
   * Runtime tag.
   * This is used to enable entity state implementations to rendezvous with the
   * variable ordinals. A compiler could obviate the need for these, but that's
   * a lot more work.
   * @type {number}
   */
  this.tag = tag;

  /**
   * Ordinal in the variable table.
   * This is populated by the {@see gf.sim.VariableTable} upon creation.
   * @type {number}
   */
  this.ordinal = 0;

  /**
   * Bitmask of {@see gf.sim.VariableFlag} values describing the behavior of
   * the variable.
   * @type {number}
   */
  this.flags = flags;
};

/**
 * Deep clones the variable.
 * @return {!gf.sim.Variable} A cloned instance.
 */
gf.sim.Variable.prototype.clone = goog.abstractMethod;

/**
 * Reads the variable.
 * @param {!Object} target Target object.
 * @param {!gf.net.PacketReader} reader Packet reader.
 */
gf.sim.Variable.prototype.read = goog.abstractMethod;

/**
 * Writes the variable.
 * @param {!Object} target Target object.
 * @param {!gf.net.PacketWriter} writer Packet writer.
 */
gf.sim.Variable.prototype.write = goog.abstractMethod;

/**
 * Copies the value from one object to another.
 * @param {!Object} source Source object.
 * @param {!Object} target Target object.
 */
gf.sim.Variable.prototype.copy = goog.abstractMethod;

/**
 * Interpolates the value between the given two states.
 * @param {!Object} source Interpolation source object.
 * @param {!Object} target Interpolation target object.
 * @param {number} t Interpolation coefficient, [0-1].
 * @param {!Object} result Storage object.
 */
gf.sim.Variable.prototype.interpolate = goog.abstractMethod;

/**
 * Next tag ID to assign.
 * @private
 * @type {number}
 */
gf.sim.Variable.nextTagId_ = 0;

/**
 * Gets a unique tag ID that can be used when creating variables.
 * These IDs are build specific and should not be used for anything but runtime
 * assignment.
 * @return {number} A tag ID.
 */
gf.sim.Variable.getUniqueTag = function() {
  return gf.sim.Variable.nextTagId_++;
};

/**
 * Stable sort comparison function for priority.
 * Requires that ordinal be set to the array order.
 * @param {!gf.sim.Variable} a Variable.
 * @param {!gf.sim.Variable} b Variable.
 * @return {number} Comparison <0, 0, >0.
 */
gf.sim.Variable.sortByPriority = function(a, b) {
  var frequentA = a.flags & gf.sim.VariableFlag.UPDATED_FREQUENTLY;
  var frequentB = b.flags & gf.sim.VariableFlag.UPDATED_FREQUENTLY;
  return (frequentB - frequentA) || (a.ordinal - b.ordinal);
};

/**
 * Bitmask values describing the behavior of variables.
 * @enum {number}
 */
gf.sim.VariableFlag = {
  /**
   * Variable is never replicated.
   * Values that exist on both client and server but can be inferred on the
   * client should set this bit.
   * Examples: sky color (inferred from weather).
   */
  NOT_REPLICATED: 1 << 0,

  /**
   * Variable will update frequently (perhaps every tick) and should get
   * optimized placement in the lookup table.
   * Examples: actor position.
   */
  UPDATED_FREQUENTLY: 1 << 1,

  /**
   * Variable is predicted on the client.
   * Signals that the variable will be modified during the prediction process.
   * Entities must also have prediction enabled on them.
   * Examples: local player position.
   */
  PREDICTED: 1 << 2,

  /**
   * Variable is interpolated on the client.
   * Any variable that must be interpolated between server sync updates can
   * set this bit.
   * Examples: remote/AI actor position, animation state.
   */
  INTERPOLATED: 1 << 3
};

/**
 * Variable containing a floating-point number.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():number} getter Prototype function that gets the value.
 * @param {!function(number)} setter Prototype function that sets the value.
 */
gf.sim.Variable.Float = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():number}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(number)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.Float, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Float.prototype.clone = function() {
  return new gf.sim.Variable.Float(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.read = function(target, reader) {
  this.setter_.call(target, reader.readFloat32());
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.write = function(target, writer) {
  writer.writeFloat32(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Float.prototype.interpolate = function(source, target, t,
    result) {
  this.setter_.call(result, goog.math.lerp(
      this.getter_.call(source), this.getter_.call(target), t));
};

/**
 * Variable containing a 3-element floating-point vector.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():!goog.vec.Vec3.Float32} getter Function that returns the
 *     value directly (no copy).
 * @param {!function(!goog.vec.Vec3.Float32)} setter Prototype function that
 *     sets the value.
 */
gf.sim.Variable.Vec3 = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():!goog.vec.Vec3.Float32}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(!goog.vec.Vec3.Float32)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.Vec3, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.clone = function() {
  return new gf.sim.Variable.Vec3(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.read = function(target, reader) {
  var v = gf.sim.Variable.Vec3.tmp_;
  reader.readVec3(v);
  this.setter_.call(target, v);
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.write = function(target, writer) {
  writer.writeVec3(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Vec3.prototype.interpolate = function(source, target, t,
    result) {
  var v = gf.sim.Variable.Vec3.tmp_;
  goog.vec.Vec3.lerp(
      this.getter_.call(source),
      this.getter_.call(target),
      t,
      v);
  this.setter_.call(result, v);
};

/**
 * Scratch Vec3 for math.
 * @private
 * @type {!goog.vec.Vec3.Float32}
 */
gf.sim.Variable.Vec3.tmp_ = goog.vec.Vec3.createFloat32();

/**
 * Variable containing a floating-point quaternion.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():!goog.vec.Quaternion.Float32} getter Function that
 *     returns the value directly (no copy).
 * @param {!function(!goog.vec.Quaternion.Float32)} setter Prototype function
 *     that sets the value.
 * @param {boolean} normalized True if the quaternion is normalized.
 */
gf.sim.Variable.Quaternion = function(tag, flags, getter, setter, normalized) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():!goog.vec.Quaternion.Float32}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(!goog.vec.Quaternion.Float32)}
   */
  this.setter_ = setter;

  /**
   * True if the quaternion values are normalized.
   * If so, an optimization can be used that allows for the sending of only
   * 3 floats instead of 4 by inferring w.
   * @private
   * @type {boolean}
   */
  this.normalized_ = normalized;
};
goog.inherits(gf.sim.Variable.Quaternion, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.clone = function() {
  return new gf.sim.Variable.Quaternion(this.tag, this.flags,
      this.getter_, this.setter_, this.normalized_);
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.read = function(target, reader) {
  var q = gf.sim.Variable.Quaternion.tmp_;
  if (this.normalized_) {
    // Reconstruct w
    reader.readVec3(q);
    // Trick is from http://www.gamedev.net/topic/461253-compressed-quaternions/
    // Known to have issues - may not be worth it
    q[3] = Math.sqrt(1 - q[0] * q[0] + q[1] * q[1] + q[2] * q[2]);
  } else {
    reader.readVec4(q);
  }
  this.setter_.call(target, q);
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.write = function(target, writer) {
  if (this.normalized_) {
    // Just ignore w
    writer.writeVec3(this.getter_.call(target));
  } else {
    writer.writeVec4(this.getter_.call(target));
  }
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.Quaternion.prototype.interpolate = function(source, target, t,
    result) {
  var q = gf.sim.Variable.Quaternion.tmp_;
  goog.vec.Quaternion.slerp(
      this.getter_.call(source),
      this.getter_.call(target),
      t,
      q);
  this.setter_.call(result, q);
};

/**
 * Scratch Quaternion for math.
 * @private
 * @type {!goog.vec.Quaternion.Float32}
 */
gf.sim.Variable.Quaternion.tmp_ = goog.vec.Quaternion.createFloat32();

/**
 * Variable containing a string.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():string} getter Prototype function that gets the value.
 * @param {!function(string)} setter Prototype function that sets the value.
 */
gf.sim.Variable.String = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():string}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(string)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.String, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.String.prototype.clone = function() {
  return new gf.sim.Variable.String(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.String.prototype.read = function(target, reader) {
  this.setter_.call(target, reader.readString());
};

/**
 * @override
 */
gf.sim.Variable.String.prototype.write = function(target, writer) {
  writer.writeString(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.String.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.String.prototype.interpolate = function(source, target, t,
    result) {
  // Instantaneous to target
  this.setter_.call(result, this.getter_.call(target));
};

/**
 * Variable containing an entity ID.
 *
 * @constructor
 * @extends {gf.sim.Variable}
 * @param {number} tag Tag, from {@see gf.sim.Variable#getUniqueTag}.
 * @param {number} flags Bitmask of {@see gf.sim.VariableFlag} values.
 * @param {!function():number} getter Prototype function that gets the value.
 * @param {!function(number)} setter Prototype function that sets the value.
 */
gf.sim.Variable.EntityID = function(tag, flags, getter, setter) {
  goog.base(this, tag, flags);

  /**
   * @private
   * @type {!function():number}
   */
  this.getter_ = getter;

  /**
   * @private
   * @type {!function(number)}
   */
  this.setter_ = setter;
};
goog.inherits(gf.sim.Variable.EntityID, gf.sim.Variable);

/**
 * @override
 */
gf.sim.Variable.EntityID.prototype.clone = function() {
  return new gf.sim.Variable.EntityID(this.tag, this.flags,
      this.getter_, this.setter_);
};

/**
 * @override
 */
gf.sim.Variable.EntityID.prototype.read = function(target, reader) {
  this.setter_.call(target, reader.readVarInt());
};

/**
 * @override
 */
gf.sim.Variable.EntityID.prototype.write = function(target, writer) {
  writer.writeVarInt(this.getter_.call(target));
};

/**
 * @override
 */
gf.sim.Variable.EntityID.prototype.copy = function(source, target) {
  this.setter_.call(target, this.getter_.call(source));
};

/**
 * @override
 */
gf.sim.Variable.EntityID.prototype.interpolate = function(source, target, t,
    result) {
  // Instantaneous to target
  this.setter_.call(result, this.getter_.call(target));
};
<|endoftext|>


<commit_before>/* global describe, it */

var config = require('../../lib/config')
var fs = require('fs')
var hogan = require('hogan.js')
var request = require('./supertest')

/**
 * Manager
 */

var manager = hogan.compile(fs.readFileSync(__dirname +
  '/../../client/manager.html', 'utf8')).render({
  application: config.application,
  segmentio_key: config.segmentio_key,
  static_url: config.static_url,
  version: config.version
})

/**
 * Planner
 */

var planner = hogan.compile(fs.readFileSync(__dirname +
  '/../../client/planner.html', 'utf8')).render({
  application: config.application,
  segmentio_key: config.segmentio_key,
  static_url: config.static_url,
  version: config.version
})

/**
 * Mocha
 */

describe('HTML', function () {
  describe('/manager', function () {
    it('should get manager.html', function (done) {
      request.get('/manager')
        .expect('content-type', 'text/html; charset=utf-8')
        .expect(200)
        .end(function (err, res) {
          if (err) return done(err)
          res.text.should.equal(manager)
          done()
        })
    })
  })

  describe('/', function () {
    it('should get planner.html', function (done) {
      request.get('/')
        .expect('content-type', 'text/html; charset=utf-8')
        .expect(200)
        .end(function (err, res) {
          if (err) return done(err)
          res.text.should.equal(planner)
          done()
        })
    })
  })
})
<commit_msg>Fix tests<commit_after>/* global describe, it */

var config = require('../../lib/config')
var fs = require('fs')
var handlebars = require('handlebars')
var request = require('./supertest')

/**
 * Manager
 */

var manager = handlebars.compile(fs.readFileSync(__dirname +
  '/../../client/manager.html', 'utf8'))({
  application: config.application,
  minified: true,
  segmentio_key: config.segmentio_key,
  static_url: config.static_url,
  version: config.version
})

/**
 * Planner
 */

var planner = handlebars.compile(fs.readFileSync(__dirname +
  '/../../client/planner.html', 'utf8'))({
  application: config.application,
  minified: true,
  segmentio_key: config.segmentio_key,
  static_url: config.static_url,
  version: config.version
})

/**
 * Mocha
 */

describe('HTML', function () {
  describe('/manager', function () {
    it('should get manager.html', function (done) {
      request.get('/manager')
        .expect('content-type', 'text/html; charset=utf-8')
        .expect(200)
        .end(function (err, res) {
          if (err) return done(err)
          res.text.should.equal(manager)
          done()
        })
    })
  })

  describe('/', function () {
    it('should get planner.html', function (done) {
      request.get('/')
        .expect('content-type', 'text/html; charset=utf-8')
        .expect(200)
        .end(function (err, res) {
          if (err) return done(err)
          res.text.should.equal(planner)
          done()
        })
    })
  })
})
<|endoftext|>


<commit_before>import Ember from 'ember';
const { getOwner } = Ember;

const headers = [
  {
    name: 'expand',
    sort: false,
    searchField: null,
    width: 30
  },
  {
    name:           'state',
    sort:           ['stateSort','name','id'],
    translationKey: 'generic.state',
    width:          125,
  },
  {
    name:           'name',
    sort:           ['displayName','id'],
    translationKey: 'clustersPage.cluster.label',
  },
  {
    name:           'hosts',
    sort:           ['numHosts','name','id'],
    translationKey: 'clustersPage.hosts.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'cpu',
    sort:           ['numGhz','name','id'],
    translationKey: 'clustersPage.cpu.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'memory',
    sort:           ['numMem','name','id'],
    translationKey: 'clustersPage.memory.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'storage',
    sort:           ['numStorage','name','id'],
    translationKey: 'clustersPage.storage.label',
    width: 100,
    classNames: 'text-center',
  },
];

export default Ember.Controller.extend({
  queryParams: ['mode'],
  mode: 'grouped',

  modalService: Ember.inject.service('modal'),
  access: Ember.inject.service(),
  projects: Ember.inject.service(),
  settings: Ember.inject.service(),
  application: Ember.inject.controller(),

  headers: headers,
  sortBy: 'name',
  searchText: null,
  bulkActions: true,

  init() {
    this._super(...arguments);
    this.set('expandedClusters',[]);
  },

  actions: {
    toggleExpand(id) {
      let list = this.get('expandedClusters');
      if ( list.includes(id) ) {
        list.removeObject(id);
      } else {
        list.addObject(id);
      }
    },
    launchOnCluster(model) {
      let authenticated = getOwner(this).lookup('route:authenticated');
      authenticated.send('switchProject', model.get("defaultProject.id"), 'authenticated.clusters.cluster.host-templates', [model.id, {queryParams: {backTo: 'clusters'}}]);
    },
  },
});
<commit_msg>Dont switch env if in the same env for add host from cluster manage<commit_after>import Ember from 'ember';
const { getOwner } = Ember;

const headers = [
  {
    name: 'expand',
    sort: false,
    searchField: null,
    width: 30
  },
  {
    name:           'state',
    sort:           ['stateSort','name','id'],
    translationKey: 'generic.state',
    width:          125,
  },
  {
    name:           'name',
    sort:           ['displayName','id'],
    translationKey: 'clustersPage.cluster.label',
  },
  {
    name:           'hosts',
    sort:           ['numHosts','name','id'],
    translationKey: 'clustersPage.hosts.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'cpu',
    sort:           ['numGhz','name','id'],
    translationKey: 'clustersPage.cpu.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'memory',
    sort:           ['numMem','name','id'],
    translationKey: 'clustersPage.memory.label',
    width: 100,
    classNames: 'text-center',
  },
  {
    name:           'storage',
    sort:           ['numStorage','name','id'],
    translationKey: 'clustersPage.storage.label',
    width: 100,
    classNames: 'text-center',
  },
];

export default Ember.Controller.extend({
  queryParams: ['mode'],
  mode: 'grouped',

  modalService: Ember.inject.service('modal'),
  access: Ember.inject.service(),
  projects: Ember.inject.service(),
  settings: Ember.inject.service(),
  application: Ember.inject.controller(),

  headers: headers,
  sortBy: 'name',
  searchText: null,
  bulkActions: true,

  init() {
    this._super(...arguments);
    this.set('expandedClusters',[]);
  },

  actions: {
    toggleExpand(id) {
      let list = this.get('expandedClusters');
      if ( list.includes(id) ) {
        list.removeObject(id);
      } else {
        list.addObject(id);
      }
    },
    launchOnCluster(model) {
      let authenticated = getOwner(this).lookup('route:authenticated');
      if (this.get('projects.current.id') === model.get('defaultProject.id')) {
        this.transitionToRoute('authenticated.clusters.cluster.host-templates', model.get('id'), {queryParams: {backTo: 'clusters'}});
      } else {
        authenticated.send('switchProject', model.get("defaultProject.id"), 'authenticated.clusters.cluster.host-templates', [model.id, {queryParams: {backTo: 'clusters'}}]);
      }
    },
  },
});
<|endoftext|>


<commit_before>QUnit.test("window()", function(assert)
{
	function sum(unpacker)
	{
		return function(rows, begin, end, current)
		{
			return Canal.of(rows, begin, end) //
			.unpack(unpacker) //
			.reduce(function(a, b)
			{
				return a + b;
			}).get();
		};
	}

	var result = Canal.of([
		{"id":1,"sex":1,"grp":0,"sal":3032},
		{"id":2,"sex":1,"grp":1,"sal":2153},
		{"id":3,"sex":2,"grp":0,"sal":2545},
		{"id":4,"sex":1,"grp":1,"sal":1894}
	]).toRows().window(
		Canal.item(sum(function(d){return d.id;}))
			.partBy(function(d){return d.grp;})
			.orderBy(function(d){return d.sex;}),
		Canal.item(sum(function(d){return d.sal;}))
			.partBy(function(d){return d.grp;}, function(d){return d.sex;})
	).collect();

	assert.propEqual(result, [
		[{"id":1,"sex":1,"grp":0,"sal":3032}, 1, 3032],
		[{"id":3,"sex":2,"grp":0,"sal":2545}, 4, 2545],
		[{"id":2,"sex":1,"grp":1,"sal":2153}, 6, 4047],
		[{"id":4,"sex":1,"grp":1,"sal":1894}, 6, 4047]
	]);
});
<commit_msg>Update the test case.<commit_after>QUnit.test("window()", function(assert)
{
	function sum(unpacker)
	{
		return function(rows, begin, end, current)
		{
			return Canal.of(rows, begin, end) //
			.unpack(unpacker) //
			.reduce(function(a, b)
			{
				return a + b;
			}).get();
		};
	}

	var result = Canal.of([
		{"id":1,"sex":1,"grp":0,"sal":3032},
		{"id":2,"sex":1,"grp":1,"sal":2153},
		{"id":3,"sex":2,"grp":0,"sal":2545},
		{"id":4,"sex":1,"grp":1,"sal":1894}
	]).toRows().window(
		Canal.item(sum(function(d){return d.id;}))
			.partBy(function(d){return d.grp;})
			.orderBy(function(d){return d.sex;}),
		Canal.item(sum(function(d){return d.sal;}))
			.partBy(function(d){return d.grp;}, function(d){return d.sex;}),
		Canal.item(sum(function(d){return d.sal;}))
			.partBy(function(d){return d.grp;})
			.orderBy(function(d){return d.sal;})
			.between(null,0)  // From the very first row to current row
	).collect();

	assert.propEqual(result, [
	    [{"id":3,"sex":2,"grp":0,"sal":2545}, 4, 2545, 2545],
		[{"id":1,"sex":1,"grp":0,"sal":3032}, 1, 3032, 5577],
		[{"id":4,"sex":1,"grp":1,"sal":1894}, 6, 4047, 1894],
		[{"id":2,"sex":1,"grp":1,"sal":2153}, 6, 4047, 4047]
	]);
});
<|endoftext|>


<commit_before>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.nut.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}

describe('Test NUT adapter', function() {
    before('Test NUT adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);

            setup.startController(true, function(id, obj) {}, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                function (_objects, _states) {
                    objects = _objects;
                    states  = _states;
                    _done();
                });
        });
    });

    it('Test NUT adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });

    // We expect ERROR as last Notify necause no nut is running there
    it('Test NUT adapter: test initial state as ERROR', function (done) {
        this.timeout(25000);

        setTimeout(function() {
            states.getState('nut.0.status.last_notify', function (err, state) {
                if (err) console.error(err);
                expect(state).to.exist;
                if (!state) {
                    console.error('state "status.last_notify" not set');
                }
                else {
                    console.log('check status.last_notify ... ' + state.val);
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal('ERROR');
                }
                states.getState('nut.0.status.severity', function (err, state) {
                    if (err) console.error(err);
                    expect(state).to.exist;
                    if (!state) {
                        console.error('state "status.severity" not set');
                    }
                    else {
                        console.log('check status.severity ... ' + state.val);
                    }
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal(4);
                    done();
                });
            });
        }, 10000);
    });

    it('Test NUT adapter: send notify Message and receive answer', function (done) {
        this.timeout(25000);
        var now = new Date().getTime();

        console.log('send notify with "COMMBAD" to adapter ...');
        sendTo('nut.0', 'notify', {notifytype: 'COMMBAD', upsname: 'nutName@127.0.0.1'});
        setTimeout(function() {
            states.getState('nut.0.status.last_notify', function (err, state) {
                if (err) console.error(err);
                expect(state).to.exist;
                if (!state) {
                    console.error('state "status.last_notify" not set');
                }
                else {
                    console.log('check status.last_notify ... ' + state.val);
                }
                expect(state.val).to.be.equal('COMMBAD');
                states.getState('nut.0.status.severity', function (err, state) {
                    if (err) console.error(err);
                    expect(state).to.exist;
                    if (!state) {
                        console.error('state "status.severity" not set');
                    }
                    else {
                        console.log('check status.severity ... ' + state.val);
                    }
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal(4);
                    done();
                });
            });
        }, 2000);
    });

    after('Test NUT adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<commit_msg>try to find more about winston-daily-rotate-file problems in node 6<commit_after>/* jshint -W097 */// jshint strict:false
/*jslint node: true */
var expect = require('chai').expect;
var setup  = require(__dirname + '/lib/setup');
var fs     = require('fs');

var objects = null;
var states  = null;
var onStateChanged = null;
var onObjectChanged = null;
var sendToID = 1;

function checkConnectionOfAdapter(cb, counter) {
    counter = counter || 0;
    console.log('Try check #' + counter);
    if (counter > 30) {
        if (cb) cb('Cannot check connection');
        return;
    }

    states.getState('system.adapter.nut.0.alive', function (err, state) {
        if (err) console.error(err);
        if (state && state.val) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkConnectionOfAdapter(cb, counter + 1);
            }, 1000);
        }
    });
}

function checkValueOfState(id, value, cb, counter) {
    counter = counter || 0;
    if (counter > 20) {
        if (cb) cb('Cannot check value Of State ' + id);
        return;
    }

    states.getState(id, function (err, state) {
        if (err) console.error(err);
        if (value === null && !state) {
            if (cb) cb();
        } else
        if (state && (value === undefined || state.val === value)) {
            if (cb) cb();
        } else {
            setTimeout(function () {
                checkValueOfState(id, value, cb, counter + 1);
            }, 500);
        }
    });
}

function sendTo(target, command, message, callback) {
    onStateChanged = function (id, state) {
        if (id === 'messagebox.system.adapter.test.0') {
            callback(state.message);
        }
    };

    states.pushMessage('system.adapter.' + target, {
        command:    command,
        message:    message,
        from:       'system.adapter.test.0',
        callback: {
            message: message,
            id:      sendToID++,
            ack:     false,
            time:    (new Date()).getTime()
        }
    });
}

describe('Test NUT adapter', function() {
    before('Test NUT adapter: Start js-controller', function (_done) {
        this.timeout(600000); // because of first install from npm

        setup.setupController(function () {
            var winstonfile = fs.readFileSync('/home/travis/build/Apollon77/ioBroker.nut/node_modules/winston-daily-rotate-file/index.js');
            fs.writeFileSync('/home/travis/build/Apollon77/ioBroker.nut/node_modules/winston-daily-rotate-file/index.js', winstonfile.substring(13));
            console.log('winston-daily-rotate-file/index.js modified successfully');

            var config = setup.getAdapterConfig();
            // enable adapter
            config.common.enabled  = true;
            config.common.loglevel = 'debug';

            //config.native.dbtype   = 'sqlite';

            setup.setAdapterConfig(config.common, config.native);

            setup.startController(true, function(id, obj) {}, function (id, state) {
                    if (onStateChanged) onStateChanged(id, state);
                },
                function (_objects, _states) {
                    objects = _objects;
                    states  = _states;
                    _done();
                });
        });
    });

    it('Test NUT adapter: Check if adapter started', function (done) {
        this.timeout(60000);
        checkConnectionOfAdapter(function (res) {
            if (res) console.log(res);
            expect(res).not.to.be.equal('Cannot check connection');
            objects.setObject('system.adapter.test.0', {
                    common: {

                    },
                    type: 'instance'
                },
                function () {
                    states.subscribeMessage('system.adapter.test.0');
                    done();
                });
        });
    });

    // We expect ERROR as last Notify necause no nut is running there
    it('Test NUT adapter: test initial state as ERROR', function (done) {
        this.timeout(25000);

        setTimeout(function() {
            states.getState('nut.0.status.last_notify', function (err, state) {
                if (err) console.error(err);
                expect(state).to.exist;
                if (!state) {
                    console.error('state "status.last_notify" not set');
                }
                else {
                    console.log('check status.last_notify ... ' + state.val);
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal('ERROR');
                }
                states.getState('nut.0.status.severity', function (err, state) {
                    if (err) console.error(err);
                    expect(state).to.exist;
                    if (!state) {
                        console.error('state "status.severity" not set');
                    }
                    else {
                        console.log('check status.severity ... ' + state.val);
                    }
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal(4);
                    done();
                });
            });
        }, 10000);
    });

    it('Test NUT adapter: send notify Message and receive answer', function (done) {
        this.timeout(25000);
        var now = new Date().getTime();

        console.log('send notify with "COMMBAD" to adapter ...');
        sendTo('nut.0', 'notify', {notifytype: 'COMMBAD', upsname: 'nutName@127.0.0.1'});
        setTimeout(function() {
            states.getState('nut.0.status.last_notify', function (err, state) {
                if (err) console.error(err);
                expect(state).to.exist;
                if (!state) {
                    console.error('state "status.last_notify" not set');
                }
                else {
                    console.log('check status.last_notify ... ' + state.val);
                }
                expect(state.val).to.be.equal('COMMBAD');
                states.getState('nut.0.status.severity', function (err, state) {
                    if (err) console.error(err);
                    expect(state).to.exist;
                    if (!state) {
                        console.error('state "status.severity" not set');
                    }
                    else {
                        console.log('check status.severity ... ' + state.val);
                    }
                    expect(state.val).to.exist;
                    expect(state.val).to.be.equal(4);
                    done();
                });
            });
        }, 2000);
    });

    after('Test NUT adapter: Stop js-controller', function (done) {
        this.timeout(10000);

        setup.stopController(function (normalTerminated) {
            console.log('Adapter normal terminated: ' + normalTerminated);
            done();
        });
    });
});
<|endoftext|>


<commit_before>var assert = require('assert');
var fs = require('fs');
var path = require('path');
var temp = require('temp').track();
var ghslug = require('github-slug');
var rewire = require('rewire');
var offline = rewire('../lib/offline');

describe('Offline', function() {
  var oldWd;
  beforeEach(function() {
    oldWd = process.cwd();
  });

  afterEach(function() {
    process.chdir(oldWd);
    offline.__set__('ghslug', ghslug);
  });

  it('should create offline-worker.js in the destination directory', function() {
    var dir = temp.mkdirSync('tmp');

    return offline({
      rootDir: dir,
    }).then(function() {
      assert.doesNotThrow(fs.accessSync.bind(fs, path.join(dir, 'offline-worker.js')));
    });
  });

  it('should not fail if the destination directory already contains a offline-worker.js file', function() {
    var dir = temp.mkdirSync('tmp');

    fs.writeFileSync(path.join(dir, 'offline-worker.js'), 'something');

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content, 'something');
    });
  });

  it('should use importScript in the service worker if the importScripts option is defined', function() {
    var dir = temp.mkdirSync('tmp');

    fs.writeFileSync('a-script.js', 'something');

    return offline({
      rootDir: dir,
      importScripts: [ 'a-script.js', ],
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('importScripts("a-script.js");'), -1);
    });
  });

  it('should use the GitHub slug as the cache ID if it is available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    offline.__set__('ghslug', function(path, callback) {
      callback(null, 'mozilla/oghliner');
    });

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('mozilla/oghliner'), -1);
    });
  });

  it('should use the app name from package.json as the cache ID if the GitHub slug is not available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    fs.writeFileSync(path.join(rootDir, 'package.json'), JSON.stringify({
      name: 'test_name',
    }));

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('test_name'), -1);
    });
  });

  it('should not fail if both the GitHub slug and package.json are not available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      assert.doesNotThrow(fs.accessSync.bind(fs, path.join(dir, 'offline-worker.js')));
    });
  });

  it('should cache files in rootDir', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    fs.writeFileSync(path.join(dir, 'test_file_1.js'), 'test_file_1');
    fs.writeFileSync(path.join(dir, 'test_file_2.js'), 'test_file_2');
    fs.writeFileSync(path.join(dir, 'test_file_3.js'), 'test_file_3');

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('test_file_1.js'), -1);
      assert.notEqual(content.indexOf('test_file_2.js'), -1);
      assert.notEqual(content.indexOf('test_file_3.js'), -1);
    });
  });
});
<commit_msg>Test fileGlobs option<commit_after>var assert = require('assert');
var fs = require('fs');
var path = require('path');
var temp = require('temp').track();
var ghslug = require('github-slug');
var rewire = require('rewire');
var offline = rewire('../lib/offline');

describe('Offline', function() {
  var oldWd;
  beforeEach(function() {
    oldWd = process.cwd();
  });

  afterEach(function() {
    process.chdir(oldWd);
    offline.__set__('ghslug', ghslug);
  });

  it('should create offline-worker.js in the destination directory', function() {
    var dir = temp.mkdirSync('tmp');

    return offline({
      rootDir: dir,
    }).then(function() {
      assert.doesNotThrow(fs.accessSync.bind(fs, path.join(dir, 'offline-worker.js')));
    });
  });

  it('should not fail if the destination directory already contains a offline-worker.js file', function() {
    var dir = temp.mkdirSync('tmp');

    fs.writeFileSync(path.join(dir, 'offline-worker.js'), 'something');

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content, 'something');
    });
  });

  it('should use importScript in the service worker if the importScripts option is defined', function() {
    var dir = temp.mkdirSync('tmp');

    fs.writeFileSync('a-script.js', 'something');

    return offline({
      rootDir: dir,
      importScripts: [ 'a-script.js', ],
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('importScripts("a-script.js");'), -1);
    });
  });

  it('should use the GitHub slug as the cache ID if it is available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    offline.__set__('ghslug', function(path, callback) {
      callback(null, 'mozilla/oghliner');
    });

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('mozilla/oghliner'), -1);
    });
  });

  it('should use the app name from package.json as the cache ID if the GitHub slug is not available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    fs.writeFileSync(path.join(rootDir, 'package.json'), JSON.stringify({
      name: 'test_name',
    }));

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('test_name'), -1);
    });
  });

  it('should not fail if both the GitHub slug and package.json are not available', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      assert.doesNotThrow(fs.accessSync.bind(fs, path.join(dir, 'offline-worker.js')));
    });
  });

  it('should cache files in rootDir', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    fs.writeFileSync(path.join(dir, 'test_file_1.js'), 'test_file_1');
    fs.writeFileSync(path.join(dir, 'test_file_2.js'), 'test_file_2');
    fs.writeFileSync(path.join(dir, 'test_file_3.js'), 'test_file_3');

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('test_file_1.js'), -1);
      assert.notEqual(content.indexOf('test_file_2.js'), -1);
      assert.notEqual(content.indexOf('test_file_3.js'), -1);
    });
  });

  it('should not cache files in rootDir that do not match fileGlobs', function() {
    var rootDir = temp.mkdirSync('tmp');
    var dir = path.join(rootDir, 'dist');
    fs.mkdirSync(dir);

    fs.writeFileSync(path.join(dir, 'test_file_1.js'), 'test_file_1');
    fs.writeFileSync(path.join(dir, 'test_file_2.js'), 'test_file_2');
    fs.writeFileSync(path.join(dir, 'test_file_3.js'), 'test_file_3');

    process.chdir(rootDir);

    return offline({
      rootDir: dir,
      fileGlobs: [
        'test_file_1.js',
        'test_file_3.js',
      ],
    }).then(function() {
      var content = fs.readFileSync(path.join(dir, 'offline-worker.js'), 'utf8');
      assert.notEqual(content.indexOf('test_file_1.js'), -1);
      assert.equal(content.indexOf('test_file_2.js'), -1);
      assert.notEqual(content.indexOf('test_file_3.js'), -1);
    });
  });
});
<|endoftext|>


<commit_before>import { createSelector, createStructuredSelector } from 'reselect';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import sortBy from 'lodash/sortBy';
import moment from 'moment';

import { formatDate } from 'utils/dates';
import { deburrUpper } from 'utils/data';

import thresholdOptions from 'data/thresholds.json';

import initialState from './map-initial-state';
import decodeLayersConfig from './map-decode-config';
import statements from './map-statement-config';

// get list data
const getMapUrlState = state => (state.query && state.query.map) || null;
const getDatasets = state => state.datasets.filter(d => !isEmpty(d.layer));
const getLoading = state => state.loading;
const getGeostore = state => state.geostore || null;
const getLatest = state => state.latest || null;
const getCountries = state => state.countries || null;

const reduceParams = (params, latestDate) => {
  if (!params) return null;
  return params.reduce((obj, param) => {
    const { format, key } = param;
    let paramValue = param.default;
    const isDate = deburrUpper(param.key).includes('DATE');
    if (isDate && !paramValue) {
      let date = latestDate || formatDate(new Date());
      if (key === 'recentDate') date = moment(date).subtract(1, 'month');
      paramValue = moment(date).format(format || 'YYYY-MM-DD');
    }

    const newObj = {
      ...obj,
      [key]: paramValue
    };
    return newObj;
  }, {});
};

const reduceSqlParams = params => {
  if (!params) return null;
  return params.reduce((obj, param) => {
    const newObj = {
      ...obj,
      [param.key]: param.key_params.reduce((subObj, item) => {
        const keyValues = {
          ...subObj,
          [item.key]: item.value
        };
        return keyValues;
      }, {})
    };
    return newObj;
  }, {});
};

export const getMapSettings = createSelector([getMapUrlState], urlState => ({
  ...initialState,
  ...urlState
}));

export const getLayers = createSelector(
  getMapSettings,
  settings => settings.layers
);

export const getBasemap = createSelector(
  getMapSettings,
  settings => settings.basemap
);

export const getMapZoom = createSelector(
  getMapSettings,
  settings => settings.zoom
);

export const getLabels = createSelector(
  getMapSettings,
  settings => settings.label
);

export const getBBox = createSelector(getGeostore, geostore => {
  const { bbox } = geostore;
  if (isEmpty(bbox)) return {};
  return { bbox, options: { padding: [10, 10] } };
});

export const getMapOptions = createSelector(getMapSettings, settings => {
  if (!settings) return null;
  const {
    center,
    zoom,
    minZoom,
    maxZoom,
    zoomControl,
    basemap,
    label,
    attributionControl
  } = settings;
  return {
    center,
    zoom,
    minZoom,
    maxZoom,
    zoomControl,
    label,
    basemap,
    attributionControl
  };
});

export const getParsedDatasets = createSelector(
  [getDatasets, getLatest, getCountries],
  (datasets, latest, countries) => {
    if (isEmpty(datasets) || isEmpty(latest) || isEmpty(countries)) return null;

    return datasets.filter(d => d.env === 'production').map(d => {
      const { layer, metadata } = d;
      const appMeta = metadata.find(m => m.application === 'gfw') || {};
      const { info } = appMeta || {};
      const defaultLayer =
        (layer &&
          layer.find(
            l =>
              l.env === 'production' &&
              l.applicationConfig &&
              l.applicationConfig.default
          )) ||
        layer[0];

      // we need a default layer so we can set it when toggled onto the map
      if (!defaultLayer) return null;

      const { isSelectorLayer, isMultiSelectorLayer, isLossLayer } = info || {};
      const { id, iso, applicationConfig } = defaultLayer || {};
      const { global } = applicationConfig || {};

      // build statement config
      let statementConfig = null;
      if (isLossLayer) {
        statementConfig = {
          ...statements.loss
        };
      } else if (global && !!iso.length && iso[0]) {
        statementConfig = {
          ...statements.isoLayer,
          tooltipDesc:
            countries &&
            countries
              .filter(c => iso.includes(c.value))
              .map(c => c.label)
              .join(', ')
        };
      }

      return {
        ...d,
        dataset: d.id,
        ...applicationConfig,
        ...info,
        layer: id,
        iso,
        tags: flatten(d.vocabulary.map(v => v.tags)),
        // dropdown selector config
        ...((isSelectorLayer || isMultiSelectorLayer) && {
          selectorLayerConfig: {
            options: layer.map(l => ({
              ...l.applicationConfig.selectorConfig,
              value: l.id
            }))
          }
        }),
        // disclaimer statement config
        statementConfig,
        // layers config
        layers:
          layer &&
          sortBy(
            layer
              .filter(l => l.env === 'production' && l.published)
              .map((l, i) => {
                const { layerConfig } = l;
                const { position, confirmedOnly, multiConfig } =
                  l.applicationConfig || {};
                const {
                  params_config,
                  decode_config,
                  sql_config,
                  body,
                  url
                } = layerConfig;
                const decodeFunction = decodeLayersConfig[l.id];
                const latestDate = latest && latest[l.id];

                // check for timeline params
                const hasParamsTimeline =
                  params_config &&
                  params_config.map(p => p.key).includes('startDate');
                const hasDecodeTimeline =
                  decode_config &&
                  decode_config.map(p => p.key).includes('startDate');
                const params =
                  params_config && reduceParams(params_config, latestDate);
                const decodeParams =
                  decode_config && reduceParams(decode_config, latestDate);
                const sqlParams = sql_config && reduceSqlParams(sql_config);

                return {
                  ...l,
                  ...l.applicationConfig,
                  // sorting position
                  position: l.applicationConfig.default
                    ? 0
                    : position ||
                      (multiConfig && multiConfig.position) ||
                      i + 1,
                  // check if needs timeline
                  hasParamsTimeline,
                  hasDecodeTimeline,
                  // params for tile url
                  ...(params && {
                    params: {
                      url: body.url || url,
                      ...params,
                      ...(hasParamsTimeline && {
                        minDate: params && params.startDate,
                        maxDate: params && params.endDate,
                        trimEndDate: params && params.endDate
                      })
                    }
                  }),
                  // params selector config
                  ...(params_config && {
                    paramsSelectorConfig: params_config.map(p => ({
                      ...p,
                      ...(p.key.includes('thresh') && {
                        sentence:
                          'Displaying {name} with {selector} canopy density',
                        options: thresholdOptions
                      })
                    }))
                  }),
                  // params for sql query
                  ...(sqlParams && {
                    sqlParams
                  }),
                  // decode func and params for canvas layers
                  ...decodeFunction,
                  ...(decodeParams && {
                    decodeParams: {
                      ...(decodeFunction && decodeFunction.decodeParams),
                      ...decodeParams,
                      ...(hasDecodeTimeline && {
                        minDate: decodeParams && decodeParams.startDate,
                        maxDate: decodeParams && decodeParams.endDate,
                        trimEndDate: decodeParams && decodeParams.endDate,
                        canPlay: true
                      })
                    }
                  }),
                  // special key for GLAD alerts
                  ...(confirmedOnly && {
                    id: 'confirmedOnly'
                  })
                };
              }),
            'position'
          )
      };
    });
  }
);

export const getDatasetIds = createSelector([getLayers], layers => {
  if (!layers || !layers.length) return null;
  return layers.map(l => l.dataset);
});

export const getLayersDatasets = createSelector(
  [getParsedDatasets, getDatasetIds],
  (datasets, datasetIds) => {
    if (isEmpty(datasets) || isEmpty(datasetIds)) return null;
    return datasets.filter(d => datasetIds.includes(d.id));
  }
);

export const getActiveDatasets = createSelector(
  [getLayersDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.env === 'production');
  }
);

export const getBoundaryDatasets = createSelector(
  [getParsedDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.env === 'production' && d.isBoundary);
  }
);

export const getActiveBoundaries = createSelector(
  [getBoundaryDatasets, getLayers],
  (datasets, layers) => {
    if (isEmpty(datasets)) return null;
    const layerIds = layers.map(layer => layer.dataset);
    return datasets.find(d => layerIds.includes(d.dataset));
  }
);

export const getDatasetsWithConfig = createSelector(
  [getActiveDatasets, getLayers],
  (datasets, allLayers) => {
    if (isEmpty(datasets) || isEmpty(allLayers)) return null;

    return datasets.map(d => {
      const layerConfig = allLayers.find(l => l.dataset === d.id) || {};
      const {
        params,
        sqlParams,
        decodeParams,
        timelineParams,
        layers,
        visibility,
        opacity
      } =
        layerConfig || {};

      return {
        ...d,
        ...layerConfig,
        ...(d.selectorLayerConfig && {
          selectorLayerConfig: {
            ...d.selectorLayerConfig,
            selected: d.selectorLayerConfig.options.find(
              l => l.value === layers[0]
            )
          }
        }),
        layers: d.layers.map(l => {
          const { hasParamsTimeline, hasDecodeTimeline } = l;

          return {
            ...l,
            visibility,
            opacity,
            active: layers && layers.includes(l.id),
            ...(!isEmpty(l.params) && {
              params: {
                ...l.params,
                ...params,
                ...(hasParamsTimeline && {
                  ...timelineParams
                })
              }
            }),
            ...(!isEmpty(l.sqlParams) && {
              sqlParams: {
                ...l.sqlParams,
                ...sqlParams
              }
            }),
            ...(!isEmpty(l.decodeParams) &&
              l.decodeFunction && {
                decodeParams: {
                  ...l.decodeParams,
                  ...(layers &&
                    layers.includes('confirmedOnly') && {
                      confirmedOnly: true
                    }),
                  ...decodeParams,
                  ...(hasDecodeTimeline && {
                    ...timelineParams
                  })
                }
              }),
            ...((l.hasParamsTimeline || l.hasDecodeTimeline) && {
              timelineConfig: {
                ...(l.hasParamsTimeline && {
                  ...l.params
                }),
                ...(l.hasDecodeTimeline && {
                  ...l.decodeParams
                }),
                ...timelineParams
              }
            })
          };
        })
      };
    });
  }
);

export const getLayerGroups = createSelector(
  [getDatasetsWithConfig, getLayers],
  (datasets, layers) => {
    if (isEmpty(datasets) || isEmpty(layers)) return null;
    return layers.map(l => datasets.find(d => d.id === l.dataset));
  }
);

export const getLegendLayerGroups = createSelector([getLayerGroups], groups => {
  if (!groups) return null;
  return groups.filter(g => !g.isBoundary);
});

export const getActiveLayers = createSelector(getLayerGroups, layerGroups => {
  if (isEmpty(layerGroups)) return [];
  return flatten(layerGroups.map(d => d.layers)).filter(l => l.active);
});

export const getMapProps = createStructuredSelector({
  layers: getLayers,
  settings: getMapSettings,
  mapOptions: getMapOptions,
  basemap: getBasemap,
  label: getLabels,
  layerGroups: getLayerGroups,
  activeLayers: getActiveLayers,
  loading: getLoading,
  bbox: getBBox
});
<commit_msg>remove dependancy on recent date key<commit_after>import { createSelector, createStructuredSelector } from 'reselect';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import sortBy from 'lodash/sortBy';
import moment from 'moment';

import { formatDate } from 'utils/dates';
import { deburrUpper } from 'utils/data';

import thresholdOptions from 'data/thresholds.json';

import initialState from './map-initial-state';
import decodeLayersConfig from './map-decode-config';
import statements from './map-statement-config';

// get list data
const getMapUrlState = state => (state.query && state.query.map) || null;
const getDatasets = state => state.datasets.filter(d => !isEmpty(d.layer));
const getLoading = state => state.loading;
const getGeostore = state => state.geostore || null;
const getLatest = state => state.latest || null;
const getCountries = state => state.countries || null;

const reduceParams = (params, latestDate) => {
  if (!params) return null;
  return params.reduce((obj, param) => {
    const { format, key, interval, count } = param;
    let paramValue = param.default;
    const isDate = deburrUpper(param.key).includes('DATE');
    if (isDate && !paramValue) {
      let date = latestDate || formatDate(new Date());
      if (interval && count) date = moment(date).subtract(count, interval);
      paramValue = moment(date).format(format || 'YYYY-MM-DD');
    }

    const newObj = {
      ...obj,
      [key]: paramValue
    };
    return newObj;
  }, {});
};

const reduceSqlParams = params => {
  if (!params) return null;
  return params.reduce((obj, param) => {
    const newObj = {
      ...obj,
      [param.key]: param.key_params.reduce((subObj, item) => {
        const keyValues = {
          ...subObj,
          [item.key]: item.value
        };
        return keyValues;
      }, {})
    };
    return newObj;
  }, {});
};

export const getMapSettings = createSelector([getMapUrlState], urlState => ({
  ...initialState,
  ...urlState
}));

export const getLayers = createSelector(
  getMapSettings,
  settings => settings.layers
);

export const getBasemap = createSelector(
  getMapSettings,
  settings => settings.basemap
);

export const getMapZoom = createSelector(
  getMapSettings,
  settings => settings.zoom
);

export const getLabels = createSelector(
  getMapSettings,
  settings => settings.label
);

export const getBBox = createSelector(getGeostore, geostore => {
  const { bbox } = geostore;
  if (isEmpty(bbox)) return {};
  return { bbox, options: { padding: [10, 10] } };
});

export const getMapOptions = createSelector(getMapSettings, settings => {
  if (!settings) return null;
  const {
    center,
    zoom,
    minZoom,
    maxZoom,
    zoomControl,
    basemap,
    label,
    attributionControl
  } = settings;
  return {
    center,
    zoom,
    minZoom,
    maxZoom,
    zoomControl,
    label,
    basemap,
    attributionControl
  };
});

export const getParsedDatasets = createSelector(
  [getDatasets, getLatest, getCountries],
  (datasets, latest, countries) => {
    if (isEmpty(datasets) || isEmpty(latest) || isEmpty(countries)) return null;

    return datasets.filter(d => d.env === 'production').map(d => {
      const { layer, metadata } = d;
      const appMeta = metadata.find(m => m.application === 'gfw') || {};
      const { info } = appMeta || {};
      const defaultLayer =
        (layer &&
          layer.find(
            l =>
              l.env === 'production' &&
              l.applicationConfig &&
              l.applicationConfig.default
          )) ||
        layer[0];

      // we need a default layer so we can set it when toggled onto the map
      if (!defaultLayer) return null;

      const { isSelectorLayer, isMultiSelectorLayer, isLossLayer } = info || {};
      const { id, iso, applicationConfig } = defaultLayer || {};
      const { global } = applicationConfig || {};

      // build statement config
      let statementConfig = null;
      if (isLossLayer) {
        statementConfig = {
          ...statements.loss
        };
      } else if (global && !!iso.length && iso[0]) {
        statementConfig = {
          ...statements.isoLayer,
          tooltipDesc:
            countries &&
            countries
              .filter(c => iso.includes(c.value))
              .map(c => c.label)
              .join(', ')
        };
      }

      return {
        ...d,
        dataset: d.id,
        ...applicationConfig,
        ...info,
        layer: id,
        iso,
        tags: flatten(d.vocabulary.map(v => v.tags)),
        // dropdown selector config
        ...((isSelectorLayer || isMultiSelectorLayer) && {
          selectorLayerConfig: {
            options: layer.map(l => ({
              ...l.applicationConfig.selectorConfig,
              value: l.id
            }))
          }
        }),
        // disclaimer statement config
        statementConfig,
        // layers config
        layers:
          layer &&
          sortBy(
            layer
              .filter(l => l.env === 'production' && l.published)
              .map((l, i) => {
                const { layerConfig } = l;
                const { position, confirmedOnly, multiConfig } =
                  l.applicationConfig || {};
                const {
                  params_config,
                  decode_config,
                  sql_config,
                  body,
                  url
                } = layerConfig;
                const decodeFunction = decodeLayersConfig[l.id];
                const latestDate = latest && latest[l.id];

                // check for timeline params
                const hasParamsTimeline =
                  params_config &&
                  params_config.map(p => p.key).includes('startDate');
                const hasDecodeTimeline =
                  decode_config &&
                  decode_config.map(p => p.key).includes('startDate');
                const params =
                  params_config && reduceParams(params_config, latestDate);
                const decodeParams =
                  decode_config && reduceParams(decode_config, latestDate);
                const sqlParams = sql_config && reduceSqlParams(sql_config);

                return {
                  ...l,
                  ...l.applicationConfig,
                  // sorting position
                  position: l.applicationConfig.default
                    ? 0
                    : position ||
                      (multiConfig && multiConfig.position) ||
                      i + 1,
                  // check if needs timeline
                  hasParamsTimeline,
                  hasDecodeTimeline,
                  // params for tile url
                  ...(params && {
                    params: {
                      url: body.url || url,
                      ...params,
                      ...(hasParamsTimeline && {
                        minDate: params && params.startDate,
                        maxDate: params && params.endDate,
                        trimEndDate: params && params.endDate
                      })
                    }
                  }),
                  // params selector config
                  ...(params_config && {
                    paramsSelectorConfig: params_config.map(p => ({
                      ...p,
                      ...(p.key.includes('thresh') && {
                        sentence:
                          'Displaying {name} with {selector} canopy density',
                        options: thresholdOptions
                      })
                    }))
                  }),
                  // params for sql query
                  ...(sqlParams && {
                    sqlParams
                  }),
                  // decode func and params for canvas layers
                  ...decodeFunction,
                  ...(decodeParams && {
                    decodeParams: {
                      ...(decodeFunction && decodeFunction.decodeParams),
                      ...decodeParams,
                      ...(hasDecodeTimeline && {
                        minDate: decodeParams && decodeParams.startDate,
                        maxDate: decodeParams && decodeParams.endDate,
                        trimEndDate: decodeParams && decodeParams.endDate,
                        canPlay: true
                      })
                    }
                  }),
                  // special key for GLAD alerts
                  ...(confirmedOnly && {
                    id: 'confirmedOnly'
                  })
                };
              }),
            'position'
          )
      };
    });
  }
);

export const getDatasetIds = createSelector([getLayers], layers => {
  if (!layers || !layers.length) return null;
  return layers.map(l => l.dataset);
});

export const getLayersDatasets = createSelector(
  [getParsedDatasets, getDatasetIds],
  (datasets, datasetIds) => {
    if (isEmpty(datasets) || isEmpty(datasetIds)) return null;
    return datasets.filter(d => datasetIds.includes(d.id));
  }
);

export const getActiveDatasets = createSelector(
  [getLayersDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.env === 'production');
  }
);

export const getBoundaryDatasets = createSelector(
  [getParsedDatasets],
  datasets => {
    if (isEmpty(datasets)) return null;
    return datasets.filter(d => d.env === 'production' && d.isBoundary);
  }
);

export const getActiveBoundaries = createSelector(
  [getBoundaryDatasets, getLayers],
  (datasets, layers) => {
    if (isEmpty(datasets)) return null;
    const layerIds = layers.map(layer => layer.dataset);
    return datasets.find(d => layerIds.includes(d.dataset));
  }
);

export const getDatasetsWithConfig = createSelector(
  [getActiveDatasets, getLayers],
  (datasets, allLayers) => {
    if (isEmpty(datasets) || isEmpty(allLayers)) return null;

    return datasets.map(d => {
      const layerConfig = allLayers.find(l => l.dataset === d.id) || {};
      const {
        params,
        sqlParams,
        decodeParams,
        timelineParams,
        layers,
        visibility,
        opacity
      } =
        layerConfig || {};

      return {
        ...d,
        ...layerConfig,
        ...(d.selectorLayerConfig && {
          selectorLayerConfig: {
            ...d.selectorLayerConfig,
            selected: d.selectorLayerConfig.options.find(
              l => l.value === layers[0]
            )
          }
        }),
        layers: d.layers.map(l => {
          const { hasParamsTimeline, hasDecodeTimeline } = l;

          return {
            ...l,
            visibility,
            opacity,
            active: layers && layers.includes(l.id),
            ...(!isEmpty(l.params) && {
              params: {
                ...l.params,
                ...params,
                ...(hasParamsTimeline && {
                  ...timelineParams
                })
              }
            }),
            ...(!isEmpty(l.sqlParams) && {
              sqlParams: {
                ...l.sqlParams,
                ...sqlParams
              }
            }),
            ...(!isEmpty(l.decodeParams) &&
              l.decodeFunction && {
                decodeParams: {
                  ...l.decodeParams,
                  ...(layers &&
                    layers.includes('confirmedOnly') && {
                      confirmedOnly: true
                    }),
                  ...decodeParams,
                  ...(hasDecodeTimeline && {
                    ...timelineParams
                  })
                }
              }),
            ...((l.hasParamsTimeline || l.hasDecodeTimeline) && {
              timelineConfig: {
                ...(l.hasParamsTimeline && {
                  ...l.params
                }),
                ...(l.hasDecodeTimeline && {
                  ...l.decodeParams
                }),
                ...timelineParams
              }
            })
          };
        })
      };
    });
  }
);

export const getLayerGroups = createSelector(
  [getDatasetsWithConfig, getLayers],
  (datasets, layers) => {
    if (isEmpty(datasets) || isEmpty(layers)) return null;
    return layers.map(l => datasets.find(d => d.id === l.dataset));
  }
);

export const getLegendLayerGroups = createSelector([getLayerGroups], groups => {
  if (!groups) return null;
  return groups.filter(g => !g.isBoundary);
});

export const getActiveLayers = createSelector(getLayerGroups, layerGroups => {
  if (isEmpty(layerGroups)) return [];
  return flatten(layerGroups.map(d => d.layers)).filter(l => l.active);
});

export const getMapProps = createStructuredSelector({
  layers: getLayers,
  settings: getMapSettings,
  mapOptions: getMapOptions,
  basemap: getBasemap,
  label: getLabels,
  layerGroups: getLayerGroups,
  activeLayers: getActiveLayers,
  loading: getLoading,
  bbox: getBBox
});
<|endoftext|>


<commit_before>//Express
var express = require("express");
var app = express();

//Static directories
app.use(express.static("public"));
app.use(express.static("public/res"));
app.use(express.static("public/scripts"));
app.use(express.static("public/stylesheets"));
app.use(express.static("public/scripts/game"));
app.use(express.static("public/res/textures"));
app.use(express.static("public/res/models"));

//Routing
app.get("/", function(request, response){
  res.sendFile("index.html");
});

//Listen
var server = app.listen(3000, function(){

  //Debug
  console.log("Server listening on port 3000");
});
<commit_msg>Network server<commit_after>//Express
var express = require("express");
var app = express();

//Static directories
app.use(express.static("public"));
app.use(express.static("public/res"));
app.use(express.static("public/scripts"));
app.use(express.static("public/stylesheets"));
app.use(express.static("public/scripts/game"));
app.use(express.static("public/res/textures"));
app.use(express.static("public/res/models"));

//Routing
app.get("/", function(request, response){
  res.sendFile("index.html");
});

//Listen
var server = app.listen(80, function(){

  //Debug
  console.log("Server listening");
});
<|endoftext|>


<commit_before>var Canvas = {
    $element: null,
    canvas: null,
    context: null,

    loop: null,

    upscaleRatio: 2,

    init: function () {
        this.$element = $('#game');
        this.canvas = this.$element[0];
        this.context = this.canvas.getContext('2d');

        this.canvas.mozImageSmoothingEnabled = false;
        this.canvas.webkitImageSmoothingEnabled = false;
        this.canvas.msImageSmoothingEnabled = false;
        this.canvas.imageSmoothingEnabled = false;

        this.resize();
        $(window).resize(this.resize.bind(this));

        console.info('[Canvas] Starting render loop...');

        this.loop = this.renderLoop.bind(this);
        this.loop();
    },

    resize: function () {
        var $document = $(document);

        var w = $document.width();
        var h = $document.height();

        this.canvas.width = w / this.upscaleRatio;
        this.canvas.height = h / this.upscaleRatio;

        this.$element.css('width', w + 'px');
        this.$element.css('height', h + 'px');

        console.info('[Canvas] Resize complete (' + w + 'px by ' + h + 'px)');
    },

    renderLoop: function () {
        window.requestAnimationFrame(this.loop);

        this.clear();

        Game.update();
        Game.draw(this.context);
    },

    clear: function () {
        this.context.fillStyle = '#000';
        this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }
};<commit_msg>Set canvas clear color to look like water, so it feels less out of place when looking at the map edges<commit_after>var Canvas = {
    $element: null,
    canvas: null,
    context: null,

    loop: null,

    upscaleRatio: 2,

    init: function () {
        this.$element = $('#game');
        this.canvas = this.$element[0];
        this.context = this.canvas.getContext('2d');

        this.canvas.mozImageSmoothingEnabled = false;
        this.canvas.webkitImageSmoothingEnabled = false;
        this.canvas.msImageSmoothingEnabled = false;
        this.canvas.imageSmoothingEnabled = false;

        this.resize();
        $(window).resize(this.resize.bind(this));

        console.info('[Canvas] Starting render loop...');

        this.loop = this.renderLoop.bind(this);
        this.loop();
    },

    resize: function () {
        var $document = $(document);

        var w = $document.width();
        var h = $document.height();

        this.canvas.width = w / this.upscaleRatio;
        this.canvas.height = h / this.upscaleRatio;

        this.$element.css('width', w + 'px');
        this.$element.css('height', h + 'px');

        console.info('[Canvas] Resize complete (' + w + 'px by ' + h + 'px)');
    },

    renderLoop: function () {
        window.requestAnimationFrame(this.loop);

        this.clear();

        Game.update();
        Game.draw(this.context);
    },

    clear: function () {
        this.context.fillStyle = '#637FBF';
        this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }
};<|endoftext|>


<commit_before>module.exports = function () {
    var getResources = function() {
        var resources = {
            "lumber": {
                "name" : "Lumber",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "lumber": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "lumber",
                "gatherDescription" : "Spend a few hours cutting down lumber for use in the village"
            },
            "firewood": {
                "name" : "Firewood",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "firewood": 5
                },
                "decreaseAmounts": {
                    "lumber": 1
                },
                "timeToGather": 2,
                "resourceKey" : "firewood",
                "gatherDescription" : "Split a piece of lumber into firewood to keep your cabin warm."
            },
            "food": {
                "name" : "Food",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "food": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "food",
                "gatherDescription" : "Harvest some of the bountiful berries and nuts from the surrounding forest for dinner tonight."
            },
            "water": {
                "name" : "Water",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "water": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "water",
                "gatherDescription" : "Fetch a bucket of water from the nearby freshwater stream, then boil it to ensure it is safe to drink."
            },
            "stone": {
                "name" : "Stone",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "stone": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "stone",
                "gatherDescription" : "Gather rock chipped from the island's cliffs to help with construction efforts."
            }

        };
        return resources;
    };

    var getBaseResources = function() {
        var resources = getResources();
        var baseResources = {};

        for (var i in resources) {
            baseResources[i] = {};
            baseResources[i].amount = resources[i].baseAmount;
            baseResources[i].description = resources[i].gatherDescription;
            baseResources[i].name = resources[i].name;
            baseResources[i].resKey = i;
        }

        return baseResources;
    };

    var getBuildings = function() {
        var buildingsStats = {
            "sawMill": {
                "name": "Saw Mill",
                "description": "A factory where machines saw logs into lumber.",
                "resourceMultipliers": {
                    "lumber": 1.25
                }
            }
        };
        return buildingsStats;
    };

    return {
        getResources : getResources,
        getBaseResources : getBaseResources,
        getBuildings : getBuildings
    };
};
<commit_msg>add two more buildings<commit_after>module.exports = function () {
    var getResources = function() {
        var resources = {
            "lumber": {
                "name" : "Lumber",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "lumber": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "lumber",
                "gatherDescription" : "Spend a few hours cutting down lumber for use in the village"
            },
            "firewood": {
                "name" : "Firewood",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "firewood": 5
                },
                "decreaseAmounts": {
                    "lumber": 1
                },
                "timeToGather": 2,
                "resourceKey" : "firewood",
                "gatherDescription" : "Split a piece of lumber into firewood to keep your cabin warm."
            },
            "food": {
                "name" : "Food",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "food": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "food",
                "gatherDescription" : "Harvest some of the bountiful berries and nuts from the surrounding forest for dinner tonight."
            },
            "water": {
                "name" : "Water",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "water": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "water",
                "gatherDescription" : "Fetch a bucket of water from the nearby freshwater stream, then boil it to ensure it is safe to drink."
            },
            "stone": {
                "name" : "Stone",
                "baseAmount" : 10,
                "increaseAmounts" : {
                    "stone": 3
                },
                "decreaseAmounts": {

                },
                "timeToGather": 2,
                "resourceKey" : "stone",
                "gatherDescription" : "Gather rock chipped from the island's cliffs to help with construction efforts."
            }

        };
        return resources;
    };

    var getBaseResources = function() {
        var resources = getResources();
        var baseResources = {};

        for (var i in resources) {
            baseResources[i] = {};
            baseResources[i].amount = resources[i].baseAmount;
            baseResources[i].description = resources[i].gatherDescription;
            baseResources[i].name = resources[i].name;
            baseResources[i].resKey = i;
        }

        return baseResources;
    };

    var getBuildings = function() {
        var buildingsStats = {
            "sawMill": {
                "name": "Saw Mill",
                "description": "A factory where machines saw logs into lumber.",
                "resourceMultipliers": {
                    "lumber": 1.25
                }
            },
            "well": {
                "name": "Well",
                "description": "A more convenient place to collect water.",
                "resourceMultipliers": {
                    "water": 2
                }
            },
            "quarry": {
                "name": "Quarry",
                "description": "A big hole in the ground... with lots of rocks.",
                "resourceMultipliers": {
                    "stone": 1.25
                }
            }
        };
        return buildingsStats;
    };

    return {
        getResources : getResources,
        getBaseResources : getBaseResources,
        getBuildings : getBuildings
    };
};
<|endoftext|>


<commit_before>'use strict';
const mongoose = require('mongoose');
const jwt_auth = require('../lib/jwt_auth');

mongoose.connect('mongodb://localhost/auth_test');
app = require('express')();
const authRouter = require('../route/user_router');
app.use('/api', authRouter);
app.get('/api/jwt_auth', jwt_auth, function(req, res){
  res.json({msg: 'sucess!'});
});

app.use((err, req, res, next) => {
  res.status(err.statusCode).json(err.message);
});
app.listen(5000);
<commit_msg>added use strict and let to declare app variable in test_server<commit_after>'use strict';
const mongoose = require('mongoose');
const jwt_auth = require('../lib/jwt_auth');

mongoose.connect('mongodb://localhost/auth_test');
let app = require('express')();
const authRouter = require('../route/user_router');
app.use('/api', authRouter);
app.get('/api/jwt_auth', jwt_auth, function(req, res){
  res.json({msg: 'sucess!'});
});

app.use((err, req, res, next) => {
  res.status(err.statusCode).json(err.message);
});
app.listen(5000);
<|endoftext|>