<commit_before>/*
 * Debug parameters.
 */

// Enable distortion everywhere.
//WEBVR_FORCE_DISTORTION = true;
// Override the distortion background color.
//WEBVR_BACKGROUND_COLOR = new THREE.Vector4(1, 0, 0, 1);
// Change the tracking prediction mode.
//WEBVR_PREDICTION_MODE = 2;
// In prediction mode, change how far into the future to predict.
//WEBVR_PREDICTION_TIME_MS = 100;

// Polyfill
require('../webvr-polyfill/src/main.js')

var THREE = require('three');
var React = require('react');
var WebVRManager = require('../webvr/main');
var VREffect = require('../webvr/VREffect');
var VRControls = require('../webvr/VRControls');

var Reoculus = require('../reoculus');
var Box = Reoculus.Box;
var World = Reoculus.World;

//Setup three.js WebGL renderer
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);

// Append the canvas element created by the renderer to document body element.
document.body.appendChild(renderer.domElement);

// Create a three.js scene.
//var scene = new THREE.Scene();
function registerAddObject(fn) {
  window.addObject = fn;
}
var scene = Reoculus.render(<World registerAddObject={registerAddObject} />);

// Create a three.js camera.
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.3, 10000);

// Apply VR headset positional data to camera.
var controls = new VRControls(camera);

// Apply VR stereo rendering to renderer.
var effect = new VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);

// Create a VR manager helper to enter and exit VR mode.
var manager = new WebVRManager(renderer, effect, {hideButton: false});

// Add a repeating grid as a floor.
var numRepeats = 1000;
var texture = THREE.ImageUtils.loadTexture(
  'img/box.png'
);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(numRepeats, numRepeats);
var geometry = new THREE.BoxGeometry(numRepeats, numRepeats, numRepeats);
var material = new THREE.MeshBasicMaterial({
  map: texture,
  color: 0x333333
});
var floor = new THREE.Mesh(geometry, material);
scene.add(floor);
floor.position.y = -1 - (numRepeats / 2);

// An aiming dot
var geometry = new THREE.BoxGeometry(0.01, 0.01, 0.01);
var material = new THREE.MeshBasicMaterial({color: 0xFF0000});
var aimer = new THREE.Mesh(geometry, material);
aimer.visible = false;
scene.add(aimer);
aimer.position.set(0, 0, -1.1);

// The canvas for the editor
var FONT_SIZE_PX = 40;
var NUM_LINES = 40;
var CANVAS_SIZE_PX = NUM_LINES * FONT_SIZE_PX + FONT_SIZE_PX * 0.2;
var canvas = document.createElement("canvas");
canvas.width = canvas.height = CANVAS_SIZE_PX;
var context = canvas.getContext("2d");
context.font = FONT_SIZE_PX + "px Inconsolata,monospace";
context.globalCompositeOperation = "darker";
var textMetrics = context.measureText("0");
var charWidth = textMetrics.width;
var numCols = Math.floor(CANVAS_SIZE_PX / charWidth);

// The display pane for the editor
var textTexture = new THREE.Texture(canvas);
textTexture.minFilter = THREE.NearestFilter;
textTexture.wrapS = THREE.ClampToEdgeWrapping;
textTexture.wrapT = THREE.ClampToEdgeWrapping;
textTexture.repeat.set(1, 1);
var geometry = new THREE.PlaneGeometry(2, 2);
var material = new THREE.MeshBasicMaterial({
  map: textTexture,
  color: 0xFFFFFF,
  side: THREE.DoubleSide
});
var editor = new THREE.Mesh(geometry, material);
scene.add(editor);
editor.visible = false;
editor.value = "hello world\nmultiline works";
editor.cursor = 0;
editor.lastValue = "";
editor.lastCursor = 0;
editor.task = "none";

// Functionality for the editor
// Returns x and y
editor.cursorPosition = function() {
  var x = 0;
  var y = 0;
  var remaining = editor.cursor;
  var lines = editor.value.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    if (remaining <= line.length) {
      x = remaining;
      break;
    }
    y++;
    remaining -= (line.length + 1);
  }
  return { x: x,
           y: y }
}

editor.setCursorPosition = function(x, y) {
  var acc = 0;
  var lines = editor.value.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    if (i > y) {
      break;
    }
    if (i == y) {
      acc += Math.min(x, line.length);
      break;
    }
    acc += line.length + 1;
  }

  editor.cursor = acc;
}

editor.cursorLeft = function() {
  if (editor.cursor > 0) {
    editor.cursor--;
  }
}

editor.cursorRight = function() {
  if (editor.cursor < editor.value.length) {
    editor.cursor++;
  }
}

editor.del = function() {
  if (editor.cursor == 0) {
    return;
  }
  editor.value = (editor.value.slice(0, editor.cursor - 1) +
                  editor.value.slice(editor.cursor));
  editor.cursor--;
}

editor.cursorUp = function() {
  var pos = editor.cursorPosition();
  editor.setCursorPosition(pos.x, pos.y - 1);
}

editor.cursorDown = function() {
  var pos = editor.cursorPosition();
  editor.setCursorPosition(pos.x, pos.y + 1);
}

// Gets its text from the text area
editor.update = function() {
  if (editor.value == editor.lastValue &&
      editor.cursor == editor.lastCursor) {
    return;
  }
  editor.lastValue = editor.value;
  editor.lastCursor = editor.cursor;

  // Display text
  context.clearRect(0, 0, CANVAS_SIZE_PX, CANVAS_SIZE_PX);
  context.fillStyle = "hsla(0, 0%, 100%, 0.6)";
  context.fillRect(0, 0, CANVAS_SIZE_PX, CANVAS_SIZE_PX);
  var lines = editor.value.split("\n")
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    context.fillStyle = "hsl(0, 0%, 25%)";
    context.fillText(line, 0, FONT_SIZE_PX + FONT_SIZE_PX * i);
  }

  // Display the cursor
  var pos = editor.cursorPosition();
  context.fillRect(
    pos.x * charWidth,
    (pos.y + 0.2) * FONT_SIZE_PX,
    5, // cursor width
    FONT_SIZE_PX);

  textTexture.needsUpdate = true;
}

editor.insert = function(text) {
  editor.value = (editor.value.slice(0, editor.cursor) + text +
                  editor.value.slice(editor.cursor));
  editor.cursor++;
}

// Player's velocity in camera-space with y projected out
var velocity = {x: 0, z: 0};

// The floor spot that the camera is looking at.
// Returns null if the camera isn't looking at the ground.
function floorSpot() {
  var vector = new THREE.Vector3(0, 0, -1);
  vector = camera.localToWorld(vector);
  vector.sub(camera.position);
  var raycaster = new THREE.Raycaster(camera.position, vector);
  var spots = raycaster.intersectObject(floor);
  if (spots.length == 0) {
    return null;
  }
  return spots[0].point;
}

// Request animation frame loop function
function animate(timestamp) {
  if (velocity.x != 0 || velocity.z != 0) {
    // Turn our velocity vector in the camera direction
    var vector = new THREE.Vector3(velocity.x, 0, velocity.z);
    vector.applyQuaternion(camera.quaternion);

    // Project and rescale to keep motion in the y=0 plane
    var len = vector.length();
    vector.y = 0;
    vector.setLength(len);

    // Move the camera
    camera.position.add(vector);
  }

  if (aimer.visible) {
    // Keep the aimer right in front of us
    var vector = new THREE.Vector3(0, 0, -0.9);
    vector.applyQuaternion(camera.quaternion);
    aimer.position.copy(camera.position);
    aimer.position.add(vector);
    aimer.rotation.copy(camera.rotation);
  }

  if (editor.visible) {
    // Keep the editor in front of us
    var vector = new THREE.Vector3(1.4, 0.3, -2);
    vector.applyQuaternion(camera.quaternion);
    editor.position.copy(camera.position);
    editor.position.add(vector);
    editor.rotation.copy(camera.rotation);
    editor.rotation.y -= 0.1;
  }

  editor.update();

  // Update VR headset position and apply to camera.
  controls.update();

  // Render the scene through the manager.
  manager.render(scene, camera, timestamp);

  requestAnimationFrame(animate);
}

// Kick off animation loop
animate();

// Returns whether the task is actually finished.
function finishEditorTask(task, params, string) {
  if (task == "create") {
    var success = addObject(string, params.x, params.z);
    return success;
  }

  console.log("unknown task: " + task);
  return false;
}

// Handle keypresses
function onKeyDown(e) {
  if (editor.visible) {
    switch(e.key) {
    case "`":
      if (finishEditorTask(editor.task, editor.params, editor.value)) {
        editor.task = "none";
        editor.params = null;
        editor.value = "";
        editor.visible = false;
      } else {
        // We failed to exit the editor
      }
      break;

    case "a":
    case "b":
    case "c":
    case "d":
    case "e":
    case "f":
    case "g":
    case "h":
    case "i":
    case "j":
    case "k":
    case "l":
    case "m":
    case "n":
    case "o":
    case "p":
    case "q":
    case "r":
    case "s":
    case "t":
    case "u":
    case "v":
    case "w":
    case "x":
    case "y":
    case "z":
    case "A":
    case "B":
    case "C":
    case "D":
    case "E":
    case "F":
    case "G":
    case "H":
    case "I":
    case "J":
    case "K":
    case "L":
    case "M":
    case "N":
    case "O":
    case "P":
    case "Q":
    case "R":
    case "S":
    case "T":
    case "U":
    case "V":
    case "W":
    case "X":
    case "Y":
    case "Z":
    case "'":
    case '"':
    case "{":
    case "}":
    case "[":
    case "]":
    case "(":
    case ")":
    case ";":
    case ":":
    case "<":
    case ">":
    case "/":
    case ".":
    case ",":
    case "-":
    case "_":
    case "=":
    case "+":
    case "!":
    case "$":
    case "&":
    case "|":
    case "*":
    case "0":
    case "1":
    case "2":
    case "3":
    case "4":
    case "5":
    case "6":
    case "7":
    case "8":
    case "9":
    case "\\":
    case " ":
    case "Enter":

      var key = e.key;
      if (key == "Enter") {
        key = "\n";
      }

      editor.insert(key);

      e.preventDefault();
      break;

    case "ArrowUp":
      editor.cursorUp();
      e.preventDefault();
      break;

    case "ArrowDown":
      editor.cursorDown();
      e.preventDefault();
      break;

    case "ArrowLeft":
      editor.cursorLeft();
      e.preventDefault();
      break;

    case "ArrowRight":
      editor.cursorRight();
      e.preventDefault();
      break;

    case "Backspace":
      editor.del();
      e.preventDefault();
      break;
    }

  } else {
    switch(e.key) {
    case "c":
    case "C":
      // "Create" functionality
      if (aimer.visible) {
        var spot = floorSpot();
        if (spot != null) {
          editor.visible = true;
          editor.task = "create";
          editor.params = {x: spot.x, y: spot.y, z: spot.z};
          editor.value = "";
        }
      }
      break;
    case "e":
    case "E":
      // "Edit" functionality?
      if (aimer.visible) {
        editor.visible = true;
        aimer.visible = false;
      }
      break;
    case "Shift":
      aimer.visible = true;
      break;
    case "w":
    case "W":
      velocity.z = -0.1;
      break;
    case "s":
    case "S":
      velocity.z = 0.1;
      break;
    case "a":
    case "A":
      velocity.x = -0.1;
      break;
    case "d":
    case "D":
      velocity.x = 0.1;
      break;
    }
  }
};
window.addEventListener("keydown", onKeyDown, true);
function onKeyUp(e) {
  if (editor.visible) {

  } else {
    switch(e.key) {
    case "Shift":
      aimer.visible = false;
      break;
    case "w":
    case "s":
      velocity.z = 0;
      break;
    case "a":
    case "d":
      velocity.x = 0;
      break;
    }
  }
};
window.addEventListener("keyup", onKeyUp, true);

