<commit_before>
var camera, scene, renderer;
var cube;
var controls;

var offset = 0;
var speed = 0;
var acc = 0.97;

var keyboard = new KeyboardState();
var projector, mouse = { x: 0, y: 0 }, INTERSECTED;

var objectList;
var dist = 0;
var current_object_index;

init();
animate();

const FADEOUT_DISTANCE = 30;
const INFOBOX_RANGE_CENTER_POSITION = 10;
const INFOBOX_RANGE_DISTANCE = 76;

function addOnTimeline(obj, d1, d2) {
  objectList.add(obj);
  dist -= d1 || 60;
  obj.position.z = dist;
  dist -= d2 || 60;
}
function resetTimeline() {
  if (objectList) {
    objectList.children.forEach(function(obj) {
      deallocMesh(obj);
      scene.remove(obj);
    });
    scene.remove(objectList);
  }
  // create new object group
  objectList = new THREE.Object3D();
  scene.add(objectList);
  dist = 90;
  current_object_index = 0;
}

function deallocMesh(obj) {
  if (obj instanceof THREE.Mesh) {
    obj.texture && obj.texture.dispose();
    obj.material && obj.material.dispose();
    obj.geometry && obj.geometry.dispose();
    delete obj.img;
  } else if (obj.children) {
    obj.children.forEach(function(o) {
      deallocMesh(o);
    });
  }
}

function init() {

  window.addEventListener( 'resize', onWindowResize, false );

  renderer = new THREE.WebGLRenderer( { alpha: true } );
  // renderer.setClearColor( 0xeeeeee, 1);
  renderer.setClearColor( 0x000000, 0);
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.getElementById( 'stage' ).appendChild( renderer.domElement );

  camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 3000 );
  camera.position.set(0, 80, 150);
  // camera.translateY(-50);
  // camera.rotation.x = 1;
  // camera.target = new THREE.Vector3( 0, -50, 50);
  // camera.up = new THREE.Vector3(0,0,1);
  camera.lookAt(new THREE.Vector3( 0, 0, 0));

  scene = new THREE.Scene();

  scene.add(createPlane({ position: {z: /*-1480*/ 500}, rotation: {x:Math.PI/2}, color: 0xffffff, opacity: 1}));
  scene.add(createPlane({ width: 110, position: {z: 500, y: -2}, rotation: {x:Math.PI/2}, color: 0xeeeeee, opacity: 1}));
  scene.add(createPlane({ width: 120, position: {z: 500, y: -4}, rotation: {x:Math.PI/2}, color: 0xdddddd, opacity: 1}));
  scene.add(createPlane({ width: 135, position: {z: 500, y: -6}, rotation: {x:Math.PI/2}, color: 0xcccccc, opacity: 1}));
  // scene.add(createPlane({ width: 150, height: 20, position: {y:-10, z: 20 }, color: 0x318ce7}));

  // initialize object to perform world/screen calculations
  projector = new THREE.Projector();

  // when the mouse moves, call the given function
  document.addEventListener( 'mousemove', onDocumentMouseMove, false );

}

function onDocumentMouseMove( event )
{
  // the following line would stop any other event handler from firing
  // (such as the mouse's TrackballControls)
  // event.preventDefault();

  // update sprite position
  updateInfoBox({ x: event.clientX, y: event.clientY });
  // sprite1.position.set( event.clientX, event.clientY - 20, 0 );

  // update the mouse variable
  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}

function createCube() {

  var geometry = new THREE.BoxGeometry( 200, 200, 200 );

  var texture = THREE.ImageUtils.loadTexture( '/public/images/crate.gif' );
  texture.anisotropy = renderer.getMaxAnisotropy();

  var material = new THREE.MeshBasicMaterial( { map: texture } );

  mesh = new THREE.Mesh( geometry, material );
  return mesh;
}

function createPlane(params) {
  params = params || {};
  params.position = params.position || {};
  params.rotation = params.rotation || {};

  var geometry = new THREE.PlaneGeometry( params.width || 100, params.height || 3000 );
  var material = new THREE.MeshBasicMaterial( {color: params.color || 0xff0000, side: THREE.DoubleSide, transparent: true, opacity: params.opacity || 1} );
  mesh = new THREE.Mesh( geometry, material );

  mesh.position.x = params.position.x || 0;
  mesh.position.y = params.position.y || 0;
  mesh.position.z = params.position.z || 0;

  mesh.rotation.x = params.rotation.x || 0;
  mesh.rotation.y = params.rotation.y || 0;
  mesh.rotation.z = params.rotation.z || 0;

  return mesh;
}

function createSprite(imgObj, params) {
  params = params || {};
  var texture;
  var sprite;
  var w = params.width || 50;
  var h = params.height || 50;

  var img = new Image();
  img.onload = function () {
    texture.needsUpdate = true;
  };
  texture = new THREE.Texture(img);
  // user image proxy if needed
  if (params.service)
    img.src = '/photo/'+params.service+'/'+encodeURIComponent(imgObj.UrlPreview);
  else
    img.src = imgObj.UrlPreview
  texture.needsUpdate = true;

  var material = new THREE.SpriteMaterial( { map: texture, useScreenCoordinates: false, transparent: true } );
  sprite = new THREE.Sprite( material );
  sprite.scale.set(w, h, 1);
  sprite.position.y = h/2;
  sprite.name = params.name || '';
  sprite.imgObj = imgObj;
  sprite.img = img;
  sprite.texture = texture;
  return sprite;
}

function createTextMarker(str) {
  var group = new THREE.Object3D();
  var text = createText( str, {
      fontsize: 32,
      fontface: 'Abel',
      backgroundColor: {r:237, g:28, b:36, a:1.0},//0xed1c24
      borderColor: {r:237, g:28, b:36, a:1.0},//0xed1c24
    } );
  text.position.x = 37;
  text.position.y = -10;
  text.position.z = 0;
  var line = createPlane({ width: 100, height: 3, position: { x: 0, y: .1, z: 0 }, rotation: { x: Math.PI/2 }, color: 0x000000});

  group.add(text);
  group.add(line);
  return group;
}

function createText( message, parameters )
{
  if ( parameters === undefined ) parameters = {};

  var fontface = parameters.hasOwnProperty("fontface") ?
    parameters["fontface"] : "Arial";

  var fontsize = parameters.hasOwnProperty("fontsize") ?
    parameters["fontsize"] : 18;

  var borderThickness = parameters.hasOwnProperty("borderThickness") ?
    parameters["borderThickness"] : 4;

  var borderColor = parameters.hasOwnProperty("borderColor") ?
    parameters["borderColor"] : { r:0, g:0, b:0, a:1.0 };

  var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
    parameters["backgroundColor"] : { r:255, g:255, b:255, a:1.0 };

  var spriteAlignment = new THREE.Vector2(-1,-1); //THREE.SpriteAlignment.topLeft;

  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
  context.font = "Bold " + fontsize + "px " + fontface;

  // get size data (height depends only on font size)
  var metrics = context.measureText( message );
  var textWidth = metrics.width;

  // background color
  context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
                  + backgroundColor.b + "," + backgroundColor.a + ")";
  // border color
  context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
                  + borderColor.b + "," + borderColor.a + ")";

  context.lineWidth = borderThickness;
  // roundRect(context, borderThickness/2, borderThickness/2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
  // 1.4 is extra height factor for text below baseline: g,j,p,q.

  // text color
  context.fillStyle = "rgba(0, 0, 0, 1.0)";

  context.fillText( message, borderThickness, fontsize + borderThickness);

  // canvas contents will be used for a texture
  var texture = new THREE.Texture(canvas)
  texture.needsUpdate = true;

  var spriteMaterial = new THREE.SpriteMaterial(
    { map: texture, useScreenCoordinates: false, alignment: spriteAlignment } );
  var sprite = new THREE.Sprite( spriteMaterial );
  sprite.scale.set(100,50,1.0);
  return sprite;
}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

  requestAnimationFrame( animate );

  renderer.render( scene, camera );

  keyUpdate();

  worldUpdate();
}

function keyUpdate() {
  keyboard.update();
  if ( keyboard.pressed("up") ) {
    pressNextItem();
  }
  if ( keyboard.pressed("down") ) {
    pressPrevItem();
  }
}

// Move next/previous with limited repeat rate at 200ms
var pressNextItem = _.throttle(function() {
  speed = 0;
  startTween({
    z: objectList.position.z
  }, {
    z: getNextPos()
  });
}, 200, {trailing: false});

var pressPrevItem = _.throttle(function() {
  speed = 0;
  startTween({
    z: objectList.position.z
  }, {
    z: getPrevPos()
  });
}, 200, {trailing: false});

function getNextPos() {
  var obj = objectList.children[current_object_index+1];
  if (!obj) {
    return -objectList.children[current_object_index].position.z;
  } else {
    current_object_index++;
    return -obj.position.z;
  }
}
function getPrevPos() {
  var obj = objectList.children[current_object_index-1];
  if (!obj) {
    return -objectList.children[current_object_index].position.z;
  } else {
    current_object_index--;
    return -obj.position.z;
  }
}

function startTween(from, to) {
  var tween = new TWEEN.Tween(from)
  .to(to, 1000 )
  .easing( TWEEN.Easing.Quadratic.Out )
  .onUpdate( function () {
    objectList.position.z = this.z;
  } )
  .start();
}

var fadeout_offset = FADEOUT_DISTANCE;
var infobox_offset = INFOBOX_RANGE_DISTANCE/2;
function worldUpdate() {
  if (speed !== 0) speed *= acc;
  if (Math.abs(speed) < 0.001) speed = 0;

  TWEEN.update();

  if (objectList) {
    // move objects
    objectList.position.z += speed;

    // bounce on min/max distance
    if (objectList.position.z < 0) {
      objectList.position.z = 0;
      speed *= -0.5;
    }
    if (objectList.position.z > -dist-40) {
      objectList.position.z = -dist-40;
      speed *= -0.2;
    }

    var boxShowObj;
    objectList.children.forEach(function(obj) {
      // check if object near viewer
      var posz =  obj.position.z + objectList.position.z;
      var opacity = posz <= 0+fadeout_offset ? 1 : Math.max(0, 1-(posz-fadeout_offset)/FADEOUT_DISTANCE);
      // check if needed to update info box
      var updatebox = posz > INFOBOX_RANGE_CENTER_POSITION-infobox_offset && posz < INFOBOX_RANGE_CENTER_POSITION+infobox_offset ;
      // keep object to send image info to show in info box later at the bottom of this function
      if(updatebox && obj.imgObj){
        boxShowObj = obj;
      }
      var meshes;
      if (obj instanceof THREE.Sprite || obj instanceof THREE.Geometry){
        meshes = [obj];
      }else if (obj.children){
        meshes = obj.children;
      }

      // fade out
      meshes.forEach(function(mesh) {
        var material;
        if (mesh.material)
          mesh.material.opacity = opacity;
         if (mesh.materials)
          mesh.materials[0].opacity = opacity;
      });
    });

    // show image info
    if(boxShowObj){
      updateInfoBox({
        show: true,
        title: boxShowObj.imgObj.Title,
        artist: boxShowObj.imgObj.Artist,
        caption: boxShowObj.imgObj.Caption
      });
    }
  }
}
<commit_msg>Updated current object index when moving with speed (leap motion)<commit_after>
var camera, scene, renderer;
var cube;
var controls;

var offset = 0;
var speed = 0;
var acc = 0.97;

var keyboard = new KeyboardState();
var projector, mouse = { x: 0, y: 0 }, INTERSECTED;

var objectList;
var dist = 0;
var current_object_index;

init();
animate();

const FADEOUT_DISTANCE = 30;
const INFOBOX_RANGE_CENTER_POSITION = 10;
const INFOBOX_RANGE_DISTANCE = 76;

