<commit_before>/**
 * @constructor
 * @property {MapInfo.Object[]} objects
 * @property {MapInfo.Npc[]} npcs
 * @property {MapInfo.Building[]} buildings
 * @property {MapInfo.Sound[]} sounds
 * @property {MapInfo.Effect[]} effects
 * @property {MapInfo.Animation[]} animations
 * @property {MapInfo.WaterPatches} waterPatches
 * @property {MapInfo.MonsterSpawn[]} monsterSpawns
 * @property {MapInfo.WaterPlane[]} waterPlanes
 * @property {MapInfo.WarpPoint[]} warps
 * @property {MapInfo.Collision[]} collisions
 * @property {MapInfo.Event[]} events
 */
var MapInfo = function() {
  this.objects = [];
  this.npcs = [];
  this.buildings = [];
  this.sounds = [];
  this.effects = [];
  this.animations = [];
  this.monsterSpawns = [];
  this.waterPlanes = [];
  this.warps = [];
  this.collisions = [];
  this.events = [];
};

/**
 * @enum {Number}
 * @readonly
 */
MapInfo.BLOCK_TYPE = {
  INFO: 0,
  OBJECT: 1,
  NPC: 2,
  BUILDING: 3,
  SOUND: 4,
  EFFECT: 5,
  ANIMATION: 6,
  WATER_PATCH: 7,
  MONSTER_SPAWN: 8,
  WATER_PLANE: 9,
  WARP_POINT: 10,
  COLLISION_OBJECT: 11,
  EVENT_OBJECT: 12
};

/**
 * @constructor
 * @property {String} name
 * @property {Number} warpId
 * @property {Number} eventId
 * @property {Number} objectType
 * @property {Number} objectId
 * @property {THREE.Vector2} mapPosition
 * @property {THREE.Quaternion} rotation
 * @property {THREE.Vector3} position
 * @property {THREE.Vector3} scale
 */
MapInfo.MapObject = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Object = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {Number} aiIndex
 * @property {String} conFilePath
 */
MapInfo.Npc = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Building = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} filePath
 * @property {Number} range
 * @property {Number} interval
 */
MapInfo.Sound = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} filePath
 */
MapInfo.Effect = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Animation = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {Number} width
 * @property {Number} height
 * @property {MapInfo.WaterPatches.Patch[]} patches
 */
MapInfo.WaterPatches = function() {
};

/**
 * @constructor
 * @property {Boolean} hasWater
 * @property {Number} height
 * @property {Number} type
 * @property {Number} id
 * @property {Number} unknown
 */
MapInfo.WaterPatches.Patch = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} name
 * @property {MapInfo.MonsterSpawn.Spawn[]} normal
 * @property {MapInfo.MonsterSpawn.Spawn[]} tactical
 * @property {Number} interval
 * @property {Number} limit
 * @property {Number} range
 * @property {Number} tacticalPoints
 */
MapInfo.MonsterSpawn = function() {
};

/**
 * @constructor
 * @property {String} name
 * @property {Number} monster
 * @property {Number} count
 */
MapInfo.MonsterSpawn.Spawn = function() {
};

/**
 * @constructor
 * @property {THREE.Vector3} start
 * @property {THREE.Vector3} end
 */
MapInfo.WaterPlane = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.WarpPoint = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Collision = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} funcName
 * @property {String} conFilePath
 */
MapInfo.Event = function() {
};

/**
 * @param {BinaryReader} rh
 * @param {Object} object
 * @returns {Object}
 */
MapInfo.loadMapObject = function(rh, object) {
  object.name        = rh.readByteStr();
  object.warpId      = rh.readUint16();
  object.eventId     = rh.readUint16();
  object.objectType  = rh.readUint32();
  object.objectId    = rh.readUint32();
  object.mapPosition = rh.readIntVector2();
  object.rotation    = rh.readQuat();
  object.position    = rh.readVector3().multiplyScalar(ZZ_SCALE_IN);
  object.scale       = rh.readVector3();
  return object;
};

/**
 * @callback MapInfo~onLoad
 * @param {MapInfo} mapInfo
 */

/**
 * @param {String} path
 * @param {MapInfo~onLoad} callback
 */
MapInfo.load = function(path, callback) {
  ROSELoader.load(path, function(/** BinaryReader */rh) {
    var blocks, count, i, j, k, object, offset, pos, type;
    var data = new MapInfo();

    blocks = rh.readUint32();
    for (i = 0; i < blocks; ++i) {
      type   = rh.readUint32();
      offset = rh.readUint32();
      pos    = rh.tell();

      rh.seek(offset);
      if (type === MapInfo.BLOCK_TYPE.INFO) {
        // Unknown format
      } else if (type === MapInfo.BLOCK_TYPE.WATER_PATCH) {
        object = MapInfo.loadMapObject(rh, new MapInfo.WaterPatches());
        object.width  = rh.readUint32();
        object.height = rh.readUint32();

        for (j = 0; j < object.width * object.height; ++j) {
          var patch = new MapInfo.WaterPatches.Patch();
          patch.hasWater = rh.readUint8() !== 0;
          patch.height   = rh.readFloat();
          patch.type     = rh.readUint32();
          patch.id       = rh.readUint32();
          patch.unknown  = rh.readUint32();
          object.patches[j] = patch;
        }

        data.waterPatches = object;
      } else {
        if (type === MapInfo.BLOCK_TYPE.WATER_PLANE) {
          data.waterPlanes.waterSize = rh.readFloat();
        }

        var objects = rh.readUint32();
        for (j = 0; j < objects; ++j) {
          switch (type) {
          case MapInfo.BLOCK_TYPE.OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Object());
            data.objects.push(object);
            break;
          case MapInfo.BLOCK_TYPE.NPC:
            object = MapInfo.loadMapObject(rh, new MapInfo.Npc());
            object.aiIndex = rh.readUint32();
            object.conFilePath = rh.readByteStr();
            data.npcs.push(object);
            break;
          case MapInfo.BLOCK_TYPE.BUILDING:
            object = MapInfo.loadMapObject(rh, new MapInfo.Building());
            data.buildings.push(object);
            break;
          case MapInfo.BLOCK_TYPE.SOUND:
            object = MapInfo.loadMapObject(rh, new MapInfo.Sound());
            object.filePath = rh.readByteStr();
            object.range = rh.readUint32();
            object.interval = rh.readUint32();
            data.sounds.push(object);
            break;
          case MapInfo.BLOCK_TYPE.EFFECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Effect());
            object.filePath = rh.readByteStr();
            data.effects.push(object);
            break;
          case MapInfo.BLOCK_TYPE.ANIMATION:
            object = MapInfo.loadMapObject(rh, new MapInfo.Animation());
            data.animations.push(object);
            break;
          case MapInfo.BLOCK_TYPE.MONSTER_SPAWN:
            object = MapInfo.loadMapObject(rh, new MapInfo.MonsterSpawn());
            object.name = rh.readByteStr();

            count = rh.readUint32();
            for (k = 0; k < count; ++k) {
              var spawn = new MapInfo.MonsterSpawn.Spawn();
              spawn.name = rh.readByteStr();
              spawn.monster = rh.readUint32();
              spawn.count = rh.readUint32();
              object.normal.push(spawn);
            }

            count = rh.readUint32();
            for (k = 0; k < count; ++k) {
              var spawn = new MapInfo.MonsterSpawn.Spawn();
              spawn.name = rh.readByteStr();
              spawn.monster = rh.readUint32();
              spawn.count = rh.readUint32();
              object.tactical.push(spawn);
            }

            object.interval = rh.readUint32();
            object.limit = rh.readUint32();
            object.range = rh.readUint32();
            object.tacticalPoints = rh.readUint32();
            data.monsterSpawns.push(object);
            break;
          case MapInfo.BLOCK_TYPE.WATER_PLANE:
            object = new MapInfo.WaterPlane();
            object.start = rh.readVector3();
            object.end = rh.readVector3();
            data.waterPlanes.push(object);
            break;
          case MapInfo.BLOCK_TYPE.WARP_POINT:
            object = MapInfo.loadMapObject(rh, new MapInfo.WarpPoint());
            data.warps.push(object);
            break;
          case MapInfo.BLOCK_TYPE.COLLISION_OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Collision());
            data.collisions.push(object);
            break;
          case MapInfo.BLOCK_TYPE.EVENT_OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Event());
            object.funcName = rh.readByteStr();
            object.conFilePath = rh.readByteStr();
            data.events.push(object);
            break;
          default:
            break;
          }
        }
      }
      rh.seek(pos);
    }

    callback(data);
  });
};
<commit_msg>Fix uninitialised monsterspawn normal and tactical<commit_after>/**
 * @constructor
 * @property {MapInfo.Object[]} objects
 * @property {MapInfo.Npc[]} npcs
 * @property {MapInfo.Building[]} buildings
 * @property {MapInfo.Sound[]} sounds
 * @property {MapInfo.Effect[]} effects
 * @property {MapInfo.Animation[]} animations
 * @property {MapInfo.WaterPatches} waterPatches
 * @property {MapInfo.MonsterSpawn[]} monsterSpawns
 * @property {MapInfo.WaterPlane[]} waterPlanes
 * @property {MapInfo.WarpPoint[]} warps
 * @property {MapInfo.Collision[]} collisions
 * @property {MapInfo.Event[]} events
 */
