<commit_before>var express = require('express');
var routes = require('./routes/staticRoutes.js');
var http = require('http');
var path = require('path');
var mongoose = require('mongoose');
var clientSession = require("client-sessions");

var User = require('./models/user.js').User;
var Wallet = require('./models/wallet.js').Wallet;
// grab the models
var walletModel = require('./models/wallet.js');
var rewardsModel = require('./models/rewards.js');
var tasksModel = require('./models/tasks.js');
//OATH
var passport = require('passport')
var GoogleStrategy = require('passport-google').Strategy;

var app = express();

// all environments
app.set('port', process.env.PORT || 8080);
app.set('host', process.env.HOST || 'localhost');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(clientSession({
    cookieName: 'session', // cookie name dictates the key name added to the request object
    secret: process.env.COOKIE_SECRET || 'This is a development secret only.  MAKE SURE to set the environment var.', // should be a large unguessable string
    duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
    activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
}));

app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(passport.initialize());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

mongoose.connect('mongodb://localhost/GamifyTasks');

var db = mongoose.connection;
var dbConnected = false; //may be unneccessary to use this
db.on('error', function() {
	console.log("database error");
});
db.once('open', function callback () {
   console.log("connected to db");
   dbConnected = true;
});

//static routes
app.get('/', routes.index);
app.get('/about', routes.about);
app.get('/contact', routes.contact);
app.get('/login', routes.login);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

// beginning of OAuth

passport.use(new GoogleStrategy({
    returnURL: 'http://' + app.get('host') + ':' + app.get('port') + '/auth/google/return',
    realm: 'http://' + app.get('host') + ':' + app.get('port')
  },
  function(identifier, profile, done) {

	if (!dbConnected) {
	    console.log("database error");
		done(new Error("An error occurred.  Please try again."));
		return;
	}

	User.update({ identifier : identifier	}, { identifier : identifier, profile : profile }, { upsert : true }).exec()

	.then(function() {
		return User.findOne({ 'identifier' : identifier }).exec();
	})

	.then(function(user) {
		Wallet.update({ userID : user._id},	{
			userID : user._id,
			green : 0,
			purple : 0,
			red : 0,
			blue : 0 }, { upsert : true }).exec()
		.then(function() {
			// Successful login/signup and wallet creation
			done(null, user);
		},
		function(err) {
			done(err);
		});
	}, function(err) {
		done(err);
	});
  }
));

// Redirect the user to Google for authentication.  When complete, Google
// will redirect the user back to the application at
//     /auth/google/return
app.get('/auth/google', passport.authenticate('google'));

// Google will redirect the user to this URL after authentication.  Finish
// the process by verifying the assertion.  If valid, the user will be
// logged in.  Otherwise, authentication has failed.
app.get('/auth/google/return',
    passport.authenticate('google', { failureRedirect: '/login',
									  session: false }),
	function (req, res) {
	    req.session.userId = req.user._id;
		res.redirect('/');
	});
//End of OAuth

//Begin API
app.get('/api/user/:id', authenticateUser, require('./routes/api/userAPI.js').getUser);
app.get('/api/wallet', authenticateUser, require('./routes/api/WalletAPI.js').getWalletByUser);
app.put('/api/wallet', authenticateUser, require('./routes/api/WalletAPI.js').updateWalletByUser);
app.get('/api/task/:id', authenticateUser, require('./routes/api/TaskAPI.js').getTaskByID);
app.get('/api/tasks/all', authenticateUser, require('./routes/api/TaskAPI.js').getTasksByUser);
app.post('/api/task', authenticateUser, require('./routes/api/TaskAPI.js').saveTask);

function authenticateUser(req, res, next) {
	User.findById(req.session.userId).exec()
	.then(function (user) {
		if (user) {
			next();
		}
		else {
			res.send(401, { error: "You must log in" });
		}
	},
	function(err) {
		res.send(500, { error: "An error occured during authentication" });
	});
}
<commit_msg>Fixed capitalization of route file names<commit_after>var express = require('express');
var routes = require('./routes/staticRoutes.js');
var http = require('http');
var path = require('path');
var mongoose = require('mongoose');
var clientSession = require("client-sessions");

var User = require('./models/user.js').User;
var Wallet = require('./models/wallet.js').Wallet;
// grab the models
var walletModel = require('./models/wallet.js');
var rewardsModel = require('./models/rewards.js');
var tasksModel = require('./models/tasks.js');
//OATH
var passport = require('passport')
var GoogleStrategy = require('passport-google').Strategy;

var app = express();

// all environments
app.set('port', process.env.PORT || 8080);
app.set('host', process.env.HOST || 'localhost');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(clientSession({
    cookieName: 'session', // cookie name dictates the key name added to the request object
    secret: process.env.COOKIE_SECRET || 'This is a development secret only.  MAKE SURE to set the environment var.', // should be a large unguessable string
    duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
    activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
}));

app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(passport.initialize());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

mongoose.connect('mongodb://localhost/GamifyTasks');

var db = mongoose.connection;
var dbConnected = false; //may be unneccessary to use this
db.on('error', function() {
	console.log("database error");
});
db.once('open', function callback () {
   console.log("connected to db");
   dbConnected = true;
});

//static routes
app.get('/', routes.index);
app.get('/about', routes.about);
app.get('/contact', routes.contact);
app.get('/login', routes.login);

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

// beginning of OAuth

passport.use(new GoogleStrategy({
    returnURL: 'http://' + app.get('host') + ':' + app.get('port') + '/auth/google/return',
    realm: 'http://' + app.get('host') + ':' + app.get('port')
  },
  function(identifier, profile, done) {

	if (!dbConnected) {
	    console.log("database error");
		done(new Error("An error occurred.  Please try again."));
		return;
	}

	User.update({ identifier : identifier	}, { identifier : identifier, profile : profile }, { upsert : true }).exec()

	.then(function() {
		return User.findOne({ 'identifier' : identifier }).exec();
	})

	.then(function(user) {
		Wallet.update({ userID : user._id},	{
			userID : user._id,
			green : 0,
			purple : 0,
			red : 0,
			blue : 0 }, { upsert : true }).exec()
		.then(function() {
			// Successful login/signup and wallet creation
			done(null, user);
		},
		function(err) {
			done(err);
		});
	}, function(err) {
		done(err);
	});
  }
));

// Redirect the user to Google for authentication.  When complete, Google
// will redirect the user back to the application at
//     /auth/google/return
app.get('/auth/google', passport.authenticate('google'));

// Google will redirect the user to this URL after authentication.  Finish
// the process by verifying the assertion.  If valid, the user will be
// logged in.  Otherwise, authentication has failed.
app.get('/auth/google/return',
    passport.authenticate('google', { failureRedirect: '/login',
									  session: false }),
	function (req, res) {
	    req.session.userId = req.user._id;
		res.redirect('/');
	});
//End of OAuth

//Begin API
app.get('/api/user/:id', authenticateUser, require('./routes/api/userAPI.js').getUser);
app.get('/api/wallet', authenticateUser, require('./routes/api/walletAPI.js').getWalletByUser);
app.put('/api/wallet', authenticateUser, require('./routes/api/walletAPI.js').updateWalletByUser);
app.get('/api/task/:id', authenticateUser, require('./routes/api/taskAPI.js').getTaskByID);
app.get('/api/tasks/all', authenticateUser, require('./routes/api/taskAPI.js').getTasksByUser);
app.post('/api/task', authenticateUser, require('./routes/api/taskAPI.js').saveTask);

function authenticateUser(req, res, next) {
	User.findById(req.session.userId).exec()
	.then(function (user) {
		if (user) {
			next();
		}
		else {
			res.send(401, { error: "You must log in" });
		}
	},
	function(err) {
		res.send(500, { error: "An error occured during authentication" });
	});
}
<|endoftext|>


<commit_before>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Measure
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DX.String
 * @requires DX.Tmpl
 * @namespace
 */

