<commit_before>const got = require('got');

const db = require('../db.js');

const cache = require('../utils/cache.js');
const time = require('../utils/time.js');

const elron = require('../providers/elron.js');
const tlt = require('../providers/tlt.js');

// Get trips for stop.
async function getTrips(req, res) {

	const { stop: stopId, stops: stopIds } = req.query;
	const now = time.getSeconds();
	const tripz = [];

	for (const id of (stopId || stopIds).split(',')) {

		// Verify stop, get type and region.
		const stop = (await db.query('SELECT type, region FROM stops WHERE id = ?', [ id ]))[0];
		if (!stop) throw new Error(`Stop with id '${id}' not found`);

		// Elron

		if (stop.type === 'train') {
			tripz.push(await elron.getTrips(id));
			continue;
		}

		// TLT + MNT

		const trips = stop.region === 'tallinn' ? await tlt.getTrips(id) : [];

		const mntTrips = await cache.use('mnt-stops', id, () => db.query(`
			SELECT TIME_TO_SEC(time) AS time, route.name, trip.destination, wheelchair, route.type, FALSE AS live, 'mnt' AS provider FROM stops AS stop
				JOIN stop_times ON stop_id = id
				JOIN trips AS trip ON trip.id = trip_id
				JOIN routes AS route ON route.id = route_id
				JOIN services AS service ON service.id = trip.service_id
			WHERE
				stop.id = ?
				AND route.type IS NOT NULL
				AND time > CURTIME()
				AND (
					(
						CURDATE() BETWEEN start AND end
						AND SUBSTR(days, WEEKDAY(CURDATE()) + 1, 1) = 1
						AND service_id NOT IN (
							SELECT service_id FROM service_exceptions WHERE type = 0 AND date = CURDATE()
						)
					) OR service_id IN (
						SELECT service_id FROM service_exceptions WHERE type = 1 AND date = CURDATE()
					)
				)
				${trips.length ? "AND route.type LIKE 'coach%'" : ''}
			LIMIT ${trips.length ? '5' : '15'}
		`, [ id ]));

		tripz.push(trips.concat(mntTrips).sort((a, b) => a.time - b.time));

	}

	// Calculate countdown and time for trips.
	for (const trips of tripz) for (let i = 0; i < trips.length; i++) {
		const trip = trips[i];

		// Fix timezone difference.
		const tzTime = time.toTime(trip.time);
		tzTime[0] -= ((new Date()).getTimezoneOffset() === -120 ? 1 : 0); // UTC-2

		trips[i] = {
			countdown: time.toCountdown(trip.time),
			...trip,
			time: tzTime
		};

	}

	res.send(tripz.length === 1 ? tripz[0] : tripz);

}

module.exports = (fastify, opts, next) => {

	fastify.get('/trips', {
		schema: {
			querystring: {
				type: 'object',
				oneOf: [
					{ required: [ 'stop' ] },
					{ required: [ 'stops' ] }
				],
				properties: {
					stop: { type: 'string' },
					stops: { type: 'string' }
				}
			}
		}
	}, getTrips);

	next();

};
<commit_msg>Revert "(web) (Temporarily) fix the bane of my existence"<commit_after>const got = require('got');

const db = require('../db.js');

const cache = require('../utils/cache.js');
const time = require('../utils/time.js');

const elron = require('../providers/elron.js');
const tlt = require('../providers/tlt.js');

// Get trips for stop.
async function getTrips(req, res) {

	const { stop: stopId, stops: stopIds } = req.query;
	const now = time.getSeconds();
	const tripz = [];

	for (const id of (stopId || stopIds).split(',')) {

		// Verify stop, get type and region.
		const stop = (await db.query('SELECT type, region FROM stops WHERE id = ?', [ id ]))[0];
		if (!stop) throw new Error(`Stop with id '${id}' not found`);

		// Elron

		if (stop.type === 'train') {
			tripz.push(await elron.getTrips(id));
			continue;
		}

		// TLT + MNT

		const trips = stop.region === 'tallinn' ? await tlt.getTrips(id) : [];

		const mntTrips = await cache.use('mnt-stops', id, () => db.query(`
			SELECT TIME_TO_SEC(time) AS time, route.name, trip.destination, wheelchair, route.type, FALSE AS live, 'mnt' AS provider FROM stops AS stop
				JOIN stop_times ON stop_id = id
				JOIN trips AS trip ON trip.id = trip_id
				JOIN routes AS route ON route.id = route_id
				JOIN services AS service ON service.id = trip.service_id
			WHERE
				stop.id = ?
				AND route.type IS NOT NULL
				AND time > CURTIME()
				AND (
					(
						CURDATE() BETWEEN start AND end
						AND SUBSTR(days, WEEKDAY(CURDATE()) + 1, 1) = 1
						AND service_id NOT IN (
							SELECT service_id FROM service_exceptions WHERE type = 0 AND date = CURDATE()
						)
					) OR service_id IN (
						SELECT service_id FROM service_exceptions WHERE type = 1 AND date = CURDATE()
					)
				)
				${trips.length ? "AND route.type LIKE 'coach%'" : ''}
			LIMIT ${trips.length ? '5' : '15'}
		`, [ id ]));

		tripz.push(trips.concat(mntTrips).sort((a, b) => a.time - b.time));

	}

	// Calculate countdown and time for trips.
	for (const trips of tripz) for (let i = 0; i < trips.length; i++) {
		const trip = trips[i];

		trips[i] = {
			countdown: time.toCountdown(trip.time),
			...trip,
			time: time.toTime(trip.time)
		};

	}

	res.send(tripz.length === 1 ? tripz[0] : tripz);

}

module.exports = (fastify, opts, next) => {

	fastify.get('/trips', {
		schema: {
			querystring: {
				type: 'object',
				oneOf: [
					{ required: [ 'stop' ] },
					{ required: [ 'stops' ] }
				],
				properties: {
					stop: { type: 'string' },
					stops: { type: 'string' }
				}
			}
		}
	}, getTrips);

	next();

};
<|endoftext|>


<commit_before>#!/usr/local/bin/node

var fs = require('fs'),
	uglify = require('uglify-js')

var storeJS = fs.readFileSync(__dirname + '/store.js').toString(),
	jsonJS = fs.readFileSync(__dirname + '/json.js').toString(),
	copy = '/* Copyright (c) 2010-2013 Marcus Westin */'

console.log('building and minifying...')
buildFile(storeJS, 'store.min.js')
buildFile(jsonJS + '\n\n' + storeJS, 'store+json2.min.js')
console.log('done')

function buildFile(js, name) {
	var ast = uglify.parser.parse(js)
	ast = uglify.uglify.ast_mangle(ast)
	ast = uglify.uglify.ast_squeeze(ast)
	var minifiedJS = uglify.uglify.gen_code(ast)
	fs.writeFile(__dirname + '/' + name, copy + '\n' + minifiedJS)
}

<commit_msg>Add `;` to the end of minified build. Fixes #131<commit_after>#!/usr/local/bin/node

var fs = require('fs'),
	uglify = require('uglify-js')

var storeJS = fs.readFileSync(__dirname + '/store.js').toString(),
	jsonJS = fs.readFileSync(__dirname + '/json.js').toString(),
	copy = '/* Copyright (c) 2010-2013 Marcus Westin */'

console.log('building and minifying...')
buildFile(storeJS, 'store.min.js')
buildFile(jsonJS + '\n\n' + storeJS, 'store+json2.min.js')
console.log('done')