var MapInfo = function() {
  this.objects = [];
  this.npcs = [];
  this.buildings = [];
  this.sounds = [];
  this.effects = [];
  this.animations = [];
  this.monsterSpawns = [];
  this.waterPlanes = [];
  this.warps = [];
  this.collisions = [];
  this.events = [];
};

/**
 * @enum {Number}
 * @readonly
 */
MapInfo.BLOCK_TYPE = {
  INFO: 0,
  OBJECT: 1,
  NPC: 2,
  BUILDING: 3,
  SOUND: 4,
  EFFECT: 5,
  ANIMATION: 6,
  WATER_PATCH: 7,
  MONSTER_SPAWN: 8,
  WATER_PLANE: 9,
  WARP_POINT: 10,
  COLLISION_OBJECT: 11,
  EVENT_OBJECT: 12
};

/**
 * @constructor
 * @property {String} name
 * @property {Number} warpId
 * @property {Number} eventId
 * @property {Number} objectType
 * @property {Number} objectId
 * @property {THREE.Vector2} mapPosition
 * @property {THREE.Quaternion} rotation
 * @property {THREE.Vector3} position
 * @property {THREE.Vector3} scale
 */
MapInfo.MapObject = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Object = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {Number} aiIndex
 * @property {String} conFilePath
 */
MapInfo.Npc = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Building = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} filePath
 * @property {Number} range
 * @property {Number} interval
 */
MapInfo.Sound = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} filePath
 */
MapInfo.Effect = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Animation = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {Number} width
 * @property {Number} height
 * @property {MapInfo.WaterPatches.Patch[]} patches
 */
MapInfo.WaterPatches = function() {
};

/**
 * @constructor
 * @property {Boolean} hasWater
 * @property {Number} height
 * @property {Number} type
 * @property {Number} id
 * @property {Number} unknown
 */
MapInfo.WaterPatches.Patch = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} name
 * @property {MapInfo.MonsterSpawn.Spawn[]} normal
 * @property {MapInfo.MonsterSpawn.Spawn[]} tactical
 * @property {Number} interval
 * @property {Number} limit
 * @property {Number} range
 * @property {Number} tacticalPoints
 */
MapInfo.MonsterSpawn = function() {
  this.normal = [];
  this.tactical = [];
};

/**
 * @constructor
 * @property {String} name
 * @property {Number} monster
 * @property {Number} count
 */
MapInfo.MonsterSpawn.Spawn = function() {
};

/**
 * @constructor
 * @property {THREE.Vector3} start
 * @property {THREE.Vector3} end
 */
MapInfo.WaterPlane = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.WarpPoint = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 */
MapInfo.Collision = function() {
};

/**
 * @constructor
 * @extends {MapInfo.MapObject}
 * @property {String} funcName
 * @property {String} conFilePath
 */
MapInfo.Event = function() {
};

/**
 * @param {BinaryReader} rh
 * @param {Object} object
 * @returns {Object}
 */
MapInfo.loadMapObject = function(rh, object) {
  object.name        = rh.readByteStr();
  object.warpId      = rh.readUint16();
  object.eventId     = rh.readUint16();
  object.objectType  = rh.readUint32();
  object.objectId    = rh.readUint32();
  object.mapPosition = rh.readIntVector2();
  object.rotation    = rh.readQuat();
  object.position    = rh.readVector3().multiplyScalar(ZZ_SCALE_IN);
  object.scale       = rh.readVector3();
  return object;
};

/**
 * @callback MapInfo~onLoad
 * @param {MapInfo} mapInfo
 */

/**
 * @param {String} path
 * @param {MapInfo~onLoad} callback
 */
MapInfo.load = function(path, callback) {
  ROSELoader.load(path, function(/** BinaryReader */rh) {
    var blocks, count, i, j, k, object, offset, pos, type;
    var data = new MapInfo();

    blocks = rh.readUint32();
    for (i = 0; i < blocks; ++i) {
      type   = rh.readUint32();
      offset = rh.readUint32();
      pos    = rh.tell();

      rh.seek(offset);
      if (type === MapInfo.BLOCK_TYPE.INFO) {
        // Unknown format
      } else if (type === MapInfo.BLOCK_TYPE.WATER_PATCH) {
        object = MapInfo.loadMapObject(rh, new MapInfo.WaterPatches());
        object.width  = rh.readUint32();
        object.height = rh.readUint32();

        for (j = 0; j < object.width * object.height; ++j) {
          var patch = new MapInfo.WaterPatches.Patch();
          patch.hasWater = rh.readUint8() !== 0;
          patch.height   = rh.readFloat();
          patch.type     = rh.readUint32();
          patch.id       = rh.readUint32();
          patch.unknown  = rh.readUint32();
          object.patches[j] = patch;
        }

        data.waterPatches = object;
      } else {
        if (type === MapInfo.BLOCK_TYPE.WATER_PLANE) {
          data.waterPlanes.waterSize = rh.readFloat();
        }

        var objects = rh.readUint32();
        for (j = 0; j < objects; ++j) {
          switch (type) {
          case MapInfo.BLOCK_TYPE.OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Object());
            data.objects.push(object);
            break;
          case MapInfo.BLOCK_TYPE.NPC:
            object = MapInfo.loadMapObject(rh, new MapInfo.Npc());
            object.aiIndex = rh.readUint32();
            object.conFilePath = rh.readByteStr();
            data.npcs.push(object);
            break;
          case MapInfo.BLOCK_TYPE.BUILDING:
            object = MapInfo.loadMapObject(rh, new MapInfo.Building());
            data.buildings.push(object);
            break;
          case MapInfo.BLOCK_TYPE.SOUND:
            object = MapInfo.loadMapObject(rh, new MapInfo.Sound());
            object.filePath = rh.readByteStr();
            object.range = rh.readUint32();
            object.interval = rh.readUint32();
            data.sounds.push(object);
            break;
          case MapInfo.BLOCK_TYPE.EFFECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Effect());
            object.filePath = rh.readByteStr();
            data.effects.push(object);
            break;
          case MapInfo.BLOCK_TYPE.ANIMATION:
            object = MapInfo.loadMapObject(rh, new MapInfo.Animation());
            data.animations.push(object);
            break;
          case MapInfo.BLOCK_TYPE.MONSTER_SPAWN:
            object = MapInfo.loadMapObject(rh, new MapInfo.MonsterSpawn());
            object.name = rh.readByteStr();

            count = rh.readUint32();
            for (k = 0; k < count; ++k) {
              var spawn = new MapInfo.MonsterSpawn.Spawn();
              spawn.name = rh.readByteStr();
              spawn.monster = rh.readUint32();
              spawn.count = rh.readUint32();
              object.normal.push(spawn);
            }

            count = rh.readUint32();
            for (k = 0; k < count; ++k) {
              var spawn = new MapInfo.MonsterSpawn.Spawn();
              spawn.name = rh.readByteStr();
              spawn.monster = rh.readUint32();
              spawn.count = rh.readUint32();
              object.tactical.push(spawn);
            }

            object.interval = rh.readUint32();
            object.limit = rh.readUint32();
            object.range = rh.readUint32();
            object.tacticalPoints = rh.readUint32();
            data.monsterSpawns.push(object);
            break;
          case MapInfo.BLOCK_TYPE.WATER_PLANE:
            object = new MapInfo.WaterPlane();
            object.start = rh.readVector3();
            object.end = rh.readVector3();
            data.waterPlanes.push(object);
            break;
          case MapInfo.BLOCK_TYPE.WARP_POINT:
            object = MapInfo.loadMapObject(rh, new MapInfo.WarpPoint());
            data.warps.push(object);
            break;
          case MapInfo.BLOCK_TYPE.COLLISION_OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Collision());
            data.collisions.push(object);
            break;
          case MapInfo.BLOCK_TYPE.EVENT_OBJECT:
            object = MapInfo.loadMapObject(rh, new MapInfo.Event());
            object.funcName = rh.readByteStr();
            object.conFilePath = rh.readByteStr();
            data.events.push(object);
            break;
          default:
            break;
          }
        }
      }
      rh.seek(pos);
    }

    callback(data);
  });
};
<|endoftext|>


<commit_before>'use strict';

const stream = require('stream');
const AsyncIter = require('./asyncIter');

exports.Writable = stream.Writable;