function addOnTimeline(obj, d1, d2) {
  objectList.add(obj);
  dist -= d1 || 60;
  obj.position.z = dist;
  dist -= d2 || 60;
}
function resetTimeline() {
  if (objectList) {
    objectList.children.forEach(function(obj) {
      deallocMesh(obj);
      scene.remove(obj);
    });
    scene.remove(objectList);
  }
  // create new object group
  objectList = new THREE.Object3D();
  scene.add(objectList);
  dist = 90;
  current_object_index = 0;
}

function deallocMesh(obj) {
  if (obj instanceof THREE.Mesh) {
    obj.texture && obj.texture.dispose();
    obj.material && obj.material.dispose();
    obj.geometry && obj.geometry.dispose();
    delete obj.img;
  } else if (obj.children) {
    obj.children.forEach(function(o) {
      deallocMesh(o);
    });
  }
}

function init() {

  window.addEventListener( 'resize', onWindowResize, false );

  renderer = new THREE.WebGLRenderer( { alpha: true } );
  // renderer.setClearColor( 0xeeeeee, 1);
  renderer.setClearColor( 0x000000, 0);
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.getElementById( 'stage' ).appendChild( renderer.domElement );

  camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 3000 );
  camera.position.set(0, 80, 150);
  // camera.translateY(-50);
  // camera.rotation.x = 1;
  // camera.target = new THREE.Vector3( 0, -50, 50);
  // camera.up = new THREE.Vector3(0,0,1);
  camera.lookAt(new THREE.Vector3( 0, 0, 0));

  scene = new THREE.Scene();

  scene.add(createPlane({ position: {z: /*-1480*/ 500}, rotation: {x:Math.PI/2}, color: 0xffffff, opacity: 1}));
  scene.add(createPlane({ width: 110, position: {z: 500, y: -2}, rotation: {x:Math.PI/2}, color: 0xeeeeee, opacity: 1}));
  scene.add(createPlane({ width: 120, position: {z: 500, y: -4}, rotation: {x:Math.PI/2}, color: 0xdddddd, opacity: 1}));
  scene.add(createPlane({ width: 135, position: {z: 500, y: -6}, rotation: {x:Math.PI/2}, color: 0xcccccc, opacity: 1}));
  // scene.add(createPlane({ width: 150, height: 20, position: {y:-10, z: 20 }, color: 0x318ce7}));

  // initialize object to perform world/screen calculations
  projector = new THREE.Projector();

  // when the mouse moves, call the given function
  document.addEventListener( 'mousemove', onDocumentMouseMove, false );

}

function onDocumentMouseMove( event )
{
  // the following line would stop any other event handler from firing
  // (such as the mouse's TrackballControls)
  // event.preventDefault();

  // update sprite position
  updateInfoBox({ x: event.clientX, y: event.clientY });
  // sprite1.position.set( event.clientX, event.clientY - 20, 0 );

  // update the mouse variable
  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}

function createCube() {

  var geometry = new THREE.BoxGeometry( 200, 200, 200 );

  var texture = THREE.ImageUtils.loadTexture( '/public/images/crate.gif' );
  texture.anisotropy = renderer.getMaxAnisotropy();

  var material = new THREE.MeshBasicMaterial( { map: texture } );

  mesh = new THREE.Mesh( geometry, material );
  return mesh;
}

function createPlane(params) {
  params = params || {};
  params.position = params.position || {};
  params.rotation = params.rotation || {};

  var geometry = new THREE.PlaneGeometry( params.width || 100, params.height || 3000 );
  var material = new THREE.MeshBasicMaterial( {color: params.color || 0xff0000, side: THREE.DoubleSide, transparent: true, opacity: params.opacity || 1} );
  mesh = new THREE.Mesh( geometry, material );

  mesh.position.x = params.position.x || 0;
  mesh.position.y = params.position.y || 0;
  mesh.position.z = params.position.z || 0;

  mesh.rotation.x = params.rotation.x || 0;
  mesh.rotation.y = params.rotation.y || 0;
  mesh.rotation.z = params.rotation.z || 0;

  return mesh;
}

function createSprite(imgObj, params) {
  params = params || {};
  var texture;
  var sprite;
  var w = params.width || 50;
  var h = params.height || 50;

  var img = new Image();
  img.onload = function () {
    texture.needsUpdate = true;
  };
  texture = new THREE.Texture(img);
  // user image proxy if needed
  if (params.service)
    img.src = '/photo/'+params.service+'/'+encodeURIComponent(imgObj.UrlPreview);
  else
    img.src = imgObj.UrlPreview
  texture.needsUpdate = true;

  var material = new THREE.SpriteMaterial( { map: texture, useScreenCoordinates: false, transparent: true } );
  sprite = new THREE.Sprite( material );
  sprite.scale.set(w, h, 1);
  sprite.position.y = h/2;
  sprite.name = params.name || '';
  sprite.imgObj = imgObj;
  sprite.img = img;
  sprite.texture = texture;
  return sprite;
}

function createTextMarker(str) {
  var group = new THREE.Object3D();
  var text = createText( str, {
      fontsize: 32,
      fontface: 'Abel',
      backgroundColor: {r:237, g:28, b:36, a:1.0},//0xed1c24
      borderColor: {r:237, g:28, b:36, a:1.0},//0xed1c24
    } );
  text.position.x = 37;
  text.position.y = -10;
  text.position.z = 0;
  var line = createPlane({ width: 100, height: 3, position: { x: 0, y: .1, z: 0 }, rotation: { x: Math.PI/2 }, color: 0x000000});

  group.add(text);
  group.add(line);
  return group;
}

function createText( message, parameters )
{
  if ( parameters === undefined ) parameters = {};

  var fontface = parameters.hasOwnProperty("fontface") ?
    parameters["fontface"] : "Arial";

  var fontsize = parameters.hasOwnProperty("fontsize") ?
    parameters["fontsize"] : 18;

  var borderThickness = parameters.hasOwnProperty("borderThickness") ?
    parameters["borderThickness"] : 4;

  var borderColor = parameters.hasOwnProperty("borderColor") ?
    parameters["borderColor"] : { r:0, g:0, b:0, a:1.0 };

  var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
    parameters["backgroundColor"] : { r:255, g:255, b:255, a:1.0 };

  var spriteAlignment = new THREE.Vector2(-1,-1); //THREE.SpriteAlignment.topLeft;

  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
  context.font = "Bold " + fontsize + "px " + fontface;

  // get size data (height depends only on font size)
  var metrics = context.measureText( message );
  var textWidth = metrics.width;

  // background color
  context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
                  + backgroundColor.b + "," + backgroundColor.a + ")";
  // border color
  context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
                  + borderColor.b + "," + borderColor.a + ")";

  context.lineWidth = borderThickness;
  // roundRect(context, borderThickness/2, borderThickness/2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
  // 1.4 is extra height factor for text below baseline: g,j,p,q.

  // text color
  context.fillStyle = "rgba(0, 0, 0, 1.0)";

  context.fillText( message, borderThickness, fontsize + borderThickness);

  // canvas contents will be used for a texture
  var texture = new THREE.Texture(canvas)
  texture.needsUpdate = true;

  var spriteMaterial = new THREE.SpriteMaterial(
    { map: texture, useScreenCoordinates: false, alignment: spriteAlignment } );
  var sprite = new THREE.Sprite( spriteMaterial );
  sprite.scale.set(100,50,1.0);
  return sprite;
}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

  requestAnimationFrame( animate );

  renderer.render( scene, camera );

  keyUpdate();

  worldUpdate();
}

function keyUpdate() {
  keyboard.update();
  if ( keyboard.pressed("up") ) {
    pressNextItem();
  }
  if ( keyboard.pressed("down") ) {
    pressPrevItem();
  }
}

// Move next/previous with limited repeat rate at 200ms
var pressNextItem = _.throttle(function() {
  speed = 0;
  startTween({
    z: objectList.position.z
  }, {
    z: getNextPos()+20
  });
}, 200, {trailing: false});

var pressPrevItem = _.throttle(function() {
  speed = 0;
  startTween({
    z: objectList.position.z
  }, {
    z: getPrevPos()+20
  });
}, 200, {trailing: false});

function getNextPos() {
  var obj = objectList.children[current_object_index+1];
  if (!obj) {
    return -objectList.children[current_object_index].position.z;
  } else {
    current_object_index++;
    return -obj.position.z;
  }
}
function getPrevPos() {
  var obj = objectList.children[current_object_index-1];
  if (!obj) {
    return -objectList.children[current_object_index].position.z;
  } else {
    current_object_index--;
    return -obj.position.z;
  }
}

var tween;
function startTween(from, to) {
  stopTween();
  tween = new TWEEN.Tween(from)
  .to(to, 500 )
  .easing( TWEEN.Easing.Quadratic.Out )
  .onUpdate( function () {
    objectList.position.z = this.z;
  } )
  .onComplete( function() {
    tween.isPlaying = false;
  })
  .start();

  tween.isPlaying = true;
}
function stopTween() {
  if (tween && tween.isPlaying) {
    console.log('stop tweening')
    tween.stop();
    tween.isPlaying = false;
  }
}

function getCurrentObjectIndex() {
  if (!objectList) return;
  var camPos = -objectList.position.z;
  var currentObj = objectList.children[current_object_index];
  if (!currentObj) return;
  var currentPos = currentObj.position.z;
  var currentDis = Math.abs(camPos - currentPos);

  var i = current_object_index, obj, dis;
  var dir = camPos > currentPos ? -1 : 1;
  while (true) {
    obj = objectList.children[i+dir];
    if (!obj) break;
    dis = Math.abs(obj.position.z - currentPos);
    if (dis < currentDis) {
      i = i+dir;
    } else {
      break;
    }
  }
  current_object_index = i;
}

var _intervalCurrentIndex;
var fadeout_offset = FADEOUT_DISTANCE;
var infobox_offset = INFOBOX_RANGE_DISTANCE/2;
function worldUpdate() {
  if (speed !== 0) speed *= acc;
  if (Math.abs(speed) < 0.001) speed = 0;
  if (speed !== 0) {
    stopTween();
  }

  TWEEN.update();

  // calculate current object index if moving without tween
  if ((!tween || !tween.isPlaying) && speed !== 0) {
    if (!_intervalCurrentIndex) {
      _intervalCurrentIndex = setInterval(getCurrentObjectIndex, 100);
    }
  } else {
    if (_intervalCurrentIndex) {
      clearInterval(_intervalCurrentIndex);
      _intervalCurrentIndex = null;
    }
  }

  if (objectList) {
    // move objects
    objectList.position.z += speed;

    // bounce on min/max distance
    if (objectList.position.z < 0) {
      objectList.position.z = 0;
      speed *= -0.5;
    }
    if (objectList.position.z > -dist-40) {
      objectList.position.z = -dist-40;
      speed *= -0.2;
    }

    var boxShowObj;
    objectList.children.forEach(function(obj) {
      // check if object near viewer
      var posz =  obj.position.z + objectList.position.z;
      var opacity = posz <= 0+fadeout_offset ? 1 : Math.max(0, 1-(posz-fadeout_offset)/FADEOUT_DISTANCE);
      // check if needed to update info box
      var updatebox = posz > INFOBOX_RANGE_CENTER_POSITION-infobox_offset && posz < INFOBOX_RANGE_CENTER_POSITION+infobox_offset ;
      // keep object to send image info to show in info box later at the bottom of this function
      if(updatebox && obj.imgObj){
        boxShowObj = obj;
      }
      var meshes;
      if (obj instanceof THREE.Sprite || obj instanceof THREE.Geometry){
        meshes = [obj];
      }else if (obj.children){
        meshes = obj.children;
      }

      // fade out
      meshes.forEach(function(mesh) {
        var material;
        if (mesh.material)
          mesh.material.opacity = opacity;
         if (mesh.materials)
          mesh.materials[0].opacity = opacity;
      });
    });

    // show image info
    if(boxShowObj){
      updateInfoBox({
        show: true,
        title: boxShowObj.imgObj.Title,
        artist: boxShowObj.imgObj.Artist,
        caption: boxShowObj.imgObj.Caption
      });
    }
  }
}
<|endoftext|>


<commit_before>
var app = angular.module("browseApp.routes", ['ui.router']);

