<commit_before>var uuid = require('node-uuid');
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
//var database = require('./database');

server.listen(3000);

// serve the www directory as a website
app.use(express.static('www'));

// rooms (simultaneous game instances)
var rooms = {};

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

  console.log('Client connected!');

  socket.on('join', function(data){
    var room = find_room(data);
    console.log('Client joined room '+room);
    join_room(socket, room);
    socket.emit('join-reply', {
      session_id: room
    });
  });

  socket.on('disconnect', function () {
    console.log('Client disconnected.');
    io.to(socket.room_id).emit('end', {});
    if(typeof socket.room_id !== 'undefined' && rooms[socket.room_id].participants() == 0){
      destroy_room(socket.room_id);
    }
  });

  socket.on('turn', function(data){
    rooms[socket.room_id].messages.turn.push(data);
    if(rooms[socket.room_id].messages.turn.length == rooms[socket.room_id].participants()){
      var td = rooms[socket.room_id].messages.turn;
      rooms[socket.room_id].messages.turn = [];
      io.emit('turn-reply', {data: td});
    }
  });

  socket.on('wait', function(){
    rooms[socket.room_id].messages.wait++;
    if(rooms[socket.room_id].messages.wait == rooms[socket.room_id].participants()){
      rooms[socket.room_id].messages.wait = 0;
      io.emit('wait-reply', {});
    }
  });

  socket.on('sync', function(data){
    var id = data.id;
    if(typeof rooms[socket.room_id].messages.sync[id] == 'undefined'){
      rooms[socket.room_id].messages.sync[id] = {};
      rooms[socket.room_id].messages.sync[id].content = data.content;
      rooms[socket.room_id].messages.sync[id].count = 0;
    }
    rooms[socket.room_id].messages.sync[id].count++;
    if(rooms[socket.room_id].messages.sync[id].count == rooms[socket.room_id].participants()){
      io.emit('sync-reply', {id: id, content: rooms[socket.room_id].messages.sync[id].content});
    }
  });

  socket.on('write-data', function(data){
    console.log('data to write '+JSON.stringify(data));
    //database.write(data);
  });

});

function find_room(data){

  var room_to_join;
  var room_keys = Object.keys(rooms);

  if(room_keys.length == 0){
    var new_room_id = uuid();
    rooms[new_room_id] = create_room(new_room_id, data.experiment, data.participants);
    room_to_join = new_room_id;
  } else {
    // first join rooms that are waiting for players
    for(var i=0; i<room_keys.length; i++){
      if(
        rooms[room_keys[i]].started == false &&
        rooms[room_keys[i]].participants() > 0 &&
        rooms[room_keys[i]].experiment_id == data.experiment &&
        rooms[room_keys[i]].total_participants == data.participants
      ){
        room_to_join = rooms[room_keys[i]].id;
        break;
      }
    }
    // then make new empty room
    if(typeof room_to_join == 'undefined'){
      var new_room_id = uuid();
      rooms[new_room_id] = create_room(new_room_id, data.experiment, data.participants);
      room_to_join = new_room_id;
    }
  }

  return room_to_join;
}

function create_room(id, experiment_id, total_participants){
  return {
    id: id,
    experiment_id: experiment_id,
    total_participants: total_participants,
    started: false,
    participants: function() {
      try {
        return Object.keys(io.nsps['/'].adapter.rooms[this.id].sockets).length;
      } catch (e) {
        //console.log(e);
        return 0;
      } finally {

      }
    },
    messages: {
      turn: [],
      wait: 0,
      sync: {}
    },
    join: function(socket) {
      socket.join(this.id);
      socket.room_id = this.id;
      console.log(this.participants() + ' of ' + total_participants + ' ready in room '+this.id);
      if(this.participants() == total_participants){
        console.log('Starting room '+this.id);
        this.start();
      }
    },
    start: function(){
      this.started = true;
      var clients = io.nsps['/'].adapter.rooms[this.id].sockets;
      var idx = 0;
      for(var c in clients){
        io.sockets.connected[c].player_id = idx;
        idx++;
        io.sockets.connected[c].emit('start', {player_id: io.sockets.connected[c].player_id});
      }
    }
  };
}

function join_room(socket, room_to_join) {
  rooms[room_to_join].join(socket);
}

function destroy_room(id) {
  console.log('Removing room '+id);
  delete rooms[id];
  console.log('Removed a room. Current rooms: '+JSON.stringify(Object.keys(rooms)));
}
<commit_msg>change room code - add update and ready msg<commit_after>var uuid = require('node-uuid');
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
//var database = require('./database');

server.listen(3000);

// serve the www directory as a website
app.use(express.static('www'));

// rooms (simultaneous game instances)
var rooms = {};

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

  console.log('Client connected!');

  socket.on('join', function(data){
    var room = find_room(data);
    console.log('Client joined room '+room);
    join_room(socket, room);
    socket.emit('join-reply', {
      session_id: room
    });
  });

  socket.on('disconnect', function () {
    console.log('Client disconnected.');
    io.to(socket.room_id).emit('end', {});
    if(typeof socket.room_id !== 'undefined' && rooms[socket.room_id].participants() == 0){
      destroy_room(socket.room_id);
    }
  });

  socket.on('turn', function(data){
    rooms[socket.room_id].messages.turn.push(data);
    if(rooms[socket.room_id].messages.turn.length == rooms[socket.room_id].participants()){
      var td = rooms[socket.room_id].messages.turn;
      rooms[socket.room_id].messages.turn = [];
      io.emit('turn-reply', {data: td});
    }
  });

  socket.on('wait', function(){
    rooms[socket.room_id].messages.wait++;
    if(rooms[socket.room_id].messages.wait == rooms[socket.room_id].participants()){
      rooms[socket.room_id].messages.wait = 0;
      io.emit('wait-reply', {});
    }
  });

  socket.on('sync', function(data){
    var id = data.id;
    if(typeof rooms[socket.room_id].messages.sync[id] == 'undefined'){
      rooms[socket.room_id].messages.sync[id] = {};
      rooms[socket.room_id].messages.sync[id].content = data.content;
      rooms[socket.room_id].messages.sync[id].count = 0;
    }
    rooms[socket.room_id].messages.sync[id].count++;
    if(rooms[socket.room_id].messages.sync[id].count == rooms[socket.room_id].participants()){
      io.emit('sync-reply', {id: id, content: rooms[socket.room_id].messages.sync[id].content});
    }
  });

  socket.on('write-data', function(data){
    console.log('data to write '+JSON.stringify(data));
    //database.write(data);
  });

});

function find_room(data){

  var room_to_join;
  var room_keys = Object.keys(rooms);

  if(room_keys.length == 0){
    var new_room_id = uuid();
    rooms[new_room_id] = create_room(new_room_id, data.experiment, data.participants);
    room_to_join = new_room_id;
  } else {
    // first join rooms that are waiting for players
    for(var i=0; i<room_keys.length; i++){
      if(
        rooms[room_keys[i]].started == false &&
        rooms[room_keys[i]].participants() > 0 &&
        rooms[room_keys[i]].experiment_id == data.experiment &&
        rooms[room_keys[i]].total_participants == data.participants
      ){
        room_to_join = rooms[room_keys[i]].id;
        break;
      }
    }
    // then make new empty room
    if(typeof room_to_join == 'undefined'){
      var new_room_id = uuid();
      rooms[new_room_id] = create_room(new_room_id, data.experiment, data.participants);
      room_to_join = new_room_id;
    }
  }

  return room_to_join;
}

function create_room(id, experiment_id, total_participants){

  var room = {};

  room.id = id;
  room.experiment_id = experiment_id;
  room.total_participants = total_participants;
  room.started = false;

  room.messages = {
    turn: [],
    wait: 0,
    sync: {},
    ready: []
  }

  // returns the number of people in the room
  room.participants = function(){
    try {
      return Object.keys(io.nsps['/'].adapter.rooms[this.id].sockets).length;
    } catch (e) {
      //console.log(e);
      return 0;
    } finally {

    }
  };

  // adds client to this room
  room.join =  function(client) {
    client.join(this.id);
    client.room_id = this.id;
    this.update_all();
    console.log(this.participants() + ' of ' + this.total_participants + ' ready in room '+this.id);
    if(this.participants() == this.total_participants){
      console.log('Confirming room '+this.id);
      this.confirm_ready();
    }
  };

  // called if someone disconnects
  room.leave = function() {
    this.update_all();
  }

  room.update_all = function(){
    var n_participants = this.participants();
    io.to(this.id).emit('room-update', {
      participants: n_participants
    });
  }

  room.confirm_ready = function() {
    this.messages.ready = []; // clear the ready message holder
    var clients = io.nsps['/'].adapter.rooms[this.id].sockets;
    var idx;

    // TODO: set a timeout here...

    for(var c in clients){
      io.sockets.connected[c].ready_id = idx;
      idx++;
      io.sockets.connected[c].once('ready-reply', function(message){
        this.messages.ready.push(message.id);
        if(this.messages.ready.length == this.total_participants){
          // TODO: end timeout here
          this.start();
        }
      });
      io.sockets.connected[c].emit('ready', {id: io.sockets.connected[c].ready_id});
    }
  };

  room.start = function(){
    this.started = true;
    var clients = io.nsps['/'].adapter.rooms[this.id].sockets;
    var idx = 0;
    for(var c in clients){
      io.sockets.connected[c].player_id = idx;
      idx++;
      io.sockets.connected[c].emit('start', {player_id: io.sockets.connected[c].player_id});
    }
  }

  return room;
}

function join_room(socket, room_to_join) {
  rooms[room_to_join].join(socket);
}

function destroy_room(id) {
  console.log('Removing room '+id);
  delete rooms[id];
  console.log('Removed a room. Current rooms: '+JSON.stringify(Object.keys(rooms)));
}
<|endoftext|>


<commit_before>// Scroll effects for the page

// Scroll distance at which the user has scrolled into the white section
const threshold1 = window.innerWidth > window.innerHeight
                      ? window.innerHeight * 1.25
                      : window.innerHeight * 0.95;

// Top bar text changes from white to black after scrolling a certain amount
function adjustTopBar(scroll) {
  const topBar = document.getElementsByClassName('top-bar')[0];

  // Text color changes suddenly at threshold
  if (scroll > threshold1) {
    topBar.classList.add('black');
  } else {
    topBar.classList.remove('black');
  }
}

function headlineParallax(scroll) {
  requestAnimationFrame(() => {
    const headline = document.getElementsByClassName('headline')[0];
    headline.style.transform = `translateY(calc( -50% - ${scroll / 50}vh ))`;
  });
}

function adjustAnimationPlayState(scroll) {
  if (window.mistSim) {
    if (scroll > threshold1) {
      window.mistSim.stop();
    } else if (!window.mistSim.running) {
      window.mistSim.start();
    }
  }
}

// Main scroll handler