var DropDown = (function(DX) {
	'use strict';

	var CN_DROPDOWN = 'dropDown',
			M_SHOWN = 'shown',
			M_HIDDEN = 'hidden',
			CN_CONTAINER = CN_DROPDOWN + '--container',
			CN_LIST_WRAP = CN_DROPDOWN + '--listWrap',
			CN_LIST = CN_DROPDOWN + '--list',
			CN_OPTION = CN_DROPDOWN + '--option',
			M_SELECTED = 'selected',
			M_HOVERED = 'hovered',
			CN_GROUP = CN_DROPDOWN + '--group',
			CN_GROUP_TITLE = CN_DROPDOWN + '--groupTitle',
			A_FOR = 'data-for',
			ESC_KEY_CODE = 27,
			defaults = {
				modifiers: [],
				width: 'control',
				optionTmpl: '
{%= optionInnerTmpl %}{%= currentMarkTmpl %}
',
				optionInnerTmpl: '{%= text %}',
				currentMarkTmpl: null,
				groupTmpl: [
					'
',
					'{%= label %}',
					'
{%= optionList %}
',
					'
'
				].join(''),
				innerTmpl: [
					'
',
					'
',
					'

',
					'
',
					'
'
				].join(''),
				hideOnClick: true
			};

	/**
	 * Check is object variable
	 * @param {*} param
	 * @returns {boolean}
	 */
	function isObject(param) {
		var type = typeof param;
		return type === 'object' && !Array.isArray(param);
	}

	function rePosition(block, control) {
		var offset = DX.Measure.getPosition(control),
			controlPosition = control.getBoundingClientRect(),
			blockPosition,
			upDirectionTopCoordinates,
			leftDirectionLeftCoordinates;

		block.style.top = offset.y + controlPosition.height + 'px';
		block.style.left = offset.x + 'px';

		blockPosition = block.getBoundingClientRect();

		upDirectionTopCoordinates =  document.body.scrollTop + controlPosition.top - blockPosition.height + controlPosition.height;
		leftDirectionLeftCoordinates = document.body.scrollLeft + controlPosition.left - blockPosition.width + controlPosition.width;

		if (blockPosition.top + blockPosition.height > window.innerHeight && upDirectionTopCoordinates > 0) {
			block.style.top = upDirectionTopCoordinates + 'px';
		}

		if (blockPosition.left + blockPosition.width > window.innerWidth && leftDirectionLeftCoordinates > 0) {
			block.style.left = leftDirectionLeftCoordinates + 'px';
		}
	}

	function reCalculateWidth(block, control, config) {
		if (isNaN(config.width)) {
			block.style.minWidth = DX.Measure.getSize(control, true).width + 'px';
		} else {
			block.style.width = config.width + 'px';
		}
	}

	function getOptionListHTML(data, config) {
		return data.reduce(function(prevValue, item) {
			var result = '';
			if (isObject(item)) {
				var isItGroup = Array.isArray(item.options);
				result = prevValue + (isItGroup ? getOptgroupHTML(item, config) : getOptionHTML(item, config));
			}
			return result;
		}, '');
	}

	function getOptgroupHTML(data, config) {
		data = Object.assign({}, data);
		data.optionList = getOptionListHTML(data.options, config);

		return DX.Tmpl.process(config.groupTmpl, data);
	}

	function getOptionHTML(data, config) {
		var dataAttrs = '',
			template = config.optionTmpl;
		data = Object.assign({}, data);
		data.classNames = DX.Bem.createModifiedClassName(CN_OPTION, data.modifiers);

		if (data.data) {
			Object.keys(data.data).forEach(function(key) {
				var item = data.data[key];
				dataAttrs += 'data-' + DX.String.hyphenate(key) + '="' + item + '" ';
			});

			data.dataAttrs = dataAttrs;
		}

		data.currentMarkTmpl = config.currentMarkTmpl;
		data.optionInnerTmpl = DX.Tmpl.process(config.optionInnerTmpl, data);

		return DX.Tmpl.process(template, data);
	}

	function createElements(control, config) {
		var body = document.body,
			block,
			list;

		block = DX.Dom.createElement('div', {
			className: DX.Bem.createModifiedClassName(CN_DROPDOWN, config.modifiers),
			innerHTML: config.innerTmpl
		});

		if (control.id) {
			block.setAttribute(A_FOR, control.id);
		}

		list = DX.$$('.' + CN_LIST, block);

		body.appendChild(block);

		return {
			block: block,
			list: list
		};
	}

	function keyDownHandler(e) {

		var key;
		key = e.key || e.which;
		if (key === ESC_KEY_CODE || key === 'Escape') {
			setTimeout(hideAllDropDowns, 0);
		}
	}
	function hideAllDropDowns() {
		var dropDownsArray = DX.$$$('.' + CN_DROPDOWN);
		Array.prototype.forEach.call(dropDownsArray, function(dropdown) {
			DX.Event.trigger(dropdown, DropDown.E_HIDE);
		});

	}

	if (typeof document !== 'undefined') {
		document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
	}
	/**
	 * Creates new dropdown
	 * @constructor DropDown
	 * @param {Node|Element} control
	 * @param {Object} config - {Array:modifiers, String|Number:width, String:optionTmpl, String:groupTmpl, String:innerTmpl, String:currentMarkTmpl}
	 */
	return function DropDown(control, config) {
		var elements,
			selectedIndex,
			hoveredIndex,
			optionElements,
			selectedOptionElement,
			hoveredOptionElement,
			isShownOnce;

		/**
		 * Dropdown is created
		 *
		 * @event dropdown:created
		 */
		function init() {
			config = Object.assign({}, defaults, (config || {}));
			elements = createElements(control, config);
			selectedIndex = 0;
			hoveredIndex = null;
			isShownOnce = false;

			initListeners();

			DX.Event.trigger(control, DropDown.E_CREATED, {
				detail: {
					block: elements.block,
					eventTarget: elements.block
				}
			});
		}
		function initListeners() {
			var block = getEventTarget();
			block.addEventListener(DropDown.E_HIDE, hide);
			document.addEventListener(DropDown.E_HIDE_ALL, hideAllDropDowns);

			if (elements.list) {
				elements.list.addEventListener('click', function(e) {
					var optionElement = DX.Dom.getAscendantByClassName(e.target, CN_OPTION),
						index;

					if (optionElement) {
						index = Array.prototype.indexOf.call(optionElements, optionElement);

						if (index !== selectedIndex) {
							selectedIndex = index;
							setSelectedIndex(index);
							DX.Event.trigger(elements.block, DropDown.E_CHANGED);
						}

						if (config.hideOnClick) {
							hide();
						}
					}
				}, true);
			}
		}
		/**
		 * Sets popup data list
		 * @method setDataList
		 * @param {Object} data
		 */
		function setDataList(data) {
			elements.list.innerHTML = getOptionListHTML(data, config);
			optionElements = DX.$$$('.' + CN_OPTION, elements.list);
		}
		/**
		 * Shows dropdown
		 * @method show
		 */
		/**
		 * Dropdown is shown
		 *
		 * @event dropdown:shown
		 */
		function show() {
			var block = elements.block;

			if (!isShownOnce) {
				isShownOnce = true;
				reCalculateWidth(block, control, config);
			}

			setHoveredIndex(0);

			DX.Bem.removeModifier(block, M_HIDDEN, CN_DROPDOWN);
			DX.Bem.addModifier(block, M_SHOWN, CN_DROPDOWN);
			rePosition(block, control);

			document.addEventListener('mousedown', function(e) {
				documentClickHandler(e);
			}, true);

			DX.Event.trigger(block, DropDown.E_SHOWN);
		}
		/**
		 * Hides dropdown
		 * @method hide
		 */
		/**
		 * Dropdown is hidden
		 *
		 * @event dropdown:hidden
		 */
		/**
		 * Hide dropdown
		 *
		 * @event dropdown:hide
		 */
		function hide() {
			var block = elements.block;

			clearHoveredIndex();
			DX.Bem.removeModifier(block, M_SHOWN, CN_DROPDOWN);
			DX.Bem.addModifier(block, M_HIDDEN, CN_DROPDOWN);

			document.removeEventListener(DX.Event.TOUCH_CLICK, documentClickHandler, true);
			DX.Event.trigger(block, DropDown.E_HIDDEN);
		}

		function normalizeIndex(index) {
			if (index <= 0) {
				index = 0;
			}
			if (optionElements && index >= optionElements.length) {
				index = optionElements.length - 1;
			}
			return index;
		}
		/**
		 * Sets popup selectedelement by index
		 * @method setSelectedIndex
		 * @param {Number} index
		 * @param {bool=} [triggerChangeEvent=false] whether or not trigger dropdown:changed event
		 */
		/**
		 * Dropdown has changed
		 *
		 * @event dropdown:changed
		 */
		function setSelectedIndex(index, triggerChangeEvent) {
			if (selectedOptionElement) {
				DX.Bem.removeModifier(selectedOptionElement, M_SELECTED, CN_OPTION);
			}

			selectedIndex = index;
			selectedOptionElement = optionElements[index];
			if (selectedOptionElement) {
				DX.Bem.addModifier(selectedOptionElement, M_SELECTED, CN_OPTION);
				if (triggerChangeEvent) {
					DX.Event.trigger(elements.block, DropDown.E_CHANGED);
				}
			}
		}
		/**
		 * Sets popup hoveredelement by index
		 * @method setHoveredIndex
		 * @param {Number} index
		 */
		function setHoveredIndex(index) {
			if (hoveredOptionElement) {
				DX.Bem.removeModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);
			}

			index = normalizeIndex(index);

			hoveredIndex = index;
			hoveredOptionElement = optionElements ? optionElements[index] : null;

			if (hoveredOptionElement) {
				DX.Bem.addModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);

			}
		}

		function clearHoveredIndex() {
			if (hoveredOptionElement) {
				DX.Bem.removeModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);
			}
			hoveredIndex = null;
			hoveredOptionElement = null;
		}
		/**
		 * Gets popup selectedelement by index
		 * @method getSelectedIndex
		 * @returns {Number} selectedIndex
		 */
		function getSelectedIndex() {
			return selectedIndex;
		}
		/**
		 * Gets popup hoveredelement by index
		 * @method getHoveredIndex
		 * @returns {Number} hoveredIndex
		 */
		function getHoveredIndex() {
			return hoveredIndex;
		}

		/**
		 * Gets HTMLNode containing dropdown
		 * @method getBlock
		 * @returns {Node}
		 */
		function getBlock() {
			return elements.block;
		}
		/**
		 * Gets element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		function getEventTarget() {
			return elements.block;
		}

		function documentClickHandler(e) {
			var target = e.target,
				closestDropDown = DX.Dom.getAscendantByClassName(target, CN_DROPDOWN),
				block = elements.block,
				id = block.getAttribute(A_FOR),
				forElement;

			if (id) {
				forElement = DX.Dom.getAscendantByAttribute(target, 'id', id);
			}

			if (closestDropDown !== block && !forElement) {
				hide();
			}
		}
		/**
		 * Gets whether dropdown is shown
		 * @method isShown
		 * @returns {bool}
		 */
		function isShown() {
			return DX.Bem.hasModifier(elements.block, M_SHOWN, CN_DROPDOWN);
		}

		init();

		this.setDataList = setDataList;
		this.setSelectedIndex = setSelectedIndex;
		this.getSelectedIndex = getSelectedIndex;
		this.setHoveredIndex = setHoveredIndex;
		this.getHoveredIndex = getHoveredIndex;
		this.show = show;
		this.hide = hide;
		this.isShown = isShown;
		this.getBlock = getBlock;
		this.getEventTarget = getEventTarget;
	};
})(DX);

/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_CREATED = 'dropdown:created';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_SHOWN = 'dropdown:shown';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDDEN = 'dropdown:hidden';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_CHANGED = 'dropdown:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDE = 'dropdown:hide';

/**
 * @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDE_ALL = 'dropdown:hideAll';

/**
 * Gets if there is any shown dropdown
 * @method isAnyShown
 * @static
 * @memberof DropDown
 * @returns {Boolean}
 */
DropDown.isAnyShown = function() {
	'use strict';
	return document.querySelectorAll('.dropDown-shown').length > 0;
};

<commit_msg>Added height calculation. Added animation-delay on groupTitles and options for stagger animations.<commit_after>/**
 * @copyright Devexperts
 *
 * @requires DX
 * @requires DX.Measure
 * @requires DX.Dom
 * @requires DX.Bem
 * @requires DX.Event
 * @requires DX.String
 * @requires DX.Tmpl
 * @namespace
 */