/*
* Configurations for states for ui-router. Controllers refer to
* the controllers located in mainctrls.js.
*
*/
app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider

    .otherwise('/categories');

    $stateProvider

    .state('allcats',{
        url: '/categories',
        templateUrl: 'templates/catlist.html',
        controller: 'controller_c'
    })

    .state('list',{
        url: '/list/:catID',
        views: {
              'table' : {
                  templateUrl: 'templates/list.table.html',
                  controller: 'workslistCtrl'
                  },
              'searchbox' : {
                  templateUrl: 'templates/list.search.html',
                  controller: 'searchbox'
                  }
          }

    })

    .state('new',{
        url: '/new/:catID',
        templateUrl: 'templates/new.html',
        controller: 'controller_n'
    });

});
<commit_msg>Make views a separate list.state<commit_after>
var app = angular.module("browseApp.routes", ['ui.router']);

/*
* Configurations for states for ui-router. Controllers refer to
* the controllers located in mainctrls.js.
*
*/
app.config(function($stateProvider, $urlRouterProvider) {

    $urlRouterProvider

    .otherwise('/categories');

    $stateProvider

    .state('allcats',{
        url: '/categories',
        templateUrl: 'templates/catlist.html',
        controller: 'controller_c'
    })

    .state('list',{
        url: '/list/:catID',
        templateUrl: 'templates/list.html',
        controller: 'controller_l',
    })

    .state('list.container',{
      views: {
            'table' : {
                templateUrl: 'templates/list.table.html',
                controller: 'workslistCtrl'
                },
            'searchbox' : {
                templateUrl: 'templates/list.search.html',
                controller: 'searchbox'
                }
        }
    })

    .state('new',{
        url: '/new/:catID',
        templateUrl: 'templates/new.html',
        controller: 'controller_n'
    });

});
<|endoftext|>


<commit_before>jQuery(function($) {
	$('div.btn-group[data-toggle-name]').each(function() {
		var group, form, name, hidden, buttons;

		group   = $(this);
		form    = group.parents('form').eq(0);
		name    = group.attr('data-toggle-name');
		hidden  = $('input[name="' + name + '"]', form);
		buttons = $('button', group);

		buttons.each(function(){
			var button, setActive;

			button = $(this);

			setActive = function setActive() {
				if(button.val() == hidden.val()) {
					button.addClass('active');
				}
			};

			button.live('click', function() {
				buttons.removeClass('active');

				hidden.val($(this).val());

				setActive();
			});

			setActive();
		});
	});
});<commit_msg>.live() is deprecated, use .on() instead<commit_after>jQuery(function($) {
	$('div.btn-group[data-toggle-name]').each(function() {
		var group, form, name, hidden, buttons;

		group   = $(this);
		form    = group.parents('form').eq(0);
		name    = group.attr('data-toggle-name');
		hidden  = $('input[name="' + name + '"]', form);
		buttons = $('button', group);

		buttons.each(function(){
			var button, setActive;

			button = $(this);

			setActive = function setActive() {
				if(button.val() == hidden.val()) {
					button.addClass('active');
				}
			};

			button.on('click', function() {
				buttons.removeClass('active');

				hidden.val($(this).val());

				setActive();
			});

			setActive();
		});
	});
});<|endoftext|>


<commit_before>$(function () {
    // Get the last updated timestamp
    var getLastUpdatedTime = function () {
        $('#last-updated-time').load('/last-updated');
    };

    getLastUpdatedTime();
    setInterval(getLastUpdatedTime, 30 * 1000);
});

<commit_msg>Increase last updated interval to match the scrape interval<commit_after>$(function () {
    // Get the last updated timestamp
    var getLastUpdatedTime = function () {
        $('#last-updated-time').load('/last-updated');
    };

    getLastUpdatedTime();
    setInterval(getLastUpdatedTime, 300 * 1000);
});

<|endoftext|>


<commit_before>var socket = io.connect();

//keep a list of rooms that the client is in so we don't make a lot of server requests
var userRoomsList;