class Readable extends stream.Readable {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Readable = Readable;

class Transform extends stream.Transform {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Transform = Transform;

class Duplex extends stream.Duplex {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Duplex = Duplex;

class PassThrough extends stream.PassThrough {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.PassThrough = PassThrough;

exports.wrap = function (stream) {
  if (typeof stream[Symbol.asyncIterator] !== 'function') {
    stream[Symbol.asyncIterator] = function () {
      return new AsyncIter(this);
    }
  }
  return stream;
}
<commit_msg>we automatically upgrade the wraped stream to streams2<commit_after>'use strict';

const stream = require('stream');
const AsyncIter = require('./asyncIter');

exports.Writable = stream.Writable;

class Readable extends stream.Readable {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Readable = Readable;

class Transform extends stream.Transform {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Transform = Transform;

class Duplex extends stream.Duplex {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.Duplex = Duplex;

class PassThrough extends stream.PassThrough {
  [Symbol.asyncIterator]() {
    return new AsyncIter(this);
  }
}
exports.PassThrough = PassThrough;

exports.wrap = function (stream) {
  if (!stream._readableState) {
    stream = new stream.Readable().wrap(stream);
  }
  if (typeof stream[Symbol.asyncIterator] !== 'function') {
    stream[Symbol.asyncIterator] = function () {
      return new AsyncIter(this);
    }
  }
  return stream;
}
<|endoftext|>


<commit_before>/**
 * @constructor
 * @property {RangeVector3} bounds
 * @property {THREE.Vector3[]} vertices
 * @property {THREE.Vector3[]} normals
 * @property {THREE.Vector3[]} tangents
 * @property {Colour3[]} colours
 * @property {THREE.Face3[]} faces
 * @property {{weights: THREE.Vector4[], indices: THREE.Vector4[]}} skin
 * @property {THREE.Vector2[][]} uv
 */
var Mesh = function() {
  this.bounds = {};
  this.vertices = [];
  this.normals = [];
  this.tangents = [];
  this.colours = [];
  this.faces = [];
  this.skin = {
    weights: [],
    indices: []
  };
  this.uv = [
    [], [], [], []
  ];
};

/**
 * Mesh format flags
 *
 * @enum {Number}
 */
Mesh.FORMAT = {
  NONE:         1 << 0,
  POSITION:     1 << 1,
  NORMAL:       1 << 2,
  COLOUR:       1 << 3,
  BLEND_WEIGHT: 1 << 4,
  BLEND_INDEX:  1 << 5,
  TANGENT:      1 << 6,
  UV1:          1 << 7,
  UV2:          1 << 8,
  UV3:          1 << 9,
  UV4:          1 << 10
};

/**
 * @param {BinaryReader} rh
 * @returns {Mesh}
 */
Mesh.loadMesh8 = function(rh) {
  var bones, boneTable, faces, format, i, vertices;
  var data = new Mesh();

  format = rh.readUint32();
  data.bounds.min = rh.readVector3();
  data.bounds.max = rh.readVector3();

  bones = rh.readUint16();
  boneTable = [];
  for (i = 0; i < bones; ++i) {
    boneTable.push(rh.readUint16());
  }

  vertices = rh.readUint16();
  data.vertices = rh.readFloatArray(3 * vertices);

  if (format & Mesh.FORMAT.NORMAL) {
    data.normals = rh.readFloatArray(3 * vertices);
  }

  if (format & Mesh.FORMAT.COLOUR) {
    data.colours = rh.readFloatArray(4 * vertices);
  }

  if (format & (Mesh.FORMAT.BLEND_INDEX | Mesh.FORMAT.BLEND_WEIGHT)) {
    var skinIdx = 0;
    data.skin.weights = new Float32Array(4 * vertices);
    data.skin.indices = new Float32Array(4 * vertices);

    for (i = 0; i < vertices; ++i) {
      data.skin.weights[skinIdx + 0] = rh.readFloat();
      data.skin.weights[skinIdx + 1] = rh.readFloat();
      data.skin.weights[skinIdx + 2] = rh.readFloat();
      data.skin.weights[skinIdx + 3] = rh.readFloat();

      data.skin.indices[skinIdx + 0] = boneTable[rh.readUint16()];
      data.skin.indices[skinIdx + 1] = boneTable[rh.readUint16()];
      data.skin.indices[skinIdx + 2] = boneTable[rh.readUint16()];
      data.skin.indices[skinIdx + 3] = boneTable[rh.readUint16()];

      skinIdx += 4;
    }
  } else {
    data.skin.weights = null;
    data.skin.indices = null;
  }

  if (format & Mesh.FORMAT.TANGENT) {
    data.tangents = rh.readFloatArray(3 * vertices);
  }

  if (format & Mesh.FORMAT.UV1) {
    data.uv[0] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV2) {
    data.uv[1] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV3) {
    data.uv[2] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV4) {
    data.uv[2] = rh.readFloatArray(2 * vertices);
  }

  faces = rh.readUint16();
  data.faces = rh.readUint16Array(3 * faces);

  return data;
};

/**
 * @param {BinaryReader} rh
 * @returns {Mesh}
 */
Mesh.loadMesh6 = function(rh) {
  var bones, boneTable, faces, format, i, vertices;
  var data = new Mesh();

  format = rh.readUint32();
  data.bounds.min = rh.readVector3();
  data.bounds.max = rh.readVector3();

  bones = rh.readUint32();
  boneTable = [];
  for (i = 0; i < bones; ++i) {
    rh.skip(4);
    boneTable.push(rh.readUint32());
  }

  vertices = rh.readUint32();
  for (i = 0; i < vertices; ++i) {
    rh.skip(4);
    data.vertices.push(rh.readVector3());
  }

  if (format & Mesh.FORMAT.NORMAL) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.normals.push(rh.readVector3());
    }
  }

  if (format & Mesh.FORMAT.COLOUR) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.colours.push(rh.readColour4());
    }
  }

  if (format & (Mesh.FORMAT.BLEND_INDEX | Mesh.FORMAT.BLEND_WEIGHT)) {
    for (i = 0; i < vertices; ++i) {
      var weight1, weight2, weight3, weight4;
      var index1, index2, index3, index4;

      rh.skip(4);
      weight1 = rh.readFloat();
      weight2 = rh.readFloat();
      weight3 = rh.readFloat();
      weight4 = rh.readFloat();
      index1 = boneTable[rh.readUint32()];
      index2 = boneTable[rh.readUint32()];
      index3 = boneTable[rh.readUint32()];
      index4 = boneTable[rh.readUint32()];

      data.skin.weights.push(new THREE.Vector4(weight1, weight2, weight3, weight4));
      data.skin.indices.push(new THREE.Vector4(index1, index2, index3, index4));
    }
  }

  if (format & Mesh.FORMAT.TANGENT) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.tangents.push(rh.readVector3());
    }
  }

  if (format & Mesh.FORMAT.UV1) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.uv[0].push(rh.readVector2());
    }
  }

  if (format & Mesh.FORMAT.UV2) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.uv[1].push(rh.readVector2());
    }
  }
  if (format & Mesh.FORMAT.UV3) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.uv[2].push(rh.readVector2());
    }
  }

  if (format & Mesh.FORMAT.UV4) {
    for (i = 0; i < vertices; ++i) {
      rh.skip(4);
      data.uv[3].push(rh.readVector2());
    }
  }

  faces = rh.readUint32();
  for (i = 0; i < faces; ++i) {
    var v1, v2, v3;
    rh.skip(4);
    v1 = rh.readUint32();
    v2 = rh.readUint32();
    v3 = rh.readUint32();
    data.faces.push(new THREE.Face3(v1, v2, v3));
  }

  return data;
};

/**
 * @returns {THREE.BufferGeometry}
 */
Mesh.prototype.createBufferGeometry = function() {
  var geometry;
  geometry = new THREE.BufferGeometry();

  geometry.addAttribute('position', new THREE.BufferAttribute(this.vertices, 3));
  geometry.addAttribute('index', new THREE.BufferAttribute(this.faces, 3));

  if (this.skin.weights) {
    geometry.addAttribute('skinWeight', new THREE.BufferAttribute(this.skin.weights, 4));
  }

  if (this.skin.indices) {
    geometry.addAttribute('skinIndex', new THREE.BufferAttribute(this.skin.indices, 4));
  }

  if (this.uv[0]) {
    geometry.addAttribute('uv', new THREE.BufferAttribute(this.uv[0], 2));
  }

  if (this.uv[1]) {
    geometry.addAttribute('uv2', new THREE.BufferAttribute(this.uv[1], 2));
  }

  if (this.uv[2] || this.uv[3]) {
    // TODO: Fallback to normal Geometry
    throw 'BufferGeometry does not support more than 2 uv channels!';
  }

  geometry.computeBoundingSphere();
  geometry.computeFaceNormals();
  geometry.computeVertexNormals();
  return geometry;
};

/**
 * @returns {THREE.Geometry}
 */