var DropDown = (function(DX) {
	'use strict';

	var CN_DROPDOWN = 'dropDown',
			M_SHOWN = 'shown',
			M_HIDDEN = 'hidden',
			CN_CONTAINER = CN_DROPDOWN + '--container',
			CN_LIST_WRAP = CN_DROPDOWN + '--listWrap',
			CN_LIST = CN_DROPDOWN + '--list',
			CN_OPTION = CN_DROPDOWN + '--option',
			M_SELECTED = 'selected',
			M_HOVERED = 'hovered',
			CN_GROUP = CN_DROPDOWN + '--group',
			CN_GROUP_TITLE = CN_DROPDOWN + '--groupTitle',
			A_FOR = 'data-for',
			ESC_KEY_CODE = 27,
			defaults = {
				modifiers: [],
				width: 'control',
				optionTmpl: '
{%= optionInnerTmpl %}{%= currentMarkTmpl %}
',
				optionInnerTmpl: '{%= text %}',
				currentMarkTmpl: null,
				groupTmpl: [
					'
',
					'{%= label %}',
					'
{%= optionList %}
',
					'
'
				].join(''),
				innerTmpl: [
					'
',
					'
',
					'

',
					'
',
					'
'
				].join(''),
				hideOnClick: true
			};

	/**
	 * Check is object variable
	 * @param {*} param
	 * @returns {boolean}
	 */
	function isObject(param) {
		var type = typeof param;
		return type === 'object' && !Array.isArray(param);
	}

	function rePosition(block, control) {
		var offset = DX.Measure.getPosition(control),
			controlPosition = control.getBoundingClientRect(),
			blockPosition,
			upDirectionTopCoordinates,
			leftDirectionLeftCoordinates;

		block.style.top = offset.y + controlPosition.height + 'px';
		block.style.left = offset.x + 'px';

		blockPosition = block.getBoundingClientRect();

		upDirectionTopCoordinates =  document.body.scrollTop + controlPosition.top - blockPosition.height + controlPosition.height;
		leftDirectionLeftCoordinates = document.body.scrollLeft + controlPosition.left - blockPosition.width + controlPosition.width;

		if (blockPosition.top + blockPosition.height > window.innerHeight && upDirectionTopCoordinates > 0) {
			block.style.top = upDirectionTopCoordinates + 'px';
		}

		if (blockPosition.left + blockPosition.width > window.innerWidth && leftDirectionLeftCoordinates > 0) {
			block.style.left = leftDirectionLeftCoordinates + 'px';
		}
	}

	function reCalculateWidth(block, control, config) {
		if (isNaN(config.width)) {
			block.style.minWidth = DX.Measure.getSize(control, true).width + 'px';
		} else {
			block.style.width = config.width + 'px';
		}
	}

	function calculateAnimationDelay(block) {
		var transitionStepDelay = 0.03,
			listChildren = block.querySelectorAll(`.${CN_OPTION}, .${CN_GROUP_TITLE}`);

		for(var i=0; i<listChildren.length; i++) {
			listChildren[i].style.animationDelay = transitionStepDelay * i + 's';
		}
	}

	function calculateHeight(block) {
		block.style.display = 'block';
		var dropDownHeight = DX.Measure.getSize(block, true).height;
		block.style.display = '';
		block.style.height = dropDownHeight + 'px';
	}

	function getOptionListHTML(data, config) {
		return data.reduce(function(prevValue, item) {
			var result = '';
			if (isObject(item)) {
				var isItGroup = Array.isArray(item.options);
				result = prevValue + (isItGroup ? getOptgroupHTML(item, config) : getOptionHTML(item, config));
			}
			return result;
		}, '');
	}

	function getOptgroupHTML(data, config) {
		data = Object.assign({}, data);
		data.optionList = getOptionListHTML(data.options, config);

		return DX.Tmpl.process(config.groupTmpl, data);
	}

	function getOptionHTML(data, config) {
		var dataAttrs = '',
			template = config.optionTmpl;
		data = Object.assign({}, data);
		data.classNames = DX.Bem.createModifiedClassName(CN_OPTION, data.modifiers);

		if (data.data) {
			Object.keys(data.data).forEach(function(key) {
				var item = data.data[key];
				dataAttrs += 'data-' + DX.String.hyphenate(key) + '="' + item + '" ';
			});

			data.dataAttrs = dataAttrs;
		}

		data.currentMarkTmpl = config.currentMarkTmpl;
		data.optionInnerTmpl = DX.Tmpl.process(config.optionInnerTmpl, data);

		return DX.Tmpl.process(template, data);
	}

	function createElements(control, config) {
		var body = document.body,
			block,
			list;

		block = DX.Dom.createElement('div', {
			className: DX.Bem.createModifiedClassName(CN_DROPDOWN, config.modifiers),
			innerHTML: config.innerTmpl
		});

		if (control.id) {
			block.setAttribute(A_FOR, control.id);
		}

		list = DX.$$('.' + CN_LIST, block);

		body.appendChild(block);

		return {
			block: block,
			list: list
		};
	}

	function keyDownHandler(e) {

		var key;
		key = e.key || e.which;
		if (key === ESC_KEY_CODE || key === 'Escape') {
			setTimeout(hideAllDropDowns, 0);
		}
	}
	function hideAllDropDowns() {
		var dropDownsArray = DX.$$$('.' + CN_DROPDOWN);
		Array.prototype.forEach.call(dropDownsArray, function(dropdown) {
			DX.Event.trigger(dropdown, DropDown.E_HIDE);
		});

	}

	if (typeof document !== 'undefined') {
		document.addEventListener(DX.Event.KEY_DOWN, keyDownHandler);
	}
	/**
	 * Creates new dropdown
	 * @constructor DropDown
	 * @param {Node|Element} control
	 * @param {Object} config - {Array:modifiers, String|Number:width, String:optionTmpl, String:groupTmpl, String:innerTmpl, String:currentMarkTmpl}
	 */
	return function DropDown(control, config) {
		var elements,
			selectedIndex,
			hoveredIndex,
			optionElements,
			selectedOptionElement,
			hoveredOptionElement,
			isShownOnce;

		/**
		 * Dropdown is created
		 *
		 * @event dropdown:created
		 */
		function init() {
			config = Object.assign({}, defaults, (config || {}));
			elements = createElements(control, config);
			selectedIndex = 0;
			hoveredIndex = null;
			isShownOnce = false;

			initListeners();

			DX.Event.trigger(control, DropDown.E_CREATED, {
				detail: {
					block: elements.block,
					eventTarget: elements.block
				}
			});
		}
		function initListeners() {
			var block = getEventTarget();
			block.addEventListener(DropDown.E_HIDE, hide);
			document.addEventListener(DropDown.E_HIDE_ALL, hideAllDropDowns);

			if (elements.list) {
				elements.list.addEventListener('click', function(e) {
					var optionElement = DX.Dom.getAscendantByClassName(e.target, CN_OPTION),
						index;

					if (optionElement) {
						index = Array.prototype.indexOf.call(optionElements, optionElement);

						if (index !== selectedIndex) {
							selectedIndex = index;
							setSelectedIndex(index);
							DX.Event.trigger(elements.block, DropDown.E_CHANGED);
						}

						if (config.hideOnClick) {
							hide();
						}
					}
				}, true);
			}
		}
		/**
		 * Sets popup data list
		 * @method setDataList
		 * @param {Object} data
		 */
		function setDataList(data) {
			elements.list.innerHTML = getOptionListHTML(data, config);
			optionElements = DX.$$$('.' + CN_OPTION, elements.list);
		}
		/**
		 * Shows dropdown
		 * @method show
		 */
		/**
		 * Dropdown is shown
		 *
		 * @event dropdown:shown
		 */
		function show() {
			var block = elements.block;

			if (!isShownOnce) {
				isShownOnce = true;
				reCalculateWidth(block, control, config);
				calculateAnimationDelay(block);
				calculateHeight(block);
			}

			setHoveredIndex(0);

			DX.Bem.removeModifier(block, M_HIDDEN, CN_DROPDOWN);
			DX.Bem.addModifier(block, M_SHOWN, CN_DROPDOWN);
			rePosition(block, control);

			document.addEventListener('mousedown', function(e) {
				documentClickHandler(e);
			}, true);

			DX.Event.trigger(block, DropDown.E_SHOWN);
		}
		/**
		 * Hides dropdown
		 * @method hide
		 */
		/**
		 * Dropdown is hidden
		 *
		 * @event dropdown:hidden
		 */
		/**
		 * Hide dropdown
		 *
		 * @event dropdown:hide
		 */
		function hide() {
			var block = elements.block;

			clearHoveredIndex();
			DX.Bem.removeModifier(block, M_SHOWN, CN_DROPDOWN);
			DX.Bem.addModifier(block, M_HIDDEN, CN_DROPDOWN);

			document.removeEventListener(DX.Event.TOUCH_CLICK, documentClickHandler, true);
			DX.Event.trigger(block, DropDown.E_HIDDEN);
		}

		function normalizeIndex(index) {
			if (index <= 0) {
				index = 0;
			}
			if (optionElements && index >= optionElements.length) {
				index = optionElements.length - 1;
			}
			return index;
		}
		/**
		 * Sets popup selectedelement by index
		 * @method setSelectedIndex
		 * @param {Number} index
		 * @param {bool=} [triggerChangeEvent=false] whether or not trigger dropdown:changed event
		 */
		/**
		 * Dropdown has changed
		 *
		 * @event dropdown:changed
		 */
		function setSelectedIndex(index, triggerChangeEvent) {
			if (selectedOptionElement) {
				DX.Bem.removeModifier(selectedOptionElement, M_SELECTED, CN_OPTION);
			}

			selectedIndex = index;
			selectedOptionElement = optionElements[index];
			if (selectedOptionElement) {
				DX.Bem.addModifier(selectedOptionElement, M_SELECTED, CN_OPTION);
				if (triggerChangeEvent) {
					DX.Event.trigger(elements.block, DropDown.E_CHANGED);
				}
			}
		}
		/**
		 * Sets popup hoveredelement by index
		 * @method setHoveredIndex
		 * @param {Number} index
		 */
		function setHoveredIndex(index) {
			if (hoveredOptionElement) {
				DX.Bem.removeModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);
			}

			index = normalizeIndex(index);

			hoveredIndex = index;
			hoveredOptionElement = optionElements ? optionElements[index] : null;

			if (hoveredOptionElement) {
				DX.Bem.addModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);

			}
		}

		function clearHoveredIndex() {
			if (hoveredOptionElement) {
				DX.Bem.removeModifier(hoveredOptionElement, M_HOVERED, CN_OPTION);
			}
			hoveredIndex = null;
			hoveredOptionElement = null;
		}
		/**
		 * Gets popup selectedelement by index
		 * @method getSelectedIndex
		 * @returns {Number} selectedIndex
		 */
		function getSelectedIndex() {
			return selectedIndex;
		}
		/**
		 * Gets popup hoveredelement by index
		 * @method getHoveredIndex
		 * @returns {Number} hoveredIndex
		 */
		function getHoveredIndex() {
			return hoveredIndex;
		}

		/**
		 * Gets HTMLNode containing dropdown
		 * @method getBlock
		 * @returns {Node}
		 */
		function getBlock() {
			return elements.block;
		}
		/**
		 * Gets element which listens to events
		 * @method getEventTarget
		 * @returns {Node}
		 */
		function getEventTarget() {
			return elements.block;
		}

		function documentClickHandler(e) {
			var target = e.target,
				closestDropDown = DX.Dom.getAscendantByClassName(target, CN_DROPDOWN),
				block = elements.block,
				id = block.getAttribute(A_FOR),
				forElement;

			if (id) {
				forElement = DX.Dom.getAscendantByAttribute(target, 'id', id);
			}

			if (closestDropDown !== block && !forElement) {
				hide();
			}
		}
		/**
		 * Gets whether dropdown is shown
		 * @method isShown
		 * @returns {bool}
		 */
		function isShown() {
			return DX.Bem.hasModifier(elements.block, M_SHOWN, CN_DROPDOWN);
		}

		init();

		this.setDataList = setDataList;
		this.setSelectedIndex = setSelectedIndex;
		this.getSelectedIndex = getSelectedIndex;
		this.setHoveredIndex = setHoveredIndex;
		this.getHoveredIndex = getHoveredIndex;
		this.show = show;
		this.hide = hide;
		this.isShown = isShown;
		this.getBlock = getBlock;
		this.getEventTarget = getEventTarget;
	};
})(DX);