function addMessage(msg, roomName, username) {
    //append to the right div/ie to the right room
    $('div#chatContainer div#room-' + roomName + ' div.chatEntries').append('
' + username + ' : ' + msg + '
');

    var roomChatEntries = $('div#chatContainer div#room-' + roomName + ' div.chatEntries');
    if (Math.abs((roomChatEntries[0].scrollHeight - roomChatEntries.scrollTop() - roomChatEntries.outerHeight()) < 200) ) {
        roomChatEntries.animate({
            scrollTop: roomChatEntries[0].scrollHeight
        }, 200);
    }
    else {
        $('#moreMsgs').filter(':hidden').fadeIn(1000).delay(3000).fadeOut(3000);
    }
}

function sentMessage() {
    if ($('#messageInput').val() != "") {
        var messageBody = $('#messageInput').val();
        var roomName = $('ul#tab li.active').text();
        var data = {messageBody : messageBody, messageRoom : roomName};
        socket.emit('sendMessage', data);

        //this current implementation is that when you send a message, for you, the message doesn't go to the server, only for others
        addMessage(messageBody, roomName ,"Me");
        $('#messageInput').val(''); //clear the input field
    }
}

function setUsername() {
    $("#loginForm").submit();
}

socket.on('sendMessageResponse', function (data) {
    addMessage(data['message'], data['roomName'], data['username']);
});

socket.on('numConnected', function (data) {
    $('#numConnected').html('Users online: ' + data.numConnected);
});

socket.on('updateRoomsList', function (roomsList) {
    userRoomsList = roomsList;
});

socket.on('loadUsersList', function (data) {
    $('#usersList-' + data.roomName).empty();
    for (var i = 0 ; i < data.usernamesList.length ; i++) {
        $('#usersList-' + data.roomName).append('
' + data.usernamesList[i] + '
');

        var usernameElement = $('div.username:contains(' + data.usernamesList[i] + ')');
        usernameElement.on({
            dragstart: function(e) {
                e.dataTransfer.setData("username", $(this).text());
                $(this).css('opacity', '0.5');
            },
            dragend: function(e) {
                $(this).css('opacity', '1');
            },
        });
    }
});

socket.on('roomInvite', function (data) {
    window.alert("User " + data.inviter + " invites you to " + data.roomName);
    socket.emit('acceptInvitation', data.roomName);
});

socket.on('createRoomResponse', function (data) {
    if(data.created) {
        //chatContainer DOM creation
        $('div#chatContainer').append('

');
        //room userList DOM creation
        $('div#usersConnected').append('

')
        //tab dom creation
        $('ul#tab').append('
'+ data.roomName +'
');

        //open tab functionality
        $('ul#tab li:contains(' + data.roomName + ') a').click(function (e) {
            e.preventDefault();
            $(this).tab('show');
            $('div.usersList').hide(); //hide all other usersLists
            $('div#usersList-' + data.roomName).show(); //show the specific room usersList

        });

        //close tab functionality
        $('ul#tab li:contains(' + data.roomName + ') span.glyphicon-remove').click(function () {
            $(this).parent().parent().remove(); //removes the li tag
            $('div#room-' + data.roomName).remove(); //remove the main chatpanel
            $('div#userlist-' + data.roomName).remove(); //remove userlist
            socket.emit('leaveRoom', data.roomName);

            $('ul#tab a:contains("Lobby")').click(); //go back to the lobby
        });

        //attach dnd event listeners
        $('#tabContainer .roomTab:contains(' + data.roomName + ') a').on({
            dragleave: function(e) {
                $(this).removeClass('over');
                e.preventDefault();
            },
            dragenter: function(e) {
                $(this).addClass('over');
                e.preventDefault();
            },
            dragover: function(e) {
                $(this).addClass('over');
                e.preventDefault();
            },
            drop: function(e) {
                $(this).removeClass('over');
                e.preventDefault();
                socket.emit('inviteUser', {'username' : e.dataTransfer.getData('username'), 'roomName' : data.roomName});
            }
        });

        $('#roomModalCloseButton').click(); //close the window

        //subscribe to room
        socket.emit('joinRoom', data.roomName);

        $('ul#tab li:contains(' + data.roomName + ') a').click();

    }
    else {
        if(data.errorCode == 1) {
            window.alert("Illegal room name! Room name can only contain alphanumeric characters, hyphen, and underscores!");
        }
        else if (data.errorCode == 2) {
            window.alert("A room with that name already exists! Please choose another name!");
        }
        else if (data.errorCode == 3) {
            window.alert("Room name 'Lobby' is reserved, please choose another name!");
        }
        else {
            window.alert("Unknown error! Room cannot be created!");
        }
    }
});

$(function() {

    jQuery.event.props.push('dataTransfer');

    $('#messageInput').keypress(function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == '13'){
            $("#submit").click();
        }
    });

    $('ul#tab a:contains("Lobby")').click(function (e) {
        e.preventDefault();
        $(this).tab('show');
        $('div.usersList').hide(); //hide all other usersLists
        $('div#usersList-Lobby').show(); //show the main usersList
    });
    //by default, show the Lobby tab
    $('ul#tab a:contains("Lobby")').tab('show');

    $('ul#tab li span.glyphicon-remove').click(function () {
        $(this).parent().parent().remove(); //removes the li tag
        var roomName = $(this).parent().text();
        $('div#room-' + roomName).remove();
        socket.emit('leaveRoom', roomName);
    });

    //Modal related functionality
    $('#addRoom').click(function () {
        $('#createRoomModal').modal('show')
    });

    $('#createRoomButton').click(function () {
        var roomName = $('input#modalInput').val();
        if(roomName) {
            socket.emit('createRoom', roomName);
            $('input#modalInput').val('');
        }
    });

    $("#modalInput").keypress(function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == '13'){
            $("#createRoomButton").click();
        }
    });
    //Modal box login ends here

    $('#setUsername').click(function() {setUsername()});
    $('#submit').click(function() {sentMessage();});
});
<commit_msg>Fixed a bug where user msg is not properly escaped<commit_after>var socket = io.connect();

//keep a list of rooms that the client is in so we don't make a lot of server requests
var userRoomsList;

/**
 * From http://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript
 * Provides a means to escape user inputted messages
 * The rationale to use this is that this is a cleaner, simple, and effective way to escape HTML entities
 * than some hack way with jQuery functions (eg. text()) that may not work cross browsers.
 **/
function escapeHtml(unsafe) {
    return unsafe
         .replace(/&/g, "&")
         .replace(/</g, "<")
         .replace(/>/g, ">")
         .replace(/"/g, """)
         .replace(/'/g, "'");
 }

function addMessage(msg, roomName, username) {
    //append to the right div/ie to the right room
    $('div#chatContainer div#room-' + roomName + ' div.chatEntries').append('
' + username + ' : ' + escapeHtml(msg) + '
');

    var roomChatEntries = $('div#chatContainer div#room-' + roomName + ' div.chatEntries');
    if (Math.abs((roomChatEntries[0].scrollHeight - roomChatEntries.scrollTop() - roomChatEntries.outerHeight()) < 200) ) {
        roomChatEntries.animate({
            scrollTop: roomChatEntries[0].scrollHeight
        }, 200);
    }
    else {
        $('#moreMsgs').filter(':hidden').fadeIn(1000).delay(3000).fadeOut(3000);
    }
}

function sentMessage() {
    if ($('#messageInput').val() != "") {
        var messageBody = $('#messageInput').val();
        var roomName = $('ul#tab li.active').text();
        var data = {messageBody : messageBody, messageRoom : roomName};
        socket.emit('sendMessage', data);

        //this current implementation is that when you send a message, for you, the message doesn't go to the server, only for others
        addMessage(messageBody, roomName ,"Me");
        $('#messageInput').val(''); //clear the input field
    }
}

function setUsername() {
    $("#loginForm").submit();
}

socket.on('sendMessageResponse', function (data) {
    addMessage(data['message'], data['roomName'], data['username']);
});

socket.on('numConnected', function (data) {
    $('#numConnected').html('Users online: ' + data.numConnected);
});

socket.on('updateRoomsList', function (roomsList) {
    userRoomsList = roomsList;
});

socket.on('loadUsersList', function (data) {
    $('#usersList-' + data.roomName).empty();
    for (var i = 0 ; i < data.usernamesList.length ; i++) {
        $('#usersList-' + data.roomName).append('
' + data.usernamesList[i] + '
');

        var usernameElement = $('div.username:contains(' + data.usernamesList[i] + ')');
        usernameElement.on({
            dragstart: function(e) {
                e.dataTransfer.setData("username", $(this).text());
                $(this).css('opacity', '0.5');
            },
            dragend: function(e) {
                $(this).css('opacity', '1');
            },
        });
    }
});

socket.on('roomInvite', function (data) {
    window.alert("User " + data.inviter + " invites you to " + data.roomName);
    socket.emit('acceptInvitation', data.roomName);
});

socket.on('createRoomResponse', function (data) {
    if(data.created) {
        //chatContainer DOM creation
        $('div#chatContainer').append('

');
        //room userList DOM creation
        $('div#usersConnected').append('

')
        //tab dom creation
        $('ul#tab').append('
'+ data.roomName +'
');

        //open tab functionality
        $('ul#tab li:contains(' + data.roomName + ') a').click(function (e) {
            e.preventDefault();
            $(this).tab('show');
            $('div.usersList').hide(); //hide all other usersLists
            $('div#usersList-' + data.roomName).show(); //show the specific room usersList

        });

        //close tab functionality
        $('ul#tab li:contains(' + data.roomName + ') span.glyphicon-remove').click(function () {
            $(this).parent().parent().remove(); //removes the li tag
            $('div#room-' + data.roomName).remove(); //remove the main chatpanel
            $('div#userlist-' + data.roomName).remove(); //remove userlist
            socket.emit('leaveRoom', data.roomName);

            $('ul#tab a:contains("Lobby")').click(); //go back to the lobby
        });

        //attach dnd event listeners
        $('#tabContainer .roomTab:contains(' + data.roomName + ') a').on({
            dragleave: function(e) {
                $(this).removeClass('over');
                e.preventDefault();
            },
            dragenter: function(e) {
                $(this).addClass('over');
                e.preventDefault();
            },
            dragover: function(e) {
                $(this).addClass('over');
                e.preventDefault();
            },
            drop: function(e) {
                $(this).removeClass('over');
                e.preventDefault();
                socket.emit('inviteUser', {'username' : e.dataTransfer.getData('username'), 'roomName' : data.roomName});
            }
        });

        $('#roomModalCloseButton').click(); //close the window

        //subscribe to room
        socket.emit('joinRoom', data.roomName);

        $('ul#tab li:contains(' + data.roomName + ') a').click();

    }
    else {
        if(data.errorCode == 1) {
            window.alert("Illegal room name! Room name can only contain alphanumeric characters, hyphen, and underscores!");
        }
        else if (data.errorCode == 2) {
            window.alert("A room with that name already exists! Please choose another name!");
        }
        else if (data.errorCode == 3) {
            window.alert("Room name 'Lobby' is reserved, please choose another name!");
        }
        else {
            window.alert("Unknown error! Room cannot be created!");
        }
    }
});

$(function() {

    jQuery.event.props.push('dataTransfer');

    $('#messageInput').keypress(function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == '13'){
            $("#submit").click();
        }
    });

    $('ul#tab a:contains("Lobby")').click(function (e) {
        e.preventDefault();
        $(this).tab('show');
        $('div.usersList').hide(); //hide all other usersLists
        $('div#usersList-Lobby').show(); //show the main usersList
    });
    //by default, show the Lobby tab
    $('ul#tab a:contains("Lobby")').tab('show');

    $('ul#tab li span.glyphicon-remove').click(function () {
        $(this).parent().parent().remove(); //removes the li tag
        var roomName = $(this).parent().text();
        $('div#room-' + roomName).remove();
        socket.emit('leaveRoom', roomName);
    });

    //Modal related functionality
    $('#addRoom').click(function () {
        $('#createRoomModal').modal('show')
    });

    $('#createRoomButton').click(function () {
        var roomName = $('input#modalInput').val();
        if(roomName) {
            socket.emit('createRoom', roomName);
            $('input#modalInput').val('');
        }
    });

    $("#modalInput").keypress(function(event){
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == '13'){
            $("#createRoomButton").click();
        }
    });
    //Modal box login ends here

    $('#setUsername').click(function() {setUsername()});
    $('#submit').click(function() {sentMessage();});
});
<|endoftext|>


<commit_before>var v = "20150930"
var workHistory = ["TEC", "ClassWave", "RingRevenue", "FarmPlus", "ASAP"]
var projectHistory = ["TimeIsMoney", "Insights", "FeedingTube", "PrimordialOoze", "TrapperKeeper", "TIMO"]
// scroll the chat as messages are added
var scrollChat = function () {
  var chat = $("#chat")
  chat.animate({ scrollTop: chat.prop("scrollHeight") }, 2000)
}

// handle user queries
var processRequest = function (query) {
  var request = $.ajax({
    data: { "query": query, "v": v },
    dataType: "json",
    type: "POST",
    url: "/message"
  })
  request.done(function (message) {
    if (message.result.fulfillment.speech !== "") {
      writeBotMessage(message)
    }
    if (message.result.action === "getBitcoinPrice") {
      setTimeout(getBitcoinPrice, 1500)
    } else if (message.result.action === "getCurrentAge") {
      setTimeout(getCurrentAge, 1500)
    } else if (message.result.action === "getProjectDetails") {
      setTimeout(getDetails, 1500, message.result.parameters.Project)
    } else if (message.result.action === "getWorkDetails") {
      setTimeout(getDetails, 1500, message.result.parameters.Work)
    } else if (message.result.action === "getResume") {
      writeResumeMessage()
    } else if (message.result.action === "expandWork") {
      // confirm request
      writeMessage("Sure thing!", false)
      // hit each work details endpoint
      setTimeout(expandAllDetails, 1000, workHistory)
    } else if (message.result.action === "expandProjects") {
      writeMessage("Alright!", false)
      setTimeout(expandAllDetails, 1000, projectHistory)
    }
  })
}

// calculate Phaedrus's current age
var getCurrentAge = function () {
  var birthday = new Date("08/30/1992")
  var today = new Date()
  var age = Math.floor((today - birthday) / (365.25 * 24 * 60 * 60 * 1000))
  var nextAge = age + 1
  var monthsTillBirthday = birthday.getMonth() - today.getMonth()
  var ageMessage = "He is " + age
  if (monthsTillBirthday >= 0 && monthsTillBirthday < 4) {
    ageMessage += ", turning " + nextAge + " on August 30"
  }
  if (monthsTillBirthday === 0) {
    ageMessage += " (so soon!)"
  }
  ageMessage += "."
  writeMessage(ageMessage, false)
}

// welcome logic
var howdy = function () {
  requestEvent("WELCOME")
}

// send event request
var requestEvent = function (event) {
  var request = $.ajax({
    data: { "v": v, "e": event },
    dataType: "json",
    type: "POST",
    url: "/event-handler"
  })
  request.done(function (message) {
    writeBotMessage(message)
  })
}

// trigger event for specific details
var getDetails = function (exp) {
  var event = exp + "-details"
  requestEvent(event)
}

// loop over array of items, make -details calls for each
var expandAllDetails = function (items) {
  var request = $.ajax({
    url: "/event-handler",
    type: "POST",
    dataType: "json",
    data: { "v": v, "e": items.splice(0,1) + "-details" },
    success: function (data, status, xhr) {
      var numMessages = data.result.fulfillment.messages.length
      writeBotMessage(data)
      if (items.length > 0)
        setTimeout(expandAllDetails, 3500*numMessages, items)
      else
        setTimeout(requestEvent, 3500*numMessages, "upto")
    }
  })
}

// fetch the current Bitcoin price from the CoinDesk API
var getBitcoinPrice = function () {
  var btcRequest = $.ajax({
    type: "GET",
    url: "http://api.coindesk.com/v1/bpi/currentprice.json",
    error: function (data, textStatus, jqXHR) {
      console.log("Error! Could not fetch Coindesk access token.")
    }
  })
  btcRequest.done(function (message) {
    var btcMsg = "$" + JSON.parse(message).bpi.USD.rate
    writeMessage(btcMsg, false)
  })
}

// is Phaedbot writing one message or a chain?
var writeBotMessage = function (message) {
  var response = message.result.fulfillment
  if (response.messages.length === 1)
    writeMessage(response.speech, false)
  else {
    for (var i = 0; i < response.messages.length; i++) {
      setTimeout(writeMessage, 3500 * i+1, response.messages[i].speech, false)
    }
  }
}

// write message with link to resume
var writeResumeMessage = function () {
  var wrapperDiv = document.createElement("div")
  wrapperDiv.className = "response row"
  var authorDiv = document.createElement("div")
  authorDiv.className = "col-xs-3 col-sm-2 col-sm-offset-3 col-md-1 col-md-offset-4"
  var authorSpan = document.createElement("span")
  authorSpan.className = "author"
  var author = document.createTextNode("Phaedbot")
  authorSpan.appendChild(author)
  authorDiv.appendChild(authorSpan)
  var messageDiv = document.createElement("div")
  messageDiv.className = "col-xs-9 col-sm-6 col-md-4"
  var messageP = document.createElement("p")
  messageP.className = "message"
  messageP.innerHTML = "You can see Phaedrus's resume by clicking the PDF icon at the top of the page, or by clicking this link."
  messageDiv.appendChild(messageP)
  wrapperDiv.appendChild(authorDiv)
  wrapperDiv.appendChild(messageDiv)
  $("#chat").append(wrapperDiv)
  $("#chat div.response").last()
    .css("opacity", 0)
    .slideDown(500)
    .animate({ opacity: 1 }, { queue: false }, { duration: 1500 })
  setTimeout(scrollChat, 1000)
}

// write a message to the chat
var writeMessage = function (message, fromUser) {
  var wrapperDiv = document.createElement("div")
  wrapperDiv.className = fromUser ? "request row" : "response row"
  var authorDiv = document.createElement("div")
  authorDiv.className = "col-xs-3 col-sm-2 col-sm-offset-3 col-md-1 col-md-offset-4"
  var authorSpan = document.createElement("span")
  authorSpan.className = "author"
  var author = document.createTextNode(fromUser ? "You" : "Phaedbot")
  authorSpan.appendChild(author)
  authorDiv.appendChild(authorSpan)
  var messageDiv = document.createElement("div")
  messageDiv.className = "col-xs-9 col-sm-6 col-md-4"
  var messageP = document.createElement("p")
  messageP.className = "message"
  var messageText = document.createTextNode(message)
  messageP.appendChild(messageText)
  messageDiv.appendChild(messageP)
  wrapperDiv.appendChild(authorDiv)
  wrapperDiv.appendChild(messageDiv)
  $("#chat").append(wrapperDiv)
  if (fromUser) {
    $("#message-contents").val("")
  } else {
    $("#chat div.response").last()
      .css("opacity", 0)
      .slideDown(500)
      .animate({ opacity: 1 }, { queue: false }, { duration: 1500 })
  }
  setTimeout(scrollChat, 1000)
}

// page lifecycle
$(document).ready(function () {
  // focus on user input element
  $("#message-contents").focus()
  // Phaedbot starts the conversation
  howdy()
  // process messages on form submit
  $("#message-form").submit(function (e) {
    e.preventDefault()
    var query = $("#message-contents").val()
    writeMessage(query, true)
    processRequest(query)
  })
})
<commit_msg>Reduce details from tsunami to splash<commit_after>var v = "20150930"
var workHistory = ["RingRevenue", "FarmPlus", "ASAP"]
var projectHistory = ["TimeIsMoney", "Insights", "FeedingTube", "TIMO"]
// scroll the chat as messages are added
var scrollChat = function () {
  var chat = $("#chat")
  chat.animate({ scrollTop: chat.prop("scrollHeight") }, 2000)
}

// handle user queries
var processRequest = function (query) {
  var request = $.ajax({
    data: { "query": query, "v": v },
    dataType: "json",
    type: "POST",
    url: "/message"
  })
  request.done(function (message) {
    if (message.result.fulfillment.speech !== "") {
      writeBotMessage(message)
    }
    if (message.result.action === "getBitcoinPrice") {
      setTimeout(getBitcoinPrice, 1500)
    } else if (message.result.action === "getCurrentAge") {
      setTimeout(getCurrentAge, 1500)
    } else if (message.result.action === "getProjectDetails") {
      setTimeout(getDetails, 1500, message.result.parameters.Project)
    } else if (message.result.action === "getWorkDetails") {
      setTimeout(getDetails, 1500, message.result.parameters.Work)
    } else if (message.result.action === "getResume") {
      writeResumeMessage()
    } else if (message.result.action === "expandWork") {
      // confirm request
      writeMessage("Sure thing!", false)
      // hit each work details endpoint
      setTimeout(expandAllDetails, 1000, workHistory)
    } else if (message.result.action === "expandProjects") {
      writeMessage("Alright!", false)
      setTimeout(expandAllDetails, 1000, projectHistory)
    }
  })
}

// calculate Phaedrus's current age
var getCurrentAge = function () {
  var birthday = new Date("08/30/1992")
  var today = new Date()
  var age = Math.floor((today - birthday) / (365.25 * 24 * 60 * 60 * 1000))
  var nextAge = age + 1
  var monthsTillBirthday = birthday.getMonth() - today.getMonth()
  var ageMessage = "He is " + age
  if (monthsTillBirthday >= 0 && monthsTillBirthday < 4) {
    ageMessage += ", turning " + nextAge + " on August 30"
  }
  if (monthsTillBirthday === 0) {
    ageMessage += " (so soon!)"
  }
  ageMessage += "."
  writeMessage(ageMessage, false)
}

// welcome logic
var howdy = function () {
  requestEvent("WELCOME")
}

// send event request
var requestEvent = function (event) {
  var request = $.ajax({
    data: { "v": v, "e": event },
    dataType: "json",
    type: "POST",
    url: "/event-handler"
  })
  request.done(function (message) {
    writeBotMessage(message)
  })
}

// trigger event for specific details
var getDetails = function (exp) {
  var event = exp + "-details"
  requestEvent(event)
}

// loop over array of items, make -details calls for each
var expandAllDetails = function (items) {
  var request = $.ajax({
    url: "/event-handler",
    type: "POST",
    dataType: "json",
    data: { "v": v, "e": items.splice(0,1) + "-details" },
    success: function (data, status, xhr) {
      var numMessages = data.result.fulfillment.messages.length
      writeBotMessage(data)
      if (items.length > 0)
        setTimeout(expandAllDetails, 3500*numMessages, items)
      else
        setTimeout(requestEvent, 3500*numMessages, "upto")
    }
  })
}

// fetch the current Bitcoin price from the CoinDesk API
var getBitcoinPrice = function () {
  var btcRequest = $.ajax({
    type: "GET",
    url: "http://api.coindesk.com/v1/bpi/currentprice.json",
    error: function (data, textStatus, jqXHR) {
      console.log("Error! Could not fetch Coindesk access token.")
    }
  })
  btcRequest.done(function (message) {
    var btcMsg = "$" + JSON.parse(message).bpi.USD.rate
    writeMessage(btcMsg, false)
  })
}

// is Phaedbot writing one message or a chain?
var writeBotMessage = function (message) {
  var response = message.result.fulfillment
  if (response.messages.length === 1)
    writeMessage(response.speech, false)
  else {
    for (var i = 0; i < response.messages.length; i++) {
      setTimeout(writeMessage, 3500 * i+1, response.messages[i].speech, false)
    }
  }
}

// write message with link to resume
var writeResumeMessage = function () {
  var wrapperDiv = document.createElement("div")
  wrapperDiv.className = "response row"
  var authorDiv = document.createElement("div")
  authorDiv.className = "col-xs-3 col-sm-2 col-sm-offset-3 col-md-1 col-md-offset-4"
  var authorSpan = document.createElement("span")
  authorSpan.className = "author"
  var author = document.createTextNode("Phaedbot")
  authorSpan.appendChild(author)
  authorDiv.appendChild(authorSpan)
  var messageDiv = document.createElement("div")
  messageDiv.className = "col-xs-9 col-sm-6 col-md-4"
  var messageP = document.createElement("p")
  messageP.className = "message"
  messageP.innerHTML = "You can see Phaedrus's resume by clicking the PDF icon at the top of the page, or by clicking this link."
  messageDiv.appendChild(messageP)
  wrapperDiv.appendChild(authorDiv)
  wrapperDiv.appendChild(messageDiv)
  $("#chat").append(wrapperDiv)
  $("#chat div.response").last()
    .css("opacity", 0)
    .slideDown(500)
    .animate({ opacity: 1 }, { queue: false }, { duration: 1500 })
  setTimeout(scrollChat, 1000)
}

// write a message to the chat
var writeMessage = function (message, fromUser) {
  var wrapperDiv = document.createElement("div")
  wrapperDiv.className = fromUser ? "request row" : "response row"
  var authorDiv = document.createElement("div")
  authorDiv.className = "col-xs-3 col-sm-2 col-sm-offset-3 col-md-1 col-md-offset-4"
  var authorSpan = document.createElement("span")
  authorSpan.className = "author"
  var author = document.createTextNode(fromUser ? "You" : "Phaedbot")
  authorSpan.appendChild(author)
  authorDiv.appendChild(authorSpan)
  var messageDiv = document.createElement("div")
  messageDiv.className = "col-xs-9 col-sm-6 col-md-4"
  var messageP = document.createElement("p")
  messageP.className = "message"
  var messageText = document.createTextNode(message)
  messageP.appendChild(messageText)
  messageDiv.appendChild(messageP)
  wrapperDiv.appendChild(authorDiv)
  wrapperDiv.appendChild(messageDiv)
  $("#chat").append(wrapperDiv)
  if (fromUser) {
    $("#message-contents").val("")
  } else {
    $("#chat div.response").last()
      .css("opacity", 0)
      .slideDown(500)
      .animate({ opacity: 1 }, { queue: false }, { duration: 1500 })
  }
  setTimeout(scrollChat, 1000)
}

// page lifecycle
$(document).ready(function () {
  // focus on user input element
  $("#message-contents").focus()
  // Phaedbot starts the conversation
  howdy()
  // process messages on form submit
  $("#message-form").submit(function (e) {
    e.preventDefault()
    var query = $("#message-contents").val()
    writeMessage(query, true)
    processRequest(query)
  })
})
<|endoftext|>


<commit_before>/* eslint-env browser, jquery */

$(document).ready(function() {
  var $question = $('#question');
  var $choices = $('#choices');
  var $answerAnother = $('#answer-another');

  $question.find('.choice').each(function(index, choice) {
    var $choice = $(choice);

    $choice.click(function() {
      $choices.replaceWith("Submitting ...");

      $.post('/response',
        {
          questionId: $question.data('question-id'),
          choiceId: $choice.data('choice-id')
        },
        function(data) {
          $question.replaceWith('Answer submitted.');
          $answerAnother.show();
        }
      );
    });
  });

  var $addQuestionForm = $('#add-question-form');
  var $questionTitle = $('#question-title');
  var $addChoice = $('#add-choice');
  var $addQuestion = $('#add-question');
  var $submitting = $('#submitting');
  var $addAnother = $('#add-another');
  var $choiceRows = $('#choice-rows');
  var choiceTemplate = $('#choice-template').html();

  $addQuestion.click(function() {
    var questionTitle = $questionTitle.val()

    var choices = $addQuestionForm.find('.choice').map(function() {
      return $(this).val();
    })
    .get()
    .filter(function(choice) { return choice != ''; });

    if (questionTitle == '') {
      alert('Question title required.');
      return;
    }

    if (choices.length == 0) {
      alert('At least one choice must not be blank.');
      return;
    }

    $addQuestionForm.remove();
    $submitting.show();

    $.post('/admin/add-question',
      {
        questionTitle: questionTitle,
        'choices[]': choices
      },
      function(data) {
        $submitting.hide();
        $addAnother.show();
      }
    );
  });

  $addChoice.click(function() {
    $choiceRows.append(choiceTemplate);
  });
});
<commit_msg>Apply eslint feedback to public/js/survey.js.<commit_after>/* eslint-env browser, jquery */

function onDocumentReady() {
  const $question = $('#question');
  const $choices = $('#choices');
  const $answerAnother = $('#answer-another');

  function addPostCallbackOnClick(index, choice) {
    const $choice = $(choice);

    function onClick() {
      $choices.replaceWith('Submitting ...');

      function onResponse() {
        $question.replaceWith('Answer submitted.');
        $answerAnother.show();
      }

      const responseData = {
        questionId: $question.data('question-id'),
        choiceId: $choice.data('choice-id'),
      };

      $.post('/response', responseData, onResponse);
    }

    $choice.click(onClick);
  }

  $question.find('.choice').each(addPostCallbackOnClick);

  const $addQuestionForm = $('#add-question-form');
  const $questionTitle = $('#question-title');
  const $addChoice = $('#add-choice');
  const $addQuestion = $('#add-question');
  const $submitting = $('#submitting');
  const $addAnother = $('#add-another');
  const $choiceRows = $('#choice-rows');
  const choiceTemplate = $('#choice-template').html();

  function onClickAddQuestion() {
    const questionTitle = $questionTitle.val();

    function getValue() {
      return $(this).val();
    }

    function rejectBlankChoices(choice) { return choice !== ''; }

    const choices = $addQuestionForm.find('.choice').map(getValue)
    .get()
    .filter(rejectBlankChoices);

    if (questionTitle === '') {
      alert('Question title required.');
      return;
    }

    if (choices.length === 0) {
      alert('At least one choice must not be blank.');
      return;
    }

    $addQuestionForm.remove();
    $submitting.show();

    const questionData = { questionTitle, 'choices[]': choices };

    function onResponse() {
      $submitting.hide();
      $addAnother.show();
    }

    $.post('/admin/add-question', questionData, onResponse);
  }

  $addQuestion.click(onClickAddQuestion);

  function onClickAddChoice() {
    $choiceRows.append(choiceTemplate);
  }

  $addChoice.click(onClickAddChoice);
}

$(document).ready(onDocumentReady);
<|endoftext|>


<commit_before>///
// Action types
///

export const OVERVIEW_SELECT_ITEM = 'OVERVIEW_SELECT_ITEM ';
export const OVERVIEW_TOGGLE_ITEM = 'OVERVIEW_TOGGLE_ITEM ';

///
// Action creators
///

export function selectItem(item) {
	return {type: OVERVIEW_SELECT_ITEM, payload: item};
}

export function toggleItem(item) {
	return {type: OVERVIEW_TOGGLE_ITEM, payload: item};
}

<commit_msg>rfct/cr80 - Updated action type values for consistency<commit_after>///
// Action types
///

export const OVERVIEW_SELECT_ITEM = 'OVERVIEW:SELECT_ITEM ';
export const OVERVIEW_TOGGLE_ITEM = 'OVERVIEW:TOGGLE_ITEM ';

///
// Action creators
///

export function selectItem(item) {
	return {type: OVERVIEW_SELECT_ITEM, payload: item};
}

export function toggleItem(item) {
	return {type: OVERVIEW_TOGGLE_ITEM, payload: item};
}

<|endoftext|>


<commit_before>/*The file to interface with the beam API */

//run 'npm i -S beam-client-node' to get these two things
const BeamClient = require('beam-client-node');
const BeamSocket = require('beam-client-node/lib/ws');
const config = require('./config.json');
const spawn = require('child_process').spawn;

let userInfo;

const client = new BeamClient();

//OAuth gives permision for all following actions
client.user('oauth',{
  tokens:{
    access: config.OAuth,
    expires: Date.now() + (365 * 24 *60 *60 *1000)
  },
});

client.request('GET', 'users/current')
.then(response => {
    console.log(response.body);
    userInfo = response.body;
    return client.chat.join(response.body.channel.id);
})
.then(response => {
    const body = response.body;
    console.log(body);
    return createChatSocket(userInfo.id, userInfo.channel.id, body.endpoints, body.authkey);
})
.catch(error => {
      console.log("Something went wrong:", error);
});

/**
 * Creates a beam chat socket and sets up listeners to various chat events.
 * @param {number} userId The user to authenticate as
 * @param {number} channelId The channel id to join
 * @param {any} endpoints An endpoints array from a beam.chat.join call.
 * @param {any} authkey An authentication key from a beam.chat.join call.
 * @returns {Promise.<>}
 */
function createChatSocket (userId, channelId, endpoints, authkey) {
    const socket = new BeamSocket(endpoints).boot();

    socket.auth(channelId, userId, authkey)
    .then(() => {
        console.log('You are now authenticated!');
        // Send a chat message
        return socket.call('msg', ['Hello world!']);
    })
    .catch(error => {
        console.log('Oh no! An error occurred!', error);
    });

    // Listen to chat messages, note that you will also receive your own!
    socket.on('ChatMessage', data => {
        console.log('We got a ChatMessage packet!');
        console.log(data);
        console.log(data.message); // lets take a closer look

        moderate(data);
    });

    // Listen to socket errors, you'll need to handle these!
    socket.on('error', error => {
        console.error('Socket error', error);
    });
}

function moderate(data){
  //This is how to send the data to be processed by the python
  var moderator = spawn('python', ['hammer.py']);
  moderator.stdin.write(JSON.stringify(data.message));
  moderator.stdin.end();

  moderator.stdout.on('data', function(data){
    if(data === "timeout"){
      //TODO
    }else if(data === "ban"){
      //TODO
    }else if(data === "purge"){
      //TODO
    }
  });

  moderator.stdout.on('end', function(){
    return;
  });
}
<commit_msg>added debug text in moderate function<commit_after>/*The file to interface with the beam API */

//run 'npm i -S beam-client-node' to get these two things
const BeamClient = require('beam-client-node');
const BeamSocket = require('beam-client-node/lib/ws');
const config = require('./config.json');
const spawn = require('child_process').spawn;

const debug = false;

let userInfo;

const client = new BeamClient();

//OAuth gives permision for all following actions
client.user('oauth',{
  tokens:{
    access: config.OAuth,
    expires: Date.now() + (365 * 24 *60 *60 *1000)
  },
});

client.request('GET', 'users/current')
.then(response => {
    console.log(response.body);
    userInfo = response.body;
    return client.chat.join(response.body.channel.id);
})
.then(response => {
    const body = response.body;
    console.log(body);
    return createChatSocket(userInfo.id, userInfo.channel.id, body.endpoints, body.authkey);
})
.catch(error => {
      console.log("Something went wrong:", error);
});

/**
 * Creates a beam chat socket and sets up listeners to various chat events.
 * @param {number} userId The user to authenticate as
 * @param {number} channelId The channel id to join
 * @param {any} endpoints An endpoints array from a beam.chat.join call.
 * @param {any} authkey An authentication key from a beam.chat.join call.
 * @returns {Promise.<>}
 */
function createChatSocket (userId, channelId, endpoints, authkey) {
    const socket = new BeamSocket(endpoints).boot();

    socket.auth(channelId, userId, authkey)
    .then(() => {
        console.log('You are now authenticated!');
        // Send a chat message
        return socket.call('msg', ['Hello world!']);
    })
    .catch(error => {
        console.log('Oh no! An error occurred!', error);
    });

    // Listen to chat messages, note that you will also receive your own!
    socket.on('ChatMessage', data => {
        console.log('We got a ChatMessage packet!');
        console.log(data);
        console.log(data.message); // lets take a closer look

        moderate(data);
    });

    // Listen to socket errors, you'll need to handle these!
    socket.on('error', error => {
        console.error('Socket error', error);
    });
}

function moderate(data){
  //This is how to send the data to be processed by the python
  var moderator = spawn('python', ['hammer.py']);
  moderator.stdin.write(JSON.stringify(data.message));
  moderator.stdin.end();

  moderator.stdout.on('data', function(data){
    if(data === "timeout"){
      if(debug) console.log("Need to timeout");
      //TODO
    }else if(data === "ban"){
      if(debug) console.log("Need to ban");
      //TODO
    }else if(data === "purge"){
      if(debug) console.log("Need to purge");
      //TODO
    }else if(data === "nothing"){
      if(debug) console.log("No action to take");
      //THIS SPAE INTINTIONALLY LEFT BLANK
    }
  });

  moderator.stdout.on('end', function(){
    return;
  });
}
<|endoftext|>


<commit_before>import React from 'react'

import Page from '../../components/page'
import Header from './header'
import PreviousEpisodeSection from './sections/previous-episodes'
import EpisodesSection from './sections/episodes'
import PanelistsSection from './sections/panelists'
import SponsorsSection from '../../components/sponsors'
import LinksSection from './sections/footer/footer-links-list'
import Footer from './sections/footer'

import SocialIconGroupSection from './sections/footer/footer-social-links-list'
import FeatureShowScript from './scripts/feature-show'

export default Home

function Home(
  {
    futureEpisodes = [],
    pastEpisodes = [],
    sponsors,
    panelists,
  }
) {
  return (
    <Page>

      <EpisodesSection episodes={futureEpisodes} />
      <PanelistsSection panelists={panelists} />

        <SponsorsSection {...sponsors} />

        <PreviousEpisodeSection episodes={pastEpisodes} />

        {pastEpisodes.length ?
 : ''}

        <SocialIconGroupSection />

        <LinksSection />

        <FeatureShowScript />

    </Page>
  )
}
<commit_msg>Remove unneeded components from home page<commit_after>import React from 'react'

import Page from '../../components/page'
import Header from './header'
import PreviousEpisodeSection from './sections/previous-episodes'
import EpisodesSection from './sections/episodes'
import PanelistsSection from './sections/panelists'
import SponsorsSection from '../../components/sponsors'
import Footer from './sections/footer'

import FeatureShowScript from './scripts/feature-show'

export default Home

function Home(
  {
    futureEpisodes = [],
    pastEpisodes = [],
    sponsors,
    panelists,
  }
) {
  return (
    <Page>

      <EpisodesSection episodes={futureEpisodes} />
      <PanelistsSection panelists={panelists} />

        <SponsorsSection {...sponsors} />

        <PreviousEpisodeSection episodes={pastEpisodes} />

        <FeatureShowScript />

    </Page>
  )
}
<|endoftext|>


<commit_before>// Инициализация класса
var Rtc = function(i2c) {
  this._time = undefined;
  this._i2c = i2c || PrimaryI2C;
  this._address = 0x68;
  this.start();
};

// Метод записывает данные data в регистр reg
Rtc.prototype.write = function(reg, data) {
  this._i2c.writeTo(this._address, [reg, data]);
};

// Метод производит чтение из регистра reg количестов байт count
Rtc.prototype.read = function(reg, count) {
  if (count === undefined) {
    count = 1;
  }
  this._i2c.writeTo(this._address, reg);
  return this._i2c.readFrom(this._address, count);
};

Rtc.prototype._decToBcd = function(val) {
  return Math.floor(val / 10) * 16 + (val % 10);
};

Rtc.prototype._bcdToDec = function(val) {
  return Math.floor(val / 16) * 10 + (val % 16);
};

Rtc.prototype.setTime = function(time) {
  if (time instanceof Date) {
    this._time = time;
  } else if (time instanceof Object){
    this._time = new Date(
      time.year,
      time.month,
      time.day,
      time.hour,
      time.minute,
      time.second
    );
  } else if (typeof time === 'number') {
    this._time = new Date(time * 1000);
  } else if (typeof time === 'string') {
    this._time = new Date(Date.parse(time));
  } else {
    this._time = new Date(getTime()*1000);
  }

  var halt = this.read(0x00, 1)[0] >> 7;
  this.write(0x00, [
    this._decToBcd(this._time.getSeconds()) | (halt << 7),
    this._decToBcd(this._time.getMinutes()),
    this._decToBcd(this._time.getHours()),
    this._decToBcd(this._time.getDay() + 1),
    this._decToBcd(this._time.getDate()),
    this._decToBcd(this._time.getMonth() + 1),
    this._decToBcd(this._time.getFullYear() - 2000)
  ]);
};

Rtc.prototype.getTime = function(format) {
  var time = this.read(0x00, 7);
  this._time = new Date(
    this._bcdToDec(time[6]) + 2000,
    this._bcdToDec(time[5]) - 1,
    this._bcdToDec(time[4]),
    this._bcdToDec(time[2] & 0x3f),
    this._bcdToDec(time[1]),
    this._bcdToDec(time[0] & 0x7f)
  );

  var res = this._time;
  switch (format) {
    case 'unixtime':
      res = Math.ceil(res.getTime() / 1000);
      break;
    case 'iso':
      res = res.getFullYear() + '-' + res.getMonth() + 1 +
         '-' + res.getDay() + 'T' + res.getHours() +
         ':' + res.getMinutes() + ':' + res.getSeconds();
      break;
    default: break;
  }
  return res;
};

Rtc.prototype.start = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7) {
    this.write(0x00, byte ^ 0x80);
  }
};

Rtc.prototype.stop = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7 === 0) {
    this.write(0x00, byte ^ 0x80);
  }
};