// Handle window resizes
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  effect.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize, false);
<commit_msg>Again-method<commit_after>/*
 * Debug parameters.
 */

// Enable distortion everywhere.
//WEBVR_FORCE_DISTORTION = true;
// Override the distortion background color.
//WEBVR_BACKGROUND_COLOR = new THREE.Vector4(1, 0, 0, 1);
// Change the tracking prediction mode.
//WEBVR_PREDICTION_MODE = 2;
// In prediction mode, change how far into the future to predict.
//WEBVR_PREDICTION_TIME_MS = 100;

// Polyfill
require('../webvr-polyfill/src/main.js')

var THREE = require('three');
var React = require('react');
var WebVRManager = require('../webvr/main');
var VREffect = require('../webvr/VREffect');
var VRControls = require('../webvr/VRControls');

var Reoculus = require('../reoculus');
var Box = Reoculus.Box;
var World = Reoculus.World;

//Setup three.js WebGL renderer
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);

// Append the canvas element created by the renderer to document body element.
document.body.appendChild(renderer.domElement);

// Create a three.js scene.
//var scene = new THREE.Scene();
function registerAddObject(fn) {
  window.addObject = fn;
}
var scene = Reoculus.render(<World registerAddObject={registerAddObject} />);

// Create a three.js camera.
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.3, 10000);

// Apply VR headset positional data to camera.
var controls = new VRControls(camera);

// Apply VR stereo rendering to renderer.
var effect = new VREffect(renderer);
effect.setSize(window.innerWidth, window.innerHeight);

// Create a VR manager helper to enter and exit VR mode.
var manager = new WebVRManager(renderer, effect, {hideButton: false});

// Add a repeating grid as a floor.
var numRepeats = 1000;
var texture = THREE.ImageUtils.loadTexture(
  'img/box.png'
);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(numRepeats, numRepeats);
var geometry = new THREE.BoxGeometry(numRepeats, numRepeats, numRepeats);
var material = new THREE.MeshBasicMaterial({
  map: texture,
  color: 0x333333
});
var floor = new THREE.Mesh(geometry, material);
scene.add(floor);
floor.position.y = -1 - (numRepeats / 2);

// An aiming dot
var geometry = new THREE.BoxGeometry(0.01, 0.01, 0.01);
var material = new THREE.MeshBasicMaterial({color: 0xFF0000});
var aimer = new THREE.Mesh(geometry, material);
aimer.visible = false;
scene.add(aimer);
aimer.position.set(0, 0, -1.1);

// The canvas for the editor
var FONT_SIZE_PX = 40;
var NUM_LINES = 40;
var CANVAS_SIZE_PX = NUM_LINES * FONT_SIZE_PX + FONT_SIZE_PX * 0.2;
var canvas = document.createElement("canvas");
canvas.width = canvas.height = CANVAS_SIZE_PX;
var context = canvas.getContext("2d");
context.font = FONT_SIZE_PX + "px Inconsolata,monospace";
context.globalCompositeOperation = "darker";
var textMetrics = context.measureText("0");
var charWidth = textMetrics.width;
var numCols = Math.floor(CANVAS_SIZE_PX / charWidth);

// The display pane for the editor
var textTexture = new THREE.Texture(canvas);
textTexture.minFilter = THREE.NearestFilter;
textTexture.wrapS = THREE.ClampToEdgeWrapping;
textTexture.wrapT = THREE.ClampToEdgeWrapping;
textTexture.repeat.set(1, 1);
var geometry = new THREE.PlaneGeometry(2, 2);
var material = new THREE.MeshBasicMaterial({
  map: textTexture,
  color: 0xFFFFFF,
  side: THREE.DoubleSide
});
var editor = new THREE.Mesh(geometry, material);
scene.add(editor);
editor.visible = false;
editor.value = "hello world\nmultiline works";
editor.cursor = 0;
editor.lastValue = "";
editor.lastCursor = 0;
editor.task = "none";

// Functionality for the editor
// Returns x and y
editor.cursorPosition = function() {
  var x = 0;
  var y = 0;
  var remaining = editor.cursor;
  var lines = editor.value.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    if (remaining <= line.length) {
      x = remaining;
      break;
    }
    y++;
    remaining -= (line.length + 1);
  }
  return { x: x,
           y: y }
}

editor.setCursorPosition = function(x, y) {
  var acc = 0;
  var lines = editor.value.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    if (i > y) {
      break;
    }
    if (i == y) {
      acc += Math.min(x, line.length);
      break;
    }
    acc += line.length + 1;
  }

  editor.cursor = acc;
}

editor.cursorLeft = function() {
  if (editor.cursor > 0) {
    editor.cursor--;
  }
}

editor.cursorRight = function() {
  if (editor.cursor < editor.value.length) {
    editor.cursor++;
  }
}

editor.del = function() {
  if (editor.cursor == 0) {
    return;
  }
  editor.value = (editor.value.slice(0, editor.cursor - 1) +
                  editor.value.slice(editor.cursor));
  editor.cursor--;
}

editor.cursorUp = function() {
  var pos = editor.cursorPosition();
  editor.setCursorPosition(pos.x, pos.y - 1);
}

editor.cursorDown = function() {
  var pos = editor.cursorPosition();
  editor.setCursorPosition(pos.x, pos.y + 1);
}

// Gets its text from the text area
editor.update = function() {
  if (editor.value == editor.lastValue &&
      editor.cursor == editor.lastCursor) {
    return;
  }
  editor.lastValue = editor.value;
  editor.lastCursor = editor.cursor;

  // Display text
  context.clearRect(0, 0, CANVAS_SIZE_PX, CANVAS_SIZE_PX);
  context.fillStyle = "hsla(0, 0%, 100%, 0.6)";
  context.fillRect(0, 0, CANVAS_SIZE_PX, CANVAS_SIZE_PX);
  var lines = editor.value.split("\n")
  for (var i = 0; i < lines.length; ++i) {
    var line = lines[i];
    context.fillStyle = "hsl(0, 0%, 25%)";
    context.fillText(line, 0, FONT_SIZE_PX + FONT_SIZE_PX * i);
  }

  // Display the cursor
  var pos = editor.cursorPosition();
  context.fillRect(
    pos.x * charWidth,
    (pos.y + 0.2) * FONT_SIZE_PX,
    5, // cursor width
    FONT_SIZE_PX);

  textTexture.needsUpdate = true;
}

editor.insert = function(text) {
  editor.value = (editor.value.slice(0, editor.cursor) + text +
                  editor.value.slice(editor.cursor));
  editor.cursor++;
}

// Player's velocity in camera-space with y projected out
var velocity = {x: 0, z: 0};

// The floor spot that the camera is looking at.
// Returns null if the camera isn't looking at the ground.
function floorSpot() {
  var vector = new THREE.Vector3(0, 0, -1);
  vector = camera.localToWorld(vector);
  vector.sub(camera.position);
  var raycaster = new THREE.Raycaster(camera.position, vector);
  var spots = raycaster.intersectObject(floor);
  if (spots.length == 0) {
    return null;
  }
  return spots[0].point;
}

// Request animation frame loop function
function animate(timestamp) {
  if (velocity.x != 0 || velocity.z != 0) {
    // Turn our velocity vector in the camera direction
    var vector = new THREE.Vector3(velocity.x, 0, velocity.z);
    vector.applyQuaternion(camera.quaternion);

    // Project and rescale to keep motion in the y=0 plane
    var len = vector.length();
    vector.y = 0;
    vector.setLength(len);

    // Move the camera
    camera.position.add(vector);
  }

  if (aimer.visible) {
    // Keep the aimer right in front of us
    var vector = new THREE.Vector3(0, 0, -0.9);
    vector.applyQuaternion(camera.quaternion);
    aimer.position.copy(camera.position);
    aimer.position.add(vector);
    aimer.rotation.copy(camera.rotation);
  }

  if (editor.visible) {
    // Keep the editor in front of us
    var vector = new THREE.Vector3(1.4, 0.3, -2);
    vector.applyQuaternion(camera.quaternion);
    editor.position.copy(camera.position);
    editor.position.add(vector);
    editor.rotation.copy(camera.rotation);
    editor.rotation.y -= 0.1;
  }

  editor.update();

  // Update VR headset position and apply to camera.
  controls.update();

  // Render the scene through the manager.
  manager.render(scene, camera, timestamp);

  requestAnimationFrame(animate);
}

// Kick off animation loop
animate();

// Returns whether the task is actually finished.
function finishEditorTask(task, params, string) {
  if (task == "create") {
    var success = addObject(string, params.x, params.z);
    if (success) {
      editor.lastCreation = string;
    }
    return success;
  }

  console.log("unknown task: " + task);
  return false;
}

// Handle keypresses
function onKeyDown(e) {
  if (editor.visible) {
    switch(e.key) {
    case "`":
      if (finishEditorTask(editor.task, editor.params, editor.value)) {
        editor.task = "none";
        editor.params = null;
        editor.value = "";
        editor.visible = false;
      } else {
        // We failed to exit the editor
      }
      break;

    case "a":
    case "b":
    case "c":
    case "d":
    case "e":
    case "f":
    case "g":
    case "h":
    case "i":
    case "j":
    case "k":
    case "l":
    case "m":
    case "n":
    case "o":
    case "p":
    case "q":
    case "r":
    case "s":
    case "t":
    case "u":
    case "v":
    case "w":
    case "x":
    case "y":
    case "z":
    case "A":
    case "B":
    case "C":
    case "D":
    case "E":
    case "F":
    case "G":
    case "H":
    case "I":
    case "J":
    case "K":
    case "L":
    case "M":
    case "N":
    case "O":
    case "P":
    case "Q":
    case "R":
    case "S":
    case "T":
    case "U":
    case "V":
    case "W":
    case "X":
    case "Y":
    case "Z":
    case "'":
    case '"':
    case "{":
    case "}":
    case "[":
    case "]":
    case "(":
    case ")":
    case ";":
    case ":":
    case "<":
    case ">":
    case "/":
    case ".":
    case ",":
    case "-":
    case "_":
    case "=":
    case "+":
    case "!":
    case "$":
    case "&":
    case "|":
    case "*":
    case "0":
    case "1":
    case "2":
    case "3":
    case "4":
    case "5":
    case "6":
    case "7":
    case "8":
    case "9":
    case "\\":
    case " ":
    case "Enter":

      var key = e.key;
      if (key == "Enter") {
        key = "\n";
      }

      editor.insert(key);

      e.preventDefault();
      break;

    case "ArrowUp":
      editor.cursorUp();
      e.preventDefault();
      break;

    case "ArrowDown":
      editor.cursorDown();
      e.preventDefault();
      break;

    case "ArrowLeft":
      editor.cursorLeft();
      e.preventDefault();
      break;

    case "ArrowRight":
      editor.cursorRight();
      e.preventDefault();
      break;

    case "Backspace":
      editor.del();
      e.preventDefault();
      break;
    }

  } else {
    switch(e.key) {
    case "c":
    case "C":
      // "Create" functionality
      if (aimer.visible) {
        var spot = floorSpot();
        if (spot != null) {
          editor.visible = true;
          editor.task = "create";
          editor.params = {x: spot.x, y: spot.y, z: spot.z};
          editor.value = "";
        }
      }
      break;
    case "g":
    case "G":
      // "Again" functionality
      if (editor.lastCreation) {
        var spot = floorSpot();
        if (spot != null) {
          addObject(editor.lastCreation, spot.x, spot.z);
        }
      }
      break;
    case "e":
    case "E":
      // "Edit" functionality?
      if (aimer.visible) {
        editor.visible = true;
        aimer.visible = false;
      }
      break;
    case "Shift":
      aimer.visible = true;
      break;
    case "w":
    case "W":
      velocity.z = -0.1;
      break;
    case "s":
    case "S":
      velocity.z = 0.1;
      break;
    case "a":
    case "A":
      velocity.x = -0.1;
      break;
    case "d":
    case "D":
      velocity.x = 0.1;
      break;
    }
  }
};
window.addEventListener("keydown", onKeyDown, true);
function onKeyUp(e) {
  if (editor.visible) {

  } else {
    switch(e.key) {
    case "Shift":
      aimer.visible = false;
      break;
    case "w":
    case "s":
      velocity.z = 0;
      break;
    case "a":
    case "d":
      velocity.x = 0;
      break;
    }
  }
};
window.addEventListener("keyup", onKeyUp, true);