/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_CREATED = 'dropdown:created';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_SHOWN = 'dropdown:shown';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDDEN = 'dropdown:hidden';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_CHANGED = 'dropdown:changed';
/** @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDE = 'dropdown:hide';

/**
 * @constant
 * @type {string}
 * @default
 * @memberof DropDown
 */
DropDown.E_HIDE_ALL = 'dropdown:hideAll';

/**
 * Gets if there is any shown dropdown
 * @method isAnyShown
 * @static
 * @memberof DropDown
 * @returns {Boolean}
 */
DropDown.isAnyShown = function() {
	'use strict';
	return document.querySelectorAll('.dropDown-shown').length > 0;
};

<|endoftext|>


<commit_before>require('nodefly').profile(
    process.env.NODEFLY_APPLICATION_KEY,
    [process.env.APPLICATION_NAME,'Heroku']
);

require('coffee-script')

var cloudBox = require('./app/cloud-box.coffee');

cloudBox.start();<commit_msg>using node cluster API<commit_after>//Nodefly
require('nodefly').profile(
    process.env.NODEFLY_APPLICATION_KEY,
    [process.env.APPLICATION_NAME,'Heroku']
);

// Cluster bootstrap
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
	// Fork workers
	console.log("Master forking " + numCPUs + " workers!");
	for (var i = 0; i < numCPUs; i++) {
		cluster.fork();
	}

	cluster.on('exit', function(worker, code, signal) {
		console.log('worker ' + worker.process.pid + ' died');
	});
}
else {
	// Worker starts a server
	require('coffee-script')
	var cloudBox = require('./app/cloud-box.coffee');

	cloudBox.start();
}<|endoftext|>


<commit_before>import PubSub from "./event/pubSub.js";
import Events from "./event/events.js";

class Exporter {
	constructor() {
		PubSub.subscribe(Events.SAVE_REQUESTED, type => {
			let parser = this[type];

			if (typeof(parser) != "undefined") {
				PubSub.publish(Events.PARSE_AND_SAVE, parser);
			} else {
				console.error("Parser " + type + " was not found!");
			}
		});
	}

	dTileJSON(map) {
		return JSON.stringify(map, function(key, value) {
			if (key.indexOf("_") == 0) {
				return undefined;
			}
			return value;
		});
	}
}

export default (new Exporter());
<commit_msg>Make export follow DTile JSON file spec<commit_after>import PubSub from "./event/pubSub.js";
import Events from "./event/events.js";

class Exporter {
	constructor() {
		PubSub.subscribe(Events.SAVE_REQUESTED, type => {
			let parser = this[type];

			if (typeof(parser) != "undefined") {
				PubSub.publish(Events.PARSE_AND_SAVE, parser);
			} else {
				console.error("Parser " + type + " was not found!");
			}
		});
	}

	/*
	 * Currently, this doesn't support multiple tilesets although that is planned
	 * for the future.
	 *
	 * You can view the file spec here:
	 * https://docs.google.com/document/d/1Ir7rhUxNcbRCfkdZw7rNOJyFTcdWUOmjvnYylzpN8O0
	 */
	dTileJSON(map) {
		let image = (() => {
			let image = map.tilesets[0].getTexture().baseTexture.source;
			console.log(image);

			let canvas = document.createElement('canvas');
	        canvas.width = image.naturalWidth;
			canvas.height = image.naturalHeight;

	        canvas.getContext('2d').drawImage(image, 0, 0);

	        return canvas.toDataURL('image/png').substring(22);
		})();

		let width = map.width,
			height = map.height,
			tileWidth = map.tileWidth,
			tileHeight = map.tileHeight,
			name = map.name;

		let tileLayers = [];
		for (let layer of map.tileLayers) {
			let tiles = [];
			for (let x = 0; x < layer.tiles.length; x++) {
				for (let y = 0; y < layer.tiles.length; y++) {
					tiles[y * width + x] = {
						id: layer.tiles[x][y].id
					};
				}
			}

			tileLayers.push({
				name: layer.name,
				tiles
			});
		}

		return JSON.stringify({
			image,
			width,
			height,
			tileWidth,
			tileHeight,
			tileLayers,
			name
		});
	}
}

export default (new Exporter());
<|endoftext|>


<commit_before>/*jslint browser: true, devel: true, es5: true, plusplus: false, unparam: true, vars: false */

(function (data, iterations, iframe, document, window, article, button, table) {

    "use strict";

    // This makes it easier to access the iframe's dom
    var inner_window = iframe.contentWindow;

    // IE9 does not support forEach, so we define it here.
    // Kudos to StackOverflow user bobince for the standatds-compliant implementation
    if (!Array.prototype.forEach) {
        Array.prototype.forEach = function (fun, that){
            var i, n;
            for (i = 0, n = this.length; i < n; i+= 1) {
                if (this[i] !== undefined) {
                    fun.call(that, this[i], i, this);
                }
            }
        };
    }

    // DOM Methods

    function text(string) {
        return document.createTextNode(string);
    }

    function paragraph() {
        var p = document.createElement('p'), i;
        for (i = 0; i < arguments.length; i += 1) {
            if (typeof arguments[i] === "string") {
                p.appendChild(text(arguments[i]));
            }
            else {
                p.appendChild(arguments[i]);
            }
        }
        return p;
    }

    function makeParent(child, typeString) {
        var element = document.createElement(typeString);
        element.appendChild(child);
        return element;
    }

    function appendChildren(element) {
        var i;
        if (arguments.length <= 1) {
            return;
        }

        for (i = 1; i < arguments.length; i += 1) {
            element.appendChild(arguments[i]);
        }

        return element;
    }

    function nodeListToArray(list) {
        var array = [], i;
        for (i = 0; i < list.length; i += 1) {
            array[i] = list[i];
        }
        return array;
    }

    function updateArticle(new_pars){
        var old_pars = nodeListToArray(article.getElementsByTagName('p'));
        old_pars.forEach(function(par){ article.removeChild(par); });
        new_pars.forEach(function(par){ article.appendChild(par); });
    }

    // Testrunner functions

    function buildQueueFromProvidedTestObject (data, iterations) {
        var queue = [], i, category;

        function wrapCategory(category){
            return function(member){
                queue.push({category: category, name: member});
            };
        }

        for(i = 0; i < iterations; i+= 1){
            for(category in data){
                if(data.hasOwnProperty(category)) {
                    data[category].forEach(wrapCategory(category));
                }
            }
        }
        return queue;
    }

    function makeResultsObject(data){

        var category_name, category, i,
            results = {};

        for(category_name in data){
            results[category_name] = {};
            category = data[category_name];
            for(i = 0; i < category.length; i += 1){
                results[category_name][category[i]] = [];
            }
        }
        return results;
    }

    function createReport(total_tests){
        var intro = "Running test ",
            counter = 0,
            countptr = text(""), //A pointer to the text node where we set the currently runing testnumber
            fat_count = makeParent(countptr, 'strong'),
            copula = " of ",
            total = makeParent(text(total_tests), 'strong'),
            period = text("."),
            par = paragraph(intro, fat_count, copula, total, period);

        updateArticle([par]);

        return function(){
            counter += 1;
            countptr.textContent = counter;
        };
    }

    function postResult(inner_window) {
        var timing = inner_window.performance.timing,
            difference = timing.loadEventEnd - timing.requestStart;
        inner_window.parent.postMessage(difference, "*");
    }

    function postIfSet() {
        var timing = inner_window.performance.timing;

        if (timing.loadEventEnd === 0){
            setTimeout(postIfSet, 10);
        }
        else {
            postResult(inner_window);
        }
    }

    function createBoundRunner(iframe, window) {

        var current_test, finalize, queue, reporter, result;

        function testNext() {
            reporter();
            current_test = queue.shift();
            iframe.src = "./tests/" + current_test.category+ "/" + current_test.name +
                "/index.html";
        }

        function iterate() {
            if (queue.length !== 0) {
                testNext();
            }
            else {
                window.removeEventListener("message", catchMessage, false);
                finalize(result);
            }
        }

        function catchMessage(message) {
            result[current_test.category][current_test.name].push(message.data);
            iterate();
        }

        window.addEventListener("message", catchMessage, false);
        iframe.onload = postIfSet;

        return function(tests, resultContainer, callback)  {
            queue = tests;
            finalize = callback;
            result = resultContainer;
            reporter = createReport(tests.length);
            testNext();
        };
    }

    // Data presentation methods

    function sum (array) {
        var result = 0, i;
        for(i = 0; i < array.length; i += 1){
            result += (+array[i]); //If the elements are strings (IE 9), we coerce them to numbers
        }
        return result;
    }

    function findMinMax (array) {
        var max = -Number.MAX_VALUE,
            min = Number.MAX_VALUE;
        array.forEach(function(element) {
            if (element >= max) {
                max = element;
            }
            if (element <= min) {
                min = element;
            }
        });
        return [min, max];
    }

    function makeFilter (smallest, largest) {
        var removals = 0;
        return function (value, index, array) {
            if (value !== smallest && value !== largest) {
                return true;
            }
            removals += 1;
            if (removals <= 2) {
                return false;
            }
            return true;

        };
    }

    function findFinalNumber (array) {

        var trimmed_array = array.filter(makeFilter.apply(null, findMinMax(array))),
            naive_average = sum(trimmed_array)/trimmed_array.length;
        return Math.round(naive_average);
    }

    function createTestURL(category, test){
        var url = document.createElement('a');
        url.href= './tests/' + category + "/" + test + "/";
        url.appendChild(document.createTextNode(test));
        return url;
    }

    function createTable (result) {

        var category, category_array, final_number, header, result_string,
            results, row, tblbody = document.createElement('tbody'), testname,
            urlfield;

        for(category in result){
            if(result.hasOwnProperty(category)){

                category_array = result[category];
                header = makeParent(text(category), 'th');
                tblbody.appendChild(makeParent(header, 'tr'));

                for(testname in category_array){
                    if(category_array.hasOwnProperty(testname)) {
                        row = document.createElement('tr');
                        urlfield = createTestURL(category, testname);

                        results = result[category][testname];
                        final_number = findFinalNumber(results);
                        result_string = text(final_number+' ms');

                        appendChildren(row, makeParent(urlfield, 'td'),
                                makeParent(result_string, 'td'));

                        tblbody.appendChild(row);
                    }
                }
            }
        }

        table.appendChild(tblbody);
    }

    function finalMessage() {
        var p1 = paragraph('Alright! Testing completed.'),
            p2 = paragraph('Do take the results with a grain of salt.'),
            p3 = paragraph(button);

        button.textContent = "One more time, please";

        return [p1, p2, p3];
    }

    function pushResultToHistory(result){
        window.location.hash = encodeURIComponent(JSON.stringify(result));
        present(result);
    }

    function present (result) {
        iframe.style.display = "none";
        updateArticle(finalMessage());
        createTable(result);
    }

    function printIncompatibilityMessage() {
        (document.getElementsByTagName('body')[0]).innerHTML = "
Unfortunately," +
    "  your browser does not support the " + "Navigation Timing API.
";
    }

    function resetTableIfSet(){
        if(table.childNodes.length !== 0) {
            table.removeChild(table.childNodes[0]);
        }
    }

    function engage () {
        resetTableIfSet();
        iframe.style.display = "block";
        createBoundRunner(iframe, window)(buildQueueFromProvidedTestObject(data, iterations),
                makeResultsObject(data), pushResultToHistory);
    }

    // Return if incompatible with Navigation Timing API

    if(window.performance === undefined) {
        printIncompatibilityMessage();
        return;
    }

    window.onload = function () {

        if (location.hash !== "")
            present (JSON.parse(String.prototype.slice.call(location.hash, 1)));
    }

    inner_window.domain = document.domain;
    button.onclick = engage;

}(TESTRUNNER.data, 6, document.getElementsByTagName('iframe')[0], document, window,
        document.getElementsByTagName('article')[0], document.getElementsByTagName('button')[0],document.getElementsByTagName('table')[0]));