function buildFile(js, name) {
	var ast = uglify.parser.parse(js)
	ast = uglify.uglify.ast_mangle(ast)
	ast = uglify.uglify.ast_squeeze(ast)
	var minifiedJS = uglify.uglify.gen_code(ast)
	fs.writeFile(__dirname + '/' + name, copy + '\n' + minifiedJS + ';')
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015-2016:
 *   Frederic Mohier, frederic.mohier@gmail.com
 *
 * This file is part of (WebUI).
 *
 * (WebUI) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * (WebUI) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with (WebUI).  If not, see <http://www.gnu.org/licenses/>.
 */

var refresh_logs=false;

// By default, we set the page to reload each period defined in configuration
var refresh_timeout = app_refresh_period;
// Check refresh period (seconds)
var check_period = 1;
// Ping period (seconds) - Set this value to 0 to disable periodical server ping.
var ping_period = 0;
// Refresh required
var refresh_required=false;

if (refresh_logs) console.debug("Refresh period is :", refresh_timeout);
if (refresh_logs) console.debug("Check period is :", check_period);
if (refresh_logs) console.debug("Ping period is :", ping_period);

var nb_refresh_try = 0;
if (! sessionStorage.getItem("refresh_active")) {
   if (refresh_logs) console.debug("Refresh active storage does not exist");
   // Store default value ...
   sessionStorage.setItem("refresh_active", refresh_timeout==0 ? '0' : '1');
}
if (refresh_logs) console.debug("Refresh active is ", sessionStorage.getItem("refresh_active"));
if (sessionStorage.getItem("refresh_active") == '1') {
   $('#header_loading').removeClass('font-greyed');
} else {
   $('#header_loading').addClass('font-greyed');
}

/*
 * This function is called on each refresh of the current page.
 * ----------------------------------------------------------------------------
 *  It is to be noted that this function makes an Ajax call on the current URL
 * to get the new version of the current page. This is the most interesting
 * strategy for refreshing ... but the drawbacks are that it gets an entire
 * Html page including ,  and ...


<commit_before><commit_msg>Add test covering eval example show/hide<commit_after><|endoftext|>


<commit_before>/*
 * browser.js: Browser specific functionality for director.
 *
 * (C) 2011, Nodejitsu Inc.
 * MIT LICENSE
 *
 */

if (!Array.prototype.filter) {
  Array.prototype.filter = function(filter, that) {
    var other = [], v;
    for (var i = 0, n = this.length; i < n; i++) {
      if (i in this && filter.call(that, v = this[i], i, this)) {
        other.push(v);
      }
    }
    return other;
  };
}

if (!Array.isArray){
  Array.isArray = function(obj) {
    return Object.prototype.toString.call(obj) === '[object Array]';
  };
}

var dloc = document.location;

function dlocHashEmpty() {
  // Non-IE browsers return '' when the address bar shows '#'; Director's logic
  // assumes both mean empty.
  return dloc.hash === '' || dloc.hash === '#';
}

var listener = {
  mode: 'modern',
  hash: dloc.hash,
  history: false,

  check: function () {
    var h = dloc.hash;
    if (h != this.hash) {
      this.hash = h;
      this.onHashChanged();
    }
  },

  fire: function () {
    if (this.mode === 'modern') {
      this.history === true ? window.onpopstate() : window.onhashchange();
    }
    else {
      this.onHashChanged();
    }
  },

  init: function (fn, history) {
    var self = this;
    this.history = history;

    if (!Router.listeners) {
      Router.listeners = [];
    }

    function onchange(onChangeEvent) {
      for (var i = 0, l = Router.listeners.length; i < l; i++) {
        Router.listeners[i](onChangeEvent);
      }
    }

    //note IE8 is being counted as 'modern' because it has the hashchange event
    if ('onhashchange' in window && (document.documentMode === undefined
      || document.documentMode > 7)) {
      // At least for now HTML5 history is available for 'modern' browsers only
      if (this.history === true) {
        // There is an old bug in Chrome that causes onpopstate to fire even
        // upon initial page load. Since the handler is run manually in init(),
        // this would cause Chrome to run it twise. Currently the only
        // workaround seems to be to set the handler after the initial page load
        // http://code.google.com/p/chromium/issues/detail?id=63040
        setTimeout(function() {
          window.onpopstate = onchange;
        }, 500);
      }
      else {
        window.onhashchange = onchange;
      }
      this.mode = 'modern';
    }
    else {
      //
      // IE support, based on a concept by Erik Arvidson ...
      //
      var frame = document.createElement('iframe');
      frame.id = 'state-frame';
      frame.style.display = 'none';
      document.body.appendChild(frame);
      this.writeFrame('');

      if ('onpropertychange' in document && 'attachEvent' in document) {
        document.attachEvent('onpropertychange', function () {
          if (event.propertyName === 'location') {
            self.check();
          }
        });
      }

      window.setInterval(function () { self.check(); }, 50);

      this.onHashChanged = onchange;
      this.mode = 'legacy';
    }

    Router.listeners.push(fn);

    return this.mode;
  },

  destroy: function (fn) {
    if (!Router || !Router.listeners) {
      return;
    }

    var listeners = Router.listeners;

    for (var i = listeners.length - 1; i >= 0; i--) {
      if (listeners[i] === fn) {
        listeners.splice(i, 1);
      }
    }
  },

  setHash: function (s) {
    // Mozilla always adds an entry to the history
    if (this.mode === 'legacy') {
      this.writeFrame(s);
    }

    if (this.history === true) {
      window.history.pushState({}, document.title, s);
      // Fire an onpopstate event manually since pushing does not obviously
      // trigger the pop event.
      this.fire();
    } else {
      dloc.hash = (s[0] === '/') ? s : '/' + s;
    }
    return this;
  },

  writeFrame: function (s) {
    // IE support...
    var f = document.getElementById('state-frame');
    var d = f.contentDocument || f.contentWindow.document;
    d.open();
    d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
    d.close();
  },

  syncHash: function () {
    // IE support...
    var s = this._hash;
    if (s != dloc.hash) {
      dloc.hash = s;
    }
    return this;
  },

  onHashChanged: function () {}
};

var Router = exports.Router = function (routes) {
  if (!(this instanceof Router)) return new Router(routes);

  this.params   = {};
  this.routes   = {};
  this.methods  = ['on', 'once', 'after', 'before'];
  this.scope    = [];
  this._methods = {};

  this._insert = this.insert;
  this.insert = this.insertEx;

  this.historySupport = (window.history != null ? window.history.pushState : null) != null

  this.configure();
  this.mount(routes || {});
};

Router.prototype.init = function (r) {
  var self = this;
  this.handler = function(onChangeEvent) {
    var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
    var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
    self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
  };

  listener.init(this.handler, this.history);

  if (this.history === false) {
    if (dlocHashEmpty() && r) {
      dloc.hash = r;
    } else if (!dlocHashEmpty()) {
      self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
    }
  }
  else {
    var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
    if (routeTo) {
      window.history.replaceState({}, document.title, routeTo);
    }

    // Router has been initialized, but due to the chrome bug it will not
    // yet actually route HTML5 history state changes. Thus, decide if should route.
    if (routeTo || this.run_in_init === true) {
      this.handler();
    }
  }

  return this;
};

Router.prototype.explode = function () {
  var v = this.history === true ? this.getPath() : dloc.hash;
  if (v.charAt(1) === '/') { v=v.slice(1) }
  return v.slice(1, v.length).split("/");
};

Router.prototype.setRoute = function (i, v, val) {
  var url = this.explode();

  if (typeof i === 'number' && typeof v === 'string') {
    url[i] = v;
  }
  else if (typeof val === 'string') {
    url.splice(i, v, s);
  }
  else {
    url = [i];
  }

  listener.setHash(url.join('/'));
  return url;
};

//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
  if (method === "once") {
    method = "on";
    route = function(route) {
      var once = false;
      return function() {
        if (once) return;
        once = true;
        return route.apply(this, arguments);
      };
    }(route);
  }
  return this._insert(method, path, route, parent);
};

Router.prototype.getRoute = function (v) {
  var ret = v;

  if (typeof v === "number") {
    ret = this.explode()[v];
  }
  else if (typeof v === "string"){
    var h = this.explode();
    ret = h.indexOf(v);
  }
  else {
    ret = this.explode();
  }

  return ret;
};

Router.prototype.destroy = function () {
  listener.destroy(this.handler);
  return this;
};

Router.prototype.getPath = function () {
  var path = window.location.pathname;
  if (path.substr(0, 1) !== '/') {
    path = '/' + path;
  }
  return path;
};
<commit_msg>Remove shims for obsolete browsers, fixes #98<commit_after>/*
 * browser.js: Browser specific functionality for director.
 *
 * (C) 2011, Nodejitsu Inc.
 * MIT LICENSE
 *
 */

var dloc = document.location;

function dlocHashEmpty() {
  // Non-IE browsers return '' when the address bar shows '#'; Director's logic
  // assumes both mean empty.
  return dloc.hash === '' || dloc.hash === '#';
}

var listener = {
  mode: 'modern',
  hash: dloc.hash,
  history: false,

  check: function () {
    var h = dloc.hash;
    if (h != this.hash) {
      this.hash = h;
      this.onHashChanged();
    }
  },

  fire: function () {
    if (this.mode === 'modern') {
      this.history === true ? window.onpopstate() : window.onhashchange();
    }
    else {
      this.onHashChanged();
    }
  },

  init: function (fn, history) {
    var self = this;
    this.history = history;

    if (!Router.listeners) {
      Router.listeners = [];
    }

    function onchange(onChangeEvent) {
      for (var i = 0, l = Router.listeners.length; i < l; i++) {
        Router.listeners[i](onChangeEvent);
      }
    }

    //note IE8 is being counted as 'modern' because it has the hashchange event
    if ('onhashchange' in window && (document.documentMode === undefined
      || document.documentMode > 7)) {
      // At least for now HTML5 history is available for 'modern' browsers only
      if (this.history === true) {
        // There is an old bug in Chrome that causes onpopstate to fire even
        // upon initial page load. Since the handler is run manually in init(),
        // this would cause Chrome to run it twise. Currently the only
        // workaround seems to be to set the handler after the initial page load
        // http://code.google.com/p/chromium/issues/detail?id=63040
        setTimeout(function() {
          window.onpopstate = onchange;
        }, 500);
      }
      else {
        window.onhashchange = onchange;
      }
      this.mode = 'modern';
    }
    else {
      //
      // IE support, based on a concept by Erik Arvidson ...
      //
      var frame = document.createElement('iframe');
      frame.id = 'state-frame';
      frame.style.display = 'none';
      document.body.appendChild(frame);
      this.writeFrame('');

      if ('onpropertychange' in document && 'attachEvent' in document) {
        document.attachEvent('onpropertychange', function () {
          if (event.propertyName === 'location') {
            self.check();
          }
        });
      }

      window.setInterval(function () { self.check(); }, 50);

      this.onHashChanged = onchange;
      this.mode = 'legacy';
    }

    Router.listeners.push(fn);

    return this.mode;
  },

  destroy: function (fn) {
    if (!Router || !Router.listeners) {
      return;
    }

    var listeners = Router.listeners;

    for (var i = listeners.length - 1; i >= 0; i--) {
      if (listeners[i] === fn) {
        listeners.splice(i, 1);
      }
    }
  },

  setHash: function (s) {
    // Mozilla always adds an entry to the history
    if (this.mode === 'legacy') {
      this.writeFrame(s);
    }

    if (this.history === true) {
      window.history.pushState({}, document.title, s);
      // Fire an onpopstate event manually since pushing does not obviously
      // trigger the pop event.
      this.fire();
    } else {
      dloc.hash = (s[0] === '/') ? s : '/' + s;
    }
    return this;
  },

  writeFrame: function (s) {
    // IE support...
    var f = document.getElementById('state-frame');
    var d = f.contentDocument || f.contentWindow.document;
    d.open();
    d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
    d.close();
  },

  syncHash: function () {
    // IE support...
    var s = this._hash;
    if (s != dloc.hash) {
      dloc.hash = s;
    }
    return this;
  },

  onHashChanged: function () {}
};

var Router = exports.Router = function (routes) {
  if (!(this instanceof Router)) return new Router(routes);

  this.params   = {};
  this.routes   = {};
  this.methods  = ['on', 'once', 'after', 'before'];
  this.scope    = [];
  this._methods = {};

  this._insert = this.insert;
  this.insert = this.insertEx;

  this.historySupport = (window.history != null ? window.history.pushState : null) != null

  this.configure();
  this.mount(routes || {});
};

Router.prototype.init = function (r) {
  var self = this;
  this.handler = function(onChangeEvent) {
    var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
    var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
    self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
  };

  listener.init(this.handler, this.history);

  if (this.history === false) {
    if (dlocHashEmpty() && r) {
      dloc.hash = r;
    } else if (!dlocHashEmpty()) {
      self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
    }
  }
  else {
    var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
    if (routeTo) {
      window.history.replaceState({}, document.title, routeTo);
    }

    // Router has been initialized, but due to the chrome bug it will not
    // yet actually route HTML5 history state changes. Thus, decide if should route.
    if (routeTo || this.run_in_init === true) {
      this.handler();
    }
  }

  return this;
};

Router.prototype.explode = function () {
  var v = this.history === true ? this.getPath() : dloc.hash;
  if (v.charAt(1) === '/') { v=v.slice(1) }
  return v.slice(1, v.length).split("/");
};

Router.prototype.setRoute = function (i, v, val) {
  var url = this.explode();

  if (typeof i === 'number' && typeof v === 'string') {
    url[i] = v;
  }
  else if (typeof val === 'string') {
    url.splice(i, v, s);
  }
  else {
    url = [i];
  }

  listener.setHash(url.join('/'));
  return url;
};

//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
  if (method === "once") {
    method = "on";
    route = function(route) {
      var once = false;
      return function() {
        if (once) return;
        once = true;
        return route.apply(this, arguments);
      };
    }(route);
  }
  return this._insert(method, path, route, parent);
};

Router.prototype.getRoute = function (v) {
  var ret = v;

  if (typeof v === "number") {
    ret = this.explode()[v];
  }
  else if (typeof v === "string"){
    var h = this.explode();
    ret = h.indexOf(v);
  }
  else {
    ret = this.explode();
  }

  return ret;
};

Router.prototype.destroy = function () {
  listener.destroy(this.handler);
  return this;
};