Mesh.prototype.createGeometry = function() {
  var geometry, i, j;
  geometry = new THREE.Geometry();
  geometry.vertices = this.vertices;
  geometry.skinWeights = this.skin.weights;
  geometry.skinIndices = this.skin.indices;
  geometry.faces = this.faces;

  for (j = 0; j < 4; ++j) {
    if (this.uv[j].length > 0) {
      geometry.faceVertexUvs[j] = [];
    }
  }

  for (i = 0; i < this.faces.length; ++i) {
    for (j = 0; j < 4; ++j) {
      if (this.uv[j].length > 0) {
        var uv1, uv2, uv3;
        uv1 = this.uv[j][this.faces[i].a];
        uv2 = this.uv[j][this.faces[i].b];
        uv3 = this.uv[j][this.faces[i].c];
        geometry.faceVertexUvs[j].push([uv1, uv2, uv3]);
      }
    }
  }

  geometry.computeBoundingSphere();
  geometry.computeFaceNormals();
  geometry.computeVertexNormals();
  return geometry;
};

/**
 * @callback Mesh~onLoad
 * @param {THREE.Geometry} geometry
 */

/**
 * @param {String} path
 * @param {Mesh~onLoad} callback
 */
Mesh.load = function(path, callback) {
  ROSELoader.load(path, function(rh) {
    var version, magic, mesh;

    magic = rh.readStrLen(7);
    rh.skip(1);

    if (magic === 'ZMS0005') {
      version = 5;
    } else if (magic === 'ZMS0006') {
      version = 6;
    } else if (magic === 'ZMS0007') {
      version = 7;
    } else if (magic === 'ZMS0008') {
      version = 8;
    } else {
      throw 'Unexpected ZMS magic header ' + magic + ' in ' + path;
    }

    if (version >= 7) {
      mesh = Mesh.loadMesh8(rh);
      mesh = mesh.createBufferGeometry();
    } else {
      mesh = Mesh.loadMesh6(rh);
      mesh = mesh.createGeometry();
    }

    callback(mesh);
  });
};
<commit_msg>Add BufferGeometry support for ZMS 5 and 6.<commit_after>/**
 * @constructor
 * @property {{min: Vector3, max: Vector3}} bounds
 * @property {Float32Array} vertices
 * @property {Float32Array?} normals
 * @property {Float32Array?} tangents
 * @property {Float32Array?} colours
 * @property {Float32Array?} faces
 * @property {Float32Array?} skinWeights
 * @property {Float32Array?} skinIndices
 * @property {Float32Array?[]} uv
 */
var Mesh = function() {
  this.bounds = {};
  this.uv = [];
};

/**
 * Mesh format flags
 *
 * @enum {Number}
 */
Mesh.FORMAT = {
  NONE:         1 << 0,
  POSITION:     1 << 1,
  NORMAL:       1 << 2,
  COLOUR:       1 << 3,
  BLEND_WEIGHT: 1 << 4,
  BLEND_INDEX:  1 << 5,
  TANGENT:      1 << 6,
  UV1:          1 << 7,
  UV2:          1 << 8,
  UV3:          1 << 9,
  UV4:          1 << 10
};

/**
 * @param {BinaryReader} rh
 * @returns {Mesh}
 */
Mesh.loadMesh8 = function(rh) {
  var bones, boneTable, faces, format, i, vertices;
  var data = new Mesh();

  format = rh.readUint32();
  data.bounds.min = rh.readVector3();
  data.bounds.max = rh.readVector3();

  bones = rh.readUint16();
  boneTable = [];
  for (i = 0; i < bones; ++i) {
    boneTable.push(rh.readUint16());
  }

  vertices = rh.readUint16();
  data.vertices = rh.readFloatArray(3 * vertices);

  if (format & Mesh.FORMAT.NORMAL) {
    data.normals = rh.readFloatArray(3 * vertices);
  }

  if (format & Mesh.FORMAT.COLOUR) {
    data.colours = rh.readFloatArray(4 * vertices);
  }

  if (format & (Mesh.FORMAT.BLEND_INDEX | Mesh.FORMAT.BLEND_WEIGHT)) {
    var skinIdx = 0;
    data.skinWeights = new Float32Array(4 * vertices);
    data.skinIndices = new Float32Array(4 * vertices);

    for (i = 0; i < vertices; ++i) {
      data.skinWeights[skinIdx + 0] = rh.readFloat();
      data.skinWeights[skinIdx + 1] = rh.readFloat();
      data.skinWeights[skinIdx + 2] = rh.readFloat();
      data.skinWeights[skinIdx + 3] = rh.readFloat();

      data.skinIndices[skinIdx + 0] = boneTable[rh.readUint16()];
      data.skinIndices[skinIdx + 1] = boneTable[rh.readUint16()];
      data.skinIndices[skinIdx + 2] = boneTable[rh.readUint16()];
      data.skinIndices[skinIdx + 3] = boneTable[rh.readUint16()];

      skinIdx += 4;
    }
  }

  if (format & Mesh.FORMAT.TANGENT) {
    data.tangents = rh.readFloatArray(3 * vertices);
  }

  if (format & Mesh.FORMAT.UV1) {
    data.uv[0] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV2) {
    data.uv[1] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV3) {
    data.uv[2] = rh.readFloatArray(2 * vertices);
  }

  if (format & Mesh.FORMAT.UV4) {
    data.uv[2] = rh.readFloatArray(2 * vertices);
  }

  faces = rh.readUint16();
  data.faces = rh.readUint16Array(3 * faces);

  return data;
};

/**
 * @param {BinaryReader} rh
 * @returns {Mesh}
 */
Mesh.loadMesh6 = function(rh) {
  var bones, boneTable, faces, format, i, idx, vertices;
  var data = new Mesh();

  format = rh.readUint32();
  data.bounds.min = rh.readVector3();
  data.bounds.max = rh.readVector3();

  bones = rh.readUint32();
  boneTable = [];
  for (i = 0; i < bones; ++i) {
    rh.skip(4);
    boneTable.push(rh.readUint32());
  }

  vertices = rh.readUint32();
  data.vertices = new Float32Array(3 * vertices);
  for (i = 0, idx = 0; i < vertices; i += 1, idx += 3) {
    rh.skip(4);
    data.vertices[idx    ] = rh.readFloat();
    data.vertices[idx + 1] = rh.readFloat();
    data.vertices[idx + 2] = rh.readFloat();
  }

  if (format & Mesh.FORMAT.NORMAL) {
    data.normals = new Float32Array(3 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 3) {
      rh.skip(4);
      data.normals[idx + 0] = rh.readFloat();
      data.normals[idx + 1] = rh.readFloat();
      data.normals[idx + 2] = rh.readFloat();
    }
  }

  if (format & Mesh.FORMAT.COLOUR) {
    data.colours = new Float32Array(4 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 4) {
      rh.skip(4);
      data.colours[idx + 0] = rh.readFloat();
      data.colours[idx + 1] = rh.readFloat();
      data.colours[idx + 2] = rh.readFloat();
      data.colours[idx + 3] = rh.readFloat();
    }
  }

  if (format & (Mesh.FORMAT.BLEND_INDEX | Mesh.FORMAT.BLEND_WEIGHT)) {
    data.skinWeights = new Float32Array(4 * vertices);
    data.skinIndices = new Float32Array(4 * vertices);

    for (i = 0, idx = 0; i < vertices; i += 1, idx += 4) {
      rh.skip(4);
      data.skinWeights[idx + 0] = rh.readFloat();
      data.skinWeights[idx + 1] = rh.readFloat();
      data.skinWeights[idx + 2] = rh.readFloat();
      data.skinWeights[idx + 3] = rh.readFloat();

      data.skinIndices[idx + 0] = boneTable[rh.readUint32()];
      data.skinIndices[idx + 1] = boneTable[rh.readUint32()];
      data.skinIndices[idx + 2] = boneTable[rh.readUint32()];
      data.skinIndices[idx + 3] = boneTable[rh.readUint32()];
    }
  }

  if (format & Mesh.FORMAT.TANGENT) {
    data.tangents = new Float32Array(3 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 3) {
      rh.skip(4);
      data.tangents[idx + 0] = rh.readFloat();
      data.tangents[idx + 1] = rh.readFloat();
      data.tangents[idx + 2] = rh.readFloat();
    }
  }

  if (format & Mesh.FORMAT.UV1) {
    data.uv[0] = new Float32Array(2 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 2) {
      rh.skip(4);
      data.uv[0][idx + 0] = rh.readFloat();
      data.uv[0][idx + 1] = rh.readFloat();
    }
  }

  if (format & Mesh.FORMAT.UV2) {
    data.uv[1] = new Float32Array(2 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 2) {
      rh.skip(4);
      data.uv[1][idx + 0] = rh.readFloat();
      data.uv[1][idx + 1] = rh.readFloat();
    }
  }

  if (format & Mesh.FORMAT.UV3) {
    data.uv[2] = new Float32Array(2 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 2) {
      rh.skip(4);
      data.uv[2][idx + 0] = rh.readFloat();
      data.uv[2][idx + 1] = rh.readFloat();
    }
  }

  if (format & Mesh.FORMAT.UV4) {
    data.uv[3] = new Float32Array(2 * vertices);
    for (i = 0, idx = 0; i < vertices; i += 1, idx += 2) {
      rh.skip(4);
      data.uv[3][idx + 0] = rh.readFloat();
      data.uv[3][idx + 1] = rh.readFloat();
    }
  }

  faces = rh.readUint32();
  data.faces = new Uint16Array(3 * faces);
  for (i = 0, idx = 0; i < faces; i += 1, idx += 3) {
    rh.skip(4);
    data.faces[idx + 0] = rh.readUint32();
    data.faces[idx + 1] = rh.readUint32();
    data.faces[idx + 2] = rh.readUint32();
  }

  return data;
};