function scrollHandler() {
  const scroll = window.scrollY;
  adjustTopBar(scroll);
  headlineParallax(scroll);
  adjustAnimationPlayState(scroll);
}

window.addEventListener('scroll', scrollHandler);
document.addEventListener('DOMContentLoaded', scrollHandler);
<commit_msg>Fix portrait headline<commit_after>// Scroll effects for the page

// Scroll distance at which the user has scrolled into the white section
const threshold1 = window.innerWidth > window.innerHeight
                      ? window.innerHeight * 1.25
                      : window.innerHeight * 0.95;

// Top bar text changes from white to black after scrolling a certain amount
function adjustTopBar(scroll) {
  const topBar = document.getElementsByClassName('top-bar')[0];

  // Text color changes suddenly at threshold
  if (scroll > threshold1) {
    topBar.classList.add('black');
  } else {
    topBar.classList.remove('black');
  }
}

function headlineParallax(scroll) {
  requestAnimationFrame(() => {
    const headline = document.getElementsByClassName('headline')[0];
    const isMobile = window.innerWidth < window.innerHeight && window.window.innerWidth < 420;
    headline.style.transform = !isMobile
      ? `translateY(calc( -50% - ${scroll / 50}vh ))`
      : `translate(-50%, calc( -50% - ${scroll / 50}vh))`;
  });
}

function adjustAnimationPlayState(scroll) {
  if (window.mistSim) {
    if (scroll > threshold1) {
      window.mistSim.stop();
    } else if (!window.mistSim.running) {
      window.mistSim.start();
    }
  }
}

// Main scroll handler

function scrollHandler() {
  const scroll = window.scrollY;
  adjustTopBar(scroll);
  headlineParallax(scroll);
  adjustAnimationPlayState(scroll);
}

window.addEventListener('scroll', scrollHandler);
document.addEventListener('DOMContentLoaded', scrollHandler);
<|endoftext|>


<commit_before>const isPromise = val => val && typeof val.then === "function";
const isFn = val => typeof val === "function";
const isUndef = val => typeof val === "undefined";
const truthy = val => !isUndef(val) && val !== null;

class Placeholder {
	constructor(prop) {
		this.prop = prop;
	}
	extractValue(val) {
		if (isUndef(this.prop)) {
			return val;
		}
		return val[this.prop];
	}
}

function findIndex(params) {
	for (let i = params.length - 1; i >= 0; i--) {
		if (params[i] instanceof Placeholder) {
			return i;
		}
	}
	return -1;
}

function ppipe(val, thisVal, err) {
	const pipe = function(fn, ...params) {
		if (!fn) {
			if (truthy(err)) {
				throw err;
			}
			return val;
		}
		const callResultFn = value => {
			let replacedPlaceHolder = false;
			while (true) {
				const plHoldrIdx = findIndex(params);
				if (plHoldrIdx === -1) break;
				replacedPlaceHolder = true;
				const curPlHolder = params[plHoldrIdx];
				params.splice(plHoldrIdx, 1, curPlHolder.extractValue(value));
			}
			if (!replacedPlaceHolder) {
				params.splice(params.length, 0, value);
			}
			return fn.call(thisVal, ...params);
		};
		let res;
		if (isPromise(val)) {
			res = truthy(err) ? Promise.reject(err) : val.then(callResultFn);
		} else {
			try {
				res = truthy(err) ? undefined : callResultFn(val);
			} catch (e) {
				err = e;
			}
		}
		return ppipe(res, undefined, err);
	};
	return new Proxy(pipe, {
		get(target, name) {
			switch (name) {
				case "then":
				case "catch":
					const res = truthy(err) ? Promise.reject(err) : Promise.resolve(val);
					return (...params) => res[name](...params);
				case "val":
					if (truthy(err)) {
						throw err;
					}
					return val;
				case "with":
					return ctx => ppipe(val, ctx, err);
				case "pipe":
					return ppipe(val, thisVal, err);
			}
			if (isPromise(val)) {
				return (...params) =>
					ppipe(val, thisVal, err)(
						x => (isFn(x[name]) ? x[name](...params) : x[name])
					);
			}
			if (
				!isUndef(val[name]) ||
				(truthy(thisVal) && typeof thisVal[name] === "function")
			) {
				const ctx = truthy(thisVal) ? thisVal : val;
				return (...params) =>
					ppipe(val, thisVal, err)(
						(...replacedParams) =>
							!isFn(ctx[name])
								? ctx[name]
								: truthy(thisVal)
									? ctx[name](...replacedParams)
									: ctx[name](...params),
						...params
					);
			}
			if (truthy(pipe[name])) {
				if (isFn(pipe[name])) {
					return pipe[name];
				}
				return (...params) => pipe[name](...params);
			}
			return (...params) => ppipe(val, thisVal, err)(x => x, ...params);
		}
	});
}

ppipe._ = new Proxy(new Placeholder(), {
	get(target, name) {
		if (name === "extractValue") {
			return (...params) => target.extractValue(...params);
		}
		return new Placeholder(name);
	}
});

module.exports = ppipe;
<commit_msg>formatting<commit_after>const isFn = val => typeof val === "function";
const isPromise = val => val && isFn(val.then);
const isUndef = val => typeof val === "undefined";
const truthy = val => !isUndef(val) && val !== null;

class Placeholder {
	constructor(prop) {
		this.prop = prop;
	}
	extractValue(val) {
		if (isUndef(this.prop)) {
			return val;
		}
		return val[this.prop];
	}
}

function findIndex(params) {
	for (let i = params.length - 1; i >= 0; i--) {
		if (params[i] instanceof Placeholder) {
			return i;
		}
	}
	return -1;
}

function ppipe(val, thisVal, err) {
	const pipe = function(fn, ...params) {
		if (!fn) {
			if (truthy(err)) {
				throw err;
			}
			return val;
		}
		const callResultFn = value => {
			let replacedPlaceHolder = false;
			while (true) {
				const plHoldrIdx = findIndex(params);
				if (plHoldrIdx === -1) break;
				replacedPlaceHolder = true;
				const curPlHolder = params[plHoldrIdx];
				params.splice(plHoldrIdx, 1, curPlHolder.extractValue(value));
			}
			if (!replacedPlaceHolder) {
				params.splice(params.length, 0, value);
			}
			return fn.call(thisVal, ...params);
		};
		let res;
		if (isPromise(val)) {
			res = truthy(err) ? Promise.reject(err) : val.then(callResultFn);
		} else {
			try {
				res = truthy(err) ? undefined : callResultFn(val);
			} catch (e) {
				err = e;
			}
		}
		return ppipe(res, undefined, err);
	};
	return new Proxy(pipe, {
		get(target, name) {
			switch (name) {
				case "then":
				case "catch":
					const res = truthy(err) ? Promise.reject(err) : Promise.resolve(val);
					return (...params) => res[name](...params);
				case "val":
					if (truthy(err)) {
						throw err;
					}
					return val;
				case "with":
					return ctx => ppipe(val, ctx, err);
				case "pipe":
					return ppipe(val, thisVal, err);
			}
			if (isPromise(val)) {
				return (...params) =>
					ppipe(val, thisVal, err)(
						x => (isFn(x[name]) ? x[name](...params) : x[name])
					);
			}
			if (!isUndef(val[name]) || (truthy(thisVal) && isFn(thisVal[name]))) {
				const ctx = truthy(thisVal) ? thisVal : val;
				return (...params) =>
					ppipe(val, thisVal, err)(
						(...replacedParams) =>
							!isFn(ctx[name])
								? ctx[name]
								: truthy(thisVal)
									? ctx[name](...replacedParams)
									: ctx[name](...params),
						...params
					);
			}
			if (truthy(pipe[name])) {
				if (isFn(pipe[name])) {
					return pipe[name];
				}
				return (...params) => pipe[name](...params);
			}
			return (...params) => ppipe(val, thisVal, err)(x => x, ...params);
		}
	});
}

ppipe._ = new Proxy(new Placeholder(), {
	get(target, name) {
		if (name === "extractValue") {
			return (...params) => target.extractValue(...params);
		}
		return new Placeholder(name);
	}
});

module.exports = ppipe;
<|endoftext|>


<commit_before>window.onload = init;

function init() {
  var root = new THREERoot({
    createCameraControls: !true,
    antialias: (window.devicePixelRatio === 1),
    fov: 80
  });

  root.renderer.setClearColor(0x000000);
  root.renderer.setPixelRatio(window.devicePixelRatio || 1);
  root.camera.position.set(0, 0, 100);

  var light = new THREE.DirectionalLight();
  light.position.set(0, 0, 1);
  root.scene.add(light);

  var width = 100;
  var height = 60;

  var slide = new Slide(width, height, 'out');
  slide.setImage(new THREE.ImageLoader().load('image.jpg'));
  root.scene.add(slide);

  var slide2 = new Slide(width, height, 'in');
  slide2.setImage(new THREE.ImageLoader().load('image2.jpg'));
  root.scene.add(slide2);

  var tl = new TimelineMax({repeat:-1, repeatDelay:1.0, yoyo: true});

  tl.add(slide.transition(), 0);
  tl.add(slide2.transition(), 0);

  createTweenScrubber(tl);

  //TweenMax.to(slide2, 2, {time:3.0, ease:Power0.easeInOut, repeat:-1});
}

////////////////////
// CLASSES
////////////////////