Router.prototype.getPath = function () {
  var path = window.location.pathname;
  if (path.substr(0, 1) !== '/') {
    path = '/' + path;
  }
  return path;
};
<|endoftext|>


<commit_before>if (typeof GameOfLife === "undefined") {
  window.GameOfLife = {};
}

var Game = window.GameOfLife.Game = function (options) {
  if (typeof options.boardEl === 'undefined') {
    throw "Game must be passed an element to occupy"
  }

  this.board = new GameOfLife.Board({
    el: options.boardEl,
    height: options.height,
    width: options.width,
    startLiveChance: options.startLiveChance
  });

  if (typeof options.toggleEl === 'undefined') {
    this.toggle();
  } else {
    this.toggleEl = options.toggleEl;
    this.toggleEl.addEventListener('click', function (event) {
      this.toggle();
    }.bind(this));
  }

  if (typeof options.resetEl !== 'undefined') {
    this.resetEl = options.resetEl;
    this.resetEl.addEventListener('click', function (event) {
      if (this.isActive()) {
        this.stop();
      }
      this.board.initializeCells();
      this.generation = 0;
      this.updateCounter();
    }.bind(this));
  }

  if (typeof options.tickEl !== 'undefined') {
    this.tickEl = options.tickEl;
    this.tickEl.addEventListener('click', function (event) {
      if (!this.isActive()) {
        this.tick();
      }
    }.bind(this));
  }

  this.counterEl = options.counterEl;
  this.generation = 0;
};

Game.prototype.isActive = function () {
  return typeof this.intervalId !== 'undefined';
};

Game.prototype.toggle = function () {
  if (this.isActive()) {
    this.stop();
  } else {
    this.start();
  }
};

Game.prototype.start = function () {
  this.intervalId = setInterval(this.tick.bind(this), 100);
  this.toggleEl.innerHTML = 'Stop';
};

Game.prototype.stop = function () {
  clearInterval(this.intervalId);
  this.intervalId = undefined;
  this.toggleEl.innerHTML = 'Start';
};

Game.prototype.tick = function () {
  this.board.tick();

  if (this.board.isStable()) {
    this.toggle();
    return;
  }

  this.generation++;
  this.updateCounter();
};

Game.prototype.updateCounter = function () {
  if (typeof this.counterEl !== 'undefined') {
    this.counterEl.innerHTML = this.generation;
  }
};
<commit_msg>stop() rather than toggle() game when complete<commit_after>if (typeof GameOfLife === "undefined") {
  window.GameOfLife = {};
}

var Game = window.GameOfLife.Game = function (options) {
  if (typeof options.boardEl === 'undefined') {
    throw "Game must be passed an element to occupy"
  }

  this.board = new GameOfLife.Board({
    el: options.boardEl,
    height: options.height,
    width: options.width,
    startLiveChance: options.startLiveChance
  });

  if (typeof options.toggleEl === 'undefined') {
    this.toggle();
  } else {
    this.toggleEl = options.toggleEl;
    this.toggleEl.addEventListener('click', function (event) {
      this.toggle();
    }.bind(this));
  }

  if (typeof options.resetEl !== 'undefined') {
    this.resetEl = options.resetEl;
    this.resetEl.addEventListener('click', function (event) {
      if (this.isActive()) {
        this.stop();
      }
      this.board.initializeCells();
      this.generation = 0;
      this.updateCounter();
    }.bind(this));
  }

  if (typeof options.tickEl !== 'undefined') {
    this.tickEl = options.tickEl;
    this.tickEl.addEventListener('click', function (event) {
      if (!this.isActive()) {
        this.tick();
      }
    }.bind(this));
  }

  this.counterEl = options.counterEl;
  this.generation = 0;
};

Game.prototype.isActive = function () {
  return typeof this.intervalId !== 'undefined';
};

Game.prototype.toggle = function () {
  if (this.isActive()) {
    this.stop();
  } else {
    this.start();
  }
};

Game.prototype.start = function () {
  this.intervalId = setInterval(this.tick.bind(this), 100);
  this.toggleEl.innerHTML = 'Stop';
};

Game.prototype.stop = function () {
  clearInterval(this.intervalId);
  this.intervalId = undefined;
  this.toggleEl.innerHTML = 'Start';
};

Game.prototype.tick = function () {
  this.board.tick();

  if (this.board.isStable()) {
    this.stop();
  } else {
    this.generation++;
    this.updateCounter();
  }
};

Game.prototype.updateCounter = function () {
  if (typeof this.counterEl !== 'undefined') {
    this.counterEl.innerHTML = this.generation;
  }
};
<|endoftext|>


<commit_before>const rollup = require('rollup').rollup
const buble = require('rollup-plugin-buble')
const json = require('rollup-plugin-json')
const commonjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const uglify = require('rollup-plugin-uglify')
const nodeResolve = require('rollup-plugin-node-resolve')
const fs = require('fs-extra-promise')
const sass = require('node-sass').render
const cssnano = require('cssnano').process
const purifycss = require('purify-css')
const optimizeJs = require('optimize-js') // optimizeJs should be used & perf'd on case by case basis
const { name, dependencies } = require('./package')
const swPrecache = require('sw-precache').write
const nodeRev = require('node-rev').default

const server = () => rollup({
  entry: 'src/server/server.js',
  external: Object.keys(dependencies).concat(['fs']),
  plugins: [
    replace({ '__CLIENT__': false }),
    json(),
    commonjs({ extensions: [ '.js', '.json' ] }),
    buble({ jsx: 'h', objectAssign: 'Object.assign' })
  ]
}).then((bundle) => bundle.write({ sourceMap: true, format: 'cjs', dest: `build/server.js` }))

const client = () => rollup({
  entry: 'src/app/entry.js',
  context: 'window',
  plugins: [
    nodeResolve({ jsnext: true }),
    commonjs({ namedExports: { 'preact-redux': ['connect', 'Provider'] } }),
    replace({ '__CLIENT__': true, 'process.env.NODE_ENV': JSON.stringify('production') }),
    buble({ jsx: 'h', objectAssign: 'Object.assign' }),
    uglify(require('./uglify'))
  ]
})
.then((bundle) => bundle.generate({ sourceMap: true, format: 'iife' }))
.then(({ code, map }) => Promise.all([
  fs.outputFileAsync(`build/public/bundle.js`, optimizeJs(code) + `//# sourceMappingURL=/bundle.js.map`),
  fs.outputFileAsync(`build/public/bundle.js.map`, map.toString())
]))

const css = () => new Promise((resolve, reject) => sass({ file: `src/app/styles/entry.scss` }, (err, result) => err ? reject(err) : resolve(result)))
  .then(({ css }) => purifycss(['src/app/components/**/*.js'], css.toString()))
  .then((purified) => cssnano(purified, { autoprefixer: { add: true } }))
  .then(({ css }) => fs.outputFileAsync(`build/public/bundle.css`, css))

const sw = () => swPrecache('build/public/sw.js', {
  cacheId: `${name}`,
  directoryIndex: '/',
  staticFileGlobs: [
    '/',
    './build/public/manifest-*.json',
    // './build/public/bundle-*.{css,js}', // depends if we inlineJs, inlineCss or not
    './build/public/*.{gif,png,svg}' // will not preache /icons
  ],
  navigateFallback: '/',
  dynamicUrlToDependencies: {
    '/': ['./src/server/routes/root.js', './build/public/bundle.css', './build/public/bundle.js', './build/public/manifest.json', './package.json'] // bust cache when these change
  },
  skipWaiting: true,
  stripPrefix: './build/public',
  runtimeCaching: [{
    urlPattern: /\/posts/, // handle remote api call
    handler: 'cacheFirst'
  }]
})

const rev = () => Promise.resolve(nodeRev({
  files: './build/public/**/*.*',
  outputDir: './build/public/',
  file: './build/public/assets.json',
  hash: true  // depends if we inlineJs, inlineCss or not
}))

const clean = () => fs.emptyDirAsync('./build')
const copy = () => fs.copyAsync(`src/app/static/`, `./build/public/`)

const tasks = new Map()
const run = (task) => {
  const start = new Date()
  return tasks.get(task)().then(() => {
    console.log(`[build] '${task}' done in ${new Date().getTime() - start.getTime()}ms`)
  }, (err) => console.error(err.stack))
}

tasks
  .set('clean', clean)
  .set('client', client)
  .set('css', css)
  .set('copy', copy)
  .set('rev', rev)
  .set('server', server)
  .set('sw', sw)
  .set('build', () => run('clean')
    .then(() => Promise.all([run('client'), run('css'), run('copy'), run('server')]))
    .then(() => run('rev'))
    .then(() => run('sw'))
  )

run(/^\w/.test(process.argv[2] || '') ? process.argv[2] : 'build')
<commit_msg>need browser option for specific libs<commit_after>const rollup = require('rollup').rollup
const buble = require('rollup-plugin-buble')
const json = require('rollup-plugin-json')
const commonjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const uglify = require('rollup-plugin-uglify')
const nodeResolve = require('rollup-plugin-node-resolve')
const fs = require('fs-extra-promise')
const sass = require('node-sass').render
const cssnano = require('cssnano').process
const purifycss = require('purify-css')
const optimizeJs = require('optimize-js') // optimizeJs should be used & perf'd on case by case basis
const { name, dependencies } = require('./package')
const swPrecache = require('sw-precache').write
const nodeRev = require('node-rev').default

const server = () => rollup({
  entry: 'src/server/server.js',
  external: Object.keys(dependencies).concat(['fs']),
  plugins: [
    replace({ '__CLIENT__': false }),
    json(),
    commonjs({ extensions: [ '.js', '.json' ] }),
    buble({ jsx: 'h', objectAssign: 'Object.assign' })
  ]
}).then((bundle) => bundle.write({ sourceMap: true, format: 'cjs', dest: `build/server.js` }))

const client = () => rollup({
  entry: 'src/app/entry.js',
  context: 'window',
  plugins: [
    nodeResolve({ jsnext: true, browser: true }),
    commonjs({ namedExports: { 'preact-redux': ['connect', 'Provider'] } }),
    replace({ '__CLIENT__': true, 'process.env.NODE_ENV': JSON.stringify('production') }),
    buble({ jsx: 'h', objectAssign: 'Object.assign' }),
    uglify(require('./uglify'))
  ]
})
.then((bundle) => bundle.generate({ sourceMap: true, format: 'iife' }))
.then(({ code, map }) => Promise.all([
  fs.outputFileAsync(`build/public/bundle.js`, optimizeJs(code) + `//# sourceMappingURL=/bundle.js.map`),
  fs.outputFileAsync(`build/public/bundle.js.map`, map.toString())
]))

const css = () => new Promise((resolve, reject) => sass({ file: `src/app/styles/entry.scss` }, (err, result) => err ? reject(err) : resolve(result)))
  .then(({ css }) => purifycss(['src/app/components/**/*.js'], css.toString()))
  .then((purified) => cssnano(purified, { autoprefixer: { add: true } }))
  .then(({ css }) => fs.outputFileAsync(`build/public/bundle.css`, css))