/**
 * @returns {THREE.BufferGeometry}
 */
Mesh.prototype.createBufferGeometry = function() {
  var geometry;
  geometry = new THREE.BufferGeometry();

  geometry.addAttribute('position', new THREE.BufferAttribute(this.vertices, 3));
  geometry.addAttribute('index', new THREE.BufferAttribute(this.faces, 3));

  if (this.skinWeights) {
    geometry.addAttribute('skinWeight', new THREE.BufferAttribute(this.skinWeights, 4));
  }

  if (this.skinIndices) {
    geometry.addAttribute('skinIndex', new THREE.BufferAttribute(this.skinIndices, 4));
  }

  if (this.uv[0]) {
    geometry.addAttribute('uv', new THREE.BufferAttribute(this.uv[0], 2));
  }

  if (this.uv[1]) {
    geometry.addAttribute('uv2', new THREE.BufferAttribute(this.uv[1], 2));
  }

  if (this.uv[2] || this.uv[3]) {
    // TODO: Fallback to normal Geometry
    throw 'BufferGeometry does not support more than 2 uv channels!';
  }

  geometry.computeBoundingSphere();
  geometry.computeFaceNormals();
  geometry.computeVertexNormals();
  return geometry;
};

/**
 * @callback Mesh~onLoad
 * @param {THREE.Geometry} geometry
 */

/**
 * @param {String} path
 * @param {Mesh~onLoad} callback
 */
Mesh.load = function(path, callback) {
  ROSELoader.load(path, function(rh) {
    var version, magic, mesh;

    magic = rh.readStrLen(7);
    rh.skip(1);

    if (magic === 'ZMS0005') {
      version = 5;
    } else if (magic === 'ZMS0006') {
      version = 6;
    } else if (magic === 'ZMS0007') {
      version = 7;
    } else if (magic === 'ZMS0008') {
      version = 8;
    } else {
      throw 'Unexpected ZMS magic header ' + magic + ' in ' + path;
    }

    if (version >= 7) {
      mesh = Mesh.loadMesh8(rh);
    } else {
      mesh = Mesh.loadMesh6(rh);
    }

    callback(mesh.createBufferGeometry());
  });
};
<|endoftext|>


<commit_before>var Pontoon = function() {

  /* public  */
  return {

    /*
     * Save data to server
     * Pontoon server push expects a POST with the following properties:
     *
     * id - list of msgid strings, the length of id should match the length of value ['Hello World']
     * value - list of msgstrs, should be empty if no changes, otherwise set to the edited value ['Hallo Welt']
     * project - url of the page being localized
     * locale - locale msgstrs are localized too
    */
    save: function() {
      // Deep copy: http://api.jquery.com/jQuery.extend
      var data = jQuery.extend(true, {}, this.client._data);

      $(data.entities).each(function() {
        delete this.node;
        delete this.ui;
        delete this.hover;
        delete this.unhover;
      });

      var self = this,
          url = ('url' in this.client._meta) ? this.client._meta['url'] : 'http://127.0.0.1:8000/push/',
          project = ('project' in this.client._meta) ? this.client._meta['project'] : this.client._doc.location.href,
          locale = $(this.client._ptn).find('input').val(),
          params = {
            'project': project,
            'locale': locale,
            // TODO: add, support other browsers - https://developer.mozilla.org/en/Using_JSON_in_Firefox
            'data': JSON.stringify(data)
          };

      $.ajaxSettings.traditional = true;
      $.post(url, params);
    },

    /*
     * Do not render HTML code
     *
     * string HTML snippet that has to be displayed as code instead of rendered
    */
    doNotRender: function(string) {
      return string.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
    },

    /**
     * Build source - translation pairs
     */
    rebuildList: function() {
      var self = this,
          list = $(this.client._ptn).find('#entitylist').empty()
          // tables still need 'cellspacing="0"' in the markup
          // http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
          .append(
            '
' +
              '

Source		Translation

' +
              '

' +
            '
');

      // Render
      $(this.client._data.entities).each(function() {
        var tr = $('
' +
        '' +
          '
' + self.doNotRender(this.original) + '
' +
        '	' +
        '' +
          '
' +
            '

' +
            '

' +
            (this.comment ? '

' : '') +
          '
' +
        '	' +
        '' +
          '
' +
            'Translation' +
            'Translation memory' +
            'Other users' +
            'Other locales' +
          '
' +
          '<textarea>' + (this.translation || '') + '</textarea>' +
        '
', self.client._ptn);

        tr.get(0).entity = this;
        if (this.node) { // For entities not found on the website
          this.node.get(0).entity = this;
        }
        this.ui = tr;

        list.find('tbody').append(tr);
      });

      // Main entity list handlers
      $("#main tr").hover(function() {
        this.entity.hover();
      }, function() {
        this.entity.unhover();
      }).click(function() {
        $(self.client._doc).find('.editableToolbar > .edit').click();
      });

      // Copy original string to translation
      $("#main .copy").click(function(e) {
      	e.stopPropagation();
        var toolbar = $(self.client._doc).find('.editableToolbar');
        toolbar.find('.edit').click().end();

      	var entity = $(this).parents('tr').get(0).entity;
      	$(entity.node).html(entity.original);
        toolbar.find('.save').click();
      });

      this.updateProgress();
    },

    /**
     * Update progress indicator and value
     */
    updateProgress: function() {
      var all = $("#main tbody tr").length,
          translated = $("#main tbody tr.translated").length;
      $('#progress span').width(Math.round(translated*100 / all) + '%');
      $('#progress-value').html(translated + '/' + all);
    },

    /**
     * Attach event handlers
     */
    attachHandlers: function() {
      var self = this;

      // Update entities and progress when saved
      $(".editableToolbar > .save", this.client._doc).click(function() {
        var element = $(this).parent().get(0).target;
        self.updateEntity(element);
        self.updateProgress();
      });

      // Update progress when cancelled
      $(".editableToolbar > .cancel", this.client._doc).click(function() {
        self.updateProgress();
      });

      // Open/close Pontoon UI
      $('#switch').unbind("click.pontoon").bind("click.pontoon", function() {
        if ($('#main').is('.opened')) {
          $('#entitylist').height(0);
        } else {
          $('#entitylist').height(300);
        }
        $('#source').height($(document).height() - $('#main').height());
        $('#main').toggleClass('opened');
      });

      // Authentication
      $('#authentication-menu .restricted .go').unbind("click.pontoon").bind("click.pontoon", function() {
        var author = $('#nickname').val() || $('#email').val();
        $('#authentication .selector').click();
        if (author) {
          $('#authentication .author').html(author).toggleClass('authenticated');
          $('#authentication-menu, #save-menu').toggleClass('menu');
        }
      });
      $('#nickname').unbind("keydown.pontoon").bind("keydown.pontoon", function(e) {
        var key = e.keyCode || e.which;
        if (key === 13) { // Enter
          $('#authentication-menu .restricted .go').click();
          return false;
        }
      });

      // Authentication toggle
      $('#authentication-menu .toggle').unbind("click.pontoon").bind("click.pontoon", function() {
        $('#authentication-menu')
          .find('.wrapper').toggle().end()
          .find('#password').toggle();
      });

      // Save menu
      $('#save-menu').find('.sign-out').unbind("click.pontoon").bind("click.pontoon", function() {
        $('#authentication .selector').click();
        $('#authentication .author').html('Sign in').toggleClass('authenticated');
        $('#authentication-menu, #save-menu').toggleClass('menu');
      }).end().find('.server').unbind("click.pontoon").bind("click.pontoon", function() {
        $('#authentication .selector').click();
        self.save();
      });

    },

    /**
     * Show and render main UI
     * Enable editable text
     */
    renderTools: function() {
      $(this.client._data.entities).each(function() {
        if (this.node) { // For entities not found on the website
          this.node.editableText();
        }
      });
      this.attachHandlers();
      this.rebuildList();
      $('#main').slideDown();
    },

    /**
     * Update entity and main UI
     *
     * element HTML Element which contains l10n entities
     */
    updateEntity: function(element) {
      var entity = element.entity,
          clone = $(element).clone();

      entity.translation = $(clone).html();
      entity.ui.find('textarea').text(entity.translation).parents('tr').addClass('translated');
    },

    /**
     * Extend entity object
     *
     * e Temporary entity object
     */
    extendEntity: function(e) {
      e.original = e.original || ""; /* Original string */
      e.translation = e.translation || ""; /* Translated string */
      e.comment = e.comment || ""; /* Comment for localizers */
      e.node = e.node || null; /* HTML Element holding string */
      e.ui = e.ui || null; /* HTML Element representing string in the main UI */

      e.hover = function() {
        this.node.get(0).showToolbar();
        this.ui.toggleClass('hovered');
      };
      e.unhover = function() {
        this.node.get(0).hideToolbar();
        this.ui.toggleClass('hovered');
      };
    },

    /**
     * Extract entities from the document, not prepared for working with Pontoon
     *
     * Create entity object from every non-empty text node
     * Exclude nodes from special tags, e.g.


<commit_before>/*
Copyright (C) <2014>  <Dan Nielsen / ReachMail, Inc.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

----

This module is a simple wrapper to the ReachMail API
(services.reachmail.net/documentation). It contains four basic functions,
`get`, `post`, `put`, and `delete` for use in accessing the ReachMail API.
Some convenience functions have been added to aid access to certain services,
more will be added at a later date.

USAGE:

var reachmail = require('/path/to/reachmailapi');

var api = new reachmail({token: 'your-token-here'});

api.get('/administration/users/current', function (http_code, response) {
    console.log(http_code);
    console.log(response);
});

----

As indicated by the above example, each function requires a callback capable
of handling the http response code and the response itself.

Function parameters:

get (service_url, callback())
post (service_url, post_body, callback())
put (service_url, post_body, callback())
delete (service_url, callback())

Questions regarding use of this software should be referred to
support@reachmail.com

*/
var VERSION = '0.7.1',
    https   = require('https'),
    util    = require('util');

function merge (def, opt) {
    defaults = def || {};

    if (opt && typeof opt === 'object') {
        var keys = Object.keys(opt);
        for (var j = 0, len = keys.length; j < len; j++) {
            var k = keys[j];
            if (opt[k] !== undefined) def[k] = opt[k];
        }
    }
    return def;
}

var request_options = function (host, path, method, headers, body) {
    this.hostname = host;
    this.port = 443;
    this.path = path;
    this.method = method;
    this.headers = headers;
    this.body = body;

    if (body !== null) {
        this.headers['Content-Length'] = body.length;
    }
}

function call_service (options, callback) {

    var request = https.request(options, function (response) {
        var status_code = response.statusCode;
        var body = '';
        response.setEncoding('utf8');
        response.on('data', function (part) {
            body += part;
        });
        response.on('end', function () {
            try {
                var json = JSON.parse(body);
                callback (status_code, json);
            } catch (err) {
                callback (status_code, err);
            }
        });
    });

    request.on('error', function (err) {
        throw err;
    });

    if (options.method == 'PUT' || options.method == 'POST') {
        request.write(options.body + '\n');
    }

    request.end();
}

function ReachMail (options) {

    if (!(this instanceof ReachMail)) return new ReachMail(options);

    var defaults = {
        token: null,

        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'User-Agent': 'reachmail-node-' + VERSION,
            'Authorization': 'token '
        },

        base_url: 'services.reachmail.net'
    };

    this.options = merge(defaults, options);
    this.options.headers['Authorization'] += this.options.token;
}
module.exports = ReachMail;

/*
 * `get(url, callback)` root prototype for HTTP GET requests
*/
ReachMail.prototype.get = function (url, callback) {
    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, "GET",
            this.options.headers, null);

    call_service (opts, callback);
}

/*
 * `post(url, content, callback)` root prototype for HTTP POST requests
 */
ReachMail.prototype.post = function (url, content, callback) {
    if (typeof content === 'function') {
        callback = content;
        content = null;
    }

    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'POST',
            this.options.headers, content);

    call_service (opts, callback);
}

/*
 * `put(url, content, callback)` root prototype for HTTP PUT requests
 */
ReachMail.prototype.put = function (url, content, callback) {
    if (typeof content === 'function') {
        callback = content;
        content = null;
    }

    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'PUT',
            this.options.headers, content);

    call_service (opts, callback);
}

/*
 * `delete(url, callback)` root prototype for HTTP DELETE requests
 */
ReachMail.prototype.delete = function (url, callback) {
    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'DELETE',
            this.options.headers, null);

    call_service (opts, callback);
}