function Slide(width, height, animationPhase) {
  var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);

  THREE.BAS.Utils.separateFaces(plane);

  var geometry = new SlideGeometry(plane);

  geometry.bufferUVs();

  var aAnimation = geometry.createAttribute('aAnimation', 2);
  var aStartPosition = geometry.createAttribute('aStartPosition', 3);
  var aControl0 = geometry.createAttribute('aControl0', 3);
  var aControl1 = geometry.createAttribute('aControl1', 3);
  var aEndPosition = geometry.createAttribute('aEndPosition', 3);

  var i, i2, i3, i4, v;

  var minDuration = 0.8;
  var maxDuration = 1.0;
  var maxDelayX = 1.0;
  var maxDelayY = 0.25;
  var stretch = 0.1;

  this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;

  var startPosition = new THREE.Vector3();
  var control0 = new THREE.Vector3();
  var control1 = new THREE.Vector3();
  var endPosition = new THREE.Vector3();

  var tempPoint = new THREE.Vector3();

  function getControlPoint0(centroid) {
    var signY = Math.sign(centroid.y);

    tempPoint.x = THREE.Math.randFloat(12, 32);
    tempPoint.y = signY * THREE.Math.randFloat(32, 48);
    tempPoint.z = THREE.Math.randFloatSpread(32);

    return tempPoint;
  }

  function getControlPoint1(centroid) {
    var signY = Math.sign(centroid.y);

    tempPoint.x = THREE.Math.randFloat(32, 48);
    tempPoint.y = -signY * THREE.Math.randFloat(32, 48);
    tempPoint.z = THREE.Math.randFloatSpread(32);

    return tempPoint;
  }

  for (i = 0, i2 = 0, i3 = 0, i4 = 0; i < geometry.faceCount; i++, i2 += 6, i3 += 9, i4 += 12) {
    var face = plane.faces[i];
    var centroid = THREE.BAS.Utils.computeCentroid(plane, face);

    // animation
    var duration = THREE.Math.randFloat(minDuration, maxDuration);
    var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
    var delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0);

    for (v = 0; v < 6; v += 2) {
      aAnimation.array[i2 + v]     = delayX + delayY + (Math.random() * stretch * duration);
      aAnimation.array[i2 + v + 1] = duration;
    }

    // positions

    endPosition.copy(centroid);
    startPosition.copy(centroid);

    if (animationPhase === 'in') {
      control0.copy(centroid).sub(getControlPoint0(centroid));
      control1.copy(centroid).sub(getControlPoint1(centroid));
    }
    else { // out
      control0.copy(centroid).add(getControlPoint0(centroid));
      control1.copy(centroid).add(getControlPoint1(centroid));
    }

    for (v = 0; v < 9; v += 3) {
      aStartPosition.array[i3 + v]     = startPosition.x;
      aStartPosition.array[i3 + v + 1] = startPosition.y;
      aStartPosition.array[i3 + v + 2] = startPosition.z;

      aControl0.array[i3 + v]     = control0.x;
      aControl0.array[i3 + v + 1] = control0.y;
      aControl0.array[i3 + v + 2] = control0.z;

      aControl1.array[i3 + v]     = control1.x;
      aControl1.array[i3 + v + 1] = control1.y;
      aControl1.array[i3 + v + 2] = control1.z;

      aEndPosition.array[i3 + v]     = endPosition.x;
      aEndPosition.array[i3 + v + 1] = endPosition.y;
      aEndPosition.array[i3 + v + 2] = endPosition.z;
    }
  }

  var material = new THREE.BAS.PhongAnimationMaterial(
    {
      shading: THREE.FlatShading,
      side: THREE.DoubleSide,
      uniforms: {
        uTime: {type: 'f', value: 0}
      },
      shaderFunctions: [
        THREE.BAS.ShaderChunk['cubic_bezier'],
        //THREE.BAS.ShaderChunk[(animationPhase === 'in' ? 'ease_out_cubic' : 'ease_in_cubic')],
        THREE.BAS.ShaderChunk['ease_in_out_cubic'],
        THREE.BAS.ShaderChunk['quaternion_rotation']
      ],
      shaderParameters: [
        'uniform float uTime;',
        'attribute vec2 aAnimation;',
        'attribute vec3 aStartPosition;',
        'attribute vec3 aControl0;',
        'attribute vec3 aControl1;',
        'attribute vec3 aEndPosition;',
      ],
      shaderVertexInit: [
        'float tDelay = aAnimation.x;',
        'float tDuration = aAnimation.y;',
        'float tTime = clamp(uTime - tDelay, 0.0, tDuration);',
        'float tProgress = ease(tTime, 0.0, 1.0, tDuration);'
        //'float tProgress = tTime / tDuration;'
      ],
      shaderTransformPosition: [
        (animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;'),
        'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
      ]
    },
    {
      map: new THREE.Texture(),
    }
  );

  THREE.Mesh.call(this, geometry, material);

  this.frustumCulled = false;
}
Slide.prototype = Object.create(THREE.Mesh.prototype);
Slide.prototype.constructor = Slide;
Object.defineProperty(Slide.prototype, 'time', {
  get: function () {
    return this.material.uniforms['uTime'].value;
  },
  set: function (v) {
    this.material.uniforms['uTime'].value = v;
  }
});

Slide.prototype.setImage = function(image) {
  this.material.uniforms.map.value.image = image;
  this.material.uniforms.map.value.needsUpdate = true;
};

Slide.prototype.transition = function() {
  return TweenMax.fromTo(this, 2.0, {time:0.0}, {time:this.totalDuration, ease:Power0.easeInOut});
};

function SlideGeometry(model) {
  THREE.BAS.ModelBufferGeometry.call(this, model);
}
SlideGeometry.prototype = Object.create(THREE.BAS.ModelBufferGeometry.prototype);
SlideGeometry.prototype.constructor = SlideGeometry;
SlideGeometry.prototype.bufferPositions = function () {
  var positionBuffer = this.createAttribute('position', 3).array;

  for (var i = 0; i < this.faceCount; i++) {
    var face = this.modelGeometry.faces[i];
    var centroid = THREE.BAS.Utils.computeCentroid(this.modelGeometry, face);

    var a = this.modelGeometry.vertices[face.a];
    var b = this.modelGeometry.vertices[face.b];
    var c = this.modelGeometry.vertices[face.c];

    positionBuffer[face.a * 3]     = a.x - centroid.x;
    positionBuffer[face.a * 3 + 1] = a.y - centroid.y;
    positionBuffer[face.a * 3 + 2] = a.z - centroid.z;

    positionBuffer[face.b * 3]     = b.x - centroid.x;
    positionBuffer[face.b * 3 + 1] = b.y - centroid.y;
    positionBuffer[face.b * 3 + 2] = b.z - centroid.z;

    positionBuffer[face.c * 3]     = c.x - centroid.x;
    positionBuffer[face.c * 3 + 1] = c.y - centroid.y;
    positionBuffer[face.c * 3 + 2] = c.z - centroid.z;
  }
};