// Handle window resizes
function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  effect.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize, false);
<|endoftext|>


<commit_before> :detaerc tiderCpungiSeussi;)tiderc ,' : tiderc'(gol.elosnoc
 nruter
}
{bbb
'use strict';

const Router = require('./index');
const moment = require('moment');
const inherits = require('util').inherits;
const middleware = require('storj-service-middleware');
const rawbody = middleware.rawbody;
const log = require('../../logger');
const authenticate = middleware.authenticate;
const { CREDIT_TYPES, PROMO_CODE, STRIPE, PROMO_EXPIRES, PROMO_AMOUNT } = require('storj-service-storage-models/lib/constants');
const paymentProcessorAdapters = require('storj-service-storage-models/lib/models/payment-processor-adapters');
const stripe = require('../vendor/stripe');
const errors = require('storj-service-error-types');

// TODO: Refactor all stripe-related endpoints into a single endpoint
// to remain payment processor agnostic.

/**
 * Handles endpoints for all user related operations
 */
function CreditsRouter(options) {
  if (!(this instanceof CreditsRouter)) {
    return new CreditsRouter(options);
  }
  this.models = options.storage.models;
  Router.apply(this, arguments);
}

inherits(CreditsRouter, Router);

function getBillingCycle(billingDate) {
  const today = new Date();
  const daysInMonth = (new Date(today.getFullYear(), (today.getMonth()), 0)).getDate();
  const startDayOfMonth = (billingDate > daysInMonth) ? daysInMonth : billingDate;
  const startDate = Date.parse(new Date(
    today.getFullYear(),
    (today.getMonth() - 1),
    startDayOfMonth
  ));
  const endDate = (moment(startDate).add('1', 'month').unix() * 1000);
  return {
    startDate: startDate,
    endDate: endDate
  };
};

function getBalance(credits, debits) {
  const sumCredits = (total, item) => {
    return total + item.paid_amount;
  };

  const sumDebits = (total, item) => {
    return total + item.amount;
  };

  const creditSum = credits.reduce(sumCredits, 0);
  const debitSum = debits.reduce(sumDebits, 0);
  const balance = debitSum - creditSum;

  return balance;
}

function getPromoBalance(credits) {
  return credits.reduce((total, item) => {
    return total + item.promo_amount;
  }, 0);
}

function handlePaymentFailure(res) {
  console.log("payment failed: ", res.locals.event.type);
  return;
}

CreditsRouter.prototype.handleReferralSignup = function(req, res) {
  const self = this;
  const Marketing = this.models.Marketing;

  Marketing.isValidReferralLink(req.body.referralLink)
    .then((marketing) => {
      self._issueSignupCredit(req.body, 'referral')
        .then((credit) => {
          self._convertReferralRecipient(credit, marketing, req.body)
            .then((referral) => res.status(200).send(referral))
            .catch((err) => res.status(500).send(err));
        })
        .catch((err) => {
          if (err.message === 'Invalid referral link') {
            return self._issueSignupCredit(req.body)
          }
        })
    })
}

CreditsRouter.prototype.handleSignups = function (req, res) {
  console.log('handlingSignup: ', req.body);

  const self = this;
  const Marketing = this.models.Marketing;
  Marketing.create(req.body.email, function(err, marketing) {
    if (err) {
      console.log('Error creating signup marketing: ', err);
      return res.status(500).send(err);
    }

    if (req.body.referralLink) {
      return self.handleReferralSignup(req, res);
    }

    self._issueSignupCredit(req.body)
      .then((credit) => res.status(200).send(credit))
      .catch((err) => res.status(500).send(err));
  })
}

/**
 * anonymous function - description
 *
 * @param  {type} credit    description
 * @param  {type} marketing refers to sender marketing document, NOT recipient
 * @param  {type} data      description
 * @return {type}           description
 */
CreditsRouter.prototype._convertReferralRecipient = function (credit, marketing, data) {
  console.log('_convertReferralRecipient: ', credit,
    'Marketing: ', marketing,
    'Link: ', data.referralLink, data.email);

  const Referral = this.models.Referral;

  return new Promise((resolve, reject) => {
    Referral
      .findOne({
        'sender.referralLink': data.referralLink,
        'recipient.email': data.email
      })
      .then((referral) => {
        console.log('REFERRAL', referral);
        if (referral) {
          console.log('MODIFYING REFERRAL; ', referral);
          return resolve(referral.convert_recipient_signup(credit));
        }
        console.log('creating new referral:', referral)
        Referral
          .create(marketing, data.email, 'link')
          .then((referral) => referral.convert_recipient_signup(credit))
          .then((referral) => resolve(referral))
      })
      .catch((err) => reject(errors.InternalError(err)))
  });
}

CreditsRouter.prototype._issueSignupCredit = function (data, userType) {
  console.log('_issueSignupCredit, data, userType: ', data, userType);

  const Credit = this.models.Credit;
  const type = userType === 'referral' ? 'REFERRAL_RECIPIENT' : 'NEW_SIGNUP';
  return new Promise((resolve, reject) => {
    const newCredit = new Credit({
      user: data.email,
      type: CREDIT_TYPES.AUTO,
      promo_code: PROMO_CODE[type],
      promo_amount: PROMO_AMOUNT[type],
      promo_expires: PROMO_EXPIRES[type]
    });

    console.log('issueSignupCredit Created: ', newCredit);
    newCredit
      .save()
      .then((credit) => {
        console.log('issueSignupCredit credit created: ', credit);
        return resolve(credit)
      })
      .catch((err) => {
        console.log('Error _issueSignupCredit: ', err);
        return reject(err)
      });
  });
}

CreditsRouter.prototype.verify = function(req, res, next) {
  if (process.env.NODE_ENV === 'production') {
    const eventId = req.body.id;
    stripe.events.retrieve(eventId, function(err, event) {
      if (err) {
        console.error('error verifying stripe event');
        next(err);
      }
      res.locals.event = event;
      next(null);
    })
  } else {
    res.locals.event = req.body;
    // NB: for manual testing only, need to remove
    // res.locals.event.data.object.customer = 'cus_97ADNC3zbcPQkR';
    next(null);
  }
}

CreditsRouter.prototype.checkType = function(type) {
  return function(req, res, next) {
    if (!type.test(res.locals.event.type)) {
      console.error("Expected event type to match: ", type);
      console.error("Received: ", res.locals.event.type);
      return res.sendStatus(400);
    }

    if(type.test(res.locals.event.type)) {
      handlePaymentFailure(res);
      return res.sendStatus(203);
    }

    return next(null);
  }
};

CreditsRouter.prototype.createCredit = function(req, res) {
  const stripeAdapter = paymentProcessorAdapters[STRIPE];
  const invoice = res.locals.event.data.object;
  const customerId = invoice.customer;

  if (invoice.object === 'invoice' && invoice.attempted === 'false') {
    console.log("invoice.object should be invoice: ", invoice.object)
    console.log("invoice.attempted should be false: ", invoice.attempted);
    return res.sendStatus(400);
  }

  // TODO: refactor this to be payment processor agnostic!
  this.models.PaymentProcessor.findOne({
      'rawData.customer.id': customerId
    })
    .then((stripeProcessor) => {
      const billingCycle = getBillingCycle(stripeProcessor.billingDate);
      const params = {
        user: user._id,
        created: {
          $gte: moment(parseInt(billingCycle.startDate, 0)),
          $lte: moment(parseInt(billingCycle.endDate, 0))
        }
      };

      return [
        this.models.Debit.find(params),
        this.models.Credit.find(params),
        this.models.Credit.find({
          user: user._id
        }),
        user,
        billingCycle
      ];
    })
    .then((promises) => (Promise.all(promises)))
    .then((results) => {
      const debits = results[0];
      const credits = results[1];
      const allCredits = results[2];
      const user = results[3];
      const billingCycle = results[4];

      const balance = getBalance(credits, debits);
      const promoBalance = getPromoBalance(allCredits);

      const invoiceAmount = (balance - promoBalance < 0) ?
        0 : balance - promoBalance;

      const promoUsed = (promoBalance - balance > 0) ?
        balance : promoBalance;

      const totalAmount = (invoiceAmount < 0) ?
        0 : invoiceAmount

      const newCredit = new this.models.Credit({
        invoiced_amount: invoiceAmount,
        paid_amount: 0,
        paid: false,
        promo_amount: promoUsed,
        user: user._id,
        payment_processor: STRIPE,
        type: CREDIT_TYPES.AUTO,
        data: {
          invoice: invoice
        }
      });

      newCredit.save((err, credit) => {
        if (err) {
          throw new Error(err);
        }
      })
    })
    .catch((err) => {
      console.error(err);
      throw new Error(err);
    })

  res.sendStatus(201);
}

CreditsRouter.prototype.confirmCredit = function(req, res) {
  if(!invoice.paid){
    return res.status(202).json({'message':'Invoice has not been paid.'})
  }

  this.models.Credit.findOne({
    'data.invoice.id': invoice.id
  })
  .then((credit) => {
    if(credit.invoiced_amount !== invoice.subtotal){
      console.error("Invoiced amount not equal to invoice subtotal.");
      console.error("Expected: ", credit.invoiced_amount);
      console.error("Received: ", invoice.subtotal);
      return res.sendStatus(202);
    }

    if(!result){
      console.error("Could not find credit with invoice id: ", invoice.id);
      return res.sendStatus(202);
    }

    credit.paid_amount = invoice.subtotal;
    credit.paid = true;
    credit.data = {
      invoice: invoice
    };

    return credit.save();
  })
  .then((result) => {
    return res.sendStatus(204)
  })
  .catch((err) => {
    console.error("Error updating credit: ", err);
    return res.sendStatus(500);
  })

};

CreditsRouter.prototype.getReferralSenderCreditsByDate = function(req, res, next) {
  const Credit = this.models.credit;
  Credit.find({
    created: { $gte: req.periodStart, $lte: req.periodEnd }
  }, function(err, credits) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }
    res.status(200).send(credits.map(credit => credit.toObject()))
  })
};