<commit_msg>When the url contains a data object, the string representing the data is passed through decodeURIcomponent before Jsonified<commit_after>/*jslint browser: true, devel: true, es5: true, plusplus: false, unparam: true, vars: false */

(function (data, iterations, iframe, document, window, article, button, table) {

    "use strict";

    // This makes it easier to access the iframe's dom
    var inner_window = iframe.contentWindow;

    // IE9 does not support forEach, so we define it here.
    // Kudos to StackOverflow user bobince for the standatds-compliant implementation
    if (!Array.prototype.forEach) {
        Array.prototype.forEach = function (fun, that){
            var i, n;
            for (i = 0, n = this.length; i < n; i+= 1) {
                if (this[i] !== undefined) {
                    fun.call(that, this[i], i, this);
                }
            }
        };
    }

    // DOM Methods

    function text(string) {
        return document.createTextNode(string);
    }

    function paragraph() {
        var p = document.createElement('p'), i;
        for (i = 0; i < arguments.length; i += 1) {
            if (typeof arguments[i] === "string") {
                p.appendChild(text(arguments[i]));
            }
            else {
                p.appendChild(arguments[i]);
            }
        }
        return p;
    }

    function makeParent(child, typeString) {
        var element = document.createElement(typeString);
        element.appendChild(child);
        return element;
    }

    function appendChildren(element) {
        var i;
        if (arguments.length <= 1) {
            return;
        }

        for (i = 1; i < arguments.length; i += 1) {
            element.appendChild(arguments[i]);
        }

        return element;
    }

    function nodeListToArray(list) {
        var array = [], i;
        for (i = 0; i < list.length; i += 1) {
            array[i] = list[i];
        }
        return array;
    }

    function updateArticle(new_pars){
        var old_pars = nodeListToArray(article.getElementsByTagName('p'));
        old_pars.forEach(function(par){ article.removeChild(par); });
        new_pars.forEach(function(par){ article.appendChild(par); });
    }

    // Testrunner functions

    function buildQueueFromProvidedTestObject (data, iterations) {
        var queue = [], i, category;

        function wrapCategory(category){
            return function(member){
                queue.push({category: category, name: member});
            };
        }

        for(i = 0; i < iterations; i+= 1){
            for(category in data){
                if(data.hasOwnProperty(category)) {
                    data[category].forEach(wrapCategory(category));
                }
            }
        }
        return queue;
    }

    function makeResultsObject(data){

        var category_name, category, i,
            results = {};

        for(category_name in data){
            results[category_name] = {};
            category = data[category_name];
            for(i = 0; i < category.length; i += 1){
                results[category_name][category[i]] = [];
            }
        }
        return results;
    }

    function createReport(total_tests){
        var intro = "Running test ",
            counter = 0,
            countptr = text(""), //A pointer to the text node where we set the currently runing testnumber
            fat_count = makeParent(countptr, 'strong'),
            copula = " of ",
            total = makeParent(text(total_tests), 'strong'),
            period = text("."),
            par = paragraph(intro, fat_count, copula, total, period);

        updateArticle([par]);

        return function(){
            counter += 1;
            countptr.textContent = counter;
        };
    }

    function postResult(inner_window) {
        var timing = inner_window.performance.timing,
            difference = timing.loadEventEnd - timing.requestStart;
        inner_window.parent.postMessage(difference, "*");
    }

    function postIfSet() {
        var timing = inner_window.performance.timing;

        if (timing.loadEventEnd === 0){
            setTimeout(postIfSet, 10);
        }
        else {
            postResult(inner_window);
        }
    }

    function createBoundRunner(iframe, window) {

        var current_test, finalize, queue, reporter, result;

        function testNext() {
            reporter();
            current_test = queue.shift();
            iframe.src = "./tests/" + current_test.category+ "/" + current_test.name +
                "/index.html";
        }

        function iterate() {
            if (queue.length !== 0) {
                testNext();
            }
            else {
                window.removeEventListener("message", catchMessage, false);
                finalize(result);
            }
        }

        function catchMessage(message) {
            result[current_test.category][current_test.name].push(message.data);
            iterate();
        }

        window.addEventListener("message", catchMessage, false);
        iframe.onload = postIfSet;

        return function(tests, resultContainer, callback)  {
            queue = tests;
            finalize = callback;
            result = resultContainer;
            reporter = createReport(tests.length);
            testNext();
        };
    }

    // Data presentation methods

    function sum (array) {
        var result = 0, i;
        for(i = 0; i < array.length; i += 1){
            result += (+array[i]); //If the elements are strings (IE 9), we coerce them to numbers
        }
        return result;
    }

    function findMinMax (array) {
        var max = -Number.MAX_VALUE,
            min = Number.MAX_VALUE;
        array.forEach(function(element) {
            if (element >= max) {
                max = element;
            }
            if (element <= min) {
                min = element;
            }
        });
        return [min, max];
    }

    function makeFilter (smallest, largest) {
        var removals = 0;
        return function (value, index, array) {
            if (value !== smallest && value !== largest) {
                return true;
            }
            removals += 1;
            if (removals <= 2) {
                return false;
            }
            return true;

        };
    }

    function findFinalNumber (array) {

        var trimmed_array = array.filter(makeFilter.apply(null, findMinMax(array))),
            naive_average = sum(trimmed_array)/trimmed_array.length;
        return Math.round(naive_average);
    }

    function createTestURL(category, test){
        var url = document.createElement('a');
        url.href= './tests/' + category + "/" + test + "/";
        url.appendChild(document.createTextNode(test));
        return url;
    }

    function createTable (result) {

        var category, category_array, final_number, header, result_string,
            results, row, tblbody = document.createElement('tbody'), testname,
            urlfield;

        for(category in result){
            if(result.hasOwnProperty(category)){

                category_array = result[category];
                header = makeParent(text(category), 'th');
                tblbody.appendChild(makeParent(header, 'tr'));

                for(testname in category_array){
                    if(category_array.hasOwnProperty(testname)) {
                        row = document.createElement('tr');
                        urlfield = createTestURL(category, testname);

                        results = result[category][testname];
                        final_number = findFinalNumber(results);
                        result_string = text(final_number+' ms');

                        appendChildren(row, makeParent(urlfield, 'td'),
                                makeParent(result_string, 'td'));

                        tblbody.appendChild(row);
                    }
                }
            }
        }

        table.appendChild(tblbody);
    }

    function finalMessage() {
        var p1 = paragraph('Alright! Testing completed.'),
            p2 = paragraph('Do take the results with a grain of salt.'),
            p3 = paragraph(button);

        button.textContent = "One more time, please";

        return [p1, p2, p3];
    }

    function pushResultToHistory(result){
        window.location.hash = encodeURIComponent(JSON.stringify(result));
        present(result);
    }

    function present (result) {
        iframe.style.display = "none";
        updateArticle(finalMessage());
        createTable(result);
    }

    function printIncompatibilityMessage() {
        (document.getElementsByTagName('body')[0]).innerHTML = "
Unfortunately," +
    "  your browser does not support the " + "Navigation Timing API.
";
    }

    function resetTableIfSet(){
        if(table.childNodes.length !== 0) {
            table.removeChild(table.childNodes[0]);
        }
    }

    function engage () {
        resetTableIfSet();
        iframe.style.display = "block";
        createBoundRunner(iframe, window)(buildQueueFromProvidedTestObject(data, iterations),
                makeResultsObject(data), pushResultToHistory);
    }

    // Return if incompatible with Navigation Timing API

    if(window.performance === undefined) {
        printIncompatibilityMessage();
        return;
    }

    window.onload = function () {

        if (location.hash !== "")
            present (JSON.parse(decodeURIComponent(String.prototype.slice.call(location.hash, 1))));
    }

    inner_window.domain = document.domain;
    button.onclick = engage;

}(TESTRUNNER.data, 6, document.getElementsByTagName('iframe')[0], document, window,
        document.getElementsByTagName('article')[0], document.getElementsByTagName('button')[0],document.getElementsByTagName('table')[0]));
<|endoftext|>


<commit_before>var TelegramBot = require('node-telegram-bot-api');
var chalk = require('chalk');
var readline = require('readline');
var rest = require('restler');
var Promise = require('bluebird');
var logger = require('./logger');
var weather = require('./weather');
var travel = require('./travel');
var dining = require('./dining');
var do_not_open = require('./do_not_open');
var broadcast = require('./broadcast');
var cinnamon = require('./cinnamon');
var db = require('./db');
var statistics = require('./statistics');
var util = require('./util');
var CREDENTIALS = require('./private/telegram_credentials.json');

var bot = new TelegramBot(CREDENTIALS.token, {
    polling: true
});

console.log(chalk.blue("============================"));
console.log(chalk.blue("                            "));
console.log(chalk.blue("      CinnaBot Started      "));
console.log(chalk.blue("                            "));
console.log(chalk.blue("============================"));
console.log(chalk.blue("                            "));

var diningSessions = {};
var feedbackSessions = {};
var nusbusSessions = {};

// start CLI app
var rl = readline.createInterface(process.stdin, process.stdout);
rl.prompt();
rl.on('line', function(line) {
    switch (line.trim()) {
        case 'bcast':
            broadcast.broadcast(bot);
            break;
        case 'exit':
            return process.exit(0);
        case 'hello':
            console.log('world!');
            break;
        default:
            console.log("Didn't catch that!");
            break;
    }
    rl.prompt();
});