function THREERoot(params) {
  params = utils.extend({
    fov: 60,
    zNear: 10,
    zFar: 100000,

    createCameraControls: true
  }, params);

  this.renderer = new THREE.WebGLRenderer({
    antialias: params.antialias
  });
  this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
  document.getElementById('three-container').appendChild(this.renderer.domElement);

  this.camera = new THREE.PerspectiveCamera(
    params.fov,
    window.innerWidth / window.innerHeight,
    params.zNear,
    params.zfar
  );

  this.scene = new THREE.Scene();

  if (params.createCameraControls) {
    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
  }

  this.resize = this.resize.bind(this);
  this.tick = this.tick.bind(this);

  this.resize();
  this.tick();

  window.addEventListener('resize', this.resize, false);
}
THREERoot.prototype = {
  tick: function () {
    this.update();
    this.render();
    requestAnimationFrame(this.tick);
  },
  update: function () {
    this.controls && this.controls.update();
  },
  render: function () {
    this.renderer.render(this.scene, this.camera);
  },
  resize: function () {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();

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

////////////////////
// UTILS
////////////////////

var utils = {
  extend: function (dst, src) {
    for (var key in src) {
      dst[key] = src[key];
    }

    return dst;
  },
  randSign: function () {
    return Math.random() > 0.5 ? 1 : -1;
  },
  ease: function (ease, t, b, c, d) {
    return b + ease.getRatio(t / d) * c;
  },
  fibSpherePoint: (function () {
    var vec = {x: 0, y: 0, z: 0};
    var G = Math.PI * (3 - Math.sqrt(5));

    return function (i, n, radius) {
      var step = 2.0 / n;
      var r, phi;

      vec.y = i * step - 1 + (step * 0.5);
      r = Math.sqrt(1 - vec.y * vec.y);
      phi = i * G;
      vec.x = Math.cos(phi) * r;
      vec.z = Math.sin(phi) * r;

      radius = radius || 1;

      vec.x *= radius;
      vec.y *= radius;
      vec.z *= radius;

      return vec;
    }
  })(),
  spherePoint: (function () {
    return function (u, v) {
      u === undefined && (u = Math.random());
      v === undefined && (v = Math.random());

      var theta = 2 * Math.PI * u;
      var phi = Math.acos(2 * v - 1);

      var vec = {};
      vec.x = (Math.sin(phi) * Math.cos(theta));
      vec.y = (Math.sin(phi) * Math.sin(theta));
      vec.z = (Math.cos(phi));

      return vec;
    }
  })()
};

function createTweenScrubber(tween, seekSpeed) {
  seekSpeed = seekSpeed || 0.001;

  function stop() {
    TweenMax.to(tween, 1, {timeScale:0});
  }

  function resume() {
    TweenMax.to(tween, 1, {timeScale:1});
  }

  function seek(dx) {
    var progress = tween.progress();
    var p = THREE.Math.clamp((progress + (dx * seekSpeed)), 0, 1);

    tween.progress(p);
  }

  var _cx = 0;

  // desktop
  var mouseDown = false;
  document.body.style.cursor = 'pointer';

  window.addEventListener('mousedown', function(e) {
    mouseDown = true;
    document.body.style.cursor = 'ew-resize';
    _cx = e.clientX;
    stop();
  });
  window.addEventListener('mouseup', function(e) {
    mouseDown = false;
    document.body.style.cursor = 'pointer';
    resume();
  });
  window.addEventListener('mousemove', function(e) {
    if (mouseDown === true) {
      var cx = e.clientX;
      var dx = cx - _cx;
      _cx = cx;

      seek(dx);
    }
  });
  // mobile
  window.addEventListener('touchstart', function(e) {
    _cx = e.touches[0].clientX;
    stop();
    e.preventDefault();
  });
  window.addEventListener('touchend', function(e) {
    resume();
    e.preventDefault();
  });
  window.addEventListener('touchmove', function(e) {
    var cx = e.touches[0].clientX;
    var dx = cx - _cx;
    _cx = cx;

    seek(dx);
    e.preventDefault();
  });
}
<commit_msg>gallery<commit_after>window.onload = init;

function init() {
  var root = new THREERoot({
    createCameraControls: !true,
    antialias: (window.devicePixelRatio === 1),
    fov: 80
  });

  root.renderer.setClearColor(0x000000);
  root.renderer.setPixelRatio(window.devicePixelRatio || 1);
  root.camera.position.set(0, 0, 100);

  var light = new THREE.DirectionalLight();
  light.position.set(0, 0, 1);
  root.scene.add(light);

  var width = 100;
  var height = 60;

  var slide = new Slide(width, height, 'out');
  slide.setImage(new THREE.ImageLoader().load('image.jpg'));
  root.scene.add(slide);

  var slide2 = new Slide(width, height, 'in');
  slide2.setImage(new THREE.ImageLoader().load('image2.jpg'));
  root.scene.add(slide2);

  var tl = new TimelineMax({repeat:-1, repeatDelay:1.0, yoyo: true});

  tl.add(slide.transition(), 0);
  tl.add(slide2.transition(), 0);

  createTweenScrubber(tl);

  //window.addEventListener('keyup', function(e) {
  //  if (e.keyCode === 80) {
  //    tl.paused(!tl.paused());
  //  }
  //});
}

////////////////////
// CLASSES
////////////////////

function Slide(width, height, animationPhase) {
  var plane = new THREE.PlaneGeometry(width, height, width * 2, height * 2);

  THREE.BAS.Utils.separateFaces(plane);

  var geometry = new SlideGeometry(plane);

  geometry.bufferUVs();

  var aAnimation = geometry.createAttribute('aAnimation', 2);
  var aStartPosition = geometry.createAttribute('aStartPosition', 3);
  var aControl0 = geometry.createAttribute('aControl0', 3);
  var aControl1 = geometry.createAttribute('aControl1', 3);
  var aEndPosition = geometry.createAttribute('aEndPosition', 3);

  var i, i2, i3, i4, v;

  var minDuration = 0.8;
  var maxDuration = 1.0;
  var maxDelayX = 1.0;
  var maxDelayY = 0.25;
  var stretch = 0.18;

  this.totalDuration = maxDuration + maxDelayX + maxDelayY + stretch;

  var startPosition = new THREE.Vector3();
  var control0 = new THREE.Vector3();
  var control1 = new THREE.Vector3();
  var endPosition = new THREE.Vector3();

  var tempPoint = new THREE.Vector3();

  function getControlPoint0(centroid) {
    var signY = Math.sign(centroid.y);

    tempPoint.x = THREE.Math.randFloat(0.1, 0.2) * 200;
    tempPoint.y = signY * THREE.Math.randFloat(0.1, 0.9) * 50;
    tempPoint.z = 0;//THREE.Math.randFloatSpread(16);

    return tempPoint;
  }

  function getControlPoint1(centroid) {
    var signY = Math.sign(centroid.y);

    tempPoint.x = THREE.Math.randFloat(0.1, 0.2) * 200;
    tempPoint.y = -signY * THREE.Math.randFloat(0.1, 0.9) * 50;
    tempPoint.z = 0;//THREE.Math.randFloatSpread(16);

    return tempPoint;
  }

  for (i = 0, i2 = 0, i3 = 0, i4 = 0; i < geometry.faceCount; i++, i2 += 6, i3 += 9, i4 += 12) {
    var face = plane.faces[i];
    var centroid = THREE.BAS.Utils.computeCentroid(plane, face);

    // animation
    var duration = THREE.Math.randFloat(minDuration, maxDuration);
    var delayX = THREE.Math.mapLinear(centroid.x, -width * 0.5, width * 0.5, 0.0, maxDelayX);
    var delayY;

    if (animationPhase === 'in') {
      delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, 0.0, maxDelayY)
    }
    else {
      delayY = THREE.Math.mapLinear(Math.abs(centroid.y), 0, height * 0.5, maxDelayY, 0.0)
    }

    for (v = 0; v < 6; v += 2) {
      aAnimation.array[i2 + v]     = delayX + delayY + (Math.random() * stretch * duration);
      aAnimation.array[i2 + v + 1] = duration;
    }

    // positions

    endPosition.copy(centroid);
    startPosition.copy(centroid);

    if (animationPhase === 'in') {
      control0.copy(centroid).sub(getControlPoint0(centroid));
      control1.copy(centroid).sub(getControlPoint1(centroid));
    }
    else { // out
      control0.copy(centroid).add(getControlPoint0(centroid));
      control1.copy(centroid).add(getControlPoint1(centroid));
    }

    for (v = 0; v < 9; v += 3) {
      aStartPosition.array[i3 + v]     = startPosition.x;
      aStartPosition.array[i3 + v + 1] = startPosition.y;
      aStartPosition.array[i3 + v + 2] = startPosition.z;

      aControl0.array[i3 + v]     = control0.x;
      aControl0.array[i3 + v + 1] = control0.y;
      aControl0.array[i3 + v + 2] = control0.z;

      aControl1.array[i3 + v]     = control1.x;
      aControl1.array[i3 + v + 1] = control1.y;
      aControl1.array[i3 + v + 2] = control1.z;

      aEndPosition.array[i3 + v]     = endPosition.x;
      aEndPosition.array[i3 + v + 1] = endPosition.y;
      aEndPosition.array[i3 + v + 2] = endPosition.z;
    }
  }

  var material = new THREE.BAS.PhongAnimationMaterial(
    {
      shading: THREE.FlatShading,
      side: THREE.DoubleSide,
      uniforms: {
        uTime: {type: 'f', value: 0}
      },
      shaderFunctions: [
        THREE.BAS.ShaderChunk['cubic_bezier'],
        //THREE.BAS.ShaderChunk[(animationPhase === 'in' ? 'ease_out_cubic' : 'ease_in_cubic')],
        THREE.BAS.ShaderChunk['ease_in_out_cubic'],
        THREE.BAS.ShaderChunk['quaternion_rotation']
      ],
      shaderParameters: [
        'uniform float uTime;',
        'attribute vec2 aAnimation;',
        'attribute vec3 aStartPosition;',
        'attribute vec3 aControl0;',
        'attribute vec3 aControl1;',
        'attribute vec3 aEndPosition;',
      ],
      shaderVertexInit: [
        'float tDelay = aAnimation.x;',
        'float tDuration = aAnimation.y;',
        'float tTime = clamp(uTime - tDelay, 0.0, tDuration);',
        'float tProgress = ease(tTime, 0.0, 1.0, tDuration);'
        //'float tProgress = tTime / tDuration;'
      ],
      shaderTransformPosition: [
        (animationPhase === 'in' ? 'transformed *= tProgress;' : 'transformed *= 1.0 - tProgress;'),
        'transformed += cubicBezier(aStartPosition, aControl0, aControl1, aEndPosition, tProgress);'
      ]
    },
    {
      map: new THREE.Texture(),
    }
  );

  THREE.Mesh.call(this, geometry, material);

  this.frustumCulled = false;
}
Slide.prototype = Object.create(THREE.Mesh.prototype);
Slide.prototype.constructor = Slide;
Object.defineProperty(Slide.prototype, 'time', {
  get: function () {
    return this.material.uniforms['uTime'].value;
  },
  set: function (v) {
    this.material.uniforms['uTime'].value = v;
  }
});

Slide.prototype.setImage = function(image) {
  this.material.uniforms.map.value.image = image;
  this.material.uniforms.map.value.needsUpdate = true;
};

Slide.prototype.transition = function() {
  return TweenMax.fromTo(this, 2.0, {time:0.0}, {time:this.totalDuration, ease:Power0.easeInOut});
};

function SlideGeometry(model) {
  THREE.BAS.ModelBufferGeometry.call(this, model);
}
SlideGeometry.prototype = Object.create(THREE.BAS.ModelBufferGeometry.prototype);
SlideGeometry.prototype.constructor = SlideGeometry;
SlideGeometry.prototype.bufferPositions = function () {
  var positionBuffer = this.createAttribute('position', 3).array;

  for (var i = 0; i < this.faceCount; i++) {
    var face = this.modelGeometry.faces[i];
    var centroid = THREE.BAS.Utils.computeCentroid(this.modelGeometry, face);

    var a = this.modelGeometry.vertices[face.a];
    var b = this.modelGeometry.vertices[face.b];
    var c = this.modelGeometry.vertices[face.c];

    positionBuffer[face.a * 3]     = a.x - centroid.x;
    positionBuffer[face.a * 3 + 1] = a.y - centroid.y;
    positionBuffer[face.a * 3 + 2] = a.z - centroid.z;

    positionBuffer[face.b * 3]     = b.x - centroid.x;
    positionBuffer[face.b * 3 + 1] = b.y - centroid.y;
    positionBuffer[face.b * 3 + 2] = b.z - centroid.z;

    positionBuffer[face.c * 3]     = c.x - centroid.x;
    positionBuffer[face.c * 3 + 1] = c.y - centroid.y;
    positionBuffer[face.c * 3 + 2] = c.z - centroid.z;
  }
};

function THREERoot(params) {
  params = utils.extend({
    fov: 60,
    zNear: 10,
    zFar: 100000,

    createCameraControls: true
  }, params);

  this.renderer = new THREE.WebGLRenderer({
    antialias: params.antialias
  });
  this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio || 1));
  document.getElementById('three-container').appendChild(this.renderer.domElement);

  this.camera = new THREE.PerspectiveCamera(
    params.fov,
    window.innerWidth / window.innerHeight,
    params.zNear,
    params.zfar
  );

  this.scene = new THREE.Scene();

  if (params.createCameraControls) {
    this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
  }

  this.resize = this.resize.bind(this);
  this.tick = this.tick.bind(this);

  this.resize();
  this.tick();

  window.addEventListener('resize', this.resize, false);
}
THREERoot.prototype = {
  tick: function () {
    this.update();
    this.render();
    requestAnimationFrame(this.tick);
  },
  update: function () {
    this.controls && this.controls.update();
  },
  render: function () {
    this.renderer.render(this.scene, this.camera);
  },
  resize: function () {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();

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

////////////////////
// UTILS
////////////////////

var utils = {
  extend: function (dst, src) {
    for (var key in src) {
      dst[key] = src[key];
    }

    return dst;
  },
  randSign: function () {
    return Math.random() > 0.5 ? 1 : -1;
  },
  ease: function (ease, t, b, c, d) {
    return b + ease.getRatio(t / d) * c;
  },
  fibSpherePoint: (function () {
    var vec = {x: 0, y: 0, z: 0};
    var G = Math.PI * (3 - Math.sqrt(5));

    return function (i, n, radius) {
      var step = 2.0 / n;
      var r, phi;

      vec.y = i * step - 1 + (step * 0.5);
      r = Math.sqrt(1 - vec.y * vec.y);
      phi = i * G;
      vec.x = Math.cos(phi) * r;
      vec.z = Math.sin(phi) * r;

      radius = radius || 1;

      vec.x *= radius;
      vec.y *= radius;
      vec.z *= radius;

      return vec;
    }
  })(),
  spherePoint: (function () {
    return function (u, v) {
      u === undefined && (u = Math.random());
      v === undefined && (v = Math.random());

      var theta = 2 * Math.PI * u;
      var phi = Math.acos(2 * v - 1);

      var vec = {};
      vec.x = (Math.sin(phi) * Math.cos(theta));
      vec.y = (Math.sin(phi) * Math.sin(theta));
      vec.z = (Math.cos(phi));

      return vec;
    }
  })()
};

function createTweenScrubber(tween, seekSpeed) {
  seekSpeed = seekSpeed || 0.001;

  function stop() {
    TweenMax.to(tween, 1, {timeScale:0});
  }

  function resume() {
    TweenMax.to(tween, 1, {timeScale:1});
  }

  function seek(dx) {
    var progress = tween.progress();
    var p = THREE.Math.clamp((progress + (dx * seekSpeed)), 0, 1);

    tween.progress(p);
  }

  var _cx = 0;

  // desktop
  var mouseDown = false;
  document.body.style.cursor = 'pointer';

  window.addEventListener('mousedown', function(e) {
    mouseDown = true;
    document.body.style.cursor = 'ew-resize';
    _cx = e.clientX;
    stop();
  });
  window.addEventListener('mouseup', function(e) {
    mouseDown = false;
    document.body.style.cursor = 'pointer';
    resume();
  });
  window.addEventListener('mousemove', function(e) {
    if (mouseDown === true) {
      var cx = e.clientX;
      var dx = cx - _cx;
      _cx = cx;

      seek(dx);
    }
  });
  // mobile
  window.addEventListener('touchstart', function(e) {
    _cx = e.touches[0].clientX;
    stop();
    e.preventDefault();
  });
  window.addEventListener('touchend', function(e) {
    resume();
    e.preventDefault();
  });
  window.addEventListener('touchmove', function(e) {
    var cx = e.touches[0].clientX;
    var dx = cx - _cx;
    _cx = cx;

    seek(dx);
    e.preventDefault();
  });
}
<|endoftext|>


<commit_before>'use strict';
const fs = require('fs');
const path = require('path');
const EventEmitter = require('events').EventEmitter;
const client = require('socket.io-client');
const { SERVER_MODE_TYPES } = require('../common/constants');
const { version: appVersion } = require('../../../package.json');
const rendererConsole = require('./utils/rendererConsole');

/**
 * 하드웨어 <-> 엔트리 워크스페이스 통신간 사용되는 클래스.
 * http[s] 서버와 socketIO 서버를 오픈한다.
 * 클라우드 모드에서 서버가 EADDRINUSE 로 실패한 경우, 클라이언트 모드로 동작한다.
 * 클라이언트 모드에서는 서버 PC 를 호스트로 엔트리와 통신한다.
 *
 * EventEmitter 는 외부로 아래와 같은 이벤트를 발생시킨다
 * - connection : 웹소켓 서버가 엔트리와 연결된 경우
 * - close : 소켓서버가 닫힘
 * - closed : 소켓, httpServer 전체가 닫힘
 * - data : 서버에서 받은 데이터. 인자는 data, type 를 발생시킨다.
 */
class Server extends EventEmitter {
    constructor(router) {
        super();
        this.router = router;
        this.packet = new Buffer([0x01, 0x00, 0x00, 0x00]);
        this.connections = [];
        this.connectionSet = {};
        this.roomCnt = 0;
        this.childServerList = {};
        this.clientTargetList = {};
        this.runningMode = SERVER_MODE_TYPES.parent;
        this.currentServerMode = SERVER_MODE_TYPES.single;
        this.masterRoomIds = [];
        this.clientRoomId = '';
        this.socketClient = undefined; // 클라이언트인 경우 세팅됨
        this.socketServer = undefined; // 호스트인 경우 세팅됨
    }

    addRoomIdsOnSecondInstance(roomId) {
        if (this.runningMode === SERVER_MODE_TYPES.parent) {
            if (this.masterRoomIds.indexOf(roomId) === -1) {
                this.masterRoomIds.push(roomId);
            }
        } else {
            if (roomId) {
                this.clientRoomId = roomId;
                this.socketClient && this.socketClient.emit('matchTarget', { roomId });
                if (this.masterRoomIds.indexOf(roomId) === -1) {
                    this.masterRoomIds.push(roomId);
                }
            }
        }
    }

    /**
     * 연결된 클라우드 PC 여부에 따라
     * single, multi 모드 전환한다.
     * 렌더러에 이미지를 표기하기 위해 사용된다.
     * currentServerMode 는 여기서 세팅만 되며,
     * 최초 구동시 메인 프로세스보다 느리게 세팅되는 렌더러 쪽에서 현재 서버모드를
     * 체크할때 가져간다.
     * @param mode
     */
    toggleServerMode(mode) {
        this.currentServerMode = mode;
        this.router.notifyServerMode(mode);
    }

    open() {
        const PORT = 23518;
        const { httpServer, address } = this._getHttpServer(PORT);

        /*
         * 에러가 발생하는 경우는 EADDRINUSE 로 상정한다.
         * 동일 IP + 클라우드 PC 환경이라고 가정하고,
         * 클라우드 내 최초 실행된 PC 가 서버, 그 뒤에 실행된 PC 에서는
         * EADDRINUSE 가 발생.
         * 그런 경우 직접 로컬호스트발 소켓 클라이언트를 연다.
         */
        httpServer.on('error', (e) => {
            this.runningMode = SERVER_MODE_TYPES.child;
            this.toggleServerMode(SERVER_MODE_TYPES.multi);

            const socket = this._createSocketClient(address);
            this.connections.push(socket);
            this.socketClient = socket;
        });

        /*
         * 서버가 정상오픈된 경우. 서버로 동작한다.
         * 일반 모드인 경우는 상관없다.
         * 클라우드 모드인 경우, 클라이언트들의 데이터까지 처리하는 부하문제가 있다.
         * 정상 오픈이 된 경우 socketIO 서버를 오픈한다.
         */
        httpServer.on('listening', (e) => {
            const mRoomIds = this.router.roomIds;
            if (mRoomIds.length > 0) {
                mRoomIds.forEach((mRoomId) => {
                    if (this.masterRoomIds.indexOf(mRoomId) === -1 && mRoomId) {
                        this.masterRoomIds.push(mRoomId);
                    }
                });
            }
            this.runningMode = SERVER_MODE_TYPES.parent;
            this.httpServer = httpServer;

            this.socketServer = this._createSocketServer(httpServer);
            this.toggleServerMode(SERVER_MODE_TYPES.single);
        });

        httpServer.listen(PORT);
    };

    /**
     * httpServer 오픈에 실패하여 (클라우드 등) 직접 socketIO 클라이언트를 만든다.
     * @param address
     * @return {SocketIOClientStatic.Socket}
     * @private
     */
    _createSocketClient(address) {
        const socket = client(address, {
            query: { childServer: true },
            reconnectionAttempts: 3,
        });

        socket.on('connect', () => {
            const roomIds = this.router.roomIds;
            if (roomIds.length > 0) {
                roomIds.forEach((roomId) => {
                    if (roomId) {
                        if (this.masterRoomIds.indexOf(roomId) === -1) {
                            this.masterRoomIds.push(roomId);
                        }
                        socket.emit('matchTarget', { roomId });
                    }
                });
            }
        });

        socket.on('message', (message) => {
            this.router.handleServerData(message);
        });

        socket.on('mode', (data) => {
            socket.mode = data;
        });

        socket.on('reconnect_failed', () => {
            socket.close();
            this.toggleServerMode(SERVER_MODE_TYPES.single);
            this.socketClient = null;
            this.open();
        });

        socket.on('disconnect', () => {
            socket.close();
            this.socketClient = null;
            this.open();
        });

        return socket;
    }

    /**
     * httpServer 오픈에 성공한 뒤, SocketIO 서버를 오픈한다.
     *
     * @param httpServer{Server}
     * @return {SocketIO.Server}
     * @private
     */
    _createSocketServer(httpServer) {
        const server = require('socket.io')(httpServer, {
            pingInterval: 1000,
        });
        server.set('transports', [
            'websocket',
            'flashsocket',
            'htmlfile',
            'xhr-polling',
            'jsonp-polling',
            'polling',
        ]);

        server.on('connection', (socket) => {
            const connection = socket;
            this.connectionSet[connection.id] = connection;
            this.connections.push(connection);
            this.emit('connection');

            rendererConsole.info('Entry connected.');

            const roomId = connection.handshake.query.roomId;
            if (connection.handshake.query.childServer === 'true') {
                this.childServerList[connection.id] = true;
            } else {
                connection.join(roomId);
                connection.roomId = roomId;
            }

            const childServerListCnt = Object.keys(this.childServerList).length;
            if (childServerListCnt > 0) {
                server.emit('mode', SERVER_MODE_TYPES.multi);
                this.toggleServerMode(SERVER_MODE_TYPES.multi);
            } else {
                server.emit('mode', SERVER_MODE_TYPES.single);
                this.toggleServerMode(SERVER_MODE_TYPES.single);
            }

            connection.on('matchTarget', (data) => {
                if (
                    connection.handshake.query.childServer === 'true' &&
                    data.roomId
                ) {
                    if (!connection.roomIds) {
                        connection.roomIds = [];
                    }

                    if (connection.roomIds.indexOf(data.roomId) === -1) {
                        connection.roomIds.push(data.roomId);
                    }

                    this.clientTargetList[data.roomId] = connection.id;
                    server.to(data.roomId).emit('matched', connection.id);
                }
            });

            connection.on('disconnect', (socket) => {
                if (connection.handshake && connection.handshake.query.childServer === 'true') {
                    if (connection.roomIds && connection.roomIds.length > 0) {
                        connection.roomIds.forEach((roomId) => {
                            server.to(roomId).emit('matching');
                        });
                    }
                    delete this.connectionSet[connection.id];
                    delete this.childServerList[connection.id];

                    const childServerListCnt = Object.keys(this.childServerList).length;
                    if (childServerListCnt <= 0) {
                        server.emit('mode', SERVER_MODE_TYPES.single);
                        this.toggleServerMode(SERVER_MODE_TYPES.single);
                    }
                } else {
                    delete this.connectionSet[connection.id];
                }
            });

            connection.on('message', (message, ack) => {
                if (
                    message.mode === SERVER_MODE_TYPES.single ||
                    this.masterRoomIds.indexOf(connection.roomId) >= 0
                ) {
                    this.router.handleServerData(message);
                } else {
                    if (connection.handshake.query.childServer === 'true') {
                        if (
                            connection.roomIds &&
                            connection.roomIds.length > 0
                        ) {
                            connection.roomIds.forEach((roomId) => {
                                server.to(roomId).emit('message', message);
                            });
                        }
                    } else if (this.clientTargetList[connection.roomId]) {
                        server.to(this.clientTargetList[connection.roomId]).emit('message', message);
                    }
                }
                if (ack) {
                    const { key = true } = message;
                    ack(key);
                }
            });

            connection.on('close', (reasonCode, description) => {
                rendererConsole.warn('Entry disconnected.');
                this.emit('close');
                this.closeSingleConnection(this);
            });
            this.setState(this.state);
        });
        return server;
    }

    /**
     * SSL 인증서가 프로젝트에 포함되어있는 경우는 https,
     * 그렇지 않은 경우는 http 서버를 오픈한다.
     *
     * @param port{number} 오픈할 포트번호
     * @return {{address: string, httpServer: Server}}
     * @private
     */
    _getHttpServer(port) {
        let httpServer;
        let address;

        const rootDir = path.resolve(__dirname, '..', '..');
        if (fs.existsSync(path.resolve(rootDir, 'ssl', 'cert.pem'))) {
            httpServer = require('https').createServer(
                {
                    key: fs.readFileSync(path.resolve(rootDir, 'ssl', 'hardware.key')),
                    cert: fs.readFileSync(path.resolve(rootDir, 'ssl', 'cert.pem')),
                    ca: [
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'ChainCA1.crt')),
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'ChainCA2.crt')),
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'RootCA.crt')),
                    ],
                },
                (req, res) => {
                    res.writeHead(200);
                    res.end();
                }
            );
            address = `https://hardware.playentry.org:${port}`;
        } else {
            httpServer = require('http').createServer((request, response) => {
                response.writeHead(200);
                response.end();
            });
            address = `http://127.0.0.1:${port}`;
        }

        return {
            httpServer,
            address,
        };
    }

    closeSingleConnection(connection) {
        const connections = this.connections;
        const index = connections.indexOf(connection);
        if (index > -1) {
            this.connections.slice(index, 1);
            connection.close();
        }
    };

    send(data) {
        const childServerListCnt = Object.keys(this.childServerList).length;
        const payload = { data, version: appVersion };

        if (
            (this.runningMode === SERVER_MODE_TYPES.parent && childServerListCnt === 0) ||
            this.runningMode === SERVER_MODE_TYPES.child
        ) {
            if (this.connections.length !== 0) {
                this.connections.map((connection) => {
                    connection.emit('message', payload);
                });
            }
        } else if (this.masterRoomIds.length > 0) {
            this.masterRoomIds.forEach((masterRoomId) => {
                this.socketServer.to(masterRoomId).emit('message', payload);
            });
        }
    };

    setState(state) {
        this.state = state;
        if (this.connections.length) {
            const packet = this.packet;
            if (state === 'connecting') {
                packet[3] = 0x01;
                this.send(packet);
            } else if (state === 'connected') {
                packet[3] = 0x02;
                this.send(packet);
            } else if (state === 'lost') {
                packet[3] = 0x03;
                this.send(packet);
            } else if (state === 'disconnected') {
                packet[3] = 0x04;
                this.send(packet);
            }
        }
    };

    disconnectHardware() {
        this.send('disconnectHardware');
    };

    close() {
        if (this.socketServer) {
            this.socketServer.close();
            this.socketServer = undefined;
        }
        if (this.httpServer) {
            this.httpServer.close();
            this.httpServer = undefined;
        }
        this.connections = [];
        this.emit('closed');
    };
}