/**
 * Export definitions
 * @private
 */
CreditsRouter.prototype._definitions = function() {
  return [
    ['POST', '/credits',
      rawbody,
      this.verify,
      this.checkType(/^invoice.created$/),
      this.createCredit
    ],
    ['POST', '/credits/confirm',
      rawbody,
      this.verify,
      this.checkType(/^invoice.payment_(succeeded|failed)$/),
      this.confirmCredit
    ],
    ['GET',
     '/credits/referral/:periodStart/:periodEnd',
     authenticate,
     this.getReferralSenderCreditsByDate
    ],
    [
      'POST', '/credits/signups',
      rawbody,
      this.handleSignups
    ]
  ];
};

module.exports = CreditsRouter;
<commit_msg>remove weird, mysterious garbage<commit_after>'use strict';

const Router = require('./index');
const moment = require('moment');
const inherits = require('util').inherits;
const middleware = require('storj-service-middleware');
const rawbody = middleware.rawbody;
const log = require('../../logger');
const authenticate = middleware.authenticate;
const { CREDIT_TYPES, PROMO_CODE, STRIPE, PROMO_EXPIRES, PROMO_AMOUNT } = require('storj-service-storage-models/lib/constants');
const paymentProcessorAdapters = require('storj-service-storage-models/lib/models/payment-processor-adapters');
const stripe = require('../vendor/stripe');
const errors = require('storj-service-error-types');

// TODO: Refactor all stripe-related endpoints into a single endpoint
// to remain payment processor agnostic.

/**
 * Handles endpoints for all user related operations
 */
function CreditsRouter(options) {
  if (!(this instanceof CreditsRouter)) {
    return new CreditsRouter(options);
  }
  this.models = options.storage.models;
  Router.apply(this, arguments);
}

inherits(CreditsRouter, Router);

function getBillingCycle(billingDate) {
  const today = new Date();
  const daysInMonth = (new Date(today.getFullYear(), (today.getMonth()), 0)).getDate();
  const startDayOfMonth = (billingDate > daysInMonth) ? daysInMonth : billingDate;
  const startDate = Date.parse(new Date(
    today.getFullYear(),
    (today.getMonth() - 1),
    startDayOfMonth
  ));
  const endDate = (moment(startDate).add('1', 'month').unix() * 1000);
  return {
    startDate: startDate,
    endDate: endDate
  };
};

function getBalance(credits, debits) {
  const sumCredits = (total, item) => {
    return total + item.paid_amount;
  };

  const sumDebits = (total, item) => {
    return total + item.amount;
  };

  const creditSum = credits.reduce(sumCredits, 0);
  const debitSum = debits.reduce(sumDebits, 0);
  const balance = debitSum - creditSum;

  return balance;
}

function getPromoBalance(credits) {
  return credits.reduce((total, item) => {
    return total + item.promo_amount;
  }, 0);
}

function handlePaymentFailure(res) {
  console.log("payment failed: ", res.locals.event.type);
  return;
}

CreditsRouter.prototype.handleReferralSignup = function(req, res) {
  const self = this;
  const Marketing = this.models.Marketing;

  Marketing.isValidReferralLink(req.body.referralLink)
    .then((marketing) => {
      self._issueSignupCredit(req.body, 'referral')
        .then((credit) => {
          self._convertReferralRecipient(credit, marketing, req.body)
            .then((referral) => res.status(200).send(referral))
            .catch((err) => res.status(500).send(err));
        })
        .catch((err) => {
          if (err.message === 'Invalid referral link') {
            return self._issueSignupCredit(req.body)
          }
        })
    })
}

CreditsRouter.prototype.handleSignups = function (req, res) {
  console.log('handlingSignup: ', req.body);

  const self = this;
  const Marketing = this.models.Marketing;
  Marketing.create(req.body.email, function(err, marketing) {
    if (err) {
      console.log('Error creating signup marketing: ', err);
      return res.status(500).send(err);
    }

    if (req.body.referralLink) {
      return self.handleReferralSignup(req, res);
    }

    self._issueSignupCredit(req.body)
      .then((credit) => res.status(200).send(credit))
      .catch((err) => res.status(500).send(err));
  })
}

/**
 * anonymous function - description
 *
 * @param  {type} credit    description
 * @param  {type} marketing refers to sender marketing document, NOT recipient
 * @param  {type} data      description
 * @return {type}           description
 */
CreditsRouter.prototype._convertReferralRecipient = function (credit, marketing, data) {
  console.log('_convertReferralRecipient: ', credit,
    'Marketing: ', marketing,
    'Link: ', data.referralLink, data.email);

  const Referral = this.models.Referral;

  return new Promise((resolve, reject) => {
    Referral
      .findOne({
        'sender.referralLink': data.referralLink,
        'recipient.email': data.email
      })
      .then((referral) => {
        console.log('REFERRAL', referral);
        if (referral) {
          console.log('MODIFYING REFERRAL; ', referral);
          return resolve(referral.convert_recipient_signup(credit));
        }
        console.log('creating new referral:', referral)
        Referral
          .create(marketing, data.email, 'link')
          .then((referral) => referral.convert_recipient_signup(credit))
          .then((referral) => resolve(referral))
      })
      .catch((err) => reject(errors.InternalError(err)))
  });
}

CreditsRouter.prototype._issueSignupCredit = function (data, userType) {
  console.log('_issueSignupCredit, data, userType: ', data, userType);

  const Credit = this.models.Credit;
  const type = userType === 'referral' ? 'REFERRAL_RECIPIENT' : 'NEW_SIGNUP';
  return new Promise((resolve, reject) => {
    const newCredit = new Credit({
      user: data.email,
      type: CREDIT_TYPES.AUTO,
      promo_code: PROMO_CODE[type],
      promo_amount: PROMO_AMOUNT[type],
      promo_expires: PROMO_EXPIRES[type]
    });

    console.log('issueSignupCredit Created: ', newCredit);
    newCredit
      .save()
      .then((credit) => {
        console.log('issueSignupCredit credit created: ', credit);
        return resolve(credit)
      })
      .catch((err) => {
        console.log('Error _issueSignupCredit: ', err);
        return reject(err)
      });
  });
}

CreditsRouter.prototype.verify = function(req, res, next) {
  if (process.env.NODE_ENV === 'production') {
    const eventId = req.body.id;
    stripe.events.retrieve(eventId, function(err, event) {
      if (err) {
        console.error('error verifying stripe event');
        next(err);
      }
      res.locals.event = event;
      next(null);
    })
  } else {
    res.locals.event = req.body;
    // NB: for manual testing only, need to remove
    // res.locals.event.data.object.customer = 'cus_97ADNC3zbcPQkR';
    next(null);
  }
}

CreditsRouter.prototype.checkType = function(type) {
  return function(req, res, next) {
    if (!type.test(res.locals.event.type)) {
      console.error("Expected event type to match: ", type);
      console.error("Received: ", res.locals.event.type);
      return res.sendStatus(400);
    }

    if(type.test(res.locals.event.type)) {
      handlePaymentFailure(res);
      return res.sendStatus(203);
    }

    return next(null);
  }
};

CreditsRouter.prototype.createCredit = function(req, res) {
  const stripeAdapter = paymentProcessorAdapters[STRIPE];
  const invoice = res.locals.event.data.object;
  const customerId = invoice.customer;

  if (invoice.object === 'invoice' && invoice.attempted === 'false') {
    console.log("invoice.object should be invoice: ", invoice.object)
    console.log("invoice.attempted should be false: ", invoice.attempted);
    return res.sendStatus(400);
  }

  // TODO: refactor this to be payment processor agnostic!
  this.models.PaymentProcessor.findOne({
      'rawData.customer.id': customerId
    })
    .then((stripeProcessor) => {
      const billingCycle = getBillingCycle(stripeProcessor.billingDate);
      const params = {
        user: user._id,
        created: {
          $gte: moment(parseInt(billingCycle.startDate, 0)),
          $lte: moment(parseInt(billingCycle.endDate, 0))
        }
      };

      return [
        this.models.Debit.find(params),
        this.models.Credit.find(params),
        this.models.Credit.find({
          user: user._id
        }),
        user,
        billingCycle
      ];
    })
    .then((promises) => (Promise.all(promises)))
    .then((results) => {
      const debits = results[0];
      const credits = results[1];
      const allCredits = results[2];
      const user = results[3];
      const billingCycle = results[4];

      const balance = getBalance(credits, debits);
      const promoBalance = getPromoBalance(allCredits);

      const invoiceAmount = (balance - promoBalance < 0) ?
        0 : balance - promoBalance;

      const promoUsed = (promoBalance - balance > 0) ?
        balance : promoBalance;

      const totalAmount = (invoiceAmount < 0) ?
        0 : invoiceAmount

      const newCredit = new this.models.Credit({
        invoiced_amount: invoiceAmount,
        paid_amount: 0,
        paid: false,
        promo_amount: promoUsed,
        user: user._id,
        payment_processor: STRIPE,
        type: CREDIT_TYPES.AUTO,
        data: {
          invoice: invoice
        }
      });

      newCredit.save((err, credit) => {
        if (err) {
          throw new Error(err);
        }
      })
    })
    .catch((err) => {
      console.error(err);
      throw new Error(err);
    })

  res.sendStatus(201);
}

CreditsRouter.prototype.confirmCredit = function(req, res) {
  if(!invoice.paid){
    return res.status(202).json({'message':'Invoice has not been paid.'})
  }

  this.models.Credit.findOne({
    'data.invoice.id': invoice.id
  })
  .then((credit) => {
    if(credit.invoiced_amount !== invoice.subtotal){
      console.error("Invoiced amount not equal to invoice subtotal.");
      console.error("Expected: ", credit.invoiced_amount);
      console.error("Received: ", invoice.subtotal);
      return res.sendStatus(202);
    }

    if(!result){
      console.error("Could not find credit with invoice id: ", invoice.id);
      return res.sendStatus(202);
    }

    credit.paid_amount = invoice.subtotal;
    credit.paid = true;
    credit.data = {
      invoice: invoice
    };

    return credit.save();
  })
  .then((result) => {
    return res.sendStatus(204)
  })
  .catch((err) => {
    console.error("Error updating credit: ", err);
    return res.sendStatus(500);
  })

};

CreditsRouter.prototype.getReferralSenderCreditsByDate = function(req, res, next) {
  const Credit = this.models.credit;
  Credit.find({
    created: { $gte: req.periodStart, $lte: req.periodEnd }
  }, function(err, credits) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }
    res.status(200).send(credits.map(credit => credit.toObject()))
  })
};

/**
 * Export definitions
 * @private
 */
CreditsRouter.prototype._definitions = function() {
  return [
    ['POST', '/credits',
      rawbody,
      this.verify,
      this.checkType(/^invoice.created$/),
      this.createCredit
    ],
    ['POST', '/credits/confirm',
      rawbody,
      this.verify,
      this.checkType(/^invoice.payment_(succeeded|failed)$/),
      this.confirmCredit
    ],
    ['GET',
     '/credits/referral/:periodStart/:periodEnd',
     authenticate,
     this.getReferralSenderCreditsByDate
    ],
    [
      'POST', '/credits/signups',
      rawbody,
      this.handleSignups
    ]
  ];
};

module.exports = CreditsRouter;
<|endoftext|>