const sw = () => swPrecache('build/public/sw.js', {
  cacheId: `${name}`,
  directoryIndex: '/',
  staticFileGlobs: [
    '/',
    './build/public/manifest-*.json',
    // './build/public/bundle-*.{css,js}', // depends if we inlineJs, inlineCss or not
    './build/public/*.{gif,png,svg}' // will not preache /icons
  ],
  navigateFallback: '/',
  dynamicUrlToDependencies: {
    '/': ['./src/server/routes/root.js', './build/public/bundle.css', './build/public/bundle.js', './build/public/manifest.json', './package.json'] // bust cache when these change
  },
  skipWaiting: true,
  stripPrefix: './build/public',
  runtimeCaching: [{
    urlPattern: /\/posts/, // handle remote api call
    handler: 'cacheFirst'
  }]
})

const rev = () => Promise.resolve(nodeRev({
  files: './build/public/**/*.*',
  outputDir: './build/public/',
  file: './build/public/assets.json',
  hash: true  // depends if we inlineJs, inlineCss or not
}))

const clean = () => fs.emptyDirAsync('./build')
const copy = () => fs.copyAsync(`src/app/static/`, `./build/public/`)

const tasks = new Map()
const run = (task) => {
  const start = new Date()
  return tasks.get(task)().then(() => {
    console.log(`[build] '${task}' done in ${new Date().getTime() - start.getTime()}ms`)
  }, (err) => console.error(err.stack))
}

tasks
  .set('clean', clean)
  .set('client', client)
  .set('css', css)
  .set('copy', copy)
  .set('rev', rev)
  .set('server', server)
  .set('sw', sw)
  .set('build', () => run('clean')
    .then(() => Promise.all([run('client'), run('css'), run('copy'), run('server')]))
    .then(() => run('rev'))
    .then(() => run('sw'))
  )

run(/^\w/.test(process.argv[2] || '') ? process.argv[2] : 'build')
<|endoftext|>


<commit_before>require('../testSetup');

import _ from '../../main/util/util';
import {LocaisService} from '../../main/service/LocaisService';
import {LocaisMock} from '../../mock/LocaisMock';

import mongoose from 'mongoose';

/**
 * Teste do Service de Locais.
 *
 * @author Estácio Pereira.
 */
describe('LocaisServiceTest', () => {

    describe('cadastrarLocal deve', () => {
        /**
         * Gera uma String com o tipo especificado que tem mais de 16 mb.
         * Cria uma string de 1000 caracteres e a repete por 16 mil vezes.
         *
         * @param {String} tipo Tipo do arquivo;
         */
        function gerarConteudoMaiorLimite(tipo) {
            let conteudo = `data:${tipo};base64,`;
            let strDezCaracteres = 'aaaaaaaaaa';
            let strMilCaracteres = '';
            for (let i = 0; i < 100; i++) {
                strMilCaracteres += strDezCaracteres;
            }
            for (let i = 0; i < 16 * 1000; i++) {
                conteudo += strMilCaracteres;
            }
            return conteudo;
        }

        it('não salvar um local nulo', () => {
            return LocaisService.cadastrarLocal(null)
                .then(
                    info => assert.fail('Deveria ter rejeitado a promise.'),
                    err => {
                        expect(err).to.be.ok;
                    });
        });

        it('salvar um local corretamente', () => {
            const mockLocal = LocaisMock.getLocal();

            expect(mockLocal.nome).to.not.be.undefined;
            expect(mockLocal.bloco).to.not.be.undefined;
            expect(mockLocal.capacidade).to.not.be.undefined;
            expect(mockLocal.funcionamento).to.not.be.undefined;
            expect(mockLocal.observacoes).to.be.undefined;
            expect(mockLocal.termoDeCondicoes).to.be.undefined;

            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(local => {
                    expect(local._id).to.be.ok;
                    expect(local.nome).to.be.eql(mockLocal.nome);
                    expect(local.bloco).to.be.eql(mockLocal.bloco);
                    expect(local.capacidade).to.be.eql(mockLocal.capacidade);
                    expect(local.funcionamento).to.be.eql(mockLocal.funcionamento);
                });
        });

        it('não salvar um local com imagem de tipo inválido', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemInvalida = {conteudo: 'data:file/pdf;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tipo inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("Tipo de imagem não suportado.");
                });
        });

        it('não salvar um local com imagem de tamanho maior que 16MB e tipo inválido', () => {
            const mockLocal = LocaisMock.getLocal();
            let conteudo = gerarConteudoMaiorLimite('file/pdf');
            const imagemInvalida = { conteudo };
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tipo e tamanho inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("O tamanho máximo suportado é 16MB. Tipo de imagem não suportado.");
                });
        });

        it('não salvar um local com imagem de tamanho maior que 16MB', () => {
            const mockLocal = LocaisMock.getLocal();
            let conteudo = gerarConteudoMaiorLimite('image/png');
            const imagemInvalida = { conteudo };
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tamanho inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("O tamanho máximo suportado é 16MB. ");
                });
        });

        it(' salvar um local com imagem de tipo válido jpeg', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/jpeg;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });

        it(' salvar um local com imagem de tipo válido bitmap', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/bitmap;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });

        it(' salvar um local com imagem de tipo válido png', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/png;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });
    });

    describe('consultarLocais deve', () => {
        it('retornar uma lista contendo todos os locais inseridos', () => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(localPersistido => {
                    expect(localPersistido._id).to.be.ok;
                    return LocaisService.consultarLocais()
                        .catch(err => assert.fail('Deveria ter consultado os locais corretamente.', err))
                        .then(locaisConsultados => {
                            expect(locaisConsultados).to.not.be.empty;
                            expect(_.isArray(locaisConsultados)).to.be.true;
                            expect(_.some(locaisConsultados, localPersistido)).to.be.true;
                        });
                });
        });
    });

    describe('consultarLocalPorId deve', () => {
        it('consultar um local que foi persistido corretamente', () => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(localPersistido => {
                    expect(localPersistido._id).to.be.ok;
                    return LocaisService.consultarLocalPorId(localPersistido._id)
                        .catch(err => assert.fail('Deveria ter consultado o local corretamente.', err))
                        .then(localConsultado => {
                            expect(localConsultado._id).to.be.ok;
                            expect(localConsultado._id).to.be.eql(localPersistido._id);
                            expect(localConsultado).to.be.eql(localPersistido);
                        });
                });
        });

        it('rejeitar promise ao consultar um local com id inexistente', () => {
            return LocaisService.consultarLocalPorId(mongoose.Types.ObjectId())
                .then(
                    info => assert.fail('Deveria ter rejeitado a promise de consulta.', info),
                    err => {
                        expect(err.status).to.be.eql(_.NOT_FOUND);
                        expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                    });
        });
    });

    describe('atualizarLocal deve', () => {
        let localPersistido;

        beforeEach(() => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal).then(local => {
                localPersistido = local;
            });
        });

        it('não atualizar o local caso alguma propriedade obrigatória tenha sido removida', () => {
            const novoLocal = _.clone(localPersistido);

            // Utilizando o delete, ignoramos o campo.
            novoLocal.nome = undefined;
            return LocaisService.atualizarLocal(localPersistido._id, novoLocal)
                .then(
                    info => assert.fail('Deveria ter rejeitado a atualização', info),
                    err => {
                        expect(err).to.be.ok;
                        expect(err).to.be.eql(`Erro de validação: ${_.CONSTANTES_LOCAL.ERRO_VALIDACAO_NOME}.`);
                    });
        });

        it('atualizar o local corretamente caso nenhuma propriedade obrigatória tenha sido violada', () => {
            const novoLocal = _.clone(localPersistido);

            novoLocal.observacoes = 'O auditório é muito legal.';
            return LocaisService.atualizarLocal(localPersistido._id, novoLocal)
                .then(localAtualizado => {
                    expect(localAtualizado._id).to.be.eql(localPersistido._id);
                    expect(localAtualizado).to.be.eql(novoLocal);
                });
        });
    });

    describe('deletarLocal deve', () => {
        let localPersistido;

        beforeEach(() => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal).then(local => {
                localPersistido = local;
            });
        });

        it('retornar promise rejeitada em caso de inexistência do local', () => {
            return LocaisService.deletarLocal(mongoose.Types.ObjectId())
                .then(
                    info => assert.fail('Deveria ter rejeitado a remoção', info),
                    err => {
                        expect(err.status).to.be.eql(_.NOT_FOUND);
                        expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                    });
        });

        it('deletar corretamente um local', () => {
            return LocaisService.deletarLocal(localPersistido._id)
                .catch(err => assert.fail('Deveria ter deletado corretamente.', err))
                .then(localRemovido => {
                    expect(localRemovido).to.be.eql(localPersistido);

                    return LocaisService.consultarLocalPorId(localRemovido._id)
                        .catch(err => {
                            expect(err.status).to.be.eql(_.NOT_FOUND);
                            expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                        });
                });
        });
    });
});
<commit_msg>Mock de string para teste<commit_after>require('../testSetup');

import _ from '../../main/util/util';
import {LocaisService} from '../../main/service/LocaisService';
import {LocaisMock} from '../../mock/LocaisMock';

import mongoose from 'mongoose';

/**
 * Teste do Service de Locais.
 *
 * @author Estácio Pereira.
 */