/*
 * Administration services
 */
ReachMail.prototype.AdministrationUsersCurrent = function (callback) {
    this.get('/administration/users/current', callback);
}

/*
** EasySmtpDelivery services
*/
ReachMail.prototype.easySmtpDelivery = function (accountId, body, callback) {
    this.post ("/easysmtp/" + accountId, body, callback);
}

/*
 * Reporting services
 */
ReachMail.prototype.reportsEasySmtp = function (accountId, startDate, endDate,
        callback) {
    var u = util.format("/reports/easysmtp/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpBounces = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/bounces/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpOptouts = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/optouts/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}
<commit_msg>feat(EasySMTP) - added functions for opens and clicks<commit_after>/*
Copyright (C) <2014>  <Dan Nielsen / ReachMail, Inc.>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

----

This module is a simple wrapper to the ReachMail API
(services.reachmail.net/documentation). It contains four basic functions,
`get`, `post`, `put`, and `delete` for use in accessing the ReachMail API.
Some convenience functions have been added to aid access to certain services,
more will be added at a later date.

USAGE:

var reachmail = require('/path/to/reachmailapi');

var api = new reachmail({token: 'your-token-here'});

api.get('/administration/users/current', function (http_code, response) {
    console.log(http_code);
    console.log(response);
});

----

As indicated by the above example, each function requires a callback capable
of handling the http response code and the response itself.

Function parameters:

get (service_url, callback())
post (service_url, post_body, callback())
put (service_url, post_body, callback())
delete (service_url, callback())

Questions regarding use of this software should be referred to
support@reachmail.com

*/
var VERSION = '0.7.1',
    https   = require('https'),
    util    = require('util');

function merge (def, opt) {
    defaults = def || {};

    if (opt && typeof opt === 'object') {
        var keys = Object.keys(opt);
        for (var j = 0, len = keys.length; j < len; j++) {
            var k = keys[j];
            if (opt[k] !== undefined) def[k] = opt[k];
        }
    }
    return def;
}

var request_options = function (host, path, method, headers, body) {
    this.hostname = host;
    this.port = 443;
    this.path = path;
    this.method = method;
    this.headers = headers;
    this.body = body;

    if (body !== null) {
        this.headers['Content-Length'] = body.length;
    }
}

function call_service (options, callback) {

    var request = https.request(options, function (response) {
        var status_code = response.statusCode;
        var body = '';
        response.setEncoding('utf8');
        response.on('data', function (part) {
            body += part;
        });
        response.on('end', function () {
            try {
                var json = JSON.parse(body);
                callback (status_code, json);
            } catch (err) {
                callback (status_code, err);
            }
        });
    });

    request.on('error', function (err) {
        throw err;
    });

    if (options.method == 'PUT' || options.method == 'POST') {
        request.write(options.body + '\n');
    }

    request.end();
}

function ReachMail (options) {

    if (!(this instanceof ReachMail)) return new ReachMail(options);

    var defaults = {
        token: null,

        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'User-Agent': 'reachmail-node-' + VERSION,
            'Authorization': 'token '
        },

        base_url: 'services.reachmail.net'
    };

    this.options = merge(defaults, options);
    this.options.headers['Authorization'] += this.options.token;
}
module.exports = ReachMail;

/*
 * `get(url, callback)` root prototype for HTTP GET requests
*/
ReachMail.prototype.get = function (url, callback) {
    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, "GET",
            this.options.headers, null);

    call_service (opts, callback);
}

/*
 * `post(url, content, callback)` root prototype for HTTP POST requests
 */
ReachMail.prototype.post = function (url, content, callback) {
    if (typeof content === 'function') {
        callback = content;
        content = null;
    }

    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'POST',
            this.options.headers, content);

    call_service (opts, callback);
}

/*
 * `put(url, content, callback)` root prototype for HTTP PUT requests
 */
ReachMail.prototype.put = function (url, content, callback) {
    if (typeof content === 'function') {
        callback = content;
        content = null;
    }

    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'PUT',
            this.options.headers, content);

    call_service (opts, callback);
}

/*
 * `delete(url, callback)` root prototype for HTTP DELETE requests
 */
ReachMail.prototype.delete = function (url, callback) {
    if (typeof callback !== 'function') {
        throw "Invalid Callback, function required";
        return this;
    }

    var opts = new request_options (this.options.base_url, url, 'DELETE',
            this.options.headers, null);

    call_service (opts, callback);
}

/*
 * Administration services
 */
ReachMail.prototype.AdministrationUsersCurrent = function (callback) {
    this.get('/administration/users/current', callback);
}

/*
** EasySmtpDelivery services
*/
ReachMail.prototype.easySmtpDelivery = function (accountId, body, callback) {
    this.post ("/easysmtp/" + accountId, body, callback);
}

/*
 * Reporting services
 */