// Экспортируем класс
exports.connect = function(i2c) {
  return new Rtc(i2c);
};
<commit_msg>without rtc<commit_after>// Инициализация класса
var Rtc = function(i2c) {
  this._time = undefined;
  this._i2c = i2c || PrimaryI2C;
  this._address = 0x68;
  this.start();
};

// Метод записывает данные data в регистр reg
Rtc.prototype.write = function(reg, data) {
  this._i2c.writeTo(this._address, [reg, data]);
};

// Метод производит чтение из регистра reg количестов байт count
Rtc.prototype.read = function(reg, count) {
  if (count === undefined) {
    count = 1;
  }
  this._i2c.writeTo(this._address, reg);
  return this._i2c.readFrom(this._address, count);
};

Rtc.prototype._decToBcd = function(val) {
  return Math.floor(val / 10) * 16 + (val % 10);
};

Rtc.prototype._bcdToDec = function(val) {
  return Math.floor(val / 16) * 10 + (val % 16);
};

Rtc.prototype.setTime = function(time) {
  if (time instanceof Date) {
    this._time = time;
  } else if (time instanceof Object){
    this._time = new Date(
      time.year,
      time.month,
      time.day,
      time.hour,
      time.minute,
      time.second
    );
  } else if (typeof time === 'number') {
    this._time = new Date(time * 1000);
  } else if (typeof time === 'string') {
    this._time = new Date(Date.parse(time));
  } else {
    this._time = new Date(getTime()*1000);
  }

  var halt = this.read(0x00, 1)[0] >> 7;
  this.write(0x00, [
    this._decToBcd(this._time.getSeconds()) | (halt << 7),
    this._decToBcd(this._time.getMinutes()),
    this._decToBcd(this._time.getHours()),
    this._decToBcd(this._time.getDay() + 1),
    this._decToBcd(this._time.getDate()),
    this._decToBcd(this._time.getMonth() + 1),
    this._decToBcd(this._time.getFullYear() - 2000)
  ]);
};