describe('LocaisServiceTest', () => {

    describe('cadastrarLocal deve', () => {
        /**
         * Cria um Mock de String para o conteúdo.
         * O mock vai retornar no método substrig um início de string válido
         * para ser utilizado no validador.
         * O método split vai retornar apenas um objeto com a propriedade length
         * com tamanho maior que o máximo (16 MB).
         *
         * @param {String} tipo Tipo do arquivo;
         */
        function gerarConteudoMaiorLimite(tipo) {
            let conteudo = `${tipo};base64,asdasdasdasdasdasdasdasddassasdasda`;
            let tamMaiorLimite = 16*1000*1005;
            return {
                substring: () => conteudo,
                split: () => {
                    return {  length: tamMaiorLimite };
                }
            };
        }

        it('não salvar um local nulo', () => {
            return LocaisService.cadastrarLocal(null)
                .then(
                    info => assert.fail('Deveria ter rejeitado a promise.'),
                    err => {
                        expect(err).to.be.ok;
                    });
        });

        it('salvar um local corretamente', () => {
            const mockLocal = LocaisMock.getLocal();

            expect(mockLocal.nome).to.not.be.undefined;
            expect(mockLocal.bloco).to.not.be.undefined;
            expect(mockLocal.capacidade).to.not.be.undefined;
            expect(mockLocal.funcionamento).to.not.be.undefined;
            expect(mockLocal.observacoes).to.be.undefined;
            expect(mockLocal.termoDeCondicoes).to.be.undefined;

            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(local => {
                    expect(local._id).to.be.ok;
                    expect(local.nome).to.be.eql(mockLocal.nome);
                    expect(local.bloco).to.be.eql(mockLocal.bloco);
                    expect(local.capacidade).to.be.eql(mockLocal.capacidade);
                    expect(local.funcionamento).to.be.eql(mockLocal.funcionamento);
                });
        });

        it('não salvar um local com imagem de tipo inválido', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemInvalida = {conteudo: 'data:file/pdf;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tipo inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("Tipo de imagem não suportado.");
                });
        });

        it('não salvar um local com imagem de tamanho maior que 16MB e tipo inválido', () => {
            const mockLocal = LocaisMock.getLocal();
            let conteudo = gerarConteudoMaiorLimite('file/pdf');
            const imagemInvalida = { conteudo };
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tipo e tamanho inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("O tamanho máximo suportado é 16MB. Tipo de imagem não suportado.");
                });
        });

        it('não salvar um local com imagem de tamanho maior que 16MB', () => {
            const mockLocal = LocaisMock.getLocal();
            let conteudo = gerarConteudoMaiorLimite('image/png');
            const imagemInvalida = { conteudo };
            mockLocal.imagens.push(imagemInvalida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => assert.fail('Não deveria salvar imagem com tamanho inválido.'))
                .catch(err => {
                    expect(err).to.exist;
                    expect(err).to.be.equal("O tamanho máximo suportado é 16MB. ");
                });
        });

        it(' salvar um local com imagem de tipo válido jpeg', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/jpeg;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });

        it(' salvar um local com imagem de tipo válido bitmap', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/bitmap;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });

        it(' salvar um local com imagem de tipo válido png', () => {
            const mockLocal = LocaisMock.getLocal();

            const imagemValida = {conteudo: 'data:image/png;base64,yEhsacSk09S86h2bA...'};
            mockLocal.imagens.push(imagemValida);
            return LocaisService.cadastrarLocal(mockLocal)
                .then(local => {
                    expect(local.imagens).to.have.length(1);
                })
                .catch(err => assert.fail('Deveria salvar imagem com tipo válido.'));
        });
    });

    describe('consultarLocais deve', () => {
        it('retornar uma lista contendo todos os locais inseridos', () => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(localPersistido => {
                    expect(localPersistido._id).to.be.ok;
                    return LocaisService.consultarLocais()
                        .catch(err => assert.fail('Deveria ter consultado os locais corretamente.', err))
                        .then(locaisConsultados => {
                            expect(locaisConsultados).to.not.be.empty;
                            expect(_.isArray(locaisConsultados)).to.be.true;
                            expect(_.some(locaisConsultados, localPersistido)).to.be.true;
                        });
                });
        });
    });

    describe('consultarLocalPorId deve', () => {
        it('consultar um local que foi persistido corretamente', () => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal)
                .catch(err => assert.fail('Deveria ter salvado corretamente.', err))
                .then(localPersistido => {
                    expect(localPersistido._id).to.be.ok;
                    return LocaisService.consultarLocalPorId(localPersistido._id)
                        .catch(err => assert.fail('Deveria ter consultado o local corretamente.', err))
                        .then(localConsultado => {
                            expect(localConsultado._id).to.be.ok;
                            expect(localConsultado._id).to.be.eql(localPersistido._id);
                            expect(localConsultado).to.be.eql(localPersistido);
                        });
                });
        });

        it('rejeitar promise ao consultar um local com id inexistente', () => {
            return LocaisService.consultarLocalPorId(mongoose.Types.ObjectId())
                .then(
                    info => assert.fail('Deveria ter rejeitado a promise de consulta.', info),
                    err => {
                        expect(err.status).to.be.eql(_.NOT_FOUND);
                        expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                    });
        });
    });

    describe('atualizarLocal deve', () => {
        let localPersistido;

        beforeEach(() => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal).then(local => {
                localPersistido = local;
            });
        });

        it('não atualizar o local caso alguma propriedade obrigatória tenha sido removida', () => {
            const novoLocal = _.clone(localPersistido);

            // Utilizando o delete, ignoramos o campo.
            novoLocal.nome = undefined;
            return LocaisService.atualizarLocal(localPersistido._id, novoLocal)
                .then(
                    info => assert.fail('Deveria ter rejeitado a atualização', info),
                    err => {
                        expect(err).to.be.ok;
                        expect(err).to.be.eql(`Erro de validação: ${_.CONSTANTES_LOCAL.ERRO_VALIDACAO_NOME}.`);
                    });
        });

        it('atualizar o local corretamente caso nenhuma propriedade obrigatória tenha sido violada', () => {
            const novoLocal = _.clone(localPersistido);

            novoLocal.observacoes = 'O auditório é muito legal.';
            return LocaisService.atualizarLocal(localPersistido._id, novoLocal)
                .then(localAtualizado => {
                    expect(localAtualizado._id).to.be.eql(localPersistido._id);
                    expect(localAtualizado).to.be.eql(novoLocal);
                });
        });
    });

    describe('deletarLocal deve', () => {
        let localPersistido;

        beforeEach(() => {
            const mockLocal = LocaisMock.getLocal();
            return LocaisService.cadastrarLocal(mockLocal).then(local => {
                localPersistido = local;
            });
        });

        it('retornar promise rejeitada em caso de inexistência do local', () => {
            return LocaisService.deletarLocal(mongoose.Types.ObjectId())
                .then(
                    info => assert.fail('Deveria ter rejeitado a remoção', info),
                    err => {
                        expect(err.status).to.be.eql(_.NOT_FOUND);
                        expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                    });
        });

        it('deletar corretamente um local', () => {
            return LocaisService.deletarLocal(localPersistido._id)
                .catch(err => assert.fail('Deveria ter deletado corretamente.', err))
                .then(localRemovido => {
                    expect(localRemovido).to.be.eql(localPersistido);

                    return LocaisService.consultarLocalPorId(localRemovido._id)
                        .catch(err => {
                            expect(err.status).to.be.eql(_.NOT_FOUND);
                            expect(err.mensagem).to.be.eql(_.CONSTANTES_LOCAL.ERRO_LOCAL_NAO_ENCONTRADO);
                        });
                });
        });
    });
});
<|endoftext|>


<commit_before>var participants = [];		//people currently running an instance of the hangout
var queue = [];				//list of participant ids waiting to speak. 0 is the current speaker
var timeOut = -1;				//unix time of when the current speaker's turn will end
var currentSpeakerId;		//id of the current speaker; on updates if it doesn't match queue, manager sets new time

var greenDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMNIxL4LdEAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuNJREFUeNrtmy2M2mAYx3/tTsxchliWnDi1kAxBhsE0aTBkbkPg0Ljhlp1CYnF3Dj03wbBnSJOaMywTXLKcOkGyICBnJrhsog9Lj7Xlo3y078s/qSx9/z+e9/N5XoMdy3Z5BpwBOaAgTw44B14CM2AC3ANDYCDPEBg5Fo+7bJ+xI9MvgBJQB97H/Lke0AH6jsU0sQBsFwMoAs0tmI6C0QJuHIs/iQAgxsvAFwnpfWgM1IDruCCMmObfAl+B1xxGd0DVsfi+VwC2y3MJxU8kQ22g6Vj83jkA2+UN8AM4IVmaAXnH4nadl8w1zX+Q6Slp5pE2DaWN2wdgu3wEuiRfXWnr9gDYLp+BS9KjS2lz/DFAaKbJvF8Nx+JqYwDSn7qkWxXH4tvaAGS0H6KGcmGzgxExzz8kdLTfdIo8DVonhA2CLYXMz6fI1koRIMvbAWqqsLhsNgI2Nj8PuLbfx94h699ALXaBssLmEW/lwAiQf//XHre0h9IYeDWPAn8EFDUwj3gsBnWBJvqo+aQLyBneBL2UcSym8wgooZ9K/i5Q1xBAHcCQc/sZeurExEta6KozEy9Lo6tyJl6qSlcVjgCOXcDL0uqqcxPIaAwgY6K5TA33AH5NTLzKDF11b6LO0fcmGpqoewC6igZHAMcuACONAYxMqcPraWi+51g8zhdCHQ0BdOYLIYC+hgD6/wBIBWZPs/Cf+iMAQrKniqrl3wvMdYOXNlJdY/H6FIDkymoaAKhFZYev8VLIqupOPBIIQMhUFQZQXSyu/u9ARCoo2gqabwcVVYedCDVRK1s0IyT7HQhAqqnyCgHIh1WSh54JSl1dRQHzlagK8shDUamwbKTYfCOqSnQpAIFwBVyk0PzFsjphWOPCRMqKphurmF8LgEBIQ/F0ZVnYbwxAIOh7ZcY3O5wmbLHUxiuGvl33Re2vzcXKDcqHs8C7PW+lx/LNbBzzsSNgIRr0vDobAkO/y9MRMBJ9ff4vhV3btM/DqFkAAAAASUVORK5CYII=');
var greenDotOverlay = greenDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var yellowDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMXCA5pLkoAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuFJREFUeNrtmyts21AUhj97BiPVCiZLBUVzpAVUKykuqca2gLDgsoVNKwoMDWtZ8NhAFloSXJJpIJXmooJIVkCjkoFbbcAnk5vZzsN52Pfml8yS+P5fzn2ecy3WrMB3XgAHQBk4lqcMHAKvAQU8APfAAOjLMwCGrqee1tk+a02mXwGnwDnwIePPdYE20HM9Nc4tgMB3LOAEaKzAdBqMJnDjeupPLgCI8TPgq4T0JjQCasB1VhBWRvPvgG/AG7ajO6DqeurHRgEEvvNSQvEz+VALaLie+r12AIHvvAV+Ag75kgKOXE/dLvIle0HzH2V6ypt5pE0DaePqAQS+8wnokH91pK2rAxD4zhfgkuLoUtqcfQwQmkUyH1Xd9dTV0gCkP3Uotiqup74vDEBG+wF6qJw0O1gp8/xjTkf7ZafIvbh1QtIg2NTI/GSKbM4VAbK87aOnjqeXzVbMxubXFtf2m9g7lKIbqOkucKaxecTbWWwEyL8fbHBLuy2NAHcSBdEIODHAPOLxJK4LNDBHjWddQM7wHjBL+66nxpMIOMU8nUa7wLmBAM4BLDm3V5gpxyZMWpiqA5swS2OqyjZhqspUHe8A7LpAmKU1VYc2sG8wgH0bw2UbuAeI6sEmrMwwVfc2+hx9L6OBjb4HoPOovwOw6wIwNBjA0JY6vK6B5ruup54mC6G2gQDak4UQQM9AAL1/AKQCs2tY+I+jEQAJ2VNN1YzuBSa6IUwb6a6ReH0OQHJlNQMA1NKyw9eEKWRddSceiQUgZKoaA6hOF1f/dyAiFRQtDc234oqqd0VScZ+WDx5p9O8fJVWSJ54JSl1dRQPzlbQK8tRDUamwrBfYfD2tSnQmAIFwBVwU0PzFrDrhxEEwYWAsUtF0fR7zCwEQCEUonq7MCvulAQgEc6/MRGaHvZwtlloyz98u+kXjr81lyg3Ki0vA+w1vpUfyzlIW85kjYCoazLw6mwDDvMvTKTByfX3+L4KL/xorzXR/AAAAAElFTkSuQmCC');
	var yellowDotOverlay = yellowDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var buttonDisabled = false;	//true when button is clicked to prevent joining the queue several times