ReachMail.prototype.reportsEasySmtp = function (accountId, startDate, endDate,
        callback) {
    var u = util.format("/reports/easysmtp/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpBounces = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/bounces/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpOptouts = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/optouts/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpOpens = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/opens/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}

ReachMail.prototype.reportsEasySmtpClicks = function (accountId, startDate,
        endDate, callback) {
    var u = util.format("/reports/easysmtp/clicks/%s?startdate=%s&enddate=%s",
        accountId, startDate, endDate);
    this.get (u, callback);
}
<|endoftext|>


<commit_before>// VVVV.js -- Visual Web Client Programming
// (c) 2011 Matthias Zauner
// VVVV.js is freely distributable under the MIT license.
// Additional authors of sub components are mentioned at the specific code locations.

/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 NODE: Cross (2d)
 Author(s): 'Matthias Zauner'
 Original Node Author(s): 'VVVV Group'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

VVVV.Nodes.Cross2d = function(id, graph) {
  this.constructor(id, "Cross (2d)", graph);

  this.meta = {
    authors: ['Matthias Zauner'],
    original_authors: ['VVVV Group'],
    credits: [],
    compatibility_issues: []
  };

  this.auto_evaluate = false;

  // input pins
  var xinIn = this.addInputPin('X In', [0], this);
  var yinIn = this.addInputPin('Y In', [0], this);
  var includeupperIn = this.addInputPin('Include Upper', [1], this);
  var includelowerIn = this.addInputPin('Include Lower', [1], this);
  var includeequalIn = this.addInputPin('Include Equal', [1], this);

  // output pins
  var xoutOut = this.addOutputPin('X Out', [0], this);
  var youtOut = this.addOutputPin('Y Out', [0], this);

  // evaluate() will be called each frame
  // (if the input pins have changed, or the nodes is flagged as auto-evaluating)
  this.evaluate = function() {

    var idx = 0;
    for (var i=0; i<yinIn.getSliceCount(); i++) {
      for (var j=0; j<xinIn.getSliceCount(); j++) {
        if (includeupperIn.getValue(0)<0.5 && i>j)
          continue;
        if (includelowerIn.getValue(0)<0.5 && i<j)
          continue;
        if (includeequalIn.getValue(0)<0.5 && i==j)
          continue;
        xoutOut.setValue(idx, xinIn.getValue(j));
        youtOut.setValue(idx, yinIn.getValue(i));
        idx++;
      }
    }
    xoutOut.setSliceCount(idx);
    youtOut.setSliceCount(idx);
  }

}
VVVV.Nodes.Cross2d.prototype = new VVVV.Core.Node();<commit_msg>adds Polar (2d)<commit_after>// VVVV.js -- Visual Web Client Programming
// (c) 2011 Matthias Zauner
// VVVV.js is freely distributable under the MIT license.
// Additional authors of sub components are mentioned at the specific code locations.

/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 NODE: Cross (2d)
 Author(s): 'Matthias Zauner'
 Original Node Author(s): 'VVVV Group'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

VVVV.Nodes.Cross2d = function(id, graph) {
  this.constructor(id, "Cross (2d)", graph);

  this.meta = {
    authors: ['Matthias Zauner'],
    original_authors: ['VVVV Group'],
    credits: [],
    compatibility_issues: []
  };

  this.auto_evaluate = false;

  // input pins
  var xinIn = this.addInputPin('X In', [0], this);
  var yinIn = this.addInputPin('Y In', [0], this);
  var includeupperIn = this.addInputPin('Include Upper', [1], this);
  var includelowerIn = this.addInputPin('Include Lower', [1], this);
  var includeequalIn = this.addInputPin('Include Equal', [1], this);

  // output pins
  var xoutOut = this.addOutputPin('X Out', [0], this);
  var youtOut = this.addOutputPin('Y Out', [0], this);

  // evaluate() will be called each frame
  // (if the input pins have changed, or the nodes is flagged as auto-evaluating)
  this.evaluate = function() {

    var idx = 0;
    for (var i=0; i<yinIn.getSliceCount(); i++) {
      for (var j=0; j<xinIn.getSliceCount(); j++) {
        if (includeupperIn.getValue(0)<0.5 && i>j)
          continue;
        if (includelowerIn.getValue(0)<0.5 && i<j)
          continue;
        if (includeequalIn.getValue(0)<0.5 && i==j)
          continue;
        xoutOut.setValue(idx, xinIn.getValue(j));
        youtOut.setValue(idx, yinIn.getValue(i));
        idx++;
      }
    }
    xoutOut.setSliceCount(idx);
    youtOut.setSliceCount(idx);
  }

}
VVVV.Nodes.Cross2d.prototype = new VVVV.Core.Node();

/*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 NODE: Polar (2d)
 Author(s): 'Matthias Zauner'
 Original Node Author(s): 'VVVV Group'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

VVVV.Nodes.Polar2d = function(id, graph) {
  this.constructor(id, "Polar (2d)", graph);

  this.meta = {
    authors: ['Matthias Zauner'],
    original_authors: ['VVVV Group'],
    credits: [],
    compatibility_issues: []
  };

  this.auto_evaluate = false;

  // input pins
  var xIn = this.addInputPin('X', [1], this);
  var yIn = this.addInputPin('Y', [0], this);

  // output pins
  var angleOut = this.addOutputPin('Angle', [0], this);
  var lengthOut = this.addOutputPin('Length', [1], this);

  this.initialize = function() {

  }

  this.evaluate = function() {

    var maxSize = this.getMaxInputSliceCount();

    for (var i=0; i<maxSize; i++) {
      var x = xIn.getValue(i);
      var y = yIn.getValue(i);

      angleOut.setValue(i, Math.atan2(y, x)/(2*Math.PI));
      lengthOut.setValue(i, Math.sqrt(x*x + y*y));
    }

    lengthOut.setSliceCount(maxSize);
  }

}
VVVV.Nodes.Polar2d.prototype = new VVVV.Core.Node();<|endoftext|>


<commit_before>function draw_circle(canvas, x, y) {
    canvas.beginPath();
    canvas.arc(x, y, 10, 0, 2 * Math.PI, false);
    canvas.closePath();
    canvas.fillStyle = 'green';
    canvas.fill();
    canvas.lineWidth = 5;
    canvas.strokeStyle = '#003300';
    canvas.stroke();
}

function move_character(character, new_x, new_y) {
    if (new_x >= width) {
        character.pos.x = width;
    } else if (new_x <= -100) {
        character.pos.x = 0;
    } else {
        character.pos.x = new_x;
    }

    if (new_y >= height) {
        character.pos.y = height;
    } else if (new_y <= -100) {
        character.pos.y = 0;
    } else {
        character.pos.y = new_y;
    }
}

function move_character_towards_cursor(character, mouseX, mouseY){
    character.move_to.x = mouseX;
    character.move_to.y = mouseY;
   var xDistance = mouseX - character.pos.x;
   var yDistance = mouseY - character.pos.y;
   var distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
   if (distance > 1) {
        var new_pos_x = character.pos.x + xDistance * 0.015;
        var new_pos_y = character.pos.y + yDistance * 0.015;
        move_character(character, new_pos_x, new_pos_y);
   }
}

var width   = 500;
var height  = 500;

document.addEventListener("DOMContentLoaded", function() {
    // get canvas element and create context
    var canvas  = document.getElementById('game');
    var context = canvas.getContext('2d');
    var socket  = io.connect();

    // set canvas to full browser width/height
    canvas.width = width;
    canvas.height = height;

    var character = {
        move: false,
        move_to: {x:0, y:0},
        id: false,
        pos: {x:0, y:0}
    };

    canvas.onmousemove = function(e) {
        move_character_towards_cursor(character, e.clientX, e.clientY);
        character.move = true;
    };

    canvas.onmousedown = function(e) {
        attack_radius = 20.0;
        w = e.clientX - character.pos.x;
        h = e.clientY - character.pos.y;
        hypo = Math.sqrt((w * w) + (h * h));

        xratio = w / hypo;
        yratio = h / hypo;

        attackX = character.pos.x + (xratio * attack_radius);
        attackY = character.pos.y + (yratio * attack_radius);

        attack_position = {x: attackX, y: attackY};

        var attack = {id: character.id, attack: attack_position, type: 'A'};
        socket.emit('attack', attack);
    };

    // draw line received from server
    socket.on('update_characters', function (all_characters) {
        context.clearRect(0, 0, canvas.width, canvas.height); // Clear out the canvas
        console.log("update character");
        for (var i in all_characters) {
            draw_circle(context, all_characters[i].x, all_characters[i].y);
        }
    });

    socket.on('character_died', function () {
        window.location = 'https://yourwaifuisshit.com';
    });

    // main loop, running every 25ms
    function mainLoop() {
        if (character.move) {
            move_character_towards_cursor(character, character.move_to.x, character.move_to.y);
        }

        console.log(character.pos.x);
        console.log(character.pos.y);

        socket.emit('move_character', character);
        setTimeout(mainLoop, 25);
    }

    socket.on('init_character', function(data) {
        character.id = data.id;
        character.pos.x = data.pos.x;
        character.pos.y = data.pos.y;
        mainLoop();
    });
});
<commit_msg>Get angle from point<commit_after>function draw_circle(canvas, x, y) {
    canvas.beginPath();
    canvas.arc(x, y, 10, 0, 2 * Math.PI, false);
    canvas.closePath();
    canvas.fillStyle = 'green';
    canvas.fill();
    canvas.lineWidth = 5;
    canvas.strokeStyle = '#003300';
    canvas.stroke();
}

function move_character(character, new_x, new_y) {
    if (new_x >= width) {
        character.pos.x = width;
    } else if (new_x <= -100) {
        character.pos.x = 0;
    } else {
        character.pos.x = new_x;
    }

    if (new_y >= height) {
        character.pos.y = height;
    } else if (new_y <= -100) {
        character.pos.y = 0;
    } else {
        character.pos.y = new_y;
    }
}

function move_character_towards_cursor(character, mouseX, mouseY){
    character.move_to.x = mouseX;
    character.move_to.y = mouseY;
   var xDistance = mouseX - character.pos.x;
   var yDistance = mouseY - character.pos.y;
   var distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
   if (distance > 1) {
        var new_pos_x = character.pos.x + xDistance * 0.015;
        var new_pos_y = character.pos.y + yDistance * 0.015;
        move_character(character, new_pos_x, new_pos_y);
   }
}

function pointToAngle(x, y, originX, originY) {
    var dx = x - originX;
    var dy = y - originY;
    var theta = Math.atan2(dy, dx);
    if (theta < 0) {
        theta += (2 * Math.PI);
    }
    return theta;
}

var width   = 500;
var height  = 500;

document.addEventListener("DOMContentLoaded", function() {
    // get canvas element and create context
    var canvas  = document.getElementById('game');
    var context = canvas.getContext('2d');
    var socket  = io.connect();

    // set canvas to full browser width/height
    canvas.width = width;
    canvas.height = height;

    var character = {
        move: false,
        move_to: {x:0, y:0},
        id: false,
        pos: {x:0, y:0}
    };

    canvas.onmousemove = function(e) {
        move_character_towards_cursor(character, e.clientX, e.clientY);
        character.move = true;
    };

    canvas.onmousedown = function(e) {
        attack_radius = 20.0;
        w = e.clientX - character.pos.x;
        h = e.clientY - character.pos.y;
        hypo = Math.sqrt((w * w) + (h * h));

        xratio = w / hypo;
        yratio = h / hypo;

        attackX = character.pos.x + (xratio * attack_radius);
        attackY = character.pos.y + (yratio * attack_radius);

        attack_position = {x: attackX, y: attackY};

        var attack = {id: character.id, attack: attack_position, type: 'A'};
        socket.emit('attack', attack);
    };

    // draw line received from server
    socket.on('update_characters', function (all_characters) {
        context.clearRect(0, 0, canvas.width, canvas.height); // Clear out the canvas
        console.log("update character");
        for (var i in all_characters) {
            draw_circle(context, all_characters[i].x, all_characters[i].y);
        }
    });

    socket.on('character_died', function () {
        window.location = 'https://yourwaifuisshit.com';
    });

    // main loop, running every 25ms
    function mainLoop() {
        if (character.move) {
            move_character_towards_cursor(character, character.move_to.x, character.move_to.y);
        }

        console.log(character.pos.x);
        console.log(character.pos.y);

        socket.emit('move_character', character);
        setTimeout(mainLoop, 25);
    }

    socket.on('init_character', function(data) {
        character.id = data.id;
        character.pos.x = data.pos.x;
        character.pos.y = data.pos.y;
        mainLoop();
    });
});
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import './css/NowPlaying.css';
import secondsToString from "./util/secondsToString";
import KComponent from "./KComponent";

import { seek, kPlayingTrack, kPlaybackSeconds, kAlbumArtURL } from "./model/playerModel";
import { setArtist, setAlbum } from "./model/browsingModel";

function percentage(fraction) {
    return `${fraction * 100}%`;
}

class NowPlaying extends KComponent {
  observables() { return {
      track: kPlayingTrack,
      playbackSeconds: kPlaybackSeconds,
      albumArtURL: kAlbumArtURL,
  }; }

  seek(e) {
      const fraction = e.nativeEvent.offsetX / this.playbackSecondsBar.clientWidth;
      seek(this.state.track.length * fraction);
      e.stopPropagation();
  }

  render() {
    const playbackFraction = this.state.track
        ? this.state.playbackSeconds / this.state.track.length
        : 0;
    const albumArtURL = this.state.albumArtURL.small;

    /*
    const track = this.state.track || {
        album: "ALBUM",
        artist: "ARTIST",
        title: "MOST AWESOME SONG EVER",
        length: 100,
    };
    */
    const track = this.state.track;

    return
 {
            if (!track) return;
            setArtist(track.albumartist);
            setAlbum(track.album);
        }}
        className={`st-now-playing ${this.props.className}`}>
      <div className={`st-album-art ${albumArtURL ? "" : "st-album-art-empty"}`}
          style={{backgroundImage: `url(${this.state.albumArtURL.small})`}} />
      {track &&

          {track.title}
          {" by "}
          {track.artist}
          {" from "}
          {track.album}

}
      {track &&

              {secondsToString(this.state.playbackSeconds)}

 this.playbackSecondsBar = el}
              onClick={(e) => {this.seek(e)}} >

              {secondsToString(track.length)}

}