<commit_before>var io = require('socket.io');
var ObjectId = require('mongoose').Types.ObjectId;
var User = require('./model/user');
var Message = require('./model/message');
var Group = require('./model/group');
var GroupSetting = require('./model/groupSetting');
var async = require('async');

var sockets = {};

exports.setup = function(server) {
  io = io.listen(server, {origins: '*:*'});

  ///
  /// Setup Socket IO
  ///
  io.configure(function (){
    io.set('authorization', function (handshakeData, callback) {
      console.log('AUTHORIZED ACTIVATED: ' + JSON.stringify(handshakeData, null, 4));
      // findDatabyip is an async example function
      var token = handshakeData.query.token;

      if (!token) return callback(new Error('You must have a session token!'));

      console.log('TokeN???? ' + token);
      User.findOne( { accessToken: token }, function (err, usr) {
	console.log('Found: ' + err + ' Usr: ' + usr);

	if (err) return callback(err);

	if (usr) {
	  console.log('SUCCESSFUL SOCKET AUTH');
	  handshakeData.user = usr;
	  callback(null, true);
	} else {
          callback(null, false);
	}
      });
    });
  });

  io.on('connection', function (socket) {

    var socketUser = socket.handshake.user;

    ///
    /// Store the socket for later so we can emit events to it.
    ///
    sockets[socketUser._id] = socket;

    ///
    /// Upon connecting, join every room that you should be in.
    ///
    socketUser.groups.forEach(function(group) {
      socket.join(group);
    });

    ///
    /// Default event to send back to the client to let them know they are all set
    ///
    socket.emit('connected', 'FastChat');

    /**
     * An event to make communication more dynamic. The typing event is immediatly fired
     * to other clients listening on the room to let them know when someone is typing.
     * Format:
     * {
     *   group: '342342343',
     *   typing: true[false]
     * }
     */
    socket.on('typing', function(typing) {
      var room = typing.group;
      var toSend = { 'typing': typing['typing'] == true, 'from': socketUser._id, 'group': room};

      if (socketUser.hasGroup(room)) {
	socket.broadcast.to(room).emit('typing', toSend);
      }
    });

    /**
     * Called when a message is sent to a group. It will send it to all people in the group
     * and then save it for later retreiving, and then send a push notification to those
     * people who are not in the group.
     * Format:
     * {
     *   group: '342342343',
     *   text: 'This is a message!'
     * }
     */
    socket.on('message', function(message) {
      console.log('Received Message: ' + JSON.stringify(message, null, 4));
      var room = message.group;

      if (!message.text) return;

      if (socketUser.hasGroup(room)) {

	var roomId = null;
	try {
	  roomId = new ObjectId(room);
	} catch(err) {
	  console.log('Tried to make the room ID and failed! ' + err);
	  return;
	}

	///
	/// Make a new message and add it to the group
	///
	var mes = new Message({'from' : socketUser._id,
			       'group': roomId,
			       'text' : message.text,
			       'sent' : new Date()
			      });

	///
	/// Broadcast the message to everyone in the group instantly
	///
	socket.broadcast.to(room).emit('message', mes);

	///
	/// Save the object we have, and add it to the group
	///
	Group.findOne({'_id' : room}, function(err, group) {
	  if (group) {
	    group.messages.push(mes);
	    group.lastMessage = mes._id;
	    group.save();
	    mes.save();

	    ///
	    /// Add a temporary property 'fromUser' with the actual user object.
	    ///
	    mes.fromUser = socketUser;

	    ///
	    /// Let's send some notifications to all people not in the room.
	    ///
	    var clients = io.sockets.clients(room);
	    var roomUsers = []; //all currently in the room
	    clients.forEach(function(client) {
	      roomUsers.push(client.handshake.user);
	    });

	    // Find all users who are in the group
	    // Find Users who groups include 'room'
	    User.find({groups: { $in : [room] } }, function(err, users) {

	      var usersNotInRoom = [];
	      for (var i = 0; i < users.length; i++) {
		var foundUser = false;
		for (var j = 0; j < roomUsers.length; j++) {
		  if (roomUsers[j]._id.equals(users[i]._id)) {
		    foundUser = true;
		    break;
		  }
		}
		if (!foundUser) usersNotInRoom.push(users[i]);
	      }

	      console.log('users NOT in room: ' + JSON.stringify(usersNotInRoom, null, 4));

	      ///
	      /// Okay, we have the users not in the room. Send a message to them.
	      ///
	      async.each(usersNotInRoom, function(user, callback) {

		GroupSetting.find({'user': user._id}, function(err, gses) {
		  console.log('FOUND: ' + JSON.stringify(gses, null, 4));

		  var thisGs = GroupSetting.forGroup(gses, roomId);

		  console.log('THIS IS: ' + JSON.stringify(thisGs, null, 4));
		  if (thisGs) {
		    thisGs.unread++;

		    var text = mes.fromUser.username + '@' + group.name + ': ' + mes.text;
		    user.push(group, text, GroupSetting.totalUnread(gses), false);
		    thisGs.save(function(err) {
		      callback();
		    });
		  } else {
		    console.log('This should never be called. ');
		    console.trace();
		    callback();
		  }
		});
	      }, function(err){
		if( err ) {
		  console.log('Error Occured in sending push notifications: ' + err);
		}
	      });

	    }); //Find all users in group

	  }
	});

      } //If has group

    }); //On message

    socket.on('disconnect', function() {
      delete sockets[socketUser._id];
    }); //end on disconnect

  }); //end on Socket.io connection

};

/**
 * This method sends a message (@see message.js) to the group
 * from no one in particular. It will simply send it out to all members of the group.
 * This is useful for system events, such as someone joining or leaving the group.
 */
exports.messageToGroup = function(groupId, event, message) {
  io.sockets.in(groupId).emit(event, message)
};

/**
 * Have the given user join the room. This is useful for if the user has been
 * invited to a group and they are currently chatting with people live. We will
 * add them to the group so they can start receiving messages without having
 * to disconnect and reconnect
 */
exports.joinGroup = function(groupId, userId) {
  var userSocket = sockets[userId];

  /// If this is false, then they were not on. Oh well.
  if (userSocket) {
    userSocket.join(groupId);
    return true;
  }
  return false;
};

exports.emitNewGroup = function(userId, group) {
  var userSocket = sockets[userId];

  if (userSocket) {
    socket.emit('new_group', group);
    return true;
  }
  return false;
};
<commit_msg>Fixed a bug referencing the wrong variable<commit_after>var io = require('socket.io');
var ObjectId = require('mongoose').Types.ObjectId;
var User = require('./model/user');
var Message = require('./model/message');
var Group = require('./model/group');
var GroupSetting = require('./model/groupSetting');
var async = require('async');

var sockets = {};

exports.setup = function(server) {
  io = io.listen(server, {origins: '*:*'});

  ///
  /// Setup Socket IO
  ///
  io.configure(function (){
    io.set('authorization', function (handshakeData, callback) {
      console.log('AUTHORIZED ACTIVATED: ' + JSON.stringify(handshakeData, null, 4));
      // findDatabyip is an async example function
      var token = handshakeData.query.token;

      if (!token) return callback(new Error('You must have a session token!'));

      console.log('TokeN???? ' + token);
      User.findOne( { accessToken: token }, function (err, usr) {
	console.log('Found: ' + err + ' Usr: ' + usr);

	if (err) return callback(err);

	if (usr) {
	  console.log('SUCCESSFUL SOCKET AUTH');
	  handshakeData.user = usr;
	  callback(null, true);
	} else {
          callback(null, false);
	}
      });
    });
  });

  io.on('connection', function (socket) {

    var socketUser = socket.handshake.user;

    ///
    /// Store the socket for later so we can emit events to it.
    ///
    sockets[socketUser._id] = socket;

    ///
    /// Upon connecting, join every room that you should be in.
    ///
    socketUser.groups.forEach(function(group) {
      socket.join(group);
    });

    ///
    /// Default event to send back to the client to let them know they are all set
    ///
    socket.emit('connected', 'FastChat');

    /**
     * An event to make communication more dynamic. The typing event is immediatly fired
     * to other clients listening on the room to let them know when someone is typing.
     * Format:
     * {
     *   group: '342342343',
     *   typing: true[false]
     * }
     */
    socket.on('typing', function(typing) {
      var room = typing.group;
      var toSend = { 'typing': typing['typing'] == true, 'from': socketUser._id, 'group': room};

      if (socketUser.hasGroup(room)) {
	socket.broadcast.to(room).emit('typing', toSend);
      }
    });

    /**
     * Called when a message is sent to a group. It will send it to all people in the group
     * and then save it for later retreiving, and then send a push notification to those
     * people who are not in the group.
     * Format:
     * {
     *   group: '342342343',
     *   text: 'This is a message!'
     * }
     */
    socket.on('message', function(message) {
      console.log('Received Message: ' + JSON.stringify(message, null, 4));
      var room = message.group;

      if (!message.text) return;

      if (socketUser.hasGroup(room)) {

	var roomId = null;
	try {
	  roomId = new ObjectId(room);
	} catch(err) {
	  console.log('Tried to make the room ID and failed! ' + err);
	  return;
	}

	///
	/// Make a new message and add it to the group
	///
	var mes = new Message({'from' : socketUser._id,
			       'group': roomId,
			       'text' : message.text,
			       'sent' : new Date()
			      });

	///
	/// Broadcast the message to everyone in the group instantly
	///
	socket.broadcast.to(room).emit('message', mes);

	///
	/// Save the object we have, and add it to the group
	///
	Group.findOne({'_id' : room}, function(err, group) {
	  if (group) {
	    group.messages.push(mes);
	    group.lastMessage = mes._id;
	    group.save();
	    mes.save();

	    ///
	    /// Add a temporary property 'fromUser' with the actual user object.
	    ///
	    mes.fromUser = socketUser;

	    ///
	    /// Let's send some notifications to all people not in the room.
	    ///
	    var clients = io.sockets.clients(room);
	    var roomUsers = []; //all currently in the room
	    clients.forEach(function(client) {
	      roomUsers.push(client.handshake.user);
	    });

	    // Find all users who are in the group
	    // Find Users who groups include 'room'
	    User.find({groups: { $in : [room] } }, function(err, users) {

	      var usersNotInRoom = [];
	      for (var i = 0; i < users.length; i++) {
		var foundUser = false;
		for (var j = 0; j < roomUsers.length; j++) {
		  if (roomUsers[j]._id.equals(users[i]._id)) {
		    foundUser = true;
		    break;
		  }
		}
		if (!foundUser) usersNotInRoom.push(users[i]);
	      }

	      console.log('users NOT in room: ' + JSON.stringify(usersNotInRoom, null, 4));

	      ///
	      /// Okay, we have the users not in the room. Send a message to them.
	      ///
	      async.each(usersNotInRoom, function(user, callback) {

		GroupSetting.find({'user': user._id}, function(err, gses) {
		  console.log('FOUND: ' + JSON.stringify(gses, null, 4));

		  var thisGs = GroupSetting.forGroup(gses, roomId);

		  console.log('THIS IS: ' + JSON.stringify(thisGs, null, 4));
		  if (thisGs) {
		    thisGs.unread++;

		    var text = mes.fromUser.username + '@' + group.name + ': ' + mes.text;
		    user.push(group, text, GroupSetting.totalUnread(gses), false);
		    thisGs.save(function(err) {
		      callback();
		    });
		  } else {
		    console.log('This should never be called. ');
		    console.trace();
		    callback();
		  }
		});
	      }, function(err){
		if( err ) {
		  console.log('Error Occured in sending push notifications: ' + err);
		}
	      });

	    }); //Find all users in group

	  }
	});

      } //If has group

    }); //On message

    socket.on('disconnect', function() {
      delete sockets[socketUser._id];
    }); //end on disconnect

  }); //end on Socket.io connection

};