module.exports = Server;
<commit_msg>[security] http listen host 를 127.0.0.1 로 바인딩<commit_after>'use strict';
const fs = require('fs');
const path = require('path');
const EventEmitter = require('events').EventEmitter;
const client = require('socket.io-client');
const { SERVER_MODE_TYPES } = require('../common/constants');
const { version: appVersion } = require('../../../package.json');
const rendererConsole = require('./utils/rendererConsole');

/**
 * 하드웨어 <-> 엔트리 워크스페이스 통신간 사용되는 클래스.
 * http[s] 서버와 socketIO 서버를 오픈한다.
 * 클라우드 모드에서 서버가 EADDRINUSE 로 실패한 경우, 클라이언트 모드로 동작한다.
 * 클라이언트 모드에서는 서버 PC 를 호스트로 엔트리와 통신한다.
 *
 * EventEmitter 는 외부로 아래와 같은 이벤트를 발생시킨다
 * - connection : 웹소켓 서버가 엔트리와 연결된 경우
 * - close : 소켓서버가 닫힘
 * - closed : 소켓, httpServer 전체가 닫힘
 * - data : 서버에서 받은 데이터. 인자는 data, type 를 발생시킨다.
 */
class Server extends EventEmitter {
    constructor(router) {
        super();
        this.router = router;
        this.packet = new Buffer([0x01, 0x00, 0x00, 0x00]);
        this.connections = [];
        this.connectionSet = {};
        this.roomCnt = 0;
        this.childServerList = {};
        this.clientTargetList = {};
        this.runningMode = SERVER_MODE_TYPES.parent;
        this.currentServerMode = SERVER_MODE_TYPES.single;
        this.masterRoomIds = [];
        this.clientRoomId = '';
        this.socketClient = undefined; // 클라이언트인 경우 세팅됨
        this.socketServer = undefined; // 호스트인 경우 세팅됨
    }