// Any kind of message
bot.on('message', function(msg) {
    try {
        console.log(msg);
        if (!msg.hasOwnProperty('text') && !msg.hasOwnProperty('location')) {
            return false;
        }
        logger.log(msg);

        if (msg.hasOwnProperty('location')) {
            return processLocation(msg);
        }

        var chatId = msg.chat.id;
        var body = msg.text;
        var command = body;
        var args = body;
        if (body.charAt(0) === '/') {
            command = body.split(' ')[0].substr(1);
            args = body.split(' ')[1];
        }
        // manage commands
        switch (command.toLowerCase()) {
            case "start":
                return help(chatId);
            case "help":
                return help(chatId);
            case "psi":
                return psi(chatId);
            case "bus":
                var busstop = args;
                return bus(chatId, busstop);
            case "nusbus":
                var nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
                return nusbus_ask(chatId);
            case "dining":
                diningSessions[chatId] = diningSessions[chatId] || new DiningSession(chatId);
                return ask_dining_feedback(chatId);
            case "spaces":
                return spaces(chatId);
            case "events":
                return events(chatId);
            case "cat":
                return catfact(chatId);
            case "feedback":
                return feedback(chatId);
            case "stats":
                return stats(chatId);
            case "links":
                return links(chatId);
            case "cancel":
                return cancel(chatId);
        }

        // manage markups
        switch (body.toLowerCase()) {
            case 'show me utown buses':
                return nusbus_query(chatId, "utown", null);
            case 'towards buona vista':
                return bus(chatId, 19051);
            case 'towards clementi':
                return bus(chatId, 19059);
            default:
                var diningSession = diningSessions[chatId] || new DiningSession(chatId);
                if (diningSession.inThread.status) {
                    return diningSession.inThread.next(body, chatId);
                }
                var feedbackSession = feedbackSessions[chatId] || new FeedbackSession(chatId);
                if (feedbackSession.onGoing) {
                    return continue_feedback(body, chatId, msg);
                }
                nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
                if (nusbusSession.onGoing) {
                    return nusbus_query(chatId, body.toLowerCase(), msg.location);
                }
                if (body.toLowerCase().indexOf("thanks") > -1) {
                    return welcome_msg(chatId);
                }
                return default_msg(chatId);
        }
    } catch (e) {
        bot.sendMessage(msg.chat.id, "Cinnabot is sleeping right now 😴 Wake him up later.").then(function() {
            bot.sendMessage(msg.chat.id, "Here's a catfact instead:").then(function() {
                catfact(msg.chat.id);
            });
        });
        bot.sendMessage('102675141', e.toString());
    }
});

function processLocation(msg) {
    console.log('eneterd process Location');
    var chatId = msg.chat.id;
    var nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
    console.log(nusbusSession);
    if (nusbusSession.onGoing) {
        return nusbus_query(chatId, msg.text, msg.location);
    }
    return default_msg(chatId);
}

function cancel(chatId) {
    diningSessions[chatId] = new DiningSession(chatId);
    feedbackSessions[chatId] = new FeedbackSession(chatId);
    bot.sendMessage(chatId, "Canceled.", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    });
}

function help(chatId) {
    var helpMessage =
        "Here's what you can ask Cinnabot!\n\n" +
        "/psi            - get the psi and weather conditions\n" +
        "/bus            - check bus timings for UTown and Dover road\n" +
        "/bus <busstop>  - check bus timings for <busstop>\n" +
        "/nusbus         - check bus timings for NUS buses\n" +
        "/dining         - tell us how the food was\n" +
        "/spaces         - view upcoming activities in USP spaces\n" +
        "/events         - view upcoming USP events\n" +
        "/links          - view useful links\n" +
        "/feedback       - send suggestions and complaints\n" +
        "/stats          - view key statistics\n";
    bot.sendMessage(chatId, helpMessage);
}

function links(chatId) {
    var linkText =
        "USEFUL LINKS:\n" +
        "==============\n\n" +
        "Check your meal credits:\n" +
        "https://bit.ly/hungrycinnamon\n\n" +
        "Report faults in Cinnamon:\n" +
        "https://bit.ly/faultycinnamon\n\n" +
        "Check your air-con credits:\n" +
        "https://bit.ly/chillycinnamon";
    bot.sendMessage(chatId, linkText, {
        disable_web_page_preview: true
    });
}

function stats(chatId) {
    function callback(data) {
        bot.sendMessage(chatId, data);
    }
    statistics.getAllSummary(callback);
}

function done_feedback(chatId, msg) {
    var feedbackSession = feedbackSessions[chatId];
    var feedbackMsg = feedbackSession.feedbackMsg;
    feedbackMsg = feedbackMsg.substring(0, feedbackMsg.length - 6);
    logger.feedback(bot, feedbackMsg, msg);
    feedbackSessions[chatId] = new FeedbackSession(chatId);
    var doneMsg = "Thanks for the feedback 😁";
    return bot.sendMessage(chatId, doneMsg);
}

function continue_feedback(body, chatId, msg) {
    var feedbackSession = feedbackSessions[chatId];
    feedbackSession.feedbackMsg += body + "\n";
    if (body.endsWith("/done")) {
        return done_feedback(chatId, msg);
    }
    return;
}

function feedback(chatId) {
    var feedbackMsg = "Thanks for using Cinnabot 😁\n";
    feedbackMsg += "Feel free to tell us how cinnabot can be improved.\n";
    feedbackMsg += "Type /done to end the feedback session.\n";
    feedbackMsg += "Type /cancel to cancel feedback";
    var feedbackSession = new FeedbackSession();
    feedbackSession.onGoing = true;
    feedbackSessions[chatId] = feedbackSession;
    return bot.sendMessage(chatId, feedbackMsg);
}

function nusbus_ask(chatId) {
    var opts = {
        reply_markup: JSON.stringify({
            keyboard: [
                ['Nearest Bustop'],
                ['UTown', 'Computing'],
                ['Central Library', 'Computer Centre'],
                ['Science', 'Business'],
                ['Kent Ridge MRT', 'Bukit Timah Campus']
            ],
            one_time_keyboard: true
        })
    };
    var greeting = "Good " + util.currentTimeGreeting() + ", where would you like NUS bus timings for?";
    bot.sendMessage(chatId, greeting, opts);
    nusbusSessions[chatId] = new NusBusSession(chatId);
    nusbusSessions[chatId].onGoing = true;
}