Rtc.prototype.getTime = function(unit) {
  var time = this.read(0x00, 7);
  this._time = new Date(
    this._bcdToDec(time[6]) + 2000,
    this._bcdToDec(time[5]) - 1,
    this._bcdToDec(time[4]),
    this._bcdToDec(time[2] & 0x3f),
    this._bcdToDec(time[1]),
    this._bcdToDec(time[0] & 0x7f)
  );

  var res = this._time;
  switch (unit) {
    case 'unixtime':
      res = Math.ceil(res.getTime() / 1000);
      break;
    case 'iso':
      res = res.getFullYear() + '-' + res.getMonth() + 1 +
         '-' + res.getDay() + 'T' + res.getHours() +
         ':' + res.getMinutes() + ':' + res.getSeconds();
      break;
    default: break;
  }
  return res;
};

Rtc.prototype.start = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7) {
    this.write(0x00, byte ^ 0x80);
  }
};

Rtc.prototype.stop = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7 === 0) {
    this.write(0x00, byte ^ 0x80);
  }
};

// Экспортируем класс
exports.connect = function(i2c) {
  return new Rtc(i2c);
};
<|endoftext|>


<commit_before>import warning from 'warning'
import { createLocation } from './LocationUtils'
import { addEventListener, removeEventListener } from './DOMUtils'
import { saveState, readState } from './DOMStateStorage'
import {
  isAbsolutePath,
  addQueryStringValueToPath,
  stripQueryStringValueFromPath,
  getQueryStringValueFromPath,
  parsePath,
  createPath
} from './PathUtils'