/**
 * This method sends a message (@see message.js) to the group
 * from no one in particular. It will simply send it out to all members of the group.
 * This is useful for system events, such as someone joining or leaving the group.
 */
exports.messageToGroup = function(groupId, event, message) {
  io.sockets.in(groupId).emit(event, message)
};

/**
 * Have the given user join the room. This is useful for if the user has been
 * invited to a group and they are currently chatting with people live. We will
 * add them to the group so they can start receiving messages without having
 * to disconnect and reconnect
 */
exports.joinGroup = function(groupId, userId) {
  var userSocket = sockets[userId];

  /// If this is false, then they were not on. Oh well.
  if (userSocket) {
    userSocket.join(groupId);
    return true;
  }
  return false;
};

exports.emitNewGroup = function(userId, group) {
  var userSocket = sockets[userId];

  if (userSocket) {
    userSocket.emit('new_group', group);
    return true;
  }
  return false;
};
<|endoftext|>


<commit_before>var app = require('http').createServer(),
    io = require('socket.io').listen(app),
    spawn = require('child_process').spawn,
    fs = require('fs');

var generateUserid = function() {
    var id = (Math.random().toString(36)+'00000000000000000').slice(2, 8);
    if (connectedUsers[id] === undefined) return id;
    else return generateUserid();
};

var connectedUsers = {};

io.of('/ruby').on('connection', function(socket) {

    var ruby,
        userid,
        socketOn = false;

    socket.on('codeBuild', function(data) {
        console.log(data);

        var fileSaveError,
            codeLoadError;

        // Save file
        fs.writeFile('games/' + userid + '.rb', data.fileContent, function(error) {
            fileSaveError = error;

            // Emit file saved
            socket.emit('buildStatus', {
                output: error || 'Code saved.'
            });

            // Check file syntax?

            // Kill old IRB
            if (ruby) {
              oldRuby = ruby;
              oldRuby.kill();
            }

            // Start IRB
            ruby = spawn('irb');

            ruby.stdout.setEncoding('utf8');
            ruby.stderr.setEncoding('utf8');

            // Funnel file into IRB
            socketOn = false;
            ruby.stdin.write(data.fileContent + '\n', function(error) {
                codeLoadError = error;

                // Emit code loaded
                socket.emit('buildStatus', {
                    output: error || 'Game loaded.'
                });

                if (!fileSaveError && !codeLoadError) {
                    socket.emit('buildStatus', {
                        output: 'Build successful!'
                    });
                } else {
                    socket.emit('terminalError', {
                        output: 'Build failed!'
                    });
                }
            });

            // Bind events on IRB
            ruby.stdout.on('data', function(data) {
                console.log('stdout: ' + data);
                if (socketOn) {
                    console.log('socket on');
                    socket.emit('terminalOutput', {
                        output: data
                    });
                }
            });

            ruby.stderr.on('data', function(data) {
                console.log('stderr: ' + data);
                socket.emit('terminalError', {
                    output: data
                });
            });

            ruby.on('close', function(code) {
                console.log('Exit code: ' + code);
            });

        });
    });

    socket.on('terminalInput', function(data) {
        console.log(data);
        socketOn = true;
        if (ruby) {
            ruby.stdin.write(data.input + '\n');
        }
        else {
            socket.emit('terminalError', {
                output: 'Game not built!'
            });
        }
    });

    socket.on('reportUserid', function(data) {
        console.log(data.userid);
        console.log(connectedUsers[data.userid]);
        var oldSocket = connectedUsers[data.userid];
        var newOrAuthenticatedUser = !oldSocket || oldSocket === data.authid;

        if (data.userid && newOrAuthenticatedUser) {
            userid = data.userid;
        } else userid = generateUserid();

        // Store socket id because XHR polling causes race conditions
        connectedUsers[userid] = socket.id;
        socket.emit('confirmUserid', {
            userid: userid,
            authid: socket.id
        });

        fs.readFile('games/' + userid + '.rb', function(error, contents) {
            if (error) {
                fs.readFile('games/game.rb', function(error, contents) {
                    socket.emit('ready', {
                        output: 'Ready!',
                        fileContent: contents.toString()
                    });
                });
            } else {
                socket.emit('ready', {
                    output: 'Ready! Welcome back.',
                    fileContent: contents.toString()
                });
            }
        });
    });

    socket.on('disconnect', function() {
        console.log('User ' + userid + 'disconnected.');
        // Only set to false if user doesn't already have a new socket
        if (socket.id == connectedUsers[userid]) connectedUsers[userid] = false;
        console.log(connectedUsers);
    });

});

app.listen(process.env.PORT || 8888);
<commit_msg>Clean up logs for user status<commit_after>var app = require('http').createServer(),
    io = require('socket.io').listen(app),
    spawn = require('child_process').spawn,
    fs = require('fs');

var generateUserid = function() {
    var id = (Math.random().toString(36)+'00000000000000000').slice(2, 8);
    if (connectedUsers[id] === undefined) return id;
    else return generateUserid();
};

var connectedUsers = {};

io.of('/ruby').on('connection', function(socket) {

    var ruby,
        userid,
        socketOn = false;

    socket.on('codeBuild', function(data) {
        console.log(data);

        var fileSaveError,
            codeLoadError;

        // Save file
        fs.writeFile('games/' + userid + '.rb', data.fileContent, function(error) {
            fileSaveError = error;

            // Emit file saved
            socket.emit('buildStatus', {
                output: error || 'Code saved.'
            });

            // Check file syntax?

            // Kill old IRB
            if (ruby) {
              oldRuby = ruby;
              oldRuby.kill();
            }

            // Start IRB
            ruby = spawn('irb');

            ruby.stdout.setEncoding('utf8');
            ruby.stderr.setEncoding('utf8');

            // Funnel file into IRB
            socketOn = false;
            ruby.stdin.write(data.fileContent + '\n', function(error) {
                codeLoadError = error;

                // Emit code loaded
                socket.emit('buildStatus', {
                    output: error || 'Game loaded.'
                });

                if (!fileSaveError && !codeLoadError) {
                    socket.emit('buildStatus', {
                        output: 'Build successful!'
                    });
                } else {
                    socket.emit('terminalError', {
                        output: 'Build failed!'
                    });
                }
            });

            // Bind events on IRB
            ruby.stdout.on('data', function(data) {
                console.log('stdout: ' + data);
                if (socketOn) {
                    console.log('socket on');
                    socket.emit('terminalOutput', {
                        output: data
                    });
                }
            });

            ruby.stderr.on('data', function(data) {
                console.log('stderr: ' + data);
                socket.emit('terminalError', {
                    output: data
                });
            });

            ruby.on('close', function(code) {
                console.log('Exit code: ' + code);
            });

        });
    });

    socket.on('terminalInput', function(data) {
        console.log(data);
        socketOn = true;
        if (ruby) {
            ruby.stdin.write(data.input + '\n');
        } else {
            socket.emit('terminalError', {
                output: 'Game not built!'
            });
        }
    });

    socket.on('reportUserid', function(data) {
        var oldSocket = connectedUsers[data.userid];
        var newOrAuthenticatedUser = !oldSocket || oldSocket === data.authid;

        if (data.userid && newOrAuthenticatedUser) {
            userid = data.userid;
        } else userid = generateUserid();

        console.log('User ' + userid + ' connected on ' + socket.id);

        // Store socket id because XHR polling causes race conditions
        connectedUsers[userid] = socket.id;
        socket.emit('confirmUserid', {
            userid: userid,
            authid: socket.id
        });

        fs.readFile('games/' + userid + '.rb', function(error, contents) {
            if (error) {
                fs.readFile('games/game.rb', function(error, contents) {
                    socket.emit('ready', {
                        output: 'Ready!',
                        fileContent: contents.toString()
                    });
                });
            } else {
                socket.emit('ready', {
                    output: 'Ready! Welcome back.',
                    fileContent: contents.toString()
                });
            }
        });
    });

    socket.on('disconnect', function() {
        console.log('User ' + userid + ' disconnected from ' + socket.id);
        // Only set to false if user doesn't already have a new socket
        if (socket.id == connectedUsers[userid]) {
            connectedUsers[userid] = false;
            console.log('User ' + userid + ' disconnected completely');
        }
    });

});

app.listen(process.env.PORT || 8888);
<|endoftext|>


<commit_before>const andFilter = "AND";
const orFilter = "OR";
const singleFilter = "single";

export const ANDFilter = () => new Filter(andFilter);
export const ORFilter = () => new Filter(orFilter);

export class Filter {
  constructor(type) {
    this.filters = {};
    this.type = type;
  }

  setFilter(name, filter) {
    this.filters[name] = filter;
  }

  getFilter(name) {
    return this.filters[name];
  }

  removeFilter(name) {
    delete this.filters[name];
  }

  evaluate() {
    let filters = Object.keys(this.filters)
      .map(k => this.filters[k])
      .filter(Boolean);
    if (this.type === singleFilter) {
      if (filters.length === 1) {
        return "(" + filters[0] + ")";
      } else {
        return undefined;
      }
    }

    filters = filters
      .map(f => {
        if (typeof f === "string") {
          return f;
        } else if (f instanceof Filter) {
          return f.evaluate();
        } else {
          throw new Error(
            "filter value must be either string or instance of Filter, got " +
              typeof f
          );
        }
      })
      .filter(Boolean);
    if (filters.length > 0) {
      return "(" + filters.join(") " + this.type + " (") + ")";
    }
    return undefined;
  }
}
<commit_msg>filter: return a valid empty filter if no filter has been set<commit_after>const andFilter = "AND";
const orFilter = "OR";
const singleFilter = "single";

export const ANDFilter = () => new Filter(andFilter);
export const ORFilter = () => new Filter(orFilter);

export class Filter {
  constructor(type) {
    this.filters = {};
    this.type = type;
  }

  setFilter(name, filter) {
    this.filters[name] = filter;
  }

  getFilter(name) {
    return this.filters[name];
  }

  removeFilter(name) {
    delete this.filters[name];
  }

  evaluate() {
    let filters = Object.keys(this.filters)
      .map(k => this.filters[k])
      .filter(Boolean);
    if (this.type === singleFilter) {
      if (filters.length === 1) {
        return "(" + filters[0] + ")";
      } else {
        return "";
      }
    }

    filters = filters
      .map(f => {
        if (typeof f === "string") {
          return f;
        } else if (f instanceof Filter) {
          return f.evaluate();
        } else {
          throw new Error(
            "filter value must be either string or instance of Filter, got " +
              typeof f
          );
        }
      })
      .filter(Boolean);
    if (filters.length > 0) {
      return "(" + filters.join(") " + this.type + " (") + ")";
    }
    return "";
  }
}
<|endoftext|>


<commit_before>'use strict';

var Node = require('../node');

var TYPE = 'closest';

var PARAMS = {
    source: Node.PARAM.NODE(Node.GEOMETRY.ANY),
    target: Node.PARAM.NODE(Node.GEOMETRY.ANY)
};