function nusbus_query(chatId, busstop_name, location) {
    var locResponse = "Please send me your location to find NUS bus timings for the nearest bus stop\n\n";
    locResponse += "You can do this by selecting the paperclip icon 📎";
    locResponse += "and attach your location 📌";

    if (busstop_name === "nearest bustop") {
        return bot.sendMessage(chatId, locResponse, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
    }

    function callback(err, data) {
        if (err) {
            return bot.sendMessage(chatId, err, {
                reply_markup: JSON.stringify({
                    hide_keyboard: true
                })
            });
        }
        bot.sendMessage(chatId, data, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
        nusbusSessions[chatId] = new NusBusSession(chatId);
    }
    travel.nusbus(callback, busstop_name, location);
}

function catfact(chatId) {
    return do_not_open.catfact(chatId, bot);
}

function cnjoke(chatId) {
    return do_not_open.cnjoke(chatId, bot);
}

function events(chatId) {
    cinnamon.getEvents(chatId, bot);
}

function spaces(chatId) {
    cinnamon.getAllSpaces(chatId, bot);
}

function ask_dining_feedback(chatId) {
    var diningSession = diningSessions[chatId];
    diningSession.inThread.status = true;
    dining.ask_when_dining_feedback(chatId, bot);
    diningSession.inThread.next = ask_where_dining_feedback;
}

function ask_where_dining_feedback(when, chatId) {
    var validOptions = ["Breakfast", "Dinner"];
    if (validOptions.indexOf(when) < 0) {
        return ask_dining_feedback(chatId);
    }
    var diningSession = diningSessions[chatId];
    diningSession.diningFeedback.when = when;
    dining.ask_where_dining_feedback(chatId, bot, when);
    diningSession.inThread.next = ask_how_dining_feedback;
}

function ask_how_dining_feedback(where, chatId) {
    var diningSession = diningSessions[chatId];
    var df = diningSession.diningFeedback;
    var validOptions = [];
    if (df.when === "Breakfast") {
        validOptions = ['Asian', 'Western', 'Muslim', 'Toast', 'Other'];
    } else if (df.when === "Dinner") {
        validOptions = ['Noodle', 'Asian', 'Western - Main Course', 'Western - Panini', 'Indian', 'Malay', 'Late Plate'];
    }
    if (validOptions.indexOf(where) < 0) {
        return ask_where_dining_feedback(df.when, chatId);
    }
    df.where = where;
    dining.ask_how_dining_feedback(chatId, bot);
    diningSession.inThread.next = done_dining_feedback;
}

function done_dining_feedback(how, chatId) {
    var diningSession = diningSessions[chatId];
    var df = diningSession.diningFeedback;
    var validOptions = ['👍', '👍👍', '👍👍👍', '👍👍👍👍', '👍👍👍👍👍'];
    if (validOptions.indexOf(how) < 0) {
        return ask_how_dining_feedback(df.where, chatId);
    }
    df.how = how.length / 2;
    dining.dining_feedback(chatId, bot, df.when, df.where, df.how);
    diningSessions[chatId] = new DiningSession(chatId);
}

function DiningSession(chatId) {
    this.chatId = chatId;
    this.inThread = {
        "status": false,
        "next": ask_where_dining_feedback
    };
    this.diningFeedback = new DiningFeedback();
    diningSessions[chatId] = this;
}

function DiningFeedback() {
    this.when = "";
    this.where = "";
    this.how = -1;
}

function FeedbackSession(chatId) {
    this.chatId = chatId;
    this.onGoing = false;
    this.feedbackMsg = "";
    feedbackSessions[chatId] = this;
}

function NusBusSession(chatId) {
    this.chatId = chatId;
    this.onGoing = false;
    nusbusSessions[chatId] = this;
}

function psi(chatId) {
    function callback(msg) {
        bot.sendMessage(chatId, msg);
    }
    weather.getWeather(callback);
}

function bus(chatId, busstop) {
    function basicCallback(data) {
        bot.sendMessage(chatId, data, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
    }

    function callback(data) {
        var opts = {
            reply_markup: JSON.stringify({
                keyboard: [
                    ['Show me UTown Buses'],
                    ['Towards Buona Vista'],
                    ['Towards Clementi'],
                ],
                one_time_keyboard: true
            })
        };
        bot.sendMessage(chatId, data, opts);
    }

    if (busstop) {
        return travel.busStopQuery(busstop, basicCallback);
    }
    var greeting = "Good " + util.currentTimeGreeting() + ". Where do you want to go today?";
    callback(greeting);
}

function welcome_msg(chatId) {
    bot.sendMessage(chatId, "You're welcome 😚", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    });
}

function default_msg(chatId) {
    bot.sendMessage(chatId, "Hey we didn't understand you! Here's a chuck norris fact instead:\n", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    }).then(function() {
        return cnjoke(chatId);
    });
}<commit_msg>formatted locResponse message<commit_after>var TelegramBot = require('node-telegram-bot-api');
var chalk = require('chalk');
var readline = require('readline');
var rest = require('restler');
var Promise = require('bluebird');
var logger = require('./logger');
var weather = require('./weather');
var travel = require('./travel');
var dining = require('./dining');
var do_not_open = require('./do_not_open');
var broadcast = require('./broadcast');
var cinnamon = require('./cinnamon');
var db = require('./db');
var statistics = require('./statistics');
var util = require('./util');
var CREDENTIALS = require('./private/telegram_credentials.json');

var bot = new TelegramBot(CREDENTIALS.token, {
    polling: true
});

console.log(chalk.blue("============================"));
console.log(chalk.blue("                            "));
console.log(chalk.blue("      CinnaBot Started      "));
console.log(chalk.blue("                            "));
console.log(chalk.blue("============================"));
console.log(chalk.blue("                            "));

var diningSessions = {};
var feedbackSessions = {};
var nusbusSessions = {};

// start CLI app
var rl = readline.createInterface(process.stdin, process.stdout);
rl.prompt();
rl.on('line', function(line) {
    switch (line.trim()) {
        case 'bcast':
            broadcast.broadcast(bot);
            break;
        case 'exit':
            return process.exit(0);
        case 'hello':
            console.log('world!');
            break;
        default:
            console.log("Didn't catch that!");
            break;
    }
    rl.prompt();
});

// Any kind of message
bot.on('message', function(msg) {
    try {
        console.log(msg);
        if (!msg.hasOwnProperty('text') && !msg.hasOwnProperty('location')) {
            return false;
        }
        logger.log(msg);

        if (msg.hasOwnProperty('location')) {
            return processLocation(msg);
        }

        var chatId = msg.chat.id;
        var body = msg.text;
        var command = body;
        var args = body;
        if (body.charAt(0) === '/') {
            command = body.split(' ')[0].substr(1);
            args = body.split(' ')[1];
        }
        // manage commands
        switch (command.toLowerCase()) {
            case "start":
                return help(chatId);
            case "help":
                return help(chatId);
            case "psi":
                return psi(chatId);
            case "bus":
                var busstop = args;
                return bus(chatId, busstop);
            case "nusbus":
                var nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
                return nusbus_ask(chatId);
            case "dining":
                diningSessions[chatId] = diningSessions[chatId] || new DiningSession(chatId);
                return ask_dining_feedback(chatId);
            case "spaces":
                return spaces(chatId);
            case "events":
                return events(chatId);
            case "cat":
                return catfact(chatId);
            case "feedback":
                return feedback(chatId);
            case "stats":
                return stats(chatId);
            case "links":
                return links(chatId);
            case "cancel":
                return cancel(chatId);
        }

        // manage markups
        switch (body.toLowerCase()) {
            case 'show me utown buses':
                return nusbus_query(chatId, "utown", null);
            case 'towards buona vista':
                return bus(chatId, 19051);
            case 'towards clementi':
                return bus(chatId, 19059);
            default:
                var diningSession = diningSessions[chatId] || new DiningSession(chatId);
                if (diningSession.inThread.status) {
                    return diningSession.inThread.next(body, chatId);
                }
                var feedbackSession = feedbackSessions[chatId] || new FeedbackSession(chatId);
                if (feedbackSession.onGoing) {
                    return continue_feedback(body, chatId, msg);
                }
                nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
                if (nusbusSession.onGoing) {
                    return nusbus_query(chatId, body.toLowerCase(), msg.location);
                }
                if (body.toLowerCase().indexOf("thanks") > -1) {
                    return welcome_msg(chatId);
                }
                return default_msg(chatId);
        }
    } catch (e) {
        bot.sendMessage(msg.chat.id, "Cinnabot is sleeping right now 😴 Wake him up later.").then(function() {
            bot.sendMessage(msg.chat.id, "Here's a catfact instead:").then(function() {
                catfact(msg.chat.id);
            });
        });
        bot.sendMessage('102675141', e.toString());
    }
});

function processLocation(msg) {
    console.log('eneterd process Location');
    var chatId = msg.chat.id;
    var nusbusSession = nusbusSessions[chatId] || new NusBusSession(chatId);
    console.log(nusbusSession);
    if (nusbusSession.onGoing) {
        return nusbus_query(chatId, msg.text, msg.location);
    }
    return default_msg(chatId);
}

function cancel(chatId) {
    diningSessions[chatId] = new DiningSession(chatId);
    feedbackSessions[chatId] = new FeedbackSession(chatId);
    bot.sendMessage(chatId, "Canceled.", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    });
}

function help(chatId) {
    var helpMessage =
        "Here's what you can ask Cinnabot!\n\n" +
        "/psi            - get the psi and weather conditions\n" +
        "/bus            - check bus timings for UTown and Dover road\n" +
        "/bus <busstop>  - check bus timings for <busstop>\n" +
        "/nusbus         - check bus timings for NUS buses\n" +
        "/dining         - tell us how the food was\n" +
        "/spaces         - view upcoming activities in USP spaces\n" +
        "/events         - view upcoming USP events\n" +
        "/links          - view useful links\n" +
        "/feedback       - send suggestions and complaints\n" +
        "/stats          - view key statistics\n";
    bot.sendMessage(chatId, helpMessage);
}

function links(chatId) {
    var linkText =
        "USEFUL LINKS:\n" +
        "==============\n\n" +
        "Check your meal credits:\n" +
        "https://bit.ly/hungrycinnamon\n\n" +
        "Report faults in Cinnamon:\n" +
        "https://bit.ly/faultycinnamon\n\n" +
        "Check your air-con credits:\n" +
        "https://bit.ly/chillycinnamon";
    bot.sendMessage(chatId, linkText, {
        disable_web_page_preview: true
    });
}

function stats(chatId) {
    function callback(data) {
        bot.sendMessage(chatId, data);
    }
    statistics.getAllSummary(callback);
}

function done_feedback(chatId, msg) {
    var feedbackSession = feedbackSessions[chatId];
    var feedbackMsg = feedbackSession.feedbackMsg;
    feedbackMsg = feedbackMsg.substring(0, feedbackMsg.length - 6);
    logger.feedback(bot, feedbackMsg, msg);
    feedbackSessions[chatId] = new FeedbackSession(chatId);
    var doneMsg = "Thanks for the feedback 😁";
    return bot.sendMessage(chatId, doneMsg);
}

function continue_feedback(body, chatId, msg) {
    var feedbackSession = feedbackSessions[chatId];
    feedbackSession.feedbackMsg += body + "\n";
    if (body.endsWith("/done")) {
        return done_feedback(chatId, msg);
    }
    return;
}

function feedback(chatId) {
    var feedbackMsg = "Thanks for using Cinnabot 😁\n";
    feedbackMsg += "Feel free to tell us how cinnabot can be improved.\n";
    feedbackMsg += "Type /done to end the feedback session.\n";
    feedbackMsg += "Type /cancel to cancel feedback";
    var feedbackSession = new FeedbackSession();
    feedbackSession.onGoing = true;
    feedbackSessions[chatId] = feedbackSession;
    return bot.sendMessage(chatId, feedbackMsg);
}

function nusbus_ask(chatId) {
    var opts = {
        reply_markup: JSON.stringify({
            keyboard: [
                ['Nearest Bustop'],
                ['UTown', 'Computing'],
                ['Central Library', 'Computer Centre'],
                ['Science', 'Business'],
                ['Kent Ridge MRT', 'Bukit Timah Campus']
            ],
            one_time_keyboard: true
        })
    };
    var greeting = "Good " + util.currentTimeGreeting() + ", where would you like NUS bus timings for?";
    bot.sendMessage(chatId, greeting, opts);
    nusbusSessions[chatId] = new NusBusSession(chatId);
    nusbusSessions[chatId].onGoing = true;
}

function nusbus_query(chatId, busstop_name, location) {
    var locResponse = "Please send me your location to find NUS bus timings for the nearest bus stop:\n\n";
    locResponse += "You can do this by selecting the paperclip icon (📎)";
    locResponse += "followed by attaching your location (📌).";

    if (busstop_name === "nearest bustop") {
        return bot.sendMessage(chatId, locResponse, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
    }

    function callback(err, data) {
        if (err) {
            return bot.sendMessage(chatId, err, {
                reply_markup: JSON.stringify({
                    hide_keyboard: true
                })
            });
        }
        bot.sendMessage(chatId, data, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
        nusbusSessions[chatId] = new NusBusSession(chatId);
    }
    travel.nusbus(callback, busstop_name, location);
}

function catfact(chatId) {
    return do_not_open.catfact(chatId, bot);
}

function cnjoke(chatId) {
    return do_not_open.cnjoke(chatId, bot);
}

function events(chatId) {
    cinnamon.getEvents(chatId, bot);
}

function spaces(chatId) {
    cinnamon.getAllSpaces(chatId, bot);
}

function ask_dining_feedback(chatId) {
    var diningSession = diningSessions[chatId];
    diningSession.inThread.status = true;
    dining.ask_when_dining_feedback(chatId, bot);
    diningSession.inThread.next = ask_where_dining_feedback;
}

function ask_where_dining_feedback(when, chatId) {
    var validOptions = ["Breakfast", "Dinner"];
    if (validOptions.indexOf(when) < 0) {
        return ask_dining_feedback(chatId);
    }
    var diningSession = diningSessions[chatId];
    diningSession.diningFeedback.when = when;
    dining.ask_where_dining_feedback(chatId, bot, when);
    diningSession.inThread.next = ask_how_dining_feedback;
}

function ask_how_dining_feedback(where, chatId) {
    var diningSession = diningSessions[chatId];
    var df = diningSession.diningFeedback;
    var validOptions = [];
    if (df.when === "Breakfast") {
        validOptions = ['Asian', 'Western', 'Muslim', 'Toast', 'Other'];
    } else if (df.when === "Dinner") {
        validOptions = ['Noodle', 'Asian', 'Western - Main Course', 'Western - Panini', 'Indian', 'Malay', 'Late Plate'];
    }
    if (validOptions.indexOf(where) < 0) {
        return ask_where_dining_feedback(df.when, chatId);
    }
    df.where = where;
    dining.ask_how_dining_feedback(chatId, bot);
    diningSession.inThread.next = done_dining_feedback;
}

function done_dining_feedback(how, chatId) {
    var diningSession = diningSessions[chatId];
    var df = diningSession.diningFeedback;
    var validOptions = ['👍', '👍👍', '👍👍👍', '👍👍👍👍', '👍👍👍👍👍'];
    if (validOptions.indexOf(how) < 0) {
        return ask_how_dining_feedback(df.where, chatId);
    }
    df.how = how.length / 2;
    dining.dining_feedback(chatId, bot, df.when, df.where, df.how);
    diningSessions[chatId] = new DiningSession(chatId);
}

function DiningSession(chatId) {
    this.chatId = chatId;
    this.inThread = {
        "status": false,
        "next": ask_where_dining_feedback
    };
    this.diningFeedback = new DiningFeedback();
    diningSessions[chatId] = this;
}

function DiningFeedback() {
    this.when = "";
    this.where = "";
    this.how = -1;
}

function FeedbackSession(chatId) {
    this.chatId = chatId;
    this.onGoing = false;
    this.feedbackMsg = "";
    feedbackSessions[chatId] = this;
}

function NusBusSession(chatId) {
    this.chatId = chatId;
    this.onGoing = false;
    nusbusSessions[chatId] = this;
}

function psi(chatId) {
    function callback(msg) {
        bot.sendMessage(chatId, msg);
    }
    weather.getWeather(callback);
}

function bus(chatId, busstop) {
    function basicCallback(data) {
        bot.sendMessage(chatId, data, {
            reply_markup: JSON.stringify({
                hide_keyboard: true
            })
        });
    }

    function callback(data) {
        var opts = {
            reply_markup: JSON.stringify({
                keyboard: [
                    ['Show me UTown Buses'],
                    ['Towards Buona Vista'],
                    ['Towards Clementi'],
                ],
                one_time_keyboard: true
            })
        };
        bot.sendMessage(chatId, data, opts);
    }

    if (busstop) {
        return travel.busStopQuery(busstop, basicCallback);
    }
    var greeting = "Good " + util.currentTimeGreeting() + ". Where do you want to go today?";
    callback(greeting);
}

function welcome_msg(chatId) {
    bot.sendMessage(chatId, "You're welcome 😚", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    });
}

function default_msg(chatId) {
    bot.sendMessage(chatId, "Hey we didn't understand you! Here's a chuck norris fact instead:\n", {
        reply_markup: JSON.stringify({
            hide_keyboard: true
        })
    }).then(function() {
        return cnjoke(chatId);
    });
}<|endoftext|>


<commit_before>'use strict'
import Immutable from 'seamless-immutable'
import {Constants} from './actions'
import _ from 'lodash'
import moment from 'moment'

export function lastNavigation (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.NAVIGATE_SEARCH:
    case Constants.NAVIGATE_VIEW_PROFILE:
    case Constants.NAVIGATE_VIEW_AGGREGATE:
      return action
    default:
      return state
  }
}