gapi.hangout.onApiReady.add(function(eventObj){
	if (eventObj.isApiReady) {
		buttonDisabled = false;
		participants = gapi.hangout.getEnabledParticipants();
		onDataChange();

		gapi.hangout.onParticipantsChanged.add(onParticipantsChange);
		gapi.hangout.data.onStateChanged.add(onDataChange);

		setInterval(updateTimeOutText, 200);
	}
});

function onParticipantsChange(eventObj) {
	participants = eventObj.participants;

	onServerUpdate();
};

function onDataChange (eventObj){
	var state = gapi.hangout.data.getState();
	queue = JSON.parse(state.queue);
	try{
		if (state.timeOut && state.serverTime){
			timeOut = JSON.parse(state.timeOut) - (JSON.parse(state.serverTime) - new Date().getTime());
		}
		else{
			timeOut = -1;
		}
	}
	catch (e){
		console.log(e);
		timeOut = -1;
	}

	onServerUpdate();
}

function onServerUpdate(){
	console.log("onServerUpdate called");
	buttonDisabled = false;
	printParticipants();
	clearOverlay();
	var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
	if (queuePosition == 0){
		placeGreenDot();
		document.getElementById('queueButton').innerHTML = "I Am Done";
	}
	else if (queuePosition > 0){
		placeYellowDot();
		document.getElementById('queueButton').innerHTML = "Put Hand Down";
	}
	else{
		document.getElementById('queueButton').innerHTML = "Raise My Hand";
	}

	//lowest order participant manages time updates
	//and checks for disconnected queue memembers
	if (isManager()){
		console.log("managing update...");
		var validSpeaker = false;
		var updateRequired = false;
		while (queue.length > 0 && !validSpeaker){

			var speakerInHangout = false;
			for (var i = 0; i < participants.length; i++){
				if (participants[i].id == queue[0]){
					if (!participants[i].hasAppEnabled){
						console.log("speaker doesn't have app on");
						queue.shift();
						updateRequired = true;
						speakerInHangout = true;
					}
					else{
						console.log("speaker running app");
						validSpeaker = true;
						speakerInHangout = true;
					}
				}
			}
			if (!speakerInHangout){
				console.log("speaker not in hangout");
				queue.shift();
				updateRequire = true;
			}
		}
		if (timeOut == -1 || new Date().getTime() > timeOut){
			console.log('new timeout value required');
			updateRequired = true;
		}
		if (updateRequired){
			console.log("sending new update");
			sendNewSpeaker();
		}
	}
}

//sends queue and updated timestamps
function sendNewSpeaker(){
	timeOut = new Date().getTime() + 1000*60;
	gapi.hangout.data.submitDelta({	'queue':JSON.stringify(queue),
									'timeOut': JSON.stringify(timeOut),
									'serverTime': JSON.stringify(new Date().getTime())});
}

function printParticipants(){
	if (queue.length > 0){
		document.getElementById('speakerName').innerHTML = getParticipantNameById(queue[0]);
		document.getElementById('speakerDisplay').style.display = 'inline';
	}
	else {
		document.getElementById('speakerDisplay').style.display = 'none';
	}

	var queueHTML = ""
	if (queue.length > 1){
		for (var i = 1; i < queue.length; i++){
			queueHTML = queueHTML + getParticipantNameById(queue[i]) + '
';
		}
		document.getElementById('queueMembers').innerHTML = queueHTML;
		document.getElementById('queueMembers').style.display = 'inline';
	}
	else {
		document.getElementById('queueMembers').style.display = 'none';
	}

	var memberHTML = "";
	for (var i = 0; i < participants.length; i++){
		if (queue.indexOf(participants[i].id) == -1){
			memberHTML = memberHTML + participants[i].person.displayName + '
';
		}
	}
	document.getElementById('otherParticipants').innerHTML = memberHTML;
}

function getParticipantNameById(id){
	for (var i = 0; i < participants.length; i++){
		if (participants[i].id == id){
			return participants[i].person.displayName;
		}
	}
	return '';
}