var Closest = Node.create(TYPE, PARAMS, {cache: true, version: 1});

module.exports = Closest;
module.exports.TYPE = TYPE;
module.exports.PARAMS = PARAMS;

var closestTemplate = Node.template([
    'WITH',
    ' source as({{=it.squery}}),',
    ' target as({{=it.tquery}})',
    'SELECT',
    ' s.*,',
    ' t.cartodb_id as closest_id,',
    ' ST_Distance(geography(t.the_geom),',
    ' geography(s.the_geom)) as closest_dist',
    'FROM',
    '(',
        'SELECT DISTINCT ON (the_geom) *',
        ' FROM source',
    ') AS s',
    'CROSS JOIN LATERAL',
    '(',
        'SELECT cartodb_id, the_geom',
        ' FROM target',
        ' ORDER BY s.the_geom_webmercator <-> the_geom_webmercator',
        ' LIMIT 1',
    ') AS t'
].join('\n'));

Closest.prototype.sql = function(){
    return closestTemplate({
        squery: this.source.getQuery(),
        tquery: this.target.getQuery()
    });
};
<commit_msg>fix geometry<commit_after>'use strict';

var Node = require('../node');

var TYPE = 'closest';

var PARAMS = {
    source: Node.PARAM.NODE(Node.GEOMETRY.ANY),
    target: Node.PARAM.NODE(Node.GEOMETRY.ANY)
};

var Closest = Node.create(TYPE, PARAMS, {cache: true, version: 1});

module.exports = Closest;
module.exports.TYPE = TYPE;
module.exports.PARAMS = PARAMS;

var closestTemplate = Node.template([
    'WITH',
    ' source as({{=it.squery}}),',
    ' target as({{=it.tquery}})',
    'SELECT',
    ' s.*,',
    ' t.cartodb_id as closest_id,',
    ' ST_Distance(geography(t.the_geom),',
    ' geography(s.the_geom)) as closest_dist',
    'FROM',
    '(',
        'SELECT DISTINCT ON (the_geom) *',
        ' FROM source',
    ') AS s',
    'CROSS JOIN LATERAL',
    '(',
        'SELECT cartodb_id, the_geom',
        ' FROM target',
        ' ORDER BY s.the_geom <-> the_geom',
        ' LIMIT 1',
    ') AS t'
].join('\n'));

Closest.prototype.sql = function(){
    return closestTemplate({
        squery: this.source.getQuery(),
        tquery: this.target.getQuery()
    });
};
<|endoftext|>


<commit_before>angular.module('oi.select')

.provider('oiSelect', function() {
    return {
        options: {
            debounce:       500,
            searchFilter:   'oiSelectCloseIcon',
            dropdownFilter: 'oiSelectHighlight',
            listFilter:     'oiSelectAscSort',
            editItem:       false,
            newItem:        false,
            closeList:      true,
            saveTrigger:    'enter'
        },
        $get: function() {
            return {
                options: this.options
            };
        }
    };
})

.factory('oiSelectEditItem', function() {
    return function(removedItem, lastQuery, getLabel) {
        return getLabel(removedItem);
    };
})