const HashChangeEvent = 'hashchange'

const getHashPath = () => {
  // We can't use window.location.hash here because it's not
  // consistent across browsers - Firefox will pre-decode it!
  const href = window.location.href
  const index = href.indexOf('#')
  return index === -1 ? '' : href.substring(index + 1)
}

const pushHashPath = (path) =>
  window.location.hash = path

const replaceHashPath = (path) => {
  const i = window.location.href.indexOf('#')

  window.location.replace(
    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
  )
}

export const ensureSlash = (path, encode) => {
  if (encode) {
    if (isAbsolutePath(path))
      return path

    return '/' + path
  }

  return path
}

export { getUserConfirmation, go } from './BrowserProtocol'

export const getCurrentLocation = (queryKey, transform) => {
  let path = transform(getHashPath(), false)
  const key = getQueryStringValueFromPath(path, queryKey)

  let state
  if (key) {
    path = stripQueryStringValueFromPath(path, queryKey)
    state = readState(key)
  }

  const init = parsePath(path)
  init.state = state

  return createLocation(init, undefined, key)
}

let prevLocation

export const startListener = (listener, queryKey, transform) => {
  const handleHashChange = () => {
    const transformedPath = transform(getHashPath(), true)

    replaceHashPath(transformedPath)

    const currentLocation = getCurrentLocation(queryKey, transform)

    if (prevLocation && currentLocation.key && prevLocation.key === currentLocation.key)
      return // Ignore extraneous hashchange events

    prevLocation = currentLocation

    listener(currentLocation)
  }

  const path = getHashPath()
  const transformedPath = transform(path, true)

  if (path !== transformedPath)
    replaceHashPath(transformedPath)

  addEventListener(window, HashChangeEvent, handleHashChange)

  return () =>
    removeEventListener(window, HashChangeEvent, handleHashChange)
}

const updateLocation = (location, queryKey, updateHash, transformPath) => {
  const { state, key } = location
  let path = transformPath(createPath(location), true)

  if (state !== undefined) {
    path = addQueryStringValueToPath(path, queryKey, key)
    saveState(key, state)
  }

  prevLocation = location

  updateHash(path)
}

export const pushLocation = (location, queryKey, transformPath) =>
  updateLocation(location, queryKey, (path) => {
    if (getHashPath() !== path) {
      pushHashPath(path)
    } else {
      warning(false, 'You cannot PUSH the same path using hash history')
    }
  }, transformPath)

export const replaceLocation = (location, queryKey, transformPath) =>
  updateLocation(location, queryKey, (path) => {
    if (getHashPath() !== path)
      replaceHashPath(path)
  }, transformPath)
<commit_msg>Better constant names<commit_after>import warning from 'warning'
import { createLocation } from './LocationUtils'
import { addEventListener, removeEventListener } from './DOMUtils'
import { saveState, readState } from './DOMStateStorage'
import {
  isAbsolutePath,
  addQueryStringValueToPath,
  stripQueryStringValueFromPath,
  getQueryStringValueFromPath,
  parsePath,
  createPath
} from './PathUtils'

const HashChangeEvent = 'hashchange'

const getHashPath = () => {
  // We can't use window.location.hash here because it's not
  // consistent across browsers - Firefox will pre-decode it!
  const href = window.location.href
  const hashIndex = href.indexOf('#')
  return hashIndex === -1 ? '' : href.substring(hashIndex + 1)
}

const pushHashPath = (path) =>
  window.location.hash = path

const replaceHashPath = (path) => {
  const hashIndex = window.location.href.indexOf('#')

  window.location.replace(
    window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path
  )
}

export const ensureSlash = (path, encode) => {
  if (encode) {
    if (isAbsolutePath(path))
      return path

    return '/' + path
  }

  return path
}

export { getUserConfirmation, go } from './BrowserProtocol'

export const getCurrentLocation = (queryKey, transform) => {
  let path = transform(getHashPath(), false)
  const key = getQueryStringValueFromPath(path, queryKey)

  let state
  if (key) {
    path = stripQueryStringValueFromPath(path, queryKey)
    state = readState(key)
  }

  const init = parsePath(path)
  init.state = state

  return createLocation(init, undefined, key)
}

let prevLocation

export const startListener = (listener, queryKey, transform) => {
  const handleHashChange = () => {
    const transformedPath = transform(getHashPath(), true)

    replaceHashPath(transformedPath)

    const currentLocation = getCurrentLocation(queryKey, transform)

    if (prevLocation && currentLocation.key && prevLocation.key === currentLocation.key)
      return // Ignore extraneous hashchange events

    prevLocation = currentLocation

    listener(currentLocation)
  }

  const path = getHashPath()
  const transformedPath = transform(path, true)

  if (path !== transformedPath)
    replaceHashPath(transformedPath)

  addEventListener(window, HashChangeEvent, handleHashChange)

  return () =>
    removeEventListener(window, HashChangeEvent, handleHashChange)
}

const updateLocation = (location, queryKey, updateHash, transformPath) => {
  const { state, key } = location
  let path = transformPath(createPath(location), true)

  if (state !== undefined) {
    path = addQueryStringValueToPath(path, queryKey, key)
    saveState(key, state)
  }

  prevLocation = location

  updateHash(path)
}

export const pushLocation = (location, queryKey, transformPath) =>
  updateLocation(location, queryKey, (path) => {
    if (getHashPath() !== path) {
      pushHashPath(path)
    } else {
      warning(false, 'You cannot PUSH the same path using hash history')
    }
  }, transformPath)

export const replaceLocation = (location, queryKey, transformPath) =>
  updateLocation(location, queryKey, (path) => {
    if (getHashPath() !== path)
      replaceHashPath(path)
  }, transformPath)
<|endoftext|>


<commit_before>class PalindromError extends Error {
    constructor(message) {
        super(message);
        this.message = message;
    }
}

class PalindromConnectionError extends PalindromError {
    /**
     *
     * @param {String} message the message that describes the error
     * @param {String} side <Server|Client> the side where the error occured
     * @param {String} url The relevant URL
     * @param {String} connectionType <WebSocket|HTTP>
     */
    constructor(message, side, url = window.location.href, connectionType) {
        if (!side || !['Server', 'Client'].includes(side)) {
            throw new TypeError(
                "Error constructing PalindromConnectionError, `side` parameter is required and can either be 'Server' or 'Client'"
            );
        }
        super(message);
        this.side = side;
        this.message = `${side} error\n\t${message.replace(/\n/g, '\n\t')}`;
        this.url = url;
        this.connectionType = connectionType;
    }
}

module.exports = {
    PalindromError,
    PalindromConnectionError
};
<commit_msg>Remove default value for url parameter in PalindromConnectionError<commit_after>class PalindromError extends Error {
    constructor(message) {
        super(message);
        this.message = message;
    }
}

class PalindromConnectionError extends PalindromError {
    /**
     *
     * @param {String} message the message that describes the error
     * @param {String} side <Server|Client> the side where the error occured
     * @param {String} url The relevant URL
     * @param {String} connectionType <WebSocket|HTTP>
     */
    constructor(message, side, url, connectionType) {
        if (!side || !['Server', 'Client'].includes(side)) {
            throw new TypeError(
                "Error constructing PalindromConnectionError, `side` parameter is required and can either be 'Server' or 'Client'"
            );
        }
        super(message);
        this.side = side;
        this.message = `${side} error\n\t${message.replace(/\n/g, '\n\t')}`;
        this.url = url;
        this.connectionType = connectionType;
    }
}

module.exports = {
    PalindromError,
    PalindromConnectionError
};
<|endoftext|>


<commit_before>var Discord = require('discord.js');

var youtubeStream = require('youtube-audio-stream');
var ytdl = require('ytdl-core');

var shouldDisallowQueue = require('./lib/permission-checks.js');
var VideoFormat = require('./lib/video-format.js');

var Saved = require('./lib/saved.js');
Saved.read();

var client = new Discord.Client();

// Handle discord.js warnings
client.on('warn', (m) => console.log('[warn]', m));

var playQueue = [];
var boundChannel = false;
var currentStream = false;
var currentVideo = false;

var botMention = false;

client.on('ready', () => {
  botMention = `<@${client.internal.user.id}>`;
  console.log(`Bot mention: ${botMention}`);
});

client.on('message', m => {
  if (!botMention) return;

  if (m.content.startsWith(`${botMention} i`)) { // init
    if (boundChannel) return;
    var channelToJoin = spliceArguments(m.content)[1];
    for (var channel of m.channel.server.channels) {
      if (channel instanceof Discord.VoiceChannel) {
        if (!channelToJoin || channel.name === channelToJoin) {
          boundChannel = m.channel;
          client.reply(m, `Binding to text channel <#${boundChannel.id}> and voice channel **${channel.name}** \`(${channel.id})\``);
          client.joinVoiceChannel(channel).catch(error);
          break;
        }
      }
    }
  }

  if (m.content.startsWith(`${botMention} d`)) { // destroy
    if (!boundChannel) return;
    client.reply(m, `Unbinding from <#${boundChannel.id}> and destroying voice connection`);
    playQueue = [];
    currentStream.destroy();
    client.internal.leaveVoiceChannel();
    boundChannel = false;
    return;
  }

  // Only respond to other messages inside the bound channel
  if (!m.channel.equals(boundChannel)) return;

  if (m.content.startsWith(`${botMention} n`)) { // next
    currentStream.destroy();
    playStopped();
  }

  if (m.content.startsWith(`${botMention} y`) // youtube
    || m.content.startsWith(`${botMention} q`) // queue
    || m.content.startsWith(`${botMention} p`)) { // play

    var vid = Saved.possiblyRetrieveVideo(spliceArguments(m.content)[1]);
    if (!vid) {
      client.reply(m, 'You need to specify a video!');
    }

    var requestUrl = 'http://www.youtube.com/watch?v=' + vid;
    ytdl.getInfo(requestUrl, (err, info) => {
      if (err) handleYTError(err);
      else possiblyQueue(info, m.author.id, m);
    });
  }

  if (m.content.startsWith(`${botMention} list s`)) { // list saved
    var formattedList = 'Here are the videos currently saved: \n';
    for (var key in Saved.saved.videos) {
      if (Saved.saved.videos.hasOwnProperty(key)) {
        formattedList += `*${key}*: ${VideoFormat.prettyPrint(Saved.saved.videos[key])}\n`;
      }
    }

    client.reply(m, formattedList);
    return; // so list doesn't get triggered
  }

  if (m.content.startsWith(`${botMention} l`)) { // list
    var formattedList = 'Here are the videos currently in the play queue, from first added to last added: \n';
    formattedList += `Currently playing: ${VideoFormat.prettyPrintWithUser(currentVideo)}\n`;
    playQueue.forEach((video, idx) => {
      formattedList += `${idx + 1}. ${VideoFormat.prettyPrintWithUser(video)}\n`;
    });
    client.reply(m, formattedList);
  }

  if (m.content.startsWith(`${botMention} s`)) { // save
    var argument = spliceArguments(m.content)[1];
    var splitArgs = spliceArguments(argument, 1);
    var requestUrl = 'http://www.youtube.com/watch?v=' + splitArgs[0];
    ytdl.getInfo(requestUrl, (err, info) => {
      if (err) handleYTError(err);
      else saveVideo(info, splitArgs[1], m);
    });
  }

  if (m.content.startsWith(`${botMention} t`)) { // time
    var streamTime = client.internal.voiceConnection.streamTime; // in ms
    var videoTime = currentVideo.length_seconds;
    client.reply(m, `${VideoFormat.prettyTime(streamTime)} / ${VideoFormat.prettyTime(videoTime * 1000)}`);
  }
});