;
  }
}

NowPlaying.propTypes = {
    className: PropTypes.string,
};

NowPlaying.defaultProps = {
    className: "",
};

export default NowPlaying;<commit_msg>Don't die if no album art<commit_after>import React, { PropTypes } from 'react';
import './css/NowPlaying.css';
import secondsToString from "./util/secondsToString";
import KComponent from "./KComponent";

import { seek, kPlayingTrack, kPlaybackSeconds, kAlbumArtURL } from "./model/playerModel";
import { setArtist, setAlbum } from "./model/browsingModel";

function percentage(fraction) {
    return `${fraction * 100}%`;
}

class NowPlaying extends KComponent {
  observables() { return {
      track: kPlayingTrack,
      playbackSeconds: kPlaybackSeconds,
      albumArtURL: kAlbumArtURL,
  }; }

  seek(e) {
      const fraction = e.nativeEvent.offsetX / this.playbackSecondsBar.clientWidth;
      seek(this.state.track.length * fraction);
      e.stopPropagation();
  }

  render() {
    const playbackFraction = this.state.track
        ? this.state.playbackSeconds / this.state.track.length
        : 0;
    const albumArtURL = (this.state.albumArtURL || {}).small;

    /*
    const track = this.state.track || {
        album: "ALBUM",
        artist: "ARTIST",
        title: "MOST AWESOME SONG EVER",
        length: 100,
    };
    */
    const track = this.state.track;

    return
 {
            if (!track) return;
            setArtist(track.albumartist);
            setAlbum(track.album);
        }}
        className={`st-now-playing ${this.props.className}`}>
      <div className={`st-album-art ${albumArtURL ? "" : "st-album-art-empty"}`}
          style={{backgroundImage: `url(${this.state.albumArtURL.small})`}} />
      {track &&

          {track.title}
          {" by "}
          {track.artist}
          {" from "}
          {track.album}

}
      {track &&

              {secondsToString(this.state.playbackSeconds)}

 this.playbackSecondsBar = el}
              onClick={(e) => {this.seek(e)}} >

              {secondsToString(track.length)}

}

;
  }
}

NowPlaying.propTypes = {
    className: PropTypes.string,
};

NowPlaying.defaultProps = {
    className: "",
};

export default NowPlaying;<|endoftext|>


<commit_before>import { initOptions, keycloak } from "keycloak"

keycloak
  .init({ onLoad: initOptions.onLoad })
  .then(authenticated => {
    if (!authenticated) {
      window.location.reload()
    } else {
      console.info("Authenticated successfully")
    }

    require("index.js")
  })
  .catch(() => {
    console.warn("Authentication failed")
  })

keycloak.onTokenExpired = () =>
  keycloak
    .updateToken()
    .then(refreshed => {
      console.info("Token refreshed")
    })
    .catch(() => {
      console.warn("Failed to refresh token, re-authentication is needed")
    })

export default keycloak
<commit_msg>Log initialization errors<commit_after>import { initOptions, keycloak } from "keycloak"

;(async() => {
  keycloak.onTokenExpired = () =>
    keycloak
      .updateToken()
      .then(() => {
        console.info("Token refreshed")
      })
      .catch(error => {
        console.warn(
          "Keycloak client failed to refresh token, re-authentication is needed"
        )
        console.warn(error)
      })

  try {
    const authenticated = await keycloak.init({ onLoad: initOptions.onLoad })
    if (!authenticated) {
      console.info("Keycloak client not authenticated, reloading page...")
      window.location.reload()
      return
    }
  } catch (error) {
    console.info("Error occurred during Keycloak client initialization")
    console.err(error)
    return
  }

  console.info("Authenticated successfully")
  try {
    require("index.js")
  } catch (error) {
    console.error(error)
  }
})()

export default keycloak
<|endoftext|>