.factory('oiUtils', ['$document', '$timeout', function($document, $timeout) {
    /**
     * Measures the width of a string within a
     * parent element (in pixels).
     *
     * @param {string} str
     * @param {object} $parent
     * @returns {int}
     */
    function measureString(str, $parent) {
        var $mirror = angular.element('<mirror>').css({
            position: 'absolute',
            width: 'auto',
            padding: 0,
            whiteSpace: 'pre',
            visibility: 'hidden',
            'z-index': -99999
        }).text(str || '');

        transferStyles($parent, $mirror, 'letterSpacing fontSize fontFamily fontWeight textTransform'.split(' '));

        $document[0].body.appendChild($mirror[0]);

        var width = $mirror[0].offsetWidth;
        $mirror.remove();

        return width;
    }

    /**
     * Copies CSS properties from one element to another.
     *
     * @param {object} $from
     * @param {object} $to
     * @param {array} properties
     */
    function transferStyles($from, $to, properties) {
        var stylesTo = {},
            stylesFrom = getComputedStyle($from[0], '');

        for (var i = 0, n = properties.length; i < n; i++) {
            stylesTo[properties[i]] = stylesFrom[properties[i]];
        }

        $to.css(stylesTo);
    }

    /**
     * Check to see if a DOM element is a descendant of another DOM element.
     *
     * @param {DOM element} container
     * @param {DOM element} contained
     * @param {string} class name of element in container
     * @returns {boolean}
     */
    function contains(container, contained, className) {
        var current = contained;

        while (current && current.ownerDocument && current.nodeType !== 11) {
            if (className) {
                if (current === container) {
                    return false;
                }
                if (current.classList.contains(className)) {
                    return true;
                }
            } else {
                if (current === container) {
                    return true;
                }
            }
            current = current.parentNode;
        }

        return false;
    }

    /**
     * Simulate focus/blur events of the inner input element to the outer element
     *
     * @param {element} outer element
     * @param {element} inner input element
     * @returns {function} deregistration function for listeners.
     */
    function bindFocusBlur(element, inputElement) {
        var isFocused = false;

        $document[0].addEventListener('click', clickHandler, true);
        element[0].addEventListener('blur', blurHandler, true);
        inputElement.on('focus', focusHandler);

        function blurHandler(event) {
            var relatedTarget = event.relatedTarget; //TODO: get relativeTarget in IE, FF (event.explicitOriginalTarget || document.activeElement);

            if (relatedTarget === inputElement[0]) {
                event.stopImmediatePropagation(); //cancel blur if focus to input element
                return;
            }

            if (relatedTarget) { //not triggered blur
                isFocused = false;

                $timeout(function () {
                    element.triggerHandler('blur'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        function focusHandler(event) {
            if (!isFocused) {
                isFocused = true;

                $timeout(function () {
                    element.triggerHandler('focus'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        function clickHandler(event) {
            var activeElement = event.target;
            var isSelectElement = contains(element[0], activeElement);

            if (isSelectElement && activeElement.nodeName !== 'INPUT') {
                $timeout(function () {
                    inputElement.triggerHandler('focus');
                });
            }

            if (!isSelectElement && isFocused) {
                isFocused = false;

                $timeout(function () {
                    element.triggerHandler('blur'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        return function () {
            $document[0].removeEventListener('click', clickHandler);
            element[0].removeEventListener('blur', blurHandler, true);
            inputElement.off('focus', focusHandler);
        }
    }

    /**
     * Sets the selected item in the dropdown menu
     * of available options.
     *
     * @param {object} list
     * @param {object} item
     */
    function scrollActiveOption(list, item) {
        var y, height_menu, height_item, scroll, scroll_top, scroll_bottom;

        if (item) {
            height_menu = list.offsetHeight;
            height_item = getWidthOrHeight(item, 'height', 'margin'); //outerHeight(true);
            scroll = list.scrollTop || 0;
            y = getOffset(item).top - getOffset(list).top + scroll;
            scroll_top = y;
            scroll_bottom = y - height_menu + height_item;

            //TODO Make animation
            if (y + height_item > height_menu + scroll) {
                list.scrollTop = scroll_bottom;
            } else if (y < scroll) {
                list.scrollTop = scroll_top;
            }
        }
    }

    // Used for matching numbers
    var core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;
    var rnumnonpx = new RegExp("^(" + core_pnum + ")(?!px)[a-z%]+$", "i");

    function augmentWidthOrHeight(elem, name, extra, isBorderBox, styles) {
        var i = extra === (isBorderBox ? 'border' : 'content') ?
                // If we already have the right measurement, avoid augmentation
                4 :
                // Otherwise initialize for horizontal or vertical properties
                name === 'width' ? 1 : 0,

            val = 0,
            cssExpand = ['Top', 'Right', 'Bottom', 'Left'];

        //TODO Use angular.element.css instead of getStyleValue after https://github.com/caitp/angular.js/commit/92bbb5e225253ebddd38ef5735d66ffef76b6a14 will be applied
        function getStyleValue(name) {
            return parseFloat(styles[name]);
        }

        for (; i < 4; i += 2) {
            // both box models exclude margin, so add it if we want it
            if (extra === 'margin') {
                val += getStyleValue(extra + cssExpand[i]);
            }

            if (isBorderBox) {
                // border-box includes padding, so remove it if we want content
                if (extra === 'content') {
                    val -= getStyleValue('padding' + cssExpand[i]);
                }

                // at this point, extra isn't border nor margin, so remove border
                if (extra !== 'margin') {
                    val -= getStyleValue('border' + cssExpand[i] + 'Width');
                }
            } else {
                val += getStyleValue('padding' + cssExpand[i]);

                // at this point, extra isn't content nor padding, so add border
                if (extra !== 'padding') {
                    val += getStyleValue('border' + cssExpand[i] + 'Width');
                }
            }
        }

        return val;
    }

    function getOffset(elem) {
        var docElem, win,
            box = elem.getBoundingClientRect(),
            doc = elem && elem.ownerDocument;

        if (!doc) {
            return;
        }

        docElem = doc.documentElement;
        win = getWindow(doc);

        return {
            top: box.top + win.pageYOffset - docElem.clientTop,
            left: box.left + win.pageXOffset - docElem.clientLeft
        };
    }

    function getWindow(elem) {
        return elem != null && elem === elem.window ? elem : elem.nodeType === 9 && elem.defaultView;
    }

    function getWidthOrHeight(elem, name, extra) {

        // Start with offset property, which is equivalent to the border-box value
        var valueIsBorderBox = true,
            val = name === 'width' ? elem.offsetWidth : elem.offsetHeight,
            styles = window.getComputedStyle(elem, null),

        //TODO Make isBorderBox after https://github.com/caitp/angular.js/commit/92bbb5e225253ebddd38ef5735d66ffef76b6a14 will be applied
            isBorderBox = false; //jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";

        // some non-html elements return undefined for offsetWidth, so check for null/undefined
        // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
        // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
        if (val <= 0 || val == null) {
            // Fall back to computed then uncomputed css if necessary
            val = styles[name];

            if (val < 0 || val == null) {
                val = elem.style[name];
            }

            // Computed unit is not pixels. Stop here and return.
            if (rnumnonpx.test(val)) {
                return val;
            }

            // we need the check for style in case a browser which returns unreliable values
            // for getComputedStyle silently falls back to the reliable elem.style
            //valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );

            // Normalize "", auto, and prepare for extra
            val = parseFloat(val) || 0;
        }

        // use the active box-sizing model to add/subtract irrelevant styles
        return val + augmentWidthOrHeight(elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles);
    }

    function copyWidth(srcElement, dstElement) {
        dstElement.css('width', getWidthOrHeight(srcElement[0], 'width', 'margin') + 'px');
    }

    function groupsIsEmpty(groups) {
        for (var k in groups) {
            if (groups.hasOwnProperty(k) && groups[k].length) {
                return false;
            }
        }
        return true;
    }

    function objToArr(obj) {
        var arr = [];

        angular.forEach(obj, function (value, key) {
            if (key.toString().charAt(0) !== '$') {
                arr.push(value);
            }
        });

        return arr;
    }

    //lodash _.intersection + filter + invert
    function intersection(xArr, yArr, xFilter, yFilter, invert) {
        var i, j, n, filteredX, filteredY, out = invert ? [].concat(xArr) : [];

        for (i = 0, n = xArr.length; i < xArr.length; i++) {
            filteredX = xFilter ? xFilter(xArr[i]) : xArr[i];

            for (j = 0; j < yArr.length; j++) {
                filteredY = yFilter ? yFilter(yArr[j]) : yArr[j];

                if (angular.equals(filteredX, filteredY, xArr, yArr, i, j)) {
                    invert ? out.splice(i + out.length - n, 1) : out.push(yArr[j]);
                    break;
                }
            }
        }
        return out;
    }

    function getValue(valueName, item, scope, getter) {
        var locals = {};

        //'name.subname' -> {name: {subname: list}}'
        valueName.split('.').reduce(function (previousValue, currentItem, index, arr) {
            return previousValue[currentItem] = index < arr.length - 1 ? {} : item;
        }, locals);

        return getter(scope, locals);
    }

    return {
        copyWidth: copyWidth,
        measureString: measureString,
        contains: contains,
        bindFocusBlur: bindFocusBlur,
        scrollActiveOption: scrollActiveOption,
        groupsIsEmpty: groupsIsEmpty,
        objToArr: objToArr,
        getValue: getValue,
        intersection: intersection
    }
}]);<commit_msg>add version info<commit_after>angular.module('oi.select')

.provider('oiSelect', function() {
    return {
        options: {
            debounce:       500,
            searchFilter:   'oiSelectCloseIcon',
            dropdownFilter: 'oiSelectHighlight',
            listFilter:     'oiSelectAscSort',
            editItem:       false,
            newItem:        false,
            closeList:      true,
            saveTrigger:    'enter'
        },
        version: {
            full: '0.2.6',
            major: 0,
            minor: 2,
            dot: 6
        },
        $get: function() {
            return {
                options: this.options,
                version: this.version
            };
        }
    };
})

.factory('oiSelectEditItem', function() {
    return function(removedItem, lastQuery, getLabel) {
        return getLabel(removedItem);
    };
})

.factory('oiUtils', ['$document', '$timeout', function($document, $timeout) {
    /**
     * Measures the width of a string within a
     * parent element (in pixels).
     *
     * @param {string} str
     * @param {object} $parent
     * @returns {int}
     */
    function measureString(str, $parent) {
        var $mirror = angular.element('<mirror>').css({
            position: 'absolute',
            width: 'auto',
            padding: 0,
            whiteSpace: 'pre',
            visibility: 'hidden',
            'z-index': -99999
        }).text(str || '');

        transferStyles($parent, $mirror, 'letterSpacing fontSize fontFamily fontWeight textTransform'.split(' '));

        $document[0].body.appendChild($mirror[0]);

        var width = $mirror[0].offsetWidth;
        $mirror.remove();

        return width;
    }

    /**
     * Copies CSS properties from one element to another.
     *
     * @param {object} $from
     * @param {object} $to
     * @param {array} properties
     */
    function transferStyles($from, $to, properties) {
        var stylesTo = {},
            stylesFrom = getComputedStyle($from[0], '');

        for (var i = 0, n = properties.length; i < n; i++) {
            stylesTo[properties[i]] = stylesFrom[properties[i]];
        }

        $to.css(stylesTo);
    }

    /**
     * Check to see if a DOM element is a descendant of another DOM element.
     *
     * @param {DOM element} container
     * @param {DOM element} contained
     * @param {string} class name of element in container
     * @returns {boolean}
     */
    function contains(container, contained, className) {
        var current = contained;

        while (current && current.ownerDocument && current.nodeType !== 11) {
            if (className) {
                if (current === container) {
                    return false;
                }
                if (current.classList.contains(className)) {
                    return true;
                }
            } else {
                if (current === container) {
                    return true;
                }
            }
            current = current.parentNode;
        }

        return false;
    }

    /**
     * Simulate focus/blur events of the inner input element to the outer element
     *
     * @param {element} outer element
     * @param {element} inner input element
     * @returns {function} deregistration function for listeners.
     */
    function bindFocusBlur(element, inputElement) {
        var isFocused = false;

        $document[0].addEventListener('click', clickHandler, true);
        element[0].addEventListener('blur', blurHandler, true);
        inputElement.on('focus', focusHandler);

        function blurHandler(event) {
            var relatedTarget = event.relatedTarget; //TODO: get relativeTarget in IE, FF (event.explicitOriginalTarget || document.activeElement);

            if (relatedTarget === inputElement[0]) {
                event.stopImmediatePropagation(); //cancel blur if focus to input element
                return;
            }

            if (relatedTarget) { //not triggered blur
                isFocused = false;

                $timeout(function () {
                    element.triggerHandler('blur'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        function focusHandler(event) {
            if (!isFocused) {
                isFocused = true;

                $timeout(function () {
                    element.triggerHandler('focus'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        function clickHandler(event) {
            var activeElement = event.target;
            var isSelectElement = contains(element[0], activeElement);

            if (isSelectElement && activeElement.nodeName !== 'INPUT') {
                $timeout(function () {
                    inputElement.triggerHandler('focus');
                });
            }

            if (!isSelectElement && isFocused) {
                isFocused = false;

                $timeout(function () {
                    element.triggerHandler('blur'); //conflict with current live cycle (case: multiple=none + tab)
                });
            }
        }

        return function () {
            $document[0].removeEventListener('click', clickHandler);
            element[0].removeEventListener('blur', blurHandler, true);
            inputElement.off('focus', focusHandler);
        }
    }

    /**
     * Sets the selected item in the dropdown menu
     * of available options.
     *
     * @param {object} list
     * @param {object} item
     */
    function scrollActiveOption(list, item) {
        var y, height_menu, height_item, scroll, scroll_top, scroll_bottom;

        if (item) {
            height_menu = list.offsetHeight;
            height_item = getWidthOrHeight(item, 'height', 'margin'); //outerHeight(true);
            scroll = list.scrollTop || 0;
            y = getOffset(item).top - getOffset(list).top + scroll;
            scroll_top = y;
            scroll_bottom = y - height_menu + height_item;

            //TODO Make animation
            if (y + height_item > height_menu + scroll) {
                list.scrollTop = scroll_bottom;
            } else if (y < scroll) {
                list.scrollTop = scroll_top;
            }
        }
    }

    // Used for matching numbers
    var core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source;
    var rnumnonpx = new RegExp("^(" + core_pnum + ")(?!px)[a-z%]+$", "i");

    function augmentWidthOrHeight(elem, name, extra, isBorderBox, styles) {
        var i = extra === (isBorderBox ? 'border' : 'content') ?
                // If we already have the right measurement, avoid augmentation
                4 :
                // Otherwise initialize for horizontal or vertical properties
                name === 'width' ? 1 : 0,

            val = 0,
            cssExpand = ['Top', 'Right', 'Bottom', 'Left'];

        //TODO Use angular.element.css instead of getStyleValue after https://github.com/caitp/angular.js/commit/92bbb5e225253ebddd38ef5735d66ffef76b6a14 will be applied
        function getStyleValue(name) {
            return parseFloat(styles[name]);
        }

        for (; i < 4; i += 2) {
            // both box models exclude margin, so add it if we want it
            if (extra === 'margin') {
                val += getStyleValue(extra + cssExpand[i]);
            }

            if (isBorderBox) {
                // border-box includes padding, so remove it if we want content
                if (extra === 'content') {
                    val -= getStyleValue('padding' + cssExpand[i]);
                }

                // at this point, extra isn't border nor margin, so remove border
                if (extra !== 'margin') {
                    val -= getStyleValue('border' + cssExpand[i] + 'Width');
                }
            } else {
                val += getStyleValue('padding' + cssExpand[i]);

                // at this point, extra isn't content nor padding, so add border
                if (extra !== 'padding') {
                    val += getStyleValue('border' + cssExpand[i] + 'Width');
                }
            }
        }

        return val;
    }

    function getOffset(elem) {
        var docElem, win,
            box = elem.getBoundingClientRect(),
            doc = elem && elem.ownerDocument;

        if (!doc) {
            return;
        }

        docElem = doc.documentElement;
        win = getWindow(doc);

        return {
            top: box.top + win.pageYOffset - docElem.clientTop,
            left: box.left + win.pageXOffset - docElem.clientLeft
        };
    }

    function getWindow(elem) {
        return elem != null && elem === elem.window ? elem : elem.nodeType === 9 && elem.defaultView;
    }

    function getWidthOrHeight(elem, name, extra) {

        // Start with offset property, which is equivalent to the border-box value
        var valueIsBorderBox = true,
            val = name === 'width' ? elem.offsetWidth : elem.offsetHeight,
            styles = window.getComputedStyle(elem, null),

        //TODO Make isBorderBox after https://github.com/caitp/angular.js/commit/92bbb5e225253ebddd38ef5735d66ffef76b6a14 will be applied
            isBorderBox = false; //jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";

        // some non-html elements return undefined for offsetWidth, so check for null/undefined
        // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
        // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
        if (val <= 0 || val == null) {
            // Fall back to computed then uncomputed css if necessary
            val = styles[name];

            if (val < 0 || val == null) {
                val = elem.style[name];
            }

            // Computed unit is not pixels. Stop here and return.
            if (rnumnonpx.test(val)) {
                return val;
            }

            // we need the check for style in case a browser which returns unreliable values
            // for getComputedStyle silently falls back to the reliable elem.style
            //valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );

            // Normalize "", auto, and prepare for extra
            val = parseFloat(val) || 0;
        }

        // use the active box-sizing model to add/subtract irrelevant styles
        return val + augmentWidthOrHeight(elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles);
    }

    function copyWidth(srcElement, dstElement) {
        dstElement.css('width', getWidthOrHeight(srcElement[0], 'width', 'margin') + 'px');
    }

    function groupsIsEmpty(groups) {
        for (var k in groups) {
            if (groups.hasOwnProperty(k) && groups[k].length) {
                return false;
            }
        }
        return true;
    }

    function objToArr(obj) {
        var arr = [];

        angular.forEach(obj, function (value, key) {
            if (key.toString().charAt(0) !== '$') {
                arr.push(value);
            }
        });

        return arr;
    }

    //lodash _.intersection + filter + invert
    function intersection(xArr, yArr, xFilter, yFilter, invert) {
        var i, j, n, filteredX, filteredY, out = invert ? [].concat(xArr) : [];

        for (i = 0, n = xArr.length; i < xArr.length; i++) {
            filteredX = xFilter ? xFilter(xArr[i]) : xArr[i];

            for (j = 0; j < yArr.length; j++) {
                filteredY = yFilter ? yFilter(yArr[j]) : yArr[j];

                if (angular.equals(filteredX, filteredY, xArr, yArr, i, j)) {
                    invert ? out.splice(i + out.length - n, 1) : out.push(yArr[j]);
                    break;
                }
            }
        }
        return out;
    }

    function getValue(valueName, item, scope, getter) {
        var locals = {};

        //'name.subname' -> {name: {subname: list}}'
        valueName.split('.').reduce(function (previousValue, currentItem, index, arr) {
            return previousValue[currentItem] = index < arr.length - 1 ? {} : item;
        }, locals);

        return getter(scope, locals);
    }

    return {
        copyWidth: copyWidth,
        measureString: measureString,
        contains: contains,
        bindFocusBlur: bindFocusBlur,
        scrollActiveOption: scrollActiveOption,
        groupsIsEmpty: groupsIsEmpty,
        objToArr: objToArr,
        getValue: getValue,
        intersection: intersection
    }
}]);<|endoftext|>