function buttonClick(){
	if (!buttonDisabled){
		buttonDisabled = true;
		var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
		if (queuePosition == 0){
			queue.shift();
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
		else if (queuePosition == -1){
			queue.push(gapi.hangout.getParticipantId());
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
	}
}

function placeGreenDot(){
	greenDotOverlay.setPosition(.4, -.35);
	greenDotOverlay.setVisible(true);
}

function placeYellowDot(){
	yellowDotOverlay.setPosition(.4, -.35);
	yellowDotOverlay.setVisible(true);
}

function clearOverlay(){
	greenDotOverlay.setVisible(false);
	yellowDotOverlay.setVisible(false);
}

//only one participant should be manager; this code might need updating
//might not be necessary at all - ok to send multiple identical updates
function isManager(){
	return true;
	return (participants[0].id == gapi.hangout.getParticipantId());
}

//what happens if manager is alt tabbed?
function updateTimeOutText(){
	if (timeOut != -1 && queue.length > 0){
		var timeDif = timeOut - new Date().getTime();
		document.getElementById('timeLeft').innerHTML = '  - ' + Math.max(0, Math.round(timeDif/1000)) + ' secounds';
		if (isManager() && timeDif < 0 || timeDif < 2000){
			console.log("Time up!");
			//force update iff queue is empty
			if (queue.length > 1){
				queue.shift();
			}
			sendNewSpeaker();
		}
	}
	else{
		document.getElementById('timeLeft').innerHTML = '';
	}
}<commit_msg>fixed undefined check<commit_after>var participants = [];		//people currently running an instance of the hangout
var queue = [];				//list of participant ids waiting to speak. 0 is the current speaker
var timeOut = -1;				//unix time of when the current speaker's turn will end
var currentSpeakerId;		//id of the current speaker; on updates if it doesn't match queue, manager sets new time

var greenDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMNIxL4LdEAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuNJREFUeNrtmy2M2mAYx3/tTsxchliWnDi1kAxBhsE0aTBkbkPg0Ljhlp1CYnF3Dj03wbBnSJOaMywTXLKcOkGyICBnJrhsog9Lj7Xlo3y078s/qSx9/z+e9/N5XoMdy3Z5BpwBOaAgTw44B14CM2AC3ANDYCDPEBg5Fo+7bJ+xI9MvgBJQB97H/Lke0AH6jsU0sQBsFwMoAs0tmI6C0QJuHIs/iQAgxsvAFwnpfWgM1IDruCCMmObfAl+B1xxGd0DVsfi+VwC2y3MJxU8kQ22g6Vj83jkA2+UN8AM4IVmaAXnH4nadl8w1zX+Q6Slp5pE2DaWN2wdgu3wEuiRfXWnr9gDYLp+BS9KjS2lz/DFAaKbJvF8Nx+JqYwDSn7qkWxXH4tvaAGS0H6KGcmGzgxExzz8kdLTfdIo8DVonhA2CLYXMz6fI1koRIMvbAWqqsLhsNgI2Nj8PuLbfx94h699ALXaBssLmEW/lwAiQf//XHre0h9IYeDWPAn8EFDUwj3gsBnWBJvqo+aQLyBneBL2UcSym8wgooZ9K/i5Q1xBAHcCQc/sZeurExEta6KozEy9Lo6tyJl6qSlcVjgCOXcDL0uqqcxPIaAwgY6K5TA33AH5NTLzKDF11b6LO0fcmGpqoewC6igZHAMcuACONAYxMqcPraWi+51g8zhdCHQ0BdOYLIYC+hgD6/wBIBWZPs/Cf+iMAQrKniqrl3wvMdYOXNlJdY/H6FIDkymoaAKhFZYev8VLIqupOPBIIQMhUFQZQXSyu/u9ARCoo2gqabwcVVYedCDVRK1s0IyT7HQhAqqnyCgHIh1WSh54JSl1dRQHzlagK8shDUamwbKTYfCOqSnQpAIFwBVyk0PzFsjphWOPCRMqKphurmF8LgEBIQ/F0ZVnYbwxAIOh7ZcY3O5wmbLHUxiuGvl33Re2vzcXKDcqHs8C7PW+lx/LNbBzzsSNgIRr0vDobAkO/y9MRMBJ9ff4vhV3btM/DqFkAAAAASUVORK5CYII=');
var greenDotOverlay = greenDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var yellowDot = gapi.hangout.av.effects.createImageResource('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB90BDwMXCA5pLkoAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAuFJREFUeNrtmyts21AUhj97BiPVCiZLBUVzpAVUKykuqca2gLDgsoVNKwoMDWtZ8NhAFloSXJJpIJXmooJIVkCjkoFbbcAnk5vZzsN52Pfml8yS+P5fzn2ecy3WrMB3XgAHQBk4lqcMHAKvAQU8APfAAOjLMwCGrqee1tk+a02mXwGnwDnwIePPdYE20HM9Nc4tgMB3LOAEaKzAdBqMJnDjeupPLgCI8TPgq4T0JjQCasB1VhBWRvPvgG/AG7ajO6DqeurHRgEEvvNSQvEz+VALaLie+r12AIHvvAV+Ag75kgKOXE/dLvIle0HzH2V6ypt5pE0DaePqAQS+8wnokH91pK2rAxD4zhfgkuLoUtqcfQwQmkUyH1Xd9dTV0gCkP3Uotiqup74vDEBG+wF6qJw0O1gp8/xjTkf7ZafIvbh1QtIg2NTI/GSKbM4VAbK87aOnjqeXzVbMxubXFtf2m9g7lKIbqOkucKaxecTbWWwEyL8fbHBLuy2NAHcSBdEIODHAPOLxJK4LNDBHjWddQM7wHjBL+66nxpMIOMU8nUa7wLmBAM4BLDm3V5gpxyZMWpiqA5swS2OqyjZhqspUHe8A7LpAmKU1VYc2sG8wgH0bw2UbuAeI6sEmrMwwVfc2+hx9L6OBjb4HoPOovwOw6wIwNBjA0JY6vK6B5ruup54mC6G2gQDak4UQQM9AAL1/AKQCs2tY+I+jEQAJ2VNN1YzuBSa6IUwb6a6ReH0OQHJlNQMA1NKyw9eEKWRddSceiQUgZKoaA6hOF1f/dyAiFRQtDc234oqqd0VScZ+WDx5p9O8fJVWSJ54JSl1dRQPzlbQK8tRDUamwrBfYfD2tSnQmAIFwBVwU0PzFrDrhxEEwYWAsUtF0fR7zCwEQCEUonq7MCvulAQgEc6/MRGaHvZwtlloyz98u+kXjr81lyg3Ki0vA+w1vpUfyzlIW85kjYCoazLw6mwDDvMvTKTByfX3+L4KL/xorzXR/AAAAAElFTkSuQmCC');
	var yellowDotOverlay = yellowDot.createOverlay(
	{'scale':
		{'magnitude': 0.15, 'reference': gapi.hangout.av.effects.ScaleReference.WIDTH}});

var buttonDisabled = false;	//true when button is clicked to prevent joining the queue several times

gapi.hangout.onApiReady.add(function(eventObj){
	if (eventObj.isApiReady) {
		buttonDisabled = false;
		participants = gapi.hangout.getEnabledParticipants();
		onDataChange();

		gapi.hangout.onParticipantsChanged.add(onParticipantsChange);
		gapi.hangout.data.onStateChanged.add(onDataChange);

		setInterval(updateTimeOutText, 200);
	}
});

function onParticipantsChange(eventObj) {
	participants = eventObj.participants;

	onServerUpdate();
};

function onDataChange (eventObj){
	var state = gapi.hangout.data.getState();
	queue = JSON.parse(state.queue);
	try{
		if (typeof state.timeOut === "undefined" || typeof state.timeOut === "undefined"){
			timeOut = -1;
		}
		else{
			timeOut = JSON.parse(state.timeOut) - (JSON.parse(state.serverTime) - new Date().getTime());
		}
	}
	catch (e){
		console.log(e);
		timeOut = -1;
	}

	onServerUpdate();
}

function onServerUpdate(){
	console.log("onServerUpdate called");
	buttonDisabled = false;
	printParticipants();
	clearOverlay();
	var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
	if (queuePosition == 0){
		placeGreenDot();
		document.getElementById('queueButton').innerHTML = "I Am Done";
	}
	else if (queuePosition > 0){
		placeYellowDot();
		document.getElementById('queueButton').innerHTML = "Put Hand Down";
	}
	else{
		document.getElementById('queueButton').innerHTML = "Raise My Hand";
	}

	//lowest order participant manages time updates
	//and checks for disconnected queue memembers
	if (isManager()){
		console.log("managing update...");
		var validSpeaker = false;
		var updateRequired = false;
		while (queue.length > 0 && !validSpeaker){

			var speakerInHangout = false;
			for (var i = 0; i < participants.length; i++){
				if (participants[i].id == queue[0]){
					if (!participants[i].hasAppEnabled){
						console.log("speaker doesn't have app on");
						queue.shift();
						updateRequired = true;
						speakerInHangout = true;
					}
					else{
						console.log("speaker running app");
						validSpeaker = true;
						speakerInHangout = true;
					}
				}
			}
			if (!speakerInHangout){
				console.log("speaker not in hangout");
				queue.shift();
				updateRequire = true;
			}
		}
		if (timeOut == -1 || new Date().getTime() > timeOut){
			console.log('new timeout value required');
			updateRequired = true;
		}
		if (updateRequired){
			console.log("sending new update");
			sendNewSpeaker();
		}
	}
}

//sends queue and updated timestamps
function sendNewSpeaker(){
	timeOut = new Date().getTime() + 1000*60;
	gapi.hangout.data.submitDelta({	'queue':JSON.stringify(queue),
									'timeOut': JSON.stringify(timeOut),
									'serverTime': JSON.stringify(new Date().getTime())});
}

function printParticipants(){
	if (queue.length > 0){
		document.getElementById('speakerName').innerHTML = getParticipantNameById(queue[0]);
		document.getElementById('speakerDisplay').style.display = 'inline';
	}
	else {
		document.getElementById('speakerDisplay').style.display = 'none';
	}

	var queueHTML = ""
	if (queue.length > 1){
		for (var i = 1; i < queue.length; i++){
			queueHTML = queueHTML + getParticipantNameById(queue[i]) + '
';
		}
		document.getElementById('queueMembers').innerHTML = queueHTML;
		document.getElementById('queueMembers').style.display = 'inline';
	}
	else {
		document.getElementById('queueMembers').style.display = 'none';
	}

	var memberHTML = "";
	for (var i = 0; i < participants.length; i++){
		if (queue.indexOf(participants[i].id) == -1){
			memberHTML = memberHTML + participants[i].person.displayName + '
';
		}
	}
	document.getElementById('otherParticipants').innerHTML = memberHTML;
}

function getParticipantNameById(id){
	for (var i = 0; i < participants.length; i++){
		if (participants[i].id == id){
			return participants[i].person.displayName;
		}
	}
	return '';
}

function buttonClick(){
	if (!buttonDisabled){
		buttonDisabled = true;
		var queuePosition = queue.indexOf(gapi.hangout.getParticipantId());
		if (queuePosition == 0){
			queue.shift();
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
		else if (queuePosition == -1){
			queue.push(gapi.hangout.getParticipantId());
			gapi.hangout.data.submitDelta({'queue':JSON.stringify(queue)});
		}
	}
}

function placeGreenDot(){
	greenDotOverlay.setPosition(.4, -.35);
	greenDotOverlay.setVisible(true);
}

function placeYellowDot(){
	yellowDotOverlay.setPosition(.4, -.35);
	yellowDotOverlay.setVisible(true);
}

function clearOverlay(){
	greenDotOverlay.setVisible(false);
	yellowDotOverlay.setVisible(false);
}

//only one participant should be manager; this code might need updating
//might not be necessary at all - ok to send multiple identical updates
function isManager(){
	return true;
	return (participants[0].id == gapi.hangout.getParticipantId());
}

//what happens if manager is alt tabbed?
function updateTimeOutText(){
	if (timeOut != -1 && queue.length > 0){
		var timeDif = timeOut - new Date().getTime();
		document.getElementById('timeLeft').innerHTML = '  - ' + Math.max(0, Math.round(timeDif/1000)) + ' secounds';
		if (isManager() && timeDif < 0 || timeDif < 2000){
			console.log("Time up!");
			//force update iff queue is empty
			if (queue.length > 1){
				queue.shift();
			}
			sendNewSpeaker();
		}
	}
	else{
		document.getElementById('timeLeft').innerHTML = '';
	}
}<|endoftext|>


<commit_before>import clamp from '../util/clamp';

function sortByArea ( a, b ) {
	return ( a.x * a.y ) - ( b.y * b.x );
}

// most parts taken from akm2's script:
// http://jsdo.it/akm2/xoYx (line 230++)
export default function ( points, maxPointCount, accuracy, width, height ) {
	let result = [ ];
	let sidePointCount = Math.ceil( width / ( 100 - accuracy ) ) * 2 + Math.ceil( height / ( 100 - accuracy ) ) * 2 + 2;
	let pointCount = Math.max( points.length, maxPointCount );
	let randomPointCount = clamp( pointCount - sidePointCount, 0, maxPointCount - sidePointCount )
	let increment = ( pointCount / randomPointCount );

	let i = 0;
	let x = 0;
	let y = 0;
	let len = 0;

	// sort the points array so that it looks like randomly shuffled
	// (it's not really random, because we want to be able to test the resulting images)
	points.sort( sortByArea );

	for ( i = 0, len = pointCount; i < len; i += increment ) {
		result.push( { x: points[~~i].x, y: points[~~i].y } );
	}

	// add more points along the edges so we always use the full canvas,
	for ( x = 0; x < width; x += ( 100 - accuracy ) ) {
		result.push( { x: ~~x, y: 0 } );
		result.push( { x: ~~x, y: height } );
	}

	for ( y = 0; y < height; y += ( 100 - accuracy ) ) {
		result.push( { x: 0, y: ~~y } );
		result.push( { x: width, y: ~~y } );
	}

	result.push( { x: 0, y: height } );
	result.push( { x: width, y: height } );

	points = null;

	return result;
}
<commit_msg>update vertex collection algorithm: add "background grid" points<commit_after>import clamp from '../util/clamp';

function addVertex ( x, y, hash ) {
	let resultKey = x + '|' + y;

	if ( ! hash[resultKey] ) {
		hash[resultKey] = { x, y };
	}

	resultKey = null;
}

export default function ( points, maxPointCount, accuracy, width, height ) {
	// using hash for all points to make sure we have a set of unique vertices.
	let resultHash = { };

	// use 25% of max point count to create a background grid.
	// this avoids having too many "big" triangles in areas of the image with low contrast
	// next to very small ones in areas with high contrast
	// for every other row, start the x value at > 0, so the grid doesn't look too regular
	let gridPointCount = Math.max( ~~( maxPointCount * ( 1 - accuracy ) ), 5 );

	// http://stackoverflow.com/a/4107092/229189
	let gridColumns = Math.round( Math.sqrt( gridPointCount ) );
	let gridRows = Math.round( Math.ceil( gridPointCount / gridColumns ) );

	let xIncrement = ~~( width / gridColumns );
	let yIncrement = ~~( height / gridRows );

	let rowIndex = 0;
	let startX = 0;

	let x = 0;
	let y = 0;

	for ( y = 0; y < height; y+= yIncrement ) {
		rowIndex++;

		startX = rowIndex % 2 === 0 ? ~~( xIncrement / 2 ) : 0;

		for ( x = startX; x < width; x += xIncrement ) {
			if ( x < width && y < height ) {
				// "distorting" the grid a little bit so that the
				// background vertices don't appear to be on a straight line (which looks boring)
				addVertex(
					~~( x + ( Math.cos( y ) * ( yIncrement ) ) ),
					~~( y + ( Math.sin( x ) * ( xIncrement ) ) ),
					resultHash
				);
			}
		}
	}

	// add points in the corners
	addVertex( 0, 0, resultHash );
	addVertex( width - 1, 0, resultHash );
	addVertex( width - 1, height - 1, resultHash );
	addVertex( 0, height - 1, resultHash );

	// add points from all edge points
	let remainingPointCount = maxPointCount - Object.keys( resultHash ).length;
	let edgePointCount = points.length;
	let increment = ~~( edgePointCount / remainingPointCount );

	if ( maxPointCount > 0 && increment > 0 ) {
		let i = 0;

		for ( i = 0; i < edgePointCount; i += increment ) {
			addVertex( points[i].x, points[i].y, resultHash );
		}
	}

	points = null;

	return Object.keys( resultHash ).map( function ( key ) { return resultHash[key] } );
}
<|endoftext|>


<commit_before>var fs = require("fs"),
    os = require("os"),
    path = require("path"),
    shell = require("shelljs"),
    spawn = require("child_process").spawn,
    temporary = require("temporary");
var gulp = require('gulp');
var yaml = require("js-yaml");

var argv = require('yargs')
    .usage('Usage: $0 [command] -O [num] -y [num]')
    .options('O', { alias: 'open-api', default: true })
    .options('h', { alias: 'sec-headers', default: undefined })
    .options('t', { alias: "tag", default: 'reyesr/mongo-supervisor' })
    .options('s', { alias: "sudo-docker", default: false })
    .options('l', { alias: "location", default: "build_dir" })
    .argv;

if (argv._.length == 0) {
    displayUsage();
} else {
    argv._.forEach(function (command) {
        switch (command) {
            case 'create-docker-img':
                createDockerImage(argv);
                break;
            case 'run-docker-img':
                createDockerImage(argv);
                runDocker(argv);
                break;
            case 'package':
                buildAppAt(tmpdir.location, createSecurity(options));
                console.log("Application built, available at " + options.location);
                break;
            default:
                console.error("Unrecognized option: " + command);
                console.error("Aborting.");
                process.exit(1);
        }
    });
}

function createSecurity(options) {
    var security = {};
    if (options.openApi) {
        security.open = true;
        console.log("Warning: No security check are set on this image, make sure its instance cannot be publicly accessed.")
    }
    if (typeof options.secHeaders == "string") {
        var splitIndex = options.secHeaders.indexOf(':');
        if (splitIndex>=0) {
            var header = options.secHeaders.substr(0, splitIndex);
            var value = options.secHeaders.substr(splitIndex+1);
            console.log("Headers:", header, value);
            security["header-key-name"] = header;
            security["header-key-value"] = value;
        } else {
            console.error("Error: security headers must be of the form X-MY-HEADER:MySecretValue");
            console.error("Aborted.");
            process.exit(99);
        }
    }
    return security;
}

function createDockerImage(options) {
    var tmpdir = new temporary.Dir();
    console.log("Temp dir", tmpdir);
    var security = createSecurity(options);
    buildAppAt(tmpdir.path + "/mongo-supervisor", security);
    buildDocker(tmpdir.path, options);

}

function displayUsage() {
    console.log("Usage: build.js [OPTIONS] [COMMAND]");
    console.log(" Possible commands: package create-docker-img run-docker");
    console.log("      package : creates a packaged mongo-supervisor");
    console.log("      create-docker-img : creates a docker image based on the docker/Dockerfile");
    console.log("      run-docker : creates and run a docker image based on the docker/Dockerfile");
    console.log(" Possible options:");
    console.log("     --open-api -O : Open rest api (no security check) [docker]");
    console.log("     --sec-headers -h HEAD:VALUE : define security header and value [docker]");
    console.log("     --tag -t tag : user this tag as the image name [docker]");
    console.log("     --sudo-docker -s : docker needs to be run with sudo [docker]");
    console.log("     --location -l : directory where to create the package [package only]");
}

function buildDocker(targetDir, options, callback) {
    console.log("building docker");
    shell.cp("docker/Dockerfile", targetDir);
    var cmdline = [].concat((options.sudoDocker?"sudo ":[]), "docker", "build", "-t", options.tag, targetDir);

    //var result = shell.exec(cmdline);
    executeCommand(cmdline, function(code) {
        if (code == 0) {
            console.log("Docker image created.");
            console.log("You can run the image with the following command: docker run --rm -p 127.0.0.1:3000:3000 -t -i '" + options.tag + "'");
        } else {
            console.error("Error, docker returned with code " + result.code + ", aborted.");
            process.exit(98);
        }
    });
}

function runDocker(options) {
    var cmdline = (options.sudoDocker?"sudo ":"") + "docker run --rm -p 127.0.0.1:3000:3000  --name=mongo_supervisor -t -i '" + options.tag + "'";
    console.log("running [" + cmdline + "]");
    var result = shell.exec(cmdline);
};

function buildAppAt(target, securityObject) {
    // shell.rm("-fr", target);
    shell.mkdir("-p", target)
    shell.cp("-rf", ["lib", "routes", "node_modules"], target);
    shell.cp(["run", "app.js", "package.json", "LICENSE"], target);
    var configobj = yaml.safeLoad(fs.readFileSync("docker/mongosupervisor.yaml", 'utf8'));
    configobj.security = securityObject || {open: true};
    fs.writeFileSync(target + "/mongosupervisor.yaml", yaml.safeDump(configobj));
}

var currentProcess = null;
function executeCommand(cmdline, callback) {
    currentProcess = spawn(cmdline[0], cmdline.slice(1));

    currentProcess.stdout.on("data", function(data) {
        process.stdout.write(data);
    });
    currentProcess.stderr.on("data", function(data) {
        process.stderr.write(data);
    });
    currentProcess.on("close", function(code) {
        console.log("Received exit code " + code + " for " + cmdline);
        currentProcess = null;
        callback(code);
    });

}

function killCurrentCommand() {
    if (currentProcess != null && typeof currentProcess.kill == "function") {
        currentProcess.kill();
        currentProcess = null;
    }
}

process.on("SIGINT", killCurrentCommand);
process.on("SIGHUP", killCurrentCommand);
process.on("exit", killCurrentCommand);<commit_msg>fix docker-run in build<commit_after>var fs = require("fs"),
    os = require("os"),
    path = require("path"),
    shell = require("shelljs"),
    spawn = require("child_process").spawn,
    temporary = require("temporary"),
    async = require("async");
var gulp = require('gulp');
var yaml = require("js-yaml");

var argv = require('yargs')
    .usage('Usage: $0 [command] -O [num] -y [num]')
    .options('O', { alias: 'open-api', default: true })
    .options('h', { alias: 'sec-headers', default: undefined })
    .options('t', { alias: "tag", default: 'reyesr/mongo-supervisor' })
    .options('s', { alias: "sudo-docker", default: false })
    .options('l', { alias: "location", default: "build_dir" })
    .argv;

function processCommands(cmdList, callback) {

    var nextCallback = function(err) {
        if (!err) {
            processCommands(cmdList.slice(1), callback);
        } else {
            callback(err);
        }
    }

    if (cmdList.length>0) {
        var command = cmdList[0];

        switch (command) {
            case 'docker-create':
                createDockerImage(argv, nextCallback);
                break;
            case 'docker-run':
                async.waterfall([
                    function(cb) {
                        createDockerImage(argv, cb);
                    },
                    function(cb) {
                        runDocker(argv, cb);
                    }
                ], nextCallback);
                break;
            case 'package':
                buildAppAt(tmpdir.location, createSecurity(options));
                console.log("Application built, available at " + options.location);
                nextCallback();
                break;
            default:
                console.error("Unrecognized option: " + command);
                console.error("Aborting.");
                process.exit(1);
        }

    } else {
        callback(null);
    }
}

if (argv._.length == 0) {
    displayUsage();
} else {
    processCommands(argv._, function() {

    });
}

function createSecurity(options) {
    var security = {};
    if (options.openApi) {
        security.open = true;
        console.log("Warning: No security check are set on this image, make sure its instance cannot be publicly accessed.")
    }
    if (typeof options.secHeaders == "string") {
        var splitIndex = options.secHeaders.indexOf(':');
        if (splitIndex>=0) {
            var header = options.secHeaders.substr(0, splitIndex);
            var value = options.secHeaders.substr(splitIndex+1);
            console.log("Headers:", header, value);
            security["header-key-name"] = header;
            security["header-key-value"] = value;
        } else {
            console.error("Error: security headers must be of the form X-MY-HEADER:MySecretValue");
            console.error("Aborted.");
            process.exit(99);
        }
    }
    return security;
}

function createDockerImage(options, callback) {
    var tmpdir = new temporary.Dir();
    console.log("Temp dir", tmpdir);
    var security = createSecurity(options);
    buildAppAt(tmpdir.path + "/mongo-supervisor", security);
    buildDocker(tmpdir.path, options, callback);
}

function displayUsage() {
    console.log("Usage: build.js [OPTIONS] [COMMAND]");
    console.log(" Possible commands: package docker-build docker-run");
    console.log("      package : creates a packaged mongo-supervisor");
    console.log("      docker-create : creates a docker image based on the docker/Dockerfile");
    console.log("      docker-run : creates and run a docker image based on the docker/Dockerfile");
    console.log(" Possible options:");
    console.log("     --open-api -O : Open rest api (no security check) [docker]");
    console.log("     --sec-headers -h HEAD:VALUE : define security header and value [docker]");
    console.log("     --tag -t tag : user this tag as the image name [docker]");
    console.log("     --sudo-docker -s : docker needs to be run with sudo [docker]");
    console.log("     --location -l : directory where to create the package [package only]");
}

function buildDocker(targetDir, options, callback) {
    console.log("building docker");
    shell.cp("docker/Dockerfile", targetDir);
    var cmdline = [].concat((options.sudoDocker?"sudo ":[]), "docker", "build", "-t", options.tag, targetDir);

    //var result = shell.exec(cmdline);
    executeCommand(cmdline, function(code) {
        if (code == 0) {
            console.log("Docker image created.");
            console.log("You can run the image with the following command: docker run --rm -p 127.0.0.1:3000:3000 -t -i '" + options.tag + "'");
            callback(null);
        } else {
            console.error("Error, docker returned with code " + code + ", aborted.");
            process.exit(98);
        }
    });
}

function runDocker(options, callback) {
    // var cmdline = (options.sudoDocker?"sudo ":"") + "docker run --rm -p 127.0.0.1:3000:3000  --name=mongo_supervisor -t -i '" + options.tag + "'";
    var cmdline = [].concat((options.sudoDocker?"sudo ":[]), "docker", "run", "--rm", "-p", "127.0.0.1:3000:3000", "--name=mongo_supervisor", options.tag);
    console.log("running [" + cmdline + "]");
    // var result = shell.exec(cmdline);
    executeCommand(cmdline, callback);
};

function buildAppAt(target, securityObject) {
    // shell.rm("-fr", target);
    shell.mkdir("-p", target)
    shell.cp("-rf", ["lib", "routes", "node_modules"], target);
    shell.cp(["run", "app.js", "package.json", "LICENSE"], target);
    var configobj = yaml.safeLoad(fs.readFileSync("docker/mongosupervisor.yaml", 'utf8'));
    configobj.security = securityObject || {open: true};
    fs.writeFileSync(target + "/mongosupervisor.yaml", yaml.safeDump(configobj));
}

var currentProcess = null;
function executeCommand(cmdline, callback) {
    console.log("Executing: ", cmdline.join(' '));
    currentProcess = spawn(cmdline[0], cmdline.slice(1));

    currentProcess.stdout.on("data", function(data) {
        process.stdout.write(data);
    });
    currentProcess.stderr.on("data", function(data) {
        process.stderr.write(data);
    });
    currentProcess.on("close", function(code) {
        console.log("Received exit code " + code + " for " + cmdline);
        currentProcess = null;
        callback(code);
    });

}

function killCurrentCommand() {
    if (currentProcess != null && typeof currentProcess.kill == "function") {
        console.error("Process aborted, process kill()ed");
        currentProcess.kill();
        currentProcess = null;
    }
}

process.on("SIGINT", killCurrentCommand);
process.on("SIGHUP", killCurrentCommand);
process.on("exit", killCurrentCommand);<|endoftext|>