    addRoomIdsOnSecondInstance(roomId) {
        if (this.runningMode === SERVER_MODE_TYPES.parent) {
            if (this.masterRoomIds.indexOf(roomId) === -1) {
                this.masterRoomIds.push(roomId);
            }
        } else {
            if (roomId) {
                this.clientRoomId = roomId;
                this.socketClient && this.socketClient.emit('matchTarget', { roomId });
                if (this.masterRoomIds.indexOf(roomId) === -1) {
                    this.masterRoomIds.push(roomId);
                }
            }
        }
    }

    /**
     * 연결된 클라우드 PC 여부에 따라
     * single, multi 모드 전환한다.
     * 렌더러에 이미지를 표기하기 위해 사용된다.
     * currentServerMode 는 여기서 세팅만 되며,
     * 최초 구동시 메인 프로세스보다 느리게 세팅되는 렌더러 쪽에서 현재 서버모드를
     * 체크할때 가져간다.
     * @param mode
     */
    toggleServerMode(mode) {
        this.currentServerMode = mode;
        this.router.notifyServerMode(mode);
    }

    open() {
        const PORT = 23518;
        const { httpServer, address } = this._getHttpServer(PORT);

        /*
         * 에러가 발생하는 경우는 EADDRINUSE 로 상정한다.
         * 동일 IP + 클라우드 PC 환경이라고 가정하고,
         * 클라우드 내 최초 실행된 PC 가 서버, 그 뒤에 실행된 PC 에서는
         * EADDRINUSE 가 발생.
         * 그런 경우 직접 로컬호스트발 소켓 클라이언트를 연다.
         */
        httpServer.on('error', (e) => {
            this.runningMode = SERVER_MODE_TYPES.child;
            this.toggleServerMode(SERVER_MODE_TYPES.multi);

            const socket = this._createSocketClient(address);
            this.connections.push(socket);
            this.socketClient = socket;
        });

        /*
         * 서버가 정상오픈된 경우. 서버로 동작한다.
         * 일반 모드인 경우는 상관없다.
         * 클라우드 모드인 경우, 클라이언트들의 데이터까지 처리하는 부하문제가 있다.
         * 정상 오픈이 된 경우 socketIO 서버를 오픈한다.
         */
        httpServer.on('listening', (e) => {
            const mRoomIds = this.router.roomIds;
            if (mRoomIds.length > 0) {
                mRoomIds.forEach((mRoomId) => {
                    if (this.masterRoomIds.indexOf(mRoomId) === -1 && mRoomId) {
                        this.masterRoomIds.push(mRoomId);
                    }
                });
            }
            this.runningMode = SERVER_MODE_TYPES.parent;
            this.httpServer = httpServer;

            this.socketServer = this._createSocketServer(httpServer);
            this.toggleServerMode(SERVER_MODE_TYPES.single);
        });

        httpServer.listen(PORT, '127.0.0.1');
    };

    /**
     * httpServer 오픈에 실패하여 (클라우드 등) 직접 socketIO 클라이언트를 만든다.
     * @param address
     * @return {SocketIOClientStatic.Socket}
     * @private
     */
    _createSocketClient(address) {
        const socket = client(address, {
            query: { childServer: true },
            reconnectionAttempts: 3,
        });

        socket.on('connect', () => {
            const roomIds = this.router.roomIds;
            if (roomIds.length > 0) {
                roomIds.forEach((roomId) => {
                    if (roomId) {
                        if (this.masterRoomIds.indexOf(roomId) === -1) {
                            this.masterRoomIds.push(roomId);
                        }
                        socket.emit('matchTarget', { roomId });
                    }
                });
            }
        });

        socket.on('message', (message) => {
            this.router.handleServerData(message);
        });

        socket.on('mode', (data) => {
            socket.mode = data;
        });

        socket.on('reconnect_failed', () => {
            socket.close();
            this.toggleServerMode(SERVER_MODE_TYPES.single);
            this.socketClient = null;
            this.open();
        });

        socket.on('disconnect', () => {
            socket.close();
            this.socketClient = null;
            this.open();
        });

        return socket;
    }

    /**
     * httpServer 오픈에 성공한 뒤, SocketIO 서버를 오픈한다.
     *
     * @param httpServer{Server}
     * @return {SocketIO.Server}
     * @private
     */
    _createSocketServer(httpServer) {
        const server = require('socket.io')(httpServer, {
            pingInterval: 1000,
        });
        server.set('transports', [
            'websocket',
            'flashsocket',
            'htmlfile',
            'xhr-polling',
            'jsonp-polling',
            'polling',
        ]);

        server.on('connection', (socket) => {
            const connection = socket;
            this.connectionSet[connection.id] = connection;
            this.connections.push(connection);
            this.emit('connection');

            rendererConsole.info('Entry connected.');

            const roomId = connection.handshake.query.roomId;
            if (connection.handshake.query.childServer === 'true') {
                this.childServerList[connection.id] = true;
            } else {
                connection.join(roomId);
                connection.roomId = roomId;
            }

            const childServerListCnt = Object.keys(this.childServerList).length;
            if (childServerListCnt > 0) {
                server.emit('mode', SERVER_MODE_TYPES.multi);
                this.toggleServerMode(SERVER_MODE_TYPES.multi);
            } else {
                server.emit('mode', SERVER_MODE_TYPES.single);
                this.toggleServerMode(SERVER_MODE_TYPES.single);
            }

            connection.on('matchTarget', (data) => {
                if (
                    connection.handshake.query.childServer === 'true' &&
                    data.roomId
                ) {
                    if (!connection.roomIds) {
                        connection.roomIds = [];
                    }

                    if (connection.roomIds.indexOf(data.roomId) === -1) {
                        connection.roomIds.push(data.roomId);
                    }

                    this.clientTargetList[data.roomId] = connection.id;
                    server.to(data.roomId).emit('matched', connection.id);
                }
            });

            connection.on('disconnect', (socket) => {
                if (connection.handshake && connection.handshake.query.childServer === 'true') {
                    if (connection.roomIds && connection.roomIds.length > 0) {
                        connection.roomIds.forEach((roomId) => {
                            server.to(roomId).emit('matching');
                        });
                    }
                    delete this.connectionSet[connection.id];
                    delete this.childServerList[connection.id];

                    const childServerListCnt = Object.keys(this.childServerList).length;
                    if (childServerListCnt <= 0) {
                        server.emit('mode', SERVER_MODE_TYPES.single);
                        this.toggleServerMode(SERVER_MODE_TYPES.single);
                    }
                } else {
                    delete this.connectionSet[connection.id];
                }
            });

            connection.on('message', (message, ack) => {
                if (
                    message.mode === SERVER_MODE_TYPES.single ||
                    this.masterRoomIds.indexOf(connection.roomId) >= 0
                ) {
                    this.router.handleServerData(message);
                } else {
                    if (connection.handshake.query.childServer === 'true') {
                        if (
                            connection.roomIds &&
                            connection.roomIds.length > 0
                        ) {
                            connection.roomIds.forEach((roomId) => {
                                server.to(roomId).emit('message', message);
                            });
                        }
                    } else if (this.clientTargetList[connection.roomId]) {
                        server.to(this.clientTargetList[connection.roomId]).emit('message', message);
                    }
                }
                if (ack) {
                    const { key = true } = message;
                    ack(key);
                }
            });

            connection.on('close', (reasonCode, description) => {
                rendererConsole.warn('Entry disconnected.');
                this.emit('close');
                this.closeSingleConnection(this);
            });
            this.setState(this.state);
        });
        return server;
    }