export function profileData (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_PROFILE_DATA:
      return action.payload
    default:
      return state
  }
}

export function profileAggregateData (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_PROFILE_AGGREGATE_DATA:
      return action.payload
    default:
      return state
  }
}

export function searchResults (state = Immutable({search: null, results: {}}), action) {
  switch (action.type) {
    case Constants.RECEIVE_SEARCH_RESULTS: {
      let {payload: {params, results}} = action
      return Immutable({
        search: params,
        results: _.fromPairs(results)
      })
    }
    case Constants.GOT_MORE: {
      let {payload: {params, results}} = action
      if (_.isEqual(params, state.search)) {
        return state.set('results', state.results.merge(_.fromPairs(results)))
      } else return state
    }
    default:
      return state
  }
}

export function expandedCallGraphNodes (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.TOGGLE_CALL_GRAPH_NODE:
      return state.update(action.payload, x => !x)
    case Constants.NAVIGATE_SEARCH:
    case Constants.NAVIGATE_VIEW_PROFILE:
      return Immutable({})
    default:
      return state
  }
}

function _formatDatetimeLocal (m) {
  m = moment(m)
  if (m.isValid()) return m.format('YYYY-MM-DDTHH:mm:ss')
  else return ''
}

export function searchOptions (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.NAVIGATE_SEARCH: {
      let options = action.payload
      if (options._start_time) {
        options = options.update('_start_time', _formatDatetimeLocal)
      }
      if (options._end_time) {
        options = options.update('_end_time', _formatDatetimeLocal)
      }
      return options
    }

    case Constants.CHANGE_SEARCH_OPTIONS:
      return action.payload
    default:
      return state
  }
}

export function expandedDropDown (state = null, action) {
  switch (action.type) {
    case Constants.SET_VISIBLE_DROPDOWN:
      return action.payload
    default:
      return state
  }
}

export function attribs (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_ATTRIB_LIST:
      return Immutable(
        _.fromPairs(
          action.payload.map(x => [x, []])
        )
      )
    case Constants.RECEIVE_ATTRIB_VALUES: {
      let {attrib, values} = action.payload
      if (values.length > 0) return state.set(attrib, values)
      else return state
    }
    default:
      return state
  }
}
<commit_msg>Clear expanded state when navigating between aggregate views<commit_after>'use strict'
import Immutable from 'seamless-immutable'
import {Constants} from './actions'
import _ from 'lodash'
import moment from 'moment'

export function lastNavigation (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.NAVIGATE_SEARCH:
    case Constants.NAVIGATE_VIEW_PROFILE:
    case Constants.NAVIGATE_VIEW_AGGREGATE:
      return action
    default:
      return state
  }
}

export function profileData (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_PROFILE_DATA:
      return action.payload
    default:
      return state
  }
}

export function profileAggregateData (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_PROFILE_AGGREGATE_DATA:
      return action.payload
    default:
      return state
  }
}

export function searchResults (state = Immutable({search: null, results: {}}), action) {
  switch (action.type) {
    case Constants.RECEIVE_SEARCH_RESULTS: {
      let {payload: {params, results}} = action
      return Immutable({
        search: params,
        results: _.fromPairs(results)
      })
    }
    case Constants.GOT_MORE: {
      let {payload: {params, results}} = action
      if (_.isEqual(params, state.search)) {
        return state.set('results', state.results.merge(_.fromPairs(results)))
      } else return state
    }
    default:
      return state
  }
}

export function expandedCallGraphNodes (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.TOGGLE_CALL_GRAPH_NODE:
      return state.update(action.payload, x => !x)
    case Constants.NAVIGATE_SEARCH:
    case Constants.NAVIGATE_VIEW_PROFILE:
    case Constants.NAVIGATE_VIEW_AGGREGATE:
      return Immutable({})
    default:
      return state
  }
}

function _formatDatetimeLocal (m) {
  m = moment(m)
  if (m.isValid()) return m.format('YYYY-MM-DDTHH:mm:ss')
  else return ''
}

export function searchOptions (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.NAVIGATE_SEARCH: {
      let options = action.payload
      if (options._start_time) {
        options = options.update('_start_time', _formatDatetimeLocal)
      }
      if (options._end_time) {
        options = options.update('_end_time', _formatDatetimeLocal)
      }
      return options
    }

    case Constants.CHANGE_SEARCH_OPTIONS:
      return action.payload
    default:
      return state
  }
}

export function expandedDropDown (state = null, action) {
  switch (action.type) {
    case Constants.SET_VISIBLE_DROPDOWN:
      return action.payload
    default:
      return state
  }
}

export function attribs (state = Immutable({}), action) {
  switch (action.type) {
    case Constants.RECEIVE_ATTRIB_LIST:
      return Immutable(
        _.fromPairs(
          action.payload.map(x => [x, []])
        )
      )
    case Constants.RECEIVE_ATTRIB_VALUES: {
      let {attrib, values} = action.payload
      if (values.length > 0) return state.set(attrib, values)
      else return state
    }
    default:
      return state
  }
}
<|endoftext|>


<commit_before>"use strict"
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var log4js = require('log4js');
var swig = require('swig');
var ws = require('ws');
var config = require('config');
var fs = require('fs');

/*
 * Configure logs
 *  - All web + signaling messaging to go in webapp.log
 *  - All Webrtc logs to go in webrtc.log
 */
var logger_cfg = path.join(__dirname, 'logger.json');
var logging_path = path.join(__dirname, '');
configureLog();
var logger = log4js.getLogger('web');
console.log('Logger json - ', logger_cfg);
console.log('Log path - ', logging_path);

var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
var swig = new swig.Swig();
app.engine('html', swig.renderFile);
app.set('view engine', 'html');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO }));

//app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

var routes = require('./routes/index');
app.use('/', routes);
//app.use('/groupcall', wsroutes);
//app.use('/users', users);

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

// error handlers

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

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

/**
 * Common methods
 */
// Configure logs
function configureLog() {
	/**
	 * Delete previous log files
	 */
	try {
		fs.unlinkSync(path.join(__dirname, 'logs/webapp.log'));
		fs.unlinkSync(path.join(__dirname, 'logs/webrtc.log'));
	}catch(err){
		console.error(err);
	}

	log4js.configure(logger_cfg, { cwd: logging_path });
};

function deletefile(filepath, callback){
	fs.stat(filepath, function (err, stats) {
		   if (err) {
		       return console.error(err);
		   }
	});
}

module.exports = app;
<commit_msg>Added logs folder creation<commit_after>"use strict"
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var log4js = require('log4js');
var swig = require('swig');
var ws = require('ws');
var config = require('config');
var fs = require('fs');

/*
 * Configure logs
 *  - All web + signaling messaging to go in webapp.log
 *  - All Webrtc logs to go in webrtc.log
 */
var logger_cfg = path.join(__dirname, 'logger.json');
var logging_path = path.join(__dirname, '');
configureLog();
var logger = log4js.getLogger('web');
console.log('Logger json - ', logger_cfg);
console.log('Log path - ', logging_path);

var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
var swig = new swig.Swig();
app.engine('html', swig.renderFile);
app.set('view engine', 'html');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO }));

//app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

var routes = require('./routes/index');
app.use('/', routes);
//app.use('/groupcall', wsroutes);
//app.use('/users', users);

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

// error handlers

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

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

/**
 * Common methods
 */
// Configure logs
function configureLog() {

	/**
	 * create log file path
	 */
	var logdir = path.join(__dirname, 'logs');

	if (!fs.existsSync(logdir)){
	    fs.mkdirSync(logdir);
	}else {
		/**
		 * Delete previous log files
		 */
		try {
			fs.unlinkSync(path.join(__dirname, 'logs/webapp.log'));
			fs.unlinkSync(path.join(__dirname, 'logs/webrtc.log'));
		}catch(err){
			console.error(err);
		}
	}

	log4js.configure(logger_cfg, { cwd: logging_path });
};

function deletefile(filepath, callback){
	fs.stat(filepath, function (err, stats) {
		   if (err) {
		       return console.error(err);
		   }
	});
}

module.exports = app;
<|endoftext|>


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

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

var app = express();

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

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

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

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

// error handlers

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

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

module.exports = app;
<commit_msg>commit the lic file<commit_after>var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

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

var app = express();

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

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

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

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

// error handlers

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

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

module.exports = app;
<|endoftext|>