function spliceArguments(message, after) {
  after = after || 2;
  var rest = message.split(' ');
  var removed = rest.splice(0, after);
  return [removed.join(' '), rest.join(' ')];
}

function saveVideo(video, keywords, m) {
  simplified = VideoFormat.simplify(video);
  if (Saved.saved.videos.hasOwnProperty(keywords)) client.reply(m, `Warning: ${simplePrint(Saved.saved.videos[keywords])} is already saved as *${keywords}*! Overwriting.`);
  Saved.saved.videos[keywords] = simplified;
  client.reply(m, `Saved video ${VideoFormat.prettyPrint(video)} as *${keywords}*`);
  Saved.write();
}

function possiblyQueue(video, userId, m) {
  video.userId = userId;
  reason = shouldDisallowQueue(playQueue, video);
  if (reason) {
    client.reply(m, `You can't queue this video right now! Reason: ${reason}`);
  } else {
    playQueue.push(video);
    client.reply(m, `Queued ${VideoFormat.prettyPrint(video)}`);

    // Start playing if not playing yet
    if (!currentVideo) nextInQueue();
  }
}

function handleYTError(err) {
  if (err.toString().indexOf('Code 150') > -1) {
    // Video unavailable in country
    client.sendMessage(boundChannel, 'This video is unavailable in the country the bot is running in! Please try a different video.');
  } else {
    client.sendMessage(boundChannel, 'An error occurred while getting video information! Please try a different video.');
  }

  console.log(err.toString());
}

function playStopped() {
  if (client.internal.voiceConnection) client.internal.voiceConnection.stopPlaying();

  client.sendMessage(boundChannel, `Finished playing ${VideoFormat.simplePrint(currentVideo)}`);
  currentVideo = false;
  nextInQueue();
}

function play(video) {
  currentVideo = video;
  if (client.internal.voiceConnection) {
    var connection = client.internal.voiceConnection;
    currentStream = youtubeStream(video.loaderUrl);
    currentStream.on('end', () => setTimeout(playStopped, 8000)); // 8 second leeway for bad timing
    connection.playRawStream(currentStream).then(intent => {
      client.sendMessage(boundChannel, `Playing ${VideoFormat.prettyPrint(video)}`);
    });
  }
}

function nextInQueue() {
  if (playQueue.length > 0) {
    next = playQueue.shift();
    play(next);
  }
}

function error(argument) {
  console.log(argument.stack);
}

// Email and password over command line
client.login(process.argv[2], process.argv[3]).catch((e) => console.log(e));
<commit_msg>Fix a crash when save is used incorrectly<commit_after>var Discord = require('discord.js');

var youtubeStream = require('youtube-audio-stream');
var ytdl = require('ytdl-core');

var shouldDisallowQueue = require('./lib/permission-checks.js');
var VideoFormat = require('./lib/video-format.js');

var Saved = require('./lib/saved.js');
Saved.read();

var client = new Discord.Client();

// Handle discord.js warnings
client.on('warn', (m) => console.log('[warn]', m));

var playQueue = [];
var boundChannel = false;
var currentStream = false;
var currentVideo = false;

var botMention = false;

client.on('ready', () => {
  botMention = `<@${client.internal.user.id}>`;
  console.log(`Bot mention: ${botMention}`);
});

client.on('message', m => {
  if (!botMention) return;

  if (m.content.startsWith(`${botMention} i`)) { // init
    if (boundChannel) return;
    var channelToJoin = spliceArguments(m.content)[1];
    for (var channel of m.channel.server.channels) {
      if (channel instanceof Discord.VoiceChannel) {
        if (!channelToJoin || channel.name === channelToJoin) {
          boundChannel = m.channel;
          client.reply(m, `Binding to text channel <#${boundChannel.id}> and voice channel **${channel.name}** \`(${channel.id})\``);
          client.joinVoiceChannel(channel).catch(error);
          break;
        }
      }
    }
  }

  if (m.content.startsWith(`${botMention} d`)) { // destroy
    if (!boundChannel) return;
    client.reply(m, `Unbinding from <#${boundChannel.id}> and destroying voice connection`);
    playQueue = [];
    currentStream.destroy();
    client.internal.leaveVoiceChannel();
    boundChannel = false;
    return;
  }

  // Only respond to other messages inside the bound channel
  if (!m.channel.equals(boundChannel)) return;

  if (m.content.startsWith(`${botMention} n`)) { // next
    currentStream.destroy();
    playStopped();
  }

  if (m.content.startsWith(`${botMention} y`) // youtube
    || m.content.startsWith(`${botMention} q`) // queue
    || m.content.startsWith(`${botMention} p`)) { // play

    var vid = Saved.possiblyRetrieveVideo(spliceArguments(m.content)[1]);
    if (!vid) {
      client.reply(m, 'You need to specify a video!');
      return;
    }

    var requestUrl = 'http://www.youtube.com/watch?v=' + vid;
    ytdl.getInfo(requestUrl, (err, info) => {
      if (err) handleYTError(err);
      else possiblyQueue(info, m.author.id, m);
    });
  }

  if (m.content.startsWith(`${botMention} list s`)) { // list saved
    var formattedList = 'Here are the videos currently saved: \n';
    for (var key in Saved.saved.videos) {
      if (Saved.saved.videos.hasOwnProperty(key)) {
        formattedList += `*${key}*: ${VideoFormat.prettyPrint(Saved.saved.videos[key])}\n`;
      }
    }

    client.reply(m, formattedList);
    return; // so list doesn't get triggered
  }

  if (m.content.startsWith(`${botMention} l`)) { // list
    var formattedList = 'Here are the videos currently in the play queue, from first added to last added: \n';
    formattedList += `Currently playing: ${VideoFormat.prettyPrintWithUser(currentVideo)}\n`;
    playQueue.forEach((video, idx) => {
      formattedList += `${idx + 1}. ${VideoFormat.prettyPrintWithUser(video)}\n`;
    });
    client.reply(m, formattedList);
  }

  if (m.content.startsWith(`${botMention} s`)) { // save
    var argument = spliceArguments(m.content)[1];
    if (!argument) {
      client.reply(m, 'You need to specify a video and a keyword!');
      return;
    }

    var splitArgs = spliceArguments(argument, 1);
    var requestUrl = 'http://www.youtube.com/watch?v=' + splitArgs[0];
    ytdl.getInfo(requestUrl, (err, info) => {
      if (err) handleYTError(err);
      else saveVideo(info, splitArgs[1], m);
    });
  }

  if (m.content.startsWith(`${botMention} t`)) { // time
    var streamTime = client.internal.voiceConnection.streamTime; // in ms
    var videoTime = currentVideo.length_seconds;
    client.reply(m, `${VideoFormat.prettyTime(streamTime)} / ${VideoFormat.prettyTime(videoTime * 1000)}`);
  }
});

function spliceArguments(message, after) {
  after = after || 2;
  var rest = message.split(' ');
  var removed = rest.splice(0, after);
  return [removed.join(' '), rest.join(' ')];
}

function saveVideo(video, keywords, m) {
  simplified = VideoFormat.simplify(video);
  if (Saved.saved.videos.hasOwnProperty(keywords)) client.reply(m, `Warning: ${simplePrint(Saved.saved.videos[keywords])} is already saved as *${keywords}*! Overwriting.`);
  Saved.saved.videos[keywords] = simplified;
  client.reply(m, `Saved video ${VideoFormat.prettyPrint(video)} as *${keywords}*`);
  Saved.write();
}

function possiblyQueue(video, userId, m) {
  video.userId = userId;
  reason = shouldDisallowQueue(playQueue, video);
  if (reason) {
    client.reply(m, `You can't queue this video right now! Reason: ${reason}`);
  } else {
    playQueue.push(video);
    client.reply(m, `Queued ${VideoFormat.prettyPrint(video)}`);

    // Start playing if not playing yet
    if (!currentVideo) nextInQueue();
  }
}

function handleYTError(err) {
  if (err.toString().indexOf('Code 150') > -1) {
    // Video unavailable in country
    client.sendMessage(boundChannel, 'This video is unavailable in the country the bot is running in! Please try a different video.');
  } else {
    client.sendMessage(boundChannel, 'An error occurred while getting video information! Please try a different video.');
  }

  console.log(err.toString());
}

function playStopped() {
  if (client.internal.voiceConnection) client.internal.voiceConnection.stopPlaying();

  client.sendMessage(boundChannel, `Finished playing ${VideoFormat.simplePrint(currentVideo)}`);
  currentVideo = false;
  nextInQueue();
}

function play(video) {
  currentVideo = video;
  if (client.internal.voiceConnection) {
    var connection = client.internal.voiceConnection;
    currentStream = youtubeStream(video.loaderUrl);
    currentStream.on('end', () => setTimeout(playStopped, 8000)); // 8 second leeway for bad timing
    connection.playRawStream(currentStream).then(intent => {
      client.sendMessage(boundChannel, `Playing ${VideoFormat.prettyPrint(video)}`);
    });
  }
}

function nextInQueue() {
  if (playQueue.length > 0) {
    next = playQueue.shift();
    play(next);
  }
}

function error(argument) {
  console.log(argument.stack);
}

// Email and password over command line
client.login(process.argv[2], process.argv[3]).catch((e) => console.log(e));
<|endoftext|>


<commit_before>module.exports.start = (input, output) => {
    const fs = require('fs')
    const path = require('path')
    const less = require('less')
    const colors = require('colors');

    const home = process.cwd()

    const stylesDir = path.join(home, input)
    const cssDir = path.join(home, output)

    console.log('Watching /' + input)

    let isCompiling = false

    fs.watch(stylesDir, {
        recursive: true
    },
        (event, file) => {
            if (path.extname(file) != '.less') return
            if (isCompiling) return

            console.log(('Detected change in \'' + stylesDir + '\'').green)

            let start = Date.now()

            isCompiling = true
            setTimeout(function () {
                isCompiling = false
            }, 100);
            const content = fs.readFileSync(path.join(stylesDir, file), 'utf-8')

            console.log(('Attemping to compile ' + file + ' to ' + input).yellow)
            less.render(content).then(output => {
                const cssFilename = path.basename(file, '.less') + '.css'
                fs.writeFileSync(path.join(cssDir, cssFilename), output.css)

                console.log('Succesfully compiled in '.green + ((Date.now() - start) + 'ms').yellow)
            })
        })
}<commit_msg>return error on failure to render<commit_after>module.exports.start = (input, output) => {
    const fs = require('fs')
    const path = require('path')
    const less = require('less')
    const colors = require('colors');

    const home = process.cwd()

    const stylesDir = path.join(home, input)
    const cssDir = path.join(home, output)

    console.log('Watching /' + input)

    let isCompiling = false

    fs.watch(stylesDir, {
        recursive: true
    },
        (event, file) => {
            if (path.extname(file) != '.less') return
            if (isCompiling) return

            console.log(('Detected change in \'' + stylesDir + '\'').green)

            let start = Date.now()

            isCompiling = true
            setTimeout(function () {
                isCompiling = false
            }, 100);
            const content = fs.readFileSync(path.join(stylesDir, file), 'utf-8')

            console.log(('Attemping to compile ' + file + ' to ' + input).yellow)
            less.render(content).then(
                output => {
                    const cssFilename = path.basename(file, '.less') + '.css'
                    fs.writeFileSync(path.join(cssDir, cssFilename), output.css)

                    console.log('Succesfully compiled in '.green + ((Date.now() - start) + 'ms').yellow)
                },
                error => {
                    console.log('Unsuccesful. Failed to render.\n' + error)
                })
        })
}<|endoftext|>