    /**
     * SSL 인증서가 프로젝트에 포함되어있는 경우는 https,
     * 그렇지 않은 경우는 http 서버를 오픈한다.
     *
     * @param port{number} 오픈할 포트번호
     * @return {{address: string, httpServer: Server}}
     * @private
     */
    _getHttpServer(port) {
        let httpServer;
        let address;

        const rootDir = path.resolve(__dirname, '..', '..');
        if (fs.existsSync(path.resolve(rootDir, 'ssl', 'cert.pem'))) {
            httpServer = require('https').createServer(
                {
                    key: fs.readFileSync(path.resolve(rootDir, 'ssl', 'hardware.key')),
                    cert: fs.readFileSync(path.resolve(rootDir, 'ssl', 'cert.pem')),
                    ca: [
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'ChainCA1.crt')),
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'ChainCA2.crt')),
                        fs.readFileSync(path.resolve(rootDir, 'ssl', 'RootCA.crt')),
                    ],
                },
                (req, res) => {
                    res.writeHead(200);
                    res.end();
                }
            );
            address = `https://hardware.playentry.org:${port}`;
        } else {
            httpServer = require('http').createServer((request, response) => {
                response.writeHead(200);
                response.end();
            });
            address = `http://127.0.0.1:${port}`;
        }

        return {
            httpServer,
            address,
        };
    }

    closeSingleConnection(connection) {
        const connections = this.connections;
        const index = connections.indexOf(connection);
        if (index > -1) {
            this.connections.slice(index, 1);
            connection.close();
        }
    };

    send(data) {
        const childServerListCnt = Object.keys(this.childServerList).length;
        const payload = { data, version: appVersion };

        if (
            (this.runningMode === SERVER_MODE_TYPES.parent && childServerListCnt === 0) ||
            this.runningMode === SERVER_MODE_TYPES.child
        ) {
            if (this.connections.length !== 0) {
                this.connections.map((connection) => {
                    connection.emit('message', payload);
                });
            }
        } else if (this.masterRoomIds.length > 0) {
            this.masterRoomIds.forEach((masterRoomId) => {
                this.socketServer.to(masterRoomId).emit('message', payload);
            });
        }
    };

    setState(state) {
        this.state = state;
        if (this.connections.length) {
            const packet = this.packet;
            if (state === 'connecting') {
                packet[3] = 0x01;
                this.send(packet);
            } else if (state === 'connected') {
                packet[3] = 0x02;
                this.send(packet);
            } else if (state === 'lost') {
                packet[3] = 0x03;
                this.send(packet);
            } else if (state === 'disconnected') {
                packet[3] = 0x04;
                this.send(packet);
            }
        }
    };

    disconnectHardware() {
        this.send('disconnectHardware');
    };

    close() {
        if (this.socketServer) {
            this.socketServer.close();
            this.socketServer = undefined;
        }
        if (this.httpServer) {
            this.httpServer.close();
            this.httpServer = undefined;
        }
        this.connections = [];
        this.emit('closed');
    };
}

module.exports = Server;
<|endoftext|>


<commit_before>var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

var apiUrl = 'http://127.0.0.1:3001/';
var webPort = 3002;

process.argv.slice( 2 ).forEach( function( value ) {
    console.log( value );
    var split = value.split('=');
    if( split[ 0 ].toLowerCase() === 'apiurl' ) apiUrl = split[ 1 ];
} );

app.locals.apiUrl = apiUrl;

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

/// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

/// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.listen(webPort);
<commit_msg>restored default apiUrl<commit_after>var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

var apiUrl = 'https://api.transparantnederland.nl/';
var webPort = 3002;

process.argv.slice( 2 ).forEach( function( value ) {
    console.log( value );
    var split = value.split('=');
    if( split[ 0 ].toLowerCase() === 'apiurl' ) apiUrl = split[ 1 ];
} );

app.locals.apiUrl = apiUrl;

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

/// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

/// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.listen(webPort);
<|endoftext|>


<commit_before>define([
    "backbone",
    "corpus/Corpus",
    "corpus/CorpusMask",
    "user/User",
    "OPrime"
], function(
    Backbone,
    Corpus,
    CorpusMask,
    User
) {
  var UserRouter = Backbone.Router.extend(
  /** @lends UserRouter.prototype */
  {
    /**
     * @class Routes URLs to handle the user dashboard. Mostly just
     *        shows the user a list of their corpora so they can switch
     *        between corpora.
     *
     * @extends Backbone.Router
     * @constructs
     */
    initialize : function() {
    },

    routes : {
      "corpus/:pouchname/:id"           : "showCorpusDashboard",
      "corpus/:pouchname/"              : "guessCorpusIdAndShowDashboard",
      "corpus/:pouchname"               : "guessCorpusIdAndShowDashboard",
      "login/:pouchname"                : "showQuickAuthenticateAndRedirectToDatabase",
      "render/:render"                  : "showDashboard",
      ""                                : "showDashboard"
    },

    /**
     * Displays the dashboard view of the user loaded in authentication
     *
     */
    showDashboard : function(renderOrNot) {
      if (OPrime.debugMode) OPrime.debug("In showDashboard: " );
//      $("#user-modal").modal("show");

    },
    /**
     * Displays the dashboard view of the user loaded in authentication
     *
     */
    showFullscreenUser : function() {
      if (OPrime.debugMode) OPrime.debug("In showFullscreenUser: " );
    },
    showQuickAuthenticateAndRedirectToDatabase : function(pouchname){
      window.app.set("corpus", new Corpus());
      window.app.get("authentication").syncUserWithServer(function(){
        var optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(pouchname);
        window.location.replace(optionalCouchAppPath+"corpus.html");
    });
    },
    guessCorpusIdAndShowDashboard : function(pouchname){
      var connection = JSON.parse(JSON.stringify(window.app.get("authentication").get("userPrivate").get("corpuses")[0]));
      if(!connection){
        return;
      }
      if(!pouchname || pouchname == undefined || pouchname == "undefined"){
        return;
      }
      /*
       * Verify that the user is in their database, and that the
       * backbone couch adaptor is saving to the corpus' database,
       * not where the user currently is.
       */
      if (OPrime.isCouchApp()) {
        var corpusPouchName = pouchname;
        if (window.location.pathname.indexOf(corpusPouchName) == -1) {
          if (corpusPouchName != "public-firstcorpus") {
            var username = "";
            try {
              username = window.app.get("authentication").get("userPrivate").get("username") || "";
            } catch (e) {
              //do nothing
            }
            if (username != "public") {
              // OPrime.bug("You're not in the database for your most recent corpus. Please authenticate and then we will take you to your database...");
            }
          }
          var optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(corpusPouchName);
          window.location.replace(optionalCouchAppPath + "user.html#/corpus/"+pouchname);
          return;
        }
      }

      /* this assumes that the user's corpus connection for this pouch is not on a different server */
      connection.pouchname = pouchname;
      window.app.changePouch(connection, function(){
        var c = new CorpusMask();
        c.set({
          "pouchname" : pouchname
        });
        c.id = "corpus";
        c.fetch({
          success : function(model) {
            if (OPrime.debugMode) OPrime.debug("Corpus fetched successfully", model);
            var corpusidfromCorpusMask = model.get("corpusid");
            /* Upgrade to version 1.38 */
            if(!corpusidfromCorpusMask){
              corpusidfromCorpusMask = model.get("corpusId");
            }
            if(!corpusidfromCorpusMask){

              var couchurl = OPrime.getCouchUrl(connection);
              var queryUrl = couchurl + "/_design/pages/_view/private_corpuses";

              var errorfunction = function(response) {
                OPrime.debug("There was a problem getting the corpusid." + JSON.stringify(response));
                OPrime.bug("There was a problem loading your corpus. Please report this error.");
              };
              var corpusself = this;
              OPrime.makeCORSRequest({
                type : 'GET',
                url : queryUrl,
                success : function(serverResults) {
                  if(!serverResults || !serverResults.rows || serverResults.rows.length === 0){
                    errorfunction("No corpus doc! this corpus is broken.");
                  }
                  var corpusidfromCorpusMask = serverResults.rows[0].id;
                  window.app.router.showCorpusDashboard(pouchname, corpusidfromCorpusMask);
                }, error: errorfunction
              });
              return;
            }

            if(corpusidfromCorpusMask){
              window.app.router.showCorpusDashboard(pouchname, corpusidfromCorpusMask);
            }else{
              OPrime.bug("There was a problem loading this corpus. Please report this.");
              if(OPrime.isChromeApp()){
                OPrime.bug("There was a problem loading this corpus, maybe it is not backed up?\n\n Attempting to back it up now...");
                /* TODO get the id of the only corpus in the database */
                window.location.replace("backup_pouches.html");
              }
            }
          },
          error : function(e, x, y ) {
            if (OPrime.debugMode) OPrime.debug("Problem opening the dashboard ", e, x, y);
            var reason = "";
            if(x){
              reason = x.reason;
            }
            if (OPrime.debugMode) OPrime.debug("There was a potential problem opening your dashboard." + reason);
          }
        });
      });
    },

    /**
     * Loads the requested corpus, and redirects the user to the corpus dashboard
     *
     * @param {String}
     *          pouchname The name of the corpus this datum is from.
     */
    showCorpusDashboard : function(pouchname, corpusid) {
      if (OPrime.debugMode) OPrime.debug("In showFullscreenCorpus: " );

      /*
       * If the corpusid is not specified, then try to guess it by re-routing us to the guess function
       */
      if(!corpusid){
        window.app.router.navigate("corpus/"+pouchname, {trigger: true});

        return;
      }
      if(!pouchname){
        if (OPrime.debugMode) OPrime.debug("the pouchname is missing, this should never happen");
        return;
      }
      var connection = JSON.parse(JSON.stringify(window.app.get("authentication").get("userPrivate").get("corpuses")[0]));
      if(!connection){
        return;
      }
      var self = this;
      connection.pouchname = pouchname;
      window.app.changePouch(connection, function(){

        var c = new Corpus();
        c.set({
          "pouchname" : pouchname
        });
        c.id = corpusid;
        c.fetch({
          success : function(model) {
            if (OPrime.debugMode) OPrime.debug("Corpus fetched successfully", model);

            c.makeSureCorpusHasADataList(function(){
              c.makeSureCorpusHasASession(function(){
                self.loadCorpusDashboard(model);
                //end success to create new data list
              },function(){
                alert("Failed to create a session. ");
              });//end failure to create new data list
              //end success to create new data list
            },function(){
              alert("Failed to create a datalist. ");
            });//end failure to create new data list

          },
          error : function(e, x, y ) {
            console.log(e);
            console.log(x);
            console.log(y);
            if(self.islooping){
              return;
            }

            self.bringCorpusToThisDevice(c, function(){
              if(OPrime.isChromeApp()){
                alert("Downloaded this corpus to this device. Attempting to load the corpus dashboard.");
              }
              self.showCorpusDashboard(pouchname, corpusid);
              self.islooping = true;

            }, function(e){
              alert("Couldn't download this corpus to this device. There was an error replicating corpus..."+e);
            });

          }
        });

      });

    },
    loadCorpusDashboard: function(c){
      var mostRecentIds = {
          corpusid : c.id,
          datalistid : c.datalists.models[0].id,
          sessionid : c.sessions.models[0].id,
          couchConnection : c.get("couchConnection")
        };
        console.log("mostRecentIds", mostRecentIds);
        window.app.get("authentication").get("userPrivate").set("mostRecentIds", mostRecentIds);
        window.app.get("authentication").saveAndInterConnectInApp(function(){
          var optionalCouchAppPath= "";
          if(c.get("couchConnection").pouchname){
             optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(c.get("couchConnection").pouchname);
          }
          window.location.replace(optionalCouchAppPath+"corpus.html");
          return;
        });
    },
    bringCorpusToThisDevice : function(corpus, callback) {
      for (var x in window.app.get("authentication").get("userPrivate").get("corpuses")){
        if(window.app.get("authentication").get("userPrivate").get("corpuses")[x].pouchname == corpus.get("pouchname")){
          corpus.set("couchConnection", window.app.get("authentication").get("userPrivate").get("corpuses")[x]);
          window.app.set("corpus",corpus);
          window.app.get("authentication").staleAuthentication = true;
          window.app.get("authentication").syncUserWithServer(function(){
            window.app.replicateOnlyFromCorpus(null, callback);
          });
          break;
        }
      }
    }

  });

  return UserRouter;
});
<commit_msg>fixes #1079 online version can flip in and out of corpora<commit_after>define([
    "backbone",
    "corpus/Corpus",
    "corpus/CorpusMask",
    "user/User",
    "OPrime"
], function(
    Backbone,
    Corpus,
    CorpusMask,
    User
) {
  var UserRouter = Backbone.Router.extend(
  /** @lends UserRouter.prototype */
  {
    /**
     * @class Routes URLs to handle the user dashboard. Mostly just
     *        shows the user a list of their corpora so they can switch
     *        between corpora.
     *
     * @extends Backbone.Router
     * @constructs
     */
    initialize : function() {
    },

    routes : {
      "corpus/:pouchname/:id"           : "showCorpusDashboard",
      "corpus/:pouchname/"              : "guessCorpusIdAndShowDashboard",
      "corpus/:pouchname"               : "guessCorpusIdAndShowDashboard",
      "login/:pouchname"                : "showQuickAuthenticateAndRedirectToDatabase",
      "render/:render"                  : "showDashboard",
      ""                                : "showDashboard"
    },

    /**
     * Displays the dashboard view of the user loaded in authentication
     *
     */
    showDashboard : function(renderOrNot) {
      if (OPrime.debugMode) OPrime.debug("In showDashboard: " );
//      $("#user-modal").modal("show");

    },
    /**
     * Displays the dashboard view of the user loaded in authentication
     *
     */
    showFullscreenUser : function() {
      if (OPrime.debugMode) OPrime.debug("In showFullscreenUser: " );
    },
    showQuickAuthenticateAndRedirectToDatabase : function(pouchname){
      window.app.set("corpus", new Corpus());
      window.app.get("authentication").syncUserWithServer(function(){
        var optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(pouchname);
        window.location.replace(optionalCouchAppPath+"corpus.html");
      });
    },
    guessCorpusIdAndShowDashboard : function(pouchname){
      var connection = JSON.parse(JSON.stringify(window.app.get("authentication").get("userPrivate").get("corpuses")[0]));
      if(!connection){
        return;
      }
      if(!pouchname || pouchname == undefined || pouchname == "undefined"){
        return;
      }
      this.veryifyWeAreInTheRightDB(pouchname);

      /* this assumes that the user's corpus connection for this pouch is not on a different server */
      connection.pouchname = pouchname;
      window.app.changePouch(connection, function(){
        var c = new CorpusMask();
        c.set({
          "pouchname" : pouchname
        });
        c.id = "corpus";
        c.fetch({
          success : function(model) {
            if (OPrime.debugMode) OPrime.debug("Corpus fetched successfully", model);
            var corpusidfromCorpusMask = model.get("corpusid");
            /* Upgrade to version 1.38 */
            if(!corpusidfromCorpusMask){
              corpusidfromCorpusMask = model.get("corpusId");
            }
            if(!corpusidfromCorpusMask){

              var couchurl = OPrime.getCouchUrl(connection);
              var queryUrl = couchurl + "/_design/pages/_view/private_corpuses";

              var errorfunction = function(response) {
                OPrime.debug("There was a problem getting the corpusid." + JSON.stringify(response));
                OPrime.bug("There was a problem loading your corpus. Please report this error.");
              };
              var corpusself = this;
              OPrime.makeCORSRequest({
                type : 'GET',
                url : queryUrl,
                success : function(serverResults) {
                  if(!serverResults || !serverResults.rows || serverResults.rows.length === 0){
                    errorfunction("No corpus doc! this corpus is broken.");
                  }
                  var corpusidfromCorpusMask = serverResults.rows[0].id;
                  window.app.router.showCorpusDashboard(pouchname, corpusidfromCorpusMask);
                }, error: errorfunction
              });
              return;
            }

            if(corpusidfromCorpusMask){
              window.app.router.showCorpusDashboard(pouchname, corpusidfromCorpusMask);
            }else{
              OPrime.bug("There was a problem loading this corpus. Please report this.");
              if(OPrime.isChromeApp()){
                OPrime.bug("There was a problem loading this corpus, maybe it is not backed up?\n\n Attempting to back it up now...");
                /* TODO get the id of the only corpus in the database */
                window.location.replace("backup_pouches.html");
              }
            }
          },
          error : function(e, x, y ) {
            if (OPrime.debugMode) OPrime.debug("Problem opening the dashboard ", e, x, y);
            var reason = "";
            if(x){
              reason = x.reason;
            }
            if (OPrime.debugMode) OPrime.debug("There was a potential problem opening your dashboard." + reason);
          }
        });
      });
    },

    /**
     * Loads the requested corpus, and redirects the user to the corpus dashboard
     *
     * @param {String}
     *          pouchname The name of the corpus this datum is from.
     */
    showCorpusDashboard : function(pouchname, corpusid) {
      if (OPrime.debugMode) OPrime.debug("In showFullscreenCorpus: " );

      /*
       * If the corpusid is not specified, then try to guess it by re-routing us to the guess function
       */
      if(!corpusid){
        window.app.router.navigate("corpus/"+pouchname, {trigger: true});

        return;
      }
      if(!pouchname){
        if (OPrime.debugMode) OPrime.debug("the pouchname is missing, this should never happen");
        return;
      }

      this.veryifyWeAreInTheRightDB(pouchname);

      var connection = JSON.parse(JSON.stringify(window.app.get("authentication").get("userPrivate").get("corpuses")[0]));
      if(!connection){
        return;
      }

      var self = this;
      connection.pouchname = pouchname;
      window.app.changePouch(connection, function(){

        var c = new Corpus();
        c.set({
          "pouchname" : pouchname
        });
        c.id = corpusid;
        c.fetch({
          success : function(model) {
            if (OPrime.debugMode) OPrime.debug("Corpus fetched successfully", model);

            c.makeSureCorpusHasADataList(function(){
              c.makeSureCorpusHasASession(function(){
                self.loadCorpusDashboard(model);
                //end success to create new data list
              },function(){
                alert("Failed to create a session. ");
              });//end failure to create new data list
              //end success to create new data list
            },function(){
              alert("Failed to create a datalist. ");
            });//end failure to create new data list

          },
          error : function(e, x, y ) {
            console.log(e);
            console.log(x);
            console.log(y);
            if(self.islooping){
              return;
            }

            self.bringCorpusToThisDevice(c, function(){
              if(OPrime.isChromeApp()){
                alert("Downloaded this corpus to this device. Attempting to load the corpus dashboard.");
              }
              self.showCorpusDashboard(pouchname, corpusid);
              self.islooping = true;

            }, function(e){
              alert("Couldn't download this corpus to this device. There was an error replicating corpus..."+e);
            });

          }
        });

      });

    },
    loadCorpusDashboard: function(c){
      var mostRecentIds = {
          corpusid : c.id,
          datalistid : c.datalists.models[0].id,
          sessionid : c.sessions.models[0].id,
          couchConnection : c.get("couchConnection")
        };
        console.log("mostRecentIds", mostRecentIds);
        window.app.get("authentication").get("userPrivate").set("mostRecentIds", mostRecentIds);
        window.app.get("authentication").saveAndInterConnectInApp(function(){
          var optionalCouchAppPath= "";
          if(c.get("couchConnection").pouchname){
             optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(c.get("couchConnection").pouchname);
          }
          window.location.replace(optionalCouchAppPath+"corpus.html");
          return;
        });
    },
    bringCorpusToThisDevice : function(corpus, callback) {
      for (var x in window.app.get("authentication").get("userPrivate").get("corpuses")){
        if(window.app.get("authentication").get("userPrivate").get("corpuses")[x].pouchname == corpus.get("pouchname")){
          corpus.set("couchConnection", window.app.get("authentication").get("userPrivate").get("corpuses")[x]);
          window.app.set("corpus",corpus);
          window.app.get("authentication").staleAuthentication = true;
          window.app.get("authentication").syncUserWithServer(function(){
            window.app.replicateOnlyFromCorpus(null, callback);
          });
          break;
        }
      }
    },
    veryifyWeAreInTheRightDB : function(pouchname){
      /*
       * Verify that the user is in their database, and that the
       * backbone couch adaptor is saving to the corpus' database,
       * not where the user currently is.
       */
      if (OPrime.isCouchApp()) {
        var corpusPouchName = pouchname;
        if (window.location.pathname.indexOf(corpusPouchName) == -1) {
          if (corpusPouchName != "public-firstcorpus") {
            var username = "";
            try {
              username = window.app.get("authentication").get("userPrivate").get("username") || "";
            } catch (e) {
              //do nothing
            }
            if (username != "public") {
              // OPrime.bug("You're not in the database for your most recent corpus. Please authenticate and then we will take you to your database...");
            }
          }
          var optionalCouchAppPath = OPrime.guessCorpusUrlBasedOnWindowOrigin(corpusPouchName);
          window.location.replace(optionalCouchAppPath + "user.html#/corpus/"+pouchname);
          return;
        }
      }
    }

  });

  return UserRouter;
});
<|endoftext|>