<commit_before> /*******************************************************************************************
  * Mafia Variables *************************************************************************
  ******************************************************************************************/

global.mGame = exports.mGame = false;
var mGameStarting = false;
var mDayTime = false;
var mNightTime = exports.mNightTime = false;
var mCounted = false;
var mPlayers = new Array();
var mRooms = require('../rooms.js');
var mNumVotes = 0;
var mNumMob = 0;
var mNumVillager = 0;
var mKillTarget = "";
var mTimer;
var mTheme = {};
var mRolesOrder = [];
var mRolesIndex = 1;

// Shuffle function courtesy of StackOverflow. If this doesn't work, I'll try something else. :P
function mShuffle(array) {
  var currentIndex = array.length
    , temporaryValue
    , randomIndex
    ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

function mEndGame() {
	mGame = exports.mGame = false;
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].inMafia = false;
	}
}

var mRemove = exports.mRemove = function(user, leave) {
	if (leave) {
		mRooms.rooms.mafia.send(user + " has left the game.");
	} else {
		mRooms.rooms.mafia.send(user + " has been killed.");
	}
	var mPlayI = mPlayers.indexOf(user);
	if ( user.mGroup === 'villager'){
		mNumVillager--;
	} else if ( user.mGroup === 'mafia'){
		mNumMob--;
	}
	mPlayers[mPlayI].inMafia = false;
	mPlayers.splice(mPlayI, 1);
	if (mNumVillager === 0){
		mRooms.rooms.mafia.add("All villagers have been killed. The mafia win!");
		mEndGame();
	}else if (mNumMob === 0){
		mRooms.rooms.mafia.add("All mafia have been killed. The villagers win!");
		mEndGame();
	}
}

function mNight() {
	if (!mGame) { return; }
	mRooms.rooms.mafia.send('It is now night. Mafia members, you have 30 seconds to choose a player to kill.');
	mNightTime = exports.mNightTime = true;
	mDayTime = false;
	setTimeout(function(){
		if (!mGame) { return; }
		if(mKillTarget !== ""){
			mRemove(mKillTarget);
			mKillTarget = "";
		} else {
			mRooms.rooms.mafia.send('No one was killed.');
		}
		mInterval();
	}, 30000);
}

function mCount() {
	if (mCounted || !mGame) { return; }
	mRooms.rooms.mafia.send('Counting votes.');
	mCounted = true;
	var mChosen = mPlayers[0];
	var mChosenNum = 0;
	var mTie = false;
	for (var i=0; i<mPlayers.length; i++) {
		if (mPlayers[i].votes > mChosenNum) {
			mChosen = mPlayers[i];
			mChosenNum = mChosen.votes;
			mTie = false;
		} else if (mPlayers[i].votes === mChosenNum) {
			mTie = true;
		}
	}
	if (mTie) {
		mRooms.rooms.mafia.send('No majority was reached.');
	} else {
		mRemove(mChosen, false);
	}
	mNight();
}

function mDay() {
	if (!mGame) { return; }
	mRooms.rooms.mafia.send('It is now day. You have 30 seconds to vote on a person to kill.');
	mDayTime = true;
	mCounted = false;
	mNumVotes = 0;
	mTimer = setTimeout(mCount, 30000);
}

function mInterval() {
	if (!mGame) { return; }
	mNightTime = exports.mNightTime = false;
	mDayTime = false;
	mRooms.rooms.mafia.send('You have 60 seconds to discuss.');
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].voted = false;
		mPlayers[i].votes = 0;
	}
	setTimeout(mDay, 60000);
}

function mGameStart() {
	if (!mGameStarting ) { return; }
	if (mPlayers.length < 5) {
		mRooms.rooms.mafia.add('At least 5 players are required to play. Retrying in 30 seconds.');
		setTimeout(mGameStart, 30000);
		return;
	}
	mGameStarting = false;
	mGame = exports.mGame = true;
	/*
	mShuffle(mPlayers);
	for (var q=0; i<mPlayers.length; q++) {
		mPlayers[q].mRole = mTheme["roles" + mRolesIndex][q]
	}
	*/
	mNumMob = 0;
	mNumVillager = 0;
	mRooms.rooms.mafia.add('A new mafia game has begun. Players: ' + mPlayers);
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].inMafia = true;
		mPlayers[i].mGroup = 'villager';
	}
	for (var j=0; j<(mPlayers.length*.25); j++) {
		var mPlayer = Math.floor(Math.random()*mPlayers.length);
		mPlayers[mPlayer].mGroup = 'mafia';
		mPlayers[mPlayer].sendTo(mRooms.rooms.mafia, "You are a mafia member. Attempt to kill the villagers.");
		mNumMob++;
	}
	for (var k=0; k<mPlayers.length; k++) {
		if (mPlayers[k].mGroup === 'mafia') { continue; }
		mPlayers[k].sendTo(mRooms.rooms.mafia, "You are a villager. Attempt to find out who the mafia are.");
		mNumVillager++;
	}
	mInterval();
}

	/*********************************************************
	 * Mafia commands
	 *********************************************************/

var commands = exports.commands = {

	mstart: function(target, room, user, connection) {
		if (room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		if (mGame) {
			this.sendReplyBox('A game is currently being played.');
		} else {
			mPlayers = [];
			mGameStarting = true;
			room.add('A new mafia game is starting. Type /mjoin to join.');
			mRooms.lobby.add('A new mafia game is starting. Join tervari.psim.us/mafia and type /mjoin to join.');
			/*
			mRooms.lobby.add(mTheme.name);
			mRooms.lobby.add(mTheme.author);
			mRooms.lobby.add(mTheme.summary);
			mRolesOrder = mTheme.roles1;
			mRolesIndex = 1;
			*/
			mTimer = setTimeout(function() {room.add('60 seconds remaining to join.'); setTimeout(mGameStart, 60000);}, 60000)
		}
	},

	mjoin: function(target, room, user, connection) {
		if (!mGameStarting || room !== mRooms.rooms.mafia) { return; }
		if (mPlayers.indexOf(user) !== -1){
			this.sendReplyBox('You have already joined the next mafia game.');
			return;
		}
		mPlayers.push(user);
		this.sendReplyBox('You have joined the mafia game.');
		/*
		if (mTheme["roles" + mRolesIndex].length <= mPlayers.length) {
			try {
				mRolesIndex++;
				mRolesOrder = mTheme["roles" + mRolesIndex];
			}
			catch(e) {
				room.add('Sign-up full. Starting game.');
				clearTimeout(mTimer)
				mGameStart()
			}
		}
		*/
	},

	mkill: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (user.mGroup !== 'mafia'){
			this.sendReplyBox('You are not a member of the mafia.');
			return;
		}
		if (!mNightTime){
			this.sendReplyBox('It is not night.');
			return;
		}
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (targetUser.mGroup === 'mafia'){
			this.sendReplyBox('You cannot kill a member of the mafia.');
			return;
		}
		if (mPlayers.indexOf(targetUser) === -1){
			this.sendReplyBox('That player is not in the game.');
			return;
		}
		mKillTarget = targetUser;
	},

	mvote: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (mPlayers.indexOf(user) === -1){
			this.sendReplyBox('You are not playing.');
			return;
		}
		if (!mDayTime){
			this.sendReplyBox('It is not day.');
			return;
		}
		if (user.voted) {
			this.sendReplyBox('You have already voted.');
			return;
		}
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (mPlayers.indexOf(targetUser) === -1){
			this.sendReplyBox('That player is not in the game.');
			return;
		}
		this.send(user + ' voted for ' + targetUser + '.');
		user.voted = true;
		targetUser.votes = targetUser.votes+1;
		mNumVotes++;
		if (mNumVotes === mPlayers.length) {
			this.send("All players have voted.");
			clearTimeout(mTimer);
			mCount();
		}
	},

	mchat: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (user.mGroup !== 'mafia'){
			this.sendReplyBox('You are not a member of the mafia.');
			return;
		}
		for (var i=0; i<mPlayers.length; i++) {
			if (mPlayers[i].mGroup === 'mafia') {
				mPlayers[i].send("Mafia " + user + ": " + target);
			}
		}
	},

	mstop: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		mEndGame();
	},

	mcancel: function(target, room, user, connection) {
		if (!mGameStarting || room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		mRooms.rooms.mafia.add('Mafia game cancelled.');
		mGameStarting = false;
	}

};
<commit_msg>global<commit_after> /*******************************************************************************************
  * Mafia Variables *************************************************************************
  ******************************************************************************************/

global.mGame = exports.mGame = false;
var mGameStarting = false;
var mDayTime = false;
global.mNightTime = exports.mNightTime = false;
var mCounted = false;
var mPlayers = new Array();
var mRooms = require('../rooms.js');
var mNumVotes = 0;
var mNumMob = 0;
var mNumVillager = 0;
var mKillTarget = "";
var mTimer;
var mTheme = {};
var mRolesOrder = [];
var mRolesIndex = 1;

// Shuffle function courtesy of StackOverflow. If this doesn't work, I'll try something else. :P
function mShuffle(array) {
  var currentIndex = array.length
    , temporaryValue
    , randomIndex
    ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

function mEndGame() {
	mGame = exports.mGame = false;
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].inMafia = false;
	}
}

var mRemove = exports.mRemove = function(user, leave) {
	if (leave) {
		mRooms.rooms.mafia.send(user + " has left the game.");
	} else {
		mRooms.rooms.mafia.send(user + " has been killed.");
	}
	var mPlayI = mPlayers.indexOf(user);
	if ( user.mGroup === 'villager'){
		mNumVillager--;
	} else if ( user.mGroup === 'mafia'){
		mNumMob--;
	}
	mPlayers[mPlayI].inMafia = false;
	mPlayers.splice(mPlayI, 1);
	if (mNumVillager === 0){
		mRooms.rooms.mafia.add("All villagers have been killed. The mafia win!");
		mEndGame();
	}else if (mNumMob === 0){
		mRooms.rooms.mafia.add("All mafia have been killed. The villagers win!");
		mEndGame();
	}
}

function mNight() {
	if (!mGame) { return; }
	mRooms.rooms.mafia.send('It is now night. Mafia members, you have 30 seconds to choose a player to kill.');
	mNightTime = exports.mNightTime = true;
	mDayTime = false;
	setTimeout(function(){
		if (!mGame) { return; }
		if(mKillTarget !== ""){
			mRemove(mKillTarget);
			mKillTarget = "";
		} else {
			mRooms.rooms.mafia.send('No one was killed.');
		}
		mInterval();
	}, 30000);
}

function mCount() {
	if (mCounted || !mGame) { return; }
	mRooms.rooms.mafia.send('Counting votes.');
	mCounted = true;
	var mChosen = mPlayers[0];
	var mChosenNum = 0;
	var mTie = false;
	for (var i=0; i<mPlayers.length; i++) {
		if (mPlayers[i].votes > mChosenNum) {
			mChosen = mPlayers[i];
			mChosenNum = mChosen.votes;
			mTie = false;
		} else if (mPlayers[i].votes === mChosenNum) {
			mTie = true;
		}
	}
	if (mTie) {
		mRooms.rooms.mafia.send('No majority was reached.');
	} else {
		mRemove(mChosen, false);
	}
	mNight();
}

function mDay() {
	if (!mGame) { return; }
	mRooms.rooms.mafia.send('It is now day. You have 30 seconds to vote on a person to kill.');
	mDayTime = true;
	mCounted = false;
	mNumVotes = 0;
	mTimer = setTimeout(mCount, 30000);
}

function mInterval() {
	if (!mGame) { return; }
	mNightTime = exports.mNightTime = false;
	mDayTime = false;
	mRooms.rooms.mafia.send('You have 60 seconds to discuss.');
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].voted = false;
		mPlayers[i].votes = 0;
	}
	setTimeout(mDay, 60000);
}

function mGameStart() {
	if (!mGameStarting ) { return; }
	if (mPlayers.length < 5) {
		mRooms.rooms.mafia.add('At least 5 players are required to play. Retrying in 30 seconds.');
		setTimeout(mGameStart, 30000);
		return;
	}
	mGameStarting = false;
	mGame = exports.mGame = true;
	/*
	mShuffle(mPlayers);
	for (var q=0; i<mPlayers.length; q++) {
		mPlayers[q].mRole = mTheme["roles" + mRolesIndex][q]
	}
	*/
	mNumMob = 0;
	mNumVillager = 0;
	mRooms.rooms.mafia.add('A new mafia game has begun. Players: ' + mPlayers);
	for (var i=0; i<mPlayers.length; i++) {
		mPlayers[i].inMafia = true;
		mPlayers[i].mGroup = 'villager';
	}
	for (var j=0; j<(mPlayers.length*.25); j++) {
		var mPlayer = Math.floor(Math.random()*mPlayers.length);
		mPlayers[mPlayer].mGroup = 'mafia';
		mPlayers[mPlayer].sendTo(mRooms.rooms.mafia, "You are a mafia member. Attempt to kill the villagers.");
		mNumMob++;
	}
	for (var k=0; k<mPlayers.length; k++) {
		if (mPlayers[k].mGroup === 'mafia') { continue; }
		mPlayers[k].sendTo(mRooms.rooms.mafia, "You are a villager. Attempt to find out who the mafia are.");
		mNumVillager++;
	}
	mInterval();
}

	/*********************************************************
	 * Mafia commands
	 *********************************************************/

var commands = exports.commands = {

	mstart: function(target, room, user, connection) {
		if (room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		if (mGame) {
			this.sendReplyBox('A game is currently being played.');
		} else {
			mPlayers = [];
			mGameStarting = true;
			room.add('A new mafia game is starting. Type /mjoin to join.');
			mRooms.lobby.add('A new mafia game is starting. Join tervari.psim.us/mafia and type /mjoin to join.');
			/*
			mRooms.lobby.add(mTheme.name);
			mRooms.lobby.add(mTheme.author);
			mRooms.lobby.add(mTheme.summary);
			mRolesOrder = mTheme.roles1;
			mRolesIndex = 1;
			*/
			mTimer = setTimeout(function() {room.add('60 seconds remaining to join.'); setTimeout(mGameStart, 60000);}, 60000)
		}
	},

	mjoin: function(target, room, user, connection) {
		if (!mGameStarting || room !== mRooms.rooms.mafia) { return; }
		if (mPlayers.indexOf(user) !== -1){
			this.sendReplyBox('You have already joined the next mafia game.');
			return;
		}
		mPlayers.push(user);
		this.sendReplyBox('You have joined the mafia game.');
		/*
		if (mTheme["roles" + mRolesIndex].length <= mPlayers.length) {
			try {
				mRolesIndex++;
				mRolesOrder = mTheme["roles" + mRolesIndex];
			}
			catch(e) {
				room.add('Sign-up full. Starting game.');
				clearTimeout(mTimer)
				mGameStart()
			}
		}
		*/
	},

	mkill: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (user.mGroup !== 'mafia'){
			this.sendReplyBox('You are not a member of the mafia.');
			return;
		}
		if (!mNightTime){
			this.sendReplyBox('It is not night.');
			return;
		}
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (targetUser.mGroup === 'mafia'){
			this.sendReplyBox('You cannot kill a member of the mafia.');
			return;
		}
		if (mPlayers.indexOf(targetUser) === -1){
			this.sendReplyBox('That player is not in the game.');
			return;
		}
		mKillTarget = targetUser;
	},

	mvote: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (mPlayers.indexOf(user) === -1){
			this.sendReplyBox('You are not playing.');
			return;
		}
		if (!mDayTime){
			this.sendReplyBox('It is not day.');
			return;
		}
		if (user.voted) {
			this.sendReplyBox('You have already voted.');
			return;
		}
		target = this.splitTarget(target);
		var targetUser = this.targetUser;
		if (mPlayers.indexOf(targetUser) === -1){
			this.sendReplyBox('That player is not in the game.');
			return;
		}
		this.send(user + ' voted for ' + targetUser + '.');
		user.voted = true;
		targetUser.votes = targetUser.votes+1;
		mNumVotes++;
		if (mNumVotes === mPlayers.length) {
			this.send("All players have voted.");
			clearTimeout(mTimer);
			mCount();
		}
	},

	mchat: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia) { return; }
		if (user.mGroup !== 'mafia'){
			this.sendReplyBox('You are not a member of the mafia.');
			return;
		}
		for (var i=0; i<mPlayers.length; i++) {
			if (mPlayers[i].mGroup === 'mafia') {
				mPlayers[i].send("Mafia " + user + ": " + target);
			}
		}
	},

	mstop: function(target, room, user, connection) {
		if (!mGame || room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		mEndGame();
	},

	mcancel: function(target, room, user, connection) {
		if (!mGameStarting || room !== mRooms.rooms.mafia || !user.can('broadcast')) { return; }
		mRooms.rooms.mafia.add('Mafia game cancelled.');
		mGameStarting = false;
	}

};
<|endoftext|>


<commit_before>/**
 * Created by wyf on 2016/11/23.
 */
var express = require('express');
var router = express.Router();
var formidable = require('formidable');
var fs = require('fs');
var path = require('path');

//139.224.195.74
var server = '139.224.195.74';
var port = 3000;
// 文件将要上传到哪个文件夹下面
var uploadfoldername = 'uploadfiles';
var uploadfolderpath = path.join(__dirname, '../../client/',uploadfoldername);

router.route('/')
    .post(function (req, res, next) {
        // 使用第三方的 formidable 插件初始化一个 form 对象
        var form = new formidable.IncomingForm();
        form.uploadDir='../tmp';

        form.parse(req, function (err, fields, files) {
            if(err){
                return console.log('formidable, form.parse err');
            }

            console.log('formidable, form.parse ok');
            // 显示参数，例如 token
            console.log('显示上传时的参数 begin');
            console.log(fields);
            console.log('显示上传时的参数 end');

            var item;
            // 计算 files 长度
            var length = 0;
            for (item in files) {
                length++;
            }
            if (length === 0) {
                return console.log('files no data');
            }

            for (item in files) {
                var file = files[item];
                // formidable 会将上传的文件存储为一个临时文件，现在获取这个文件的目录
                var tempfilepath = file.path;
                // 获取文件类型
                var type = file.type;

                // 获取文件名，并根据文件名获取扩展名
                var filename = file.name;
                var extname = filename.lastIndexOf('.') >= 0
                    ? filename.slice(filename.lastIndexOf('.') - filename.length)
                    : '';
                // 文件名没有扩展名时候，则从文件类型中取扩展名
                if (extname === '' && type.indexOf('/') >= 0) {
                    extname = '.' + type.split('/')[1];
                }
                // 将文件名重新赋值为一个随机数（避免文件重名）
                filename = Math.random().toString().slice(2) + extname;

                // 构建将要存储的文件的路径
                var filenewpath = path.join(uploadfolderpath, filename);

                // 将临时文件保存为正式的文件
                fs.rename(tempfilepath, filenewpath, function (err) {
                    // 存储结果
                    var result = '';
                    console.log(tempfilepath);
                    console.log(filenewpath);

                    if (err) {
                        // 发生错误
                        console.log(err);
                        console.log('fs.rename err');
                        result = 'error|save error';
                    } else {
                        // 保存成功
                        console.log('fs.rename done');
                        // 拼接图片url地址
                        result = 'http://' + server + ':' + port + '/' + uploadfoldername + '/' + filename;
                    }

                    // 返回结果
                    res.writeHead(200, {
                        'Content-type': 'text/html'
                    });
                    res.end(result);
                }); // fs.rename
            } // for in
        });
    });

module.exports = router;<commit_msg>modify rich editor<commit_after>/**
 * Created by wyf on 2016/11/23.
 */
var express = require('express');
var router = express.Router();
var formidable = require('formidable');
var fs = require('fs');
var path = require('path');

//139.224.195.74
var server = 'blog.yvanwang.com';
var port = 3000;
// 文件将要上传到哪个文件夹下面
var uploadfoldername = 'uploadfiles';
var uploadfolderpath = path.join(__dirname, '../../client/',uploadfoldername);

router.route('/')
    .post(function (req, res, next) {
        // 使用第三方的 formidable 插件初始化一个 form 对象
        var form = new formidable.IncomingForm();
        form.uploadDir='../tmp';

        form.parse(req, function (err, fields, files) {
            if(err){
                return console.log('formidable, form.parse err');
            }

            console.log('formidable, form.parse ok');
            // 显示参数，例如 token
            console.log('显示上传时的参数 begin');
            console.log(fields);
            console.log('显示上传时的参数 end');

            var item;
            // 计算 files 长度
            var length = 0;
            for (item in files) {
                length++;
            }
            if (length === 0) {
                return console.log('files no data');
            }

            for (item in files) {
                var file = files[item];
                // formidable 会将上传的文件存储为一个临时文件，现在获取这个文件的目录
                var tempfilepath = file.path;
                // 获取文件类型
                var type = file.type;

                // 获取文件名，并根据文件名获取扩展名
                var filename = file.name;
                var extname = filename.lastIndexOf('.') >= 0
                    ? filename.slice(filename.lastIndexOf('.') - filename.length)
                    : '';
                // 文件名没有扩展名时候，则从文件类型中取扩展名
                if (extname === '' && type.indexOf('/') >= 0) {
                    extname = '.' + type.split('/')[1];
                }
                // 将文件名重新赋值为一个随机数（避免文件重名）
                filename = Math.random().toString().slice(2) + extname;

                // 构建将要存储的文件的路径
                var filenewpath = path.join(uploadfolderpath, filename);

                // 将临时文件保存为正式的文件
                fs.rename(tempfilepath, filenewpath, function (err) {
                    // 存储结果
                    var result = '';
                    console.log(tempfilepath);
                    console.log(filenewpath);

                    if (err) {
                        // 发生错误
                        console.log(err);
                        console.log('fs.rename err');
                        result = 'error|save error';
                    } else {
                        // 保存成功
                        console.log('fs.rename done');
                        // 拼接图片url地址
                        result = 'http://' + server + ':' + port + '/' + uploadfoldername + '/' + filename;
                    }

                    // 返回结果
                    res.writeHead(200, {
                        'Content-type': 'text/html'
                    });
                    res.end(result);
                }); // fs.rename
            } // for in
        });
    });

module.exports = router;<|endoftext|>


<commit_before>var pg = require('pg').native;
var Q = require('q');
var stackWhoConfig = require('./common/config.js');

var SearchService = function(){

    var search = function(searchOptions, fn){
        var deferred = Q.defer();

        var sql = 'SELECT * FROM users WHERE ';
        var sqlSuffix = ' ORDER By ("user"->>\'reputation\')::int';

        var answerTagsCount = 0;
        var answerTagsSql = '';

        searchOptions.answerTags.forEach(function(tag){
            answerTagsCount++;
            var or = answerTagsCount > 1 ? ' OR ' :'';
            answerTagsSql += or + '\'{"' + tag + '"}\'::text[] <@ (json_val_arr("user"->\'top_tags\', \'tag_name\'))';
        });

        if (answerTagsSql){
            answerTagsSql = '(' + answerTagsSql + ')';
        }

        var locationCount = 0;
        var locationSql = '';
        searchOptions.locations.forEach(function(location){
            locationCount++;
            var or = locationCount > 1 ? ' OR ' :'';
            locationSql += or + 'lower("user"->>\'location\') LIKE \'%' + location + '%\'';
        });

        if(locationSql){
            locationSql = '(' + locationSql + ')';
        }

        var combinedSql = answerTagsSql && locationSql ? answerTagsSql + ' AND ' + locationSql :
                          answerTagsSql ? answerTagsSql : locationSql;

        combinedSql = combinedSql + sqlSuffix;

        pg.connect(stackWhoConfig.dbConnectionString, function(err, client, done) {
            if(err) {
                deferred.reject(err);
                return;
            }
            client.query(sql + combinedSql, function(err, result) {
                done();

                if(err) {
                    return deferred.reject(err);
                }

                var rows = result.rows.map(function(obj){
                    return obj.user;
                })

                deferred.resolve(rows);
            });
        });

        return deferred.promise;
    };

    return {
        search: search
    }
};

module.exports = SearchService;<commit_msg>fix(searchService): make sure to sort descending<commit_after>var pg = require('pg').native;
var Q = require('q');
var stackWhoConfig = require('./common/config.js');

var SearchService = function(){

    var search = function(searchOptions, fn){
        var deferred = Q.defer();

        var sql = 'SELECT * FROM users WHERE ';
        var sqlSuffix = ' ORDER By ("user"->>\'reputation\')::int DESC';

        var answerTagsCount = 0;
        var answerTagsSql = '';

        searchOptions.answerTags.forEach(function(tag){
            answerTagsCount++;
            var or = answerTagsCount > 1 ? ' OR ' :'';
            answerTagsSql += or + '\'{"' + tag + '"}\'::text[] <@ (json_val_arr("user"->\'top_tags\', \'tag_name\'))';
        });

        if (answerTagsSql){
            answerTagsSql = '(' + answerTagsSql + ')';
        }

        var locationCount = 0;
        var locationSql = '';
        searchOptions.locations.forEach(function(location){
            locationCount++;
            var or = locationCount > 1 ? ' OR ' :'';
            locationSql += or + 'lower("user"->>\'location\') LIKE \'%' + location + '%\'';
        });

        if(locationSql){
            locationSql = '(' + locationSql + ')';
        }

        var combinedSql = answerTagsSql && locationSql ? answerTagsSql + ' AND ' + locationSql :
                          answerTagsSql ? answerTagsSql : locationSql;

        combinedSql = combinedSql + sqlSuffix;

        pg.connect(stackWhoConfig.dbConnectionString, function(err, client, done) {
            if(err) {
                deferred.reject(err);
                return;
            }
            client.query(sql + combinedSql, function(err, result) {
                done();

                if(err) {
                    return deferred.reject(err);
                }

                var rows = result.rows.map(function(obj){
                    return obj.user;
                })

                deferred.resolve(rows);
            });
        });

        return deferred.promise;
    };

    return {
        search: search
    }
};

module.exports = SearchService;<|endoftext|>


<commit_before>//META{"name":"autoGif"}*//

/*@cc_on
@if (@_jscript)
    // Offer to self-install for clueless users that try to run this directly.
    var shell = WScript.CreateObject("WScript.Shell");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
    var pathSelf = WScript.ScriptFullName;
    // Put the user at ease by addressing them in the first person
    shell.Popup("It looks like you mistakenly tried to run me directly. (don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
        shell.Popup("I'm in the correct folder already.\nJust reload Discord with Ctrl+R.", 0, "I'm already installed", 0x40);
    } else if (!fs.FolderExists(pathPlugins)) {
        shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
    } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
        // Show the user where to put plugins in the future
        shell.Exec("explorer " + pathPlugins);
        shell.Popup("I'm installed!\nJust reload Discord with Ctrl+R.", 0, "Successfully installed", 0x40);
    }
    WScript.Quit();
@else @*/

var autoGif = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    const getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];

    function getOwnerInstance(e, {include, exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]} = {}) {
        if (e === undefined) {
            return undefined;
        }

        // Set up filter; if no include filter is given, match all except those in exclude
        const excluding = include === undefined;
        const filter = excluding ? exclude : include;

        // Get displayName of the React class associated with this element
        // Based on getName(), but only check for an explicit displayName
        function getDisplayName(owner) {
            const type = owner._currentElement.type;
            const constructor = owner._instance && owner._instance.constructor;
            return type.displayName || constructor && constructor.displayName || null;
        }
        // Check class name against filters
        function classFilter(owner) {
            const name = getDisplayName(owner);
            return (name !== null && !!(filter.includes(name) ^ excluding));
        }

        // Walk up the hierarchy until a proper React object is found
        for (let prev, curr=getInternalInstance(e); !_.isNil(curr); prev=curr, curr=curr._hostParent) {
            // Before checking its parent, try to find a React object for prev among renderedChildren
            // This finds React objects which don't have a direct counterpart in the DOM hierarchy
            // e.g. Message, ChannelMember, ...
            if (prev !== undefined && !_.isNil(curr._renderedChildren)) {
                /* jshint loopfunc: true */
                let owner = Object.values(curr._renderedChildren)
                    .find(v => !_.isNil(v._instance) && v.getHostNode() === prev.getHostNode());
                if (!_.isNil(owner) && classFilter(owner)) {
                    return owner._instance;
                }
            }

            if (_.isNil(curr._currentElement)) {
                continue;
            }

            // Get a React object if one corresponds to this DOM element
            // e.g. .user-popout -> UserPopout, ...
            let owner = curr._currentElement._owner;
            if (!_.isNil(owner) && classFilter(owner)) {
                return owner._instance;
            }
        }

        return null;
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    // Automatically play GIFs and "GIFV" Videos
    function processAccessories(mutation) {
        var accessories = mutationFind(mutation, ".accessory");

        // Handle GIF
        accessories.find(".image:has(canvas)").each(function () {
            var image = $(this);
            var canvas = image.children("canvas").first();
            // Replace GIF preview with actual image
            var src = canvas.attr("src");
            if(src !== undefined) {
                image.replaceWith($("<img>", {
                    src: canvas.attr("src"),
                    width: canvas.attr("width"),
                    height: canvas.attr("height"),
                }).addClass("image kawaii-autogif"));
            }
        });

        // Handle GIFV
        accessories.find(".embed-thumbnail-gifv:has(video)").each(function () {
            var embed = $(this);
            var video = embed.children("video").first();
            // Remove the class, embed-thumbnail-gifv, to avoid the "GIF" overlay
            embed.removeClass("embed-thumbnail-gifv").addClass("kawaii-autogif");
            // Prevent the default behavior of pausing the video
            embed.parent().on("mouseout.autoGif", function (event) {
                event.stopPropagation();
            });
            video[0].play();
        });
    }

    function animateAvatar() {
        /* jshint validthis: true */
        try {
            const messageGroup = getOwnerInstance(this, {include: ["MessageGroup"]});
            if (messageGroup.state.animatedAvatar) {
                setTimeout(() => messageGroup.setState({animate: true}));
            }
        } catch (err) {
            // Something (not surprisingly) broke, but this isn't critical enough to completely bail over
            //console.error("DiscordAutoGif", this, err);
            return;
        }
    }

    function processAvatars(mutation) {
        mutationFind(mutation, ".message-group").each(animateAvatar);
    }

    autoGif.prototype.load = function () {};

    autoGif.prototype.unload = function () {};

    autoGif.prototype.start = function () {
        // process entire document
        var mutation = {target: document, addedNodes: [document]};
        processAccessories(mutation);
        processAvatars(mutation);

        $(".theme-dark, .theme-light").on("mouseleave.autoGif", ".message-group", animateAvatar);
    };

    autoGif.prototype.stop = function () {
        $(".theme-dark, .theme-light").off(".autoGif", ".message-group");
    };

    autoGif.prototype.observer = function (mutation) {
        processAccessories(mutation);
        processAvatars(mutation);
    };

    autoGif.prototype.getSettingsPanel = function () {
        return "";
    };

    autoGif.prototype.getName = function () {
        return "Autogif";
    };

    autoGif.prototype.getDescription = function () {
        return "Autoplay gifs without having to hover.";
    };

    autoGif.prototype.getVersion = function () {
        return "1.0.0";
    };

    autoGif.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();

/*@end @*/
<commit_msg>Restore animated avatars when stopped<commit_after>//META{"name":"autoGif"}*//

/*@cc_on
@if (@_jscript)
    // Offer to self-install for clueless users that try to run this directly.
    var shell = WScript.CreateObject("WScript.Shell");
    var fs = new ActiveXObject("Scripting.FileSystemObject");
    var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
    var pathSelf = WScript.ScriptFullName;
    // Put the user at ease by addressing them in the first person
    shell.Popup("It looks like you mistakenly tried to run me directly. (don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
    if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
        shell.Popup("I'm in the correct folder already.\nJust reload Discord with Ctrl+R.", 0, "I'm already installed", 0x40);
    } else if (!fs.FolderExists(pathPlugins)) {
        shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
    } else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
        fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
        // Show the user where to put plugins in the future
        shell.Exec("explorer " + pathPlugins);
        shell.Popup("I'm installed!\nJust reload Discord with Ctrl+R.", 0, "Successfully installed", 0x40);
    }
    WScript.Quit();
@else @*/

var autoGif = function () {};

(function () {
    "use strict";

    // This is super hackish, and will likely break as Discord's internal API changes
    // Anything using this or what it returns should be prepared to catch some exceptions
    const getInternalInstance = e => e[Object.keys(e).find(k => k.startsWith("__reactInternalInstance"))];

    function getOwnerInstance(e, {include, exclude=["Popout", "Tooltip", "Scroller", "BackgroundFlash"]} = {}) {
        if (e === undefined) {
            return undefined;
        }

        // Set up filter; if no include filter is given, match all except those in exclude
        const excluding = include === undefined;
        const filter = excluding ? exclude : include;

        // Get displayName of the React class associated with this element
        // Based on getName(), but only check for an explicit displayName
        function getDisplayName(owner) {
            const type = owner._currentElement.type;
            const constructor = owner._instance && owner._instance.constructor;
            return type.displayName || constructor && constructor.displayName || null;
        }
        // Check class name against filters
        function classFilter(owner) {
            const name = getDisplayName(owner);
            return (name !== null && !!(filter.includes(name) ^ excluding));
        }

        // Walk up the hierarchy until a proper React object is found
        for (let prev, curr=getInternalInstance(e); !_.isNil(curr); prev=curr, curr=curr._hostParent) {
            // Before checking its parent, try to find a React object for prev among renderedChildren
            // This finds React objects which don't have a direct counterpart in the DOM hierarchy
            // e.g. Message, ChannelMember, ...
            if (prev !== undefined && !_.isNil(curr._renderedChildren)) {
                /* jshint loopfunc: true */
                let owner = Object.values(curr._renderedChildren)
                    .find(v => !_.isNil(v._instance) && v.getHostNode() === prev.getHostNode());
                if (!_.isNil(owner) && classFilter(owner)) {
                    return owner._instance;
                }
            }

            if (_.isNil(curr._currentElement)) {
                continue;
            }

            // Get a React object if one corresponds to this DOM element
            // e.g. .user-popout -> UserPopout, ...
            let owner = curr._currentElement._owner;
            if (!_.isNil(owner) && classFilter(owner)) {
                return owner._instance;
            }
        }

        return null;
    }

    // Helper function for finding all elements matching selector affected by a mutation
    function mutationFind(mutation, selector) {
        var target = $(mutation.target), addedNodes = $(mutation.addedNodes);
        var mutated = target.add(addedNodes).filter(selector);
        var descendants = addedNodes.find(selector);
        var ancestors = target.parents(selector);
        return mutated.add(descendants).add(ancestors);
    }

    // Automatically play GIFs and "GIFV" Videos
    function processAccessories(mutation) {
        var accessories = mutationFind(mutation, ".accessory");

        // Handle GIF
        accessories.find(".image:has(canvas)").each(function () {
            var image = $(this);
            var canvas = image.children("canvas").first();
            // Replace GIF preview with actual image
            var src = canvas.attr("src");
            if(src !== undefined) {
                image.replaceWith($("<img>", {
                    src: canvas.attr("src"),
                    width: canvas.attr("width"),
                    height: canvas.attr("height"),
                }).addClass("image kawaii-autogif"));
            }
        });

        // Handle GIFV
        accessories.find(".embed-thumbnail-gifv:has(video)").each(function () {
            var embed = $(this);
            var video = embed.children("video").first();
            // Remove the class, embed-thumbnail-gifv, to avoid the "GIF" overlay
            embed.removeClass("embed-thumbnail-gifv").addClass("kawaii-autogif");
            // Prevent the default behavior of pausing the video
            embed.parent().on("mouseout.autoGif", function (event) {
                event.stopPropagation();
            });
            video[0].play();
        });
    }

    // Mark elements with modified state so that they may be restored later
    const animationForced = new WeakSet();

    function animateAvatar() {
        /* jshint validthis: true */
        try {
            const messageGroup = getOwnerInstance(this, {include: ["MessageGroup"]});
            if (messageGroup.state.animatedAvatar) {
                animationForced.add(this);
                setTimeout(() => messageGroup.setState({animate: true}));
            }
        } catch (err) {
            // Something (not surprisingly) broke, but this isn't critical enough to completely bail over
            //console.error("DiscordAutoGif", this, err);
            return;
        }
    }

    function processAvatars(mutation) {
        mutationFind(mutation, ".message-group").each(animateAvatar);
    }

    autoGif.prototype.load = function () {};

    autoGif.prototype.unload = function () {};

    autoGif.prototype.start = function () {
        // process entire document
        var mutation = {target: document, addedNodes: [document]};
        processAccessories(mutation);
        processAvatars(mutation);

        $(".theme-dark, .theme-light").on("mouseleave.autoGif", ".message-group", animateAvatar);
    };

    autoGif.prototype.stop = function () {
        $(".theme-dark, .theme-light").off(".autoGif", ".message-group");
        // Restore original state
        $(".message-group").each(function () {
            if (!animationForced.delete(this)) {
                // This element's state was not modified
                // Don't trigger a pointless re-render with setState
                return;
            }
            try {
                getOwnerInstance(this, {include: ["MessageGroup"]}).setState({animate: false});
            } catch (err) {
                // Something (not surprisingly) broke, but this isn't critical enough to completely bail over
                //console.error("DiscordAutoGif", this, err);
                return;
            }
        });
    };

    autoGif.prototype.observer = function (mutation) {
        processAccessories(mutation);
        processAvatars(mutation);
    };

    autoGif.prototype.getSettingsPanel = function () {
        return "";
    };

    autoGif.prototype.getName = function () {
        return "Autogif";
    };

    autoGif.prototype.getDescription = function () {
        return "Autoplay gifs without having to hover.";
    };

    autoGif.prototype.getVersion = function () {
        return "1.0.0";
    };

    autoGif.prototype.getAuthor = function () {
        return "noodlebox";
    };
})();

/*@end @*/
<|endoftext|>


<commit_before>Meteor.startup(function() {

  if (Meteor.users.find({}).count() === 0) {

    var adminIds = [54118672, 57771272, 32213572, 28932772, 87620262, 11527138, 8187187];

    Meteor.call('MeetupAPI', 'getMembers', {"group_urlname": "Meteor-Las-Vegas"}, function(err, response) {

      for (var i = 0, l = response.meta.count; i < l; i++) {
        var node = response.results[i];

        if(response.results[i].hasOwnProperty("photo") && response.results[i].photo.photo_link !== "") {
          var thumbnailUrl = response.results[i].photo.photo_link;
        } else {
          var thumbnailUrl = "default-avatar.png";
        }

        var socialLinks = [];
        for (service in response.results[i].other_services) {
          if(service === "twitter") {
            var username = response.results[i].other_services['twitter']['identifier'];
            socialLinks.push({'service': 'twitter', 'url': 'https://twitter.com/' + username});
          } else if(service) {
            var url = response.results[i].other_services[service]['identifier'];
            socialLinks.push({'service': service, 'url': url});
          }
        }

        var userId = Meteor.users.insert({
          createdAt: new Date(),
          profile: {
            'meetupId': response.results[i].id,
            'name': response.results[i].name,
            'bio': response.results[i].bio,
            'meetupProfileUrl': response.results[i].link,
            'socialLinks': socialLinks,
            'thumbnailUrl': thumbnailUrl,
            'points': _.random(5, 250)
          },
          services: {
            meetup: {
              id: response.results[i].id
            }
          }
        });

        if (_(adminIds).contains(response.results[i].id)) {
          Roles.addUsersToRoles(userId, ['admin'])
        }

      }
    });

  }

});
<commit_msg>fix default avatar path<commit_after>Meteor.startup(function() {

  if (Meteor.users.find({}).count() === 0) {

    var adminIds = [54118672, 57771272, 32213572, 28932772, 87620262, 11527138, 8187187];

    Meteor.call('MeetupAPI', 'getMembers', {"group_urlname": "Meteor-Las-Vegas"}, function(err, response) {

      for (var i = 0, l = response.meta.count; i < l; i++) {
        var node = response.results[i];

        if(response.results[i].hasOwnProperty("photo") && response.results[i].photo.photo_link !== "") {
          var thumbnailUrl = response.results[i].photo.photo_link;
        } else {
          var thumbnailUrl = "/default-avatar.png";
        }

        var socialLinks = [];
        for (service in response.results[i].other_services) {
          if(service === "twitter") {
            var username = response.results[i].other_services['twitter']['identifier'];
            socialLinks.push({'service': 'twitter', 'url': 'https://twitter.com/' + username});
          } else if(service) {
            var url = response.results[i].other_services[service]['identifier'];
            socialLinks.push({'service': service, 'url': url});
          }
        }

        var userId = Meteor.users.insert({
          createdAt: new Date(),
          profile: {
            'meetupId': response.results[i].id,
            'name': response.results[i].name,
            'bio': response.results[i].bio,
            'meetupProfileUrl': response.results[i].link,
            'socialLinks': socialLinks,
            'thumbnailUrl': thumbnailUrl,
            'points': _.random(5, 250)
          },
          services: {
            meetup: {
              id: response.results[i].id
            }
          }
        });

        if (_(adminIds).contains(response.results[i].id)) {
          Roles.addUsersToRoles(userId, ['admin'])
        }

      }
    });

  }

});
<|endoftext|>


<commit_before>module.exports = {
	views: [{
		"name": "Team Profile",
		"views": [
            {
                "name": "teamAverages",
                "disableInfo": true,
                "disablePaging": true
            },
            {
                "name": "matches",
                "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ]
            },
            {
                "name": "comments",
                "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ]
            }
        ]
	},{
		"name": "Team Consistencies",
		"views": [
            {
                "name": "teamConsistency",
                "lengthMenu": [ [25, 50, 100, -1], [25, 50, 100, "All"] ]
            }
        ]
	}],

	matches: function (mongoCollection, callback) {
		mongoCollection
			.find({"competition": 2017})
			.sort({"_id": -1})
			.toArray(function (err, matches) {
				callback([
					{
                        name: "Team Matches",
                        cols: [2,3,4,5,6,7,8,9,10,11,12,14],
                        headers: [
                            {
                                text: "Team",
                                value: "team"
                            },
                            {
                                text: "Event",
                                value: "event"
                            },
                            {
                                text: "Crossed Baseline",
                                value: "baseline"
                            },
                            {
                                text: "Auto. Low Goal kPa",
                                value: "auto-low-kPa"
                            },
                            {
                                text: "Auto. High Goal kPa",
                                value: "auto-high-kPa"
                            },
                            {
                                text: "Auto. Gears Placed",
                                value: "auto-gears"
                            },
                            {
                                text: "Auto. Gears Dropped",
                                value: "auto-drop-gears"
                            },
                            {
                                text: "Teleop. Low Goal kPa",
                                value: "teleop-low-kPa"
                            },
                            {
                                text: "Teleop. High Goal kPa",
                                value: "teleop-high-kPa"
                            },
                            {
                                text: "Teleop. Gears Placed",
                                value: "teleop-gears"
                            },
                            {
                                text: "Teleop. Gears Dropped",
                                value: "teleop-drop-gears"
                            },
                            {
                                text: "Attempted Touchpad",
                                value: "touchpad-attempted"
                            },
                            {
                                text: "Pressed Touchpad",
                                value: "touchpad"
                            },
                            {
                                text: "Comments",
                                value: "comments"
                            },
                            {
                                text: "Do Not Pick",
                                value: "do-not-pick"
                            }
                        ],
                        data: matches
					}
				]);

				db.close();
			});
	},

    comments: function (mongoCollection, callback) {
		mongoCollection
			.find({"competition": 2017, "comments": { $ne: "" }})
			.sort({"_id": -1})
			.toArray(function (err, matches) {
				callback([
					{
                        name: "Team Comments",
                        cols: [],
                        headers: [
                            {
                                text: "Team",
                                value: "team"
                            },
                            {
                                text: "Event",
                                value: "event"
                            },
                            {
                                text: "Comments",
                                value: "comments"
                            }
                        ],
                        data: matches
					}
				]);

				db.close();
			});
	},

    teamAverages: function (mongoCollection, callback) {
        mongoCollection.aggregate([
          {
              $match: {"competition": 2017}
          },
          {
              $group: {
                  _id: {
                      "team": '$team',
                      "event": '$event'
                  },

                  "baseline": { $avg: '$baseline' },

                  "auto-low-kPa": { $avg: '$auto-low-kPa' },
                  "auto-high-kPa": { $avg: '$auto-high-kPa' },
                  "auto-gears": { $avg: '$auto-gears' },

                  "teleop-low-kPa": { $avg: '$teleop-low-kPa' },
                  "teleop-high-kPa": { $avg: '$teleop-high-kPa' },
                  "teleop-gears": { $avg: '$teleop-gears' },

                  "touchpad": { $avg: '$touchpad' },
                  "touchpad-attempted": { $avg: '$touchpad-attempted' },

                  "auto-drop-gears": { $avg: '$auto-drop-gears' },
                  "teleop-drop-gears": { $avg: '$teleop-drop-gears' }
              }
          },
          {
             $addFields: {
               "touchpad-success": {
                   $cond: [
                       { "$eq": ["$touchpad", 0] },
                       0,
                       { "$divide": ['$touchpad', '$touchpad-attempted']}
                   ]
               }
             }
          }
        ]).toArray(function(err, matches) {
            callback([
                {
                    name: "Team Averages",
                    cols: [2,3,4,5,6,7,8,9,10],
                    headers: [
                        {
                            text: "Team",
                            value: "_id.team"
                        },
                        {
                            text: "Event",
                            value: "_id.event"
                        },
                        {
                            text: "Crossed Baseline",
                            value: "baseline"
                        },
                        {
                            text: "Auto. Low Goal kPa",
                            value: "auto-low-kPa"
                        },
                        {
                            text: "Auto. High Goal kPa",
                            value: "auto-high-kPa"
                        },
                        {
                            text: "Auto. Gears Placed",
                            value: "auto-gears"
                        },
                        {
                            text: "Teleop. Low Goal kPa",
                            value: "teleop-low-kPa"
                        },
                        {
                            text: "Teleop. High Goal kPa",
                            value: "teleop-high-kPa"
                        },
                        {
                            text: "Teleop. Gears Placed",
                            value: "teleop-gears"
                        },
                        {
                            text: "Pressed Touchpad",
                            value: "touchpad"
                        },
                        {
                            text: "Touchpad Success Rate",
                            value: "touchpad-success"
                        }
                    ],
                    data: matches
                }
            ]);

            db.close();
        });
	},

    teamConsistency: function (mongoCollection, callback) {
        mongoCollection.aggregate([
          {
              $match: {"competition": 2017}
          },
          {
              $group: {
                  _id: {
                      "team": '$team',
                      "event": '$event'
                  },

                  "baseline": { $stdDevPop: '$baseline' },

                  "auto-low-kPa": { $stdDevPop: '$auto-low-kPa' },
                  "auto-high-kPa": { $stdDevPop: '$auto-high-kPa' },
                  "auto-gears": { $stdDevPop: '$auto-gears' },

                  "teleop-low-kPa": { $stdDevPop: '$teleop-low-kPa' },
                  "teleop-high-kPa": { $stdDevPop: '$teleop-high-kPa' },
                  "teleop-gears": { $stdDevPop: '$teleop-gears' },

                  "touchpad": { $stdDevPop: '$touchpad' },
                  "touchpad-attempted": { $stdDevPop: '$touchpad-attempted' },

                  "auto-drop-gears": { $stdDevPop: '$auto-drop-gears' },
                  "teleop-drop-gears": { $stdDevPop: '$teleop-drop-gears' }
              }
          }
        ]).toArray(function(err, matches) {
            callback([
                {
                    name: "Team Consistency",
                    cols: [2,3,4,5,6,7,8,9,10,11,12],
                    headers: [
                        {
                            text: "Team",
                            value: "_id.team"
                        },
                        {
                            text: "Event",
                            value: "_id.event"
                        },
                        {
                            text: "Crossed Baseline",
                            value: "baseline"
                        },
                        {
                            text: "Auto. Low Goal kPa",
                            value: "auto-low-kPa"
                        },
                        {
                            text: "Auto. High Goal kPa",
                            value: "auto-high-kPa"
                        },
                        {
                            text: "Auto. Gears Placed",
                            value: "auto-gears"
                        },
                        {
                            text: "Auto. Gears Dropped",
                            value: "auto-drop-gears"
                        },
                        {
                            text: "Teleop. Low Goal kPa",
                            value: "teleop-low-kPa"
                        },
                        {
                            text: "Teleop. High Goal kPa",
                            value: "teleop-high-kPa"
                        },
                        {
                            text: "Teleop. Gears Placed",
                            value: "teleop-gears"
                        },
                        {
                            text: "Teleop. Gears Dropped",
                            value: "teleop-drop-gears"
                        },
                        {
                            text: "Attempted Touchpad",
                            value: "touchpad-attempted"
                        },
                        {
                            text: "Pressed Touchpad",
                            value: "touchpad"
                        }
                    ],
                    data: matches
                }
            ]);

            db.close();
        });
	}
};<commit_msg>Work on Views<commit_after>module.exports = {
	views: [{
		"name": "Match Strategy Profile",
		"views": [
            {
                "name": "teamAverages",
                "disableInfo": true,
                "disablePaging": true
            },
            {
                "name": "comments",
                "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ]
            }
        ]
	},{
		"name": "Pick List Profile",
		"views": [
            {
                "name": "teamAverages",
                "disableInfo": true,
                "disablePaging": true
            },
            {
                "name": "matches",
                "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ]
            },
            {
                "name": "comments",
                "lengthMenu": [ [10, 25, 50, 100, -1], [10, 25, 50, 100, "All"] ]
            }
        ]
	},{
		"name": "Team Consistencies",
		"views": [
            {
                "name": "teamConsistency",
                "lengthMenu": [ [25, 50, 100, -1], [25, 50, 100, "All"] ]
            }
        ]
	}],

	matches: function (mongoCollection, callback) {
		mongoCollection
			.find({"competition": 2017})
			.sort({"_id": -1})
			.toArray(function (err, matches) {
				callback([
					{
                        name: "Team Matches",
                        cols: [2,3,4,5,6,7,8,9,10,11,12,14],
                        headers: [
                            {
                                text: "Team",
                                value: "team"
                            },
                            {
                                text: "Event",
                                value: "event"
                            },
                            {
                                text: "Crossed Baseline",
                                value: "baseline"
                            },
                            {
                                text: "Auto. Low Goal kPa",
                                value: "auto-low-kPa"
                            },
                            {
                                text: "Auto. High Goal kPa",
                                value: "auto-high-kPa"
                            },
                            {
                                text: "Auto. Gears Placed",
                                value: "auto-gears"
                            },
                            {
                                text: "Auto. Gears Dropped",
                                value: "auto-drop-gears"
                            },
                            {
                                text: "Teleop. Low Goal kPa",
                                value: "teleop-low-kPa"
                            },
                            {
                                text: "Teleop. High Goal kPa",
                                value: "teleop-high-kPa"
                            },
                            {
                                text: "Teleop. Gears Placed",
                                value: "teleop-gears"
                            },
                            {
                                text: "Teleop. Gears Dropped",
                                value: "teleop-drop-gears"
                            },
                            {
                                text: "Attempted Touchpad",
                                value: "touchpad-attempted"
                            },
                            {
                                text: "Pressed Touchpad",
                                value: "touchpad"
                            },
                            {
                                text: "Comments",
                                value: "comments"
                            },
                            {
                                text: "Do Not Pick",
                                value: "do-not-pick"
                            }
                        ],
                        data: matches
					}
				]);

				db.close();
			});
	},

    comments: function (mongoCollection, callback) {
		mongoCollection
			.find({"competition": 2017, "comments": { $ne: "" }})
			.sort({"_id": -1})
			.toArray(function (err, matches) {
				callback([
					{
                        name: "Team Comments",
                        cols: [],
                        headers: [
                            {
                                text: "Team",
                                value: "team"
                            },
                            {
                                text: "Event",
                                value: "event"
                            },
                            {
                                text: "Comments",
                                value: "comments"
                            }
                        ],
                        data: matches
					}
				]);

				db.close();
			});
	},

    teamAverages: function (mongoCollection, callback) {
        mongoCollection.aggregate([
          {
              $match: {"competition": 2017}
          },
          {
              $group: {
                  _id: {
                      "team": '$team',
                      "event": '$event'
                  },

                  "baseline": { $avg: '$baseline' },

                  "auto-low-kPa": { $avg: '$auto-low-kPa' },
                  "auto-high-kPa": { $avg: '$auto-high-kPa' },
                  "auto-gears": { $avg: '$auto-gears' },

                  "teleop-low-kPa": { $avg: '$teleop-low-kPa' },
                  "teleop-high-kPa": { $avg: '$teleop-high-kPa' },
                  "teleop-gears": { $avg: '$teleop-gears' },

                  "touchpad": { $avg: '$touchpad' },
                  "touchpad-attempted": { $avg: '$touchpad-attempted' },

                  "auto-drop-gears": { $avg: '$auto-drop-gears' },
                  "teleop-drop-gears": { $avg: '$teleop-drop-gears' }
              }
          },
          {
             $addFields: {
               "touchpad-success": {
                   $cond: [
                       { "$eq": ["$touchpad", 0] },
                       0,
                       { "$divide": ['$touchpad', '$touchpad-attempted']}
                   ]
               }
             }
          }
        ]).toArray(function(err, matches) {
            callback([
                {
                    name: "Team Averages",
                    cols: [2,3,4,5,6,7,8,9,10],
                    headers: [
                        {
                            text: "Team",
                            value: "_id.team"
                        },
                        {
                            text: "Event",
                            value: "_id.event"
                        },
                        {
                            text: "Crossed Baseline",
                            value: "baseline"
                        },
                        {
                            text: "Auto. Low Goal kPa",
                            value: "auto-low-kPa"
                        },
                        {
                            text: "Teleop. Low Goal kPa",
                            value: "teleop-low-kPa"
                        },
                        {
                            text: "Auto. High Goal kPa",
                            value: "auto-high-kPa"
                        },
                        {
                            text: "Teleop. High Goal kPa",
                            value: "teleop-high-kPa"
                        },
                        {
                            text: "Auto. Gears Placed",
                            value: "auto-gears"
                        },
                        {
                            text: "Teleop. Gears Placed",
                            value: "teleop-gears"
                        },
                        {
                            text: "Pressed Touchpad",
                            value: "touchpad"
                        },
                        {
                            text: "Climber Success Rate",
                            value: "touchpad-success"
                        }
                    ],
                    data: matches
                }
            ]);

            db.close();
        });
	},

    teamConsistency: function (mongoCollection, callback) {
        mongoCollection.aggregate([
          {
              $match: {"competition": 2017}
          },
          {
              $group: {
                  _id: {
                      "team": '$team',
                      "event": '$event'
                  },

                  "baseline": { $stdDevPop: '$baseline' },

                  "auto-low-kPa": { $stdDevPop: '$auto-low-kPa' },
                  "auto-high-kPa": { $stdDevPop: '$auto-high-kPa' },
                  "auto-gears": { $stdDevPop: '$auto-gears' },

                  "teleop-low-kPa": { $stdDevPop: '$teleop-low-kPa' },
                  "teleop-high-kPa": { $stdDevPop: '$teleop-high-kPa' },
                  "teleop-gears": { $stdDevPop: '$teleop-gears' },

                  "touchpad": { $stdDevPop: '$touchpad' },
                  "touchpad-attempted": { $stdDevPop: '$touchpad-attempted' },

                  "auto-drop-gears": { $stdDevPop: '$auto-drop-gears' },
                  "teleop-drop-gears": { $stdDevPop: '$teleop-drop-gears' }
              }
          }
        ]).toArray(function(err, matches) {
            callback([
                {
                    name: "Team Consistency",
                    cols: [2,3,4,5,6,7,8,9,10,11,12],
                    headers: [
                        {
                            text: "Team",
                            value: "_id.team"
                        },
                        {
                            text: "Event",
                            value: "_id.event"
                        },
                        {
                            text: "Crossed Baseline",
                            value: "baseline"
                        },
                        {
                            text: "Auto. Low Goal kPa",
                            value: "auto-low-kPa"
                        },
                        {
                            text: "Auto. High Goal kPa",
                            value: "auto-high-kPa"
                        },
                        {
                            text: "Auto. Gears Placed",
                            value: "auto-gears"
                        },
                        {
                            text: "Auto. Gears Dropped",
                            value: "auto-drop-gears"
                        },
                        {
                            text: "Teleop. Low Goal kPa",
                            value: "teleop-low-kPa"
                        },
                        {
                            text: "Teleop. High Goal kPa",
                            value: "teleop-high-kPa"
                        },
                        {
                            text: "Teleop. Gears Placed",
                            value: "teleop-gears"
                        },
                        {
                            text: "Teleop. Gears Dropped",
                            value: "teleop-drop-gears"
                        },
                        {
                            text: "Attempted Touchpad",
                            value: "touchpad-attempted"
                        },
                        {
                            text: "Pressed Touchpad",
                            value: "touchpad"
                        }
                    ],
                    data: matches
                }
            ]);

            db.close();
        });
	}
};<|endoftext|>


<commit_before>var User = require('../User.js');
var Room = require('../Room.js');
var rooms;
describe("Room", function() {
    var room;
    beforeEach(function() {
      room = new Room();
      // clear the rooms
      rooms = Room.getRooms();
      for (var member in rooms) delete rooms[member];
    });
    describe('.resetBets()', function() {
      it("should set all user's bets property to false", function() {
        room.users = [{ bets: 1 }, { bets: 2 }];
        room.resetBets();
        for(var i = 0; i < room.users.length; i++) {
          expect(room.users[i].bets).toEqual(false);
        }
      });
    });
    describe('.selectNewCaller()', function() {
      var firstUser;
      beforeEach(function() {
        firstUser = new User();
        firstUser.username = "1";
        room.users.push( firstUser );
        room.users.push( new User() );
      });
      it("should call resetBets()", function() {
        spyOn(room, 'resetBets').and.callThrough();
        room.selectNewCaller();
        expect(room.resetBets).toHaveBeenCalled();
      });
      it("should select a new caller from the users in the room sequentially, starting with user 0", function() {
        room.selectNewCaller();
        expect(room.caller).toEqual(firstUser);
      });
      it("should populate the questions", function() {
        room.selectNewCaller();
        expect(room.question.length).toEqual(4);
      });
      it("should setState('callerSelectQuestion')", function() {
        spyOn(room,'setState');
        room.selectNewCaller();
        expect(room.setState).toHaveBeenCalledWith('callerSelectQuestion');
      });
    });
    describe('.selectQuestion(index)', function() {
      beforeEach(function() {
        spyOn(room,'setState');
      });
      it('should assign to .question the value in question[index]', function() {
        room.question = [ "test"  ];
        room.state.status = "callerSelectQuestion";
        room.selectRandomQuestion();
        expect(room.question).toEqual('test');
      });
    });
});
<commit_msg>added a spec for Room.selectQuestion()<commit_after>var User = require('../User.js');
var Room = require('../Room.js');
var rooms;
describe("Room", function() {
    var room;
    beforeEach(function() {
      room = new Room();
      // clear the rooms
      rooms = Room.getRooms();
      for (var member in rooms) delete rooms[member];
    });
    describe('.resetBets()', function() {
      it("should set all user's bets property to false", function() {
        room.users = [{ bets: 1 }, { bets: 2 }];
        room.resetBets();
        for(var i = 0; i < room.users.length; i++) {
          expect(room.users[i].bets).toEqual(false);
        }
      });
    });
    describe('.selectNewCaller()', function() {
      var firstUser;
      beforeEach(function() {
        firstUser = new User();
        firstUser.username = "1";
        room.users.push( firstUser );
        room.users.push( new User() );
      });
      it("should call resetBets()", function() {
        spyOn(room, 'resetBets').and.callThrough();
        room.selectNewCaller();
        expect(room.resetBets).toHaveBeenCalled();
      });
      it("should select a new caller from the users in the room sequentially, starting with user 0", function() {
        room.selectNewCaller();
        expect(room.caller).toEqual(firstUser);
      });
      it("should populate the questions", function() {
        room.selectNewCaller();
        expect(room.question.length).toEqual(4);
      });
      it("should setState('callerSelectQuestion')", function() {
        spyOn(room,'setState');
        room.selectNewCaller();
        expect(room.setState).toHaveBeenCalledWith('callerSelectQuestion');
      });
    });
    describe('.selectQuestion(index)', function() {
      beforeEach(function() {
        spyOn(room,'setState');
        room.question = [ "test" ];
        room.state.status = "callerSelectQuestion";
      });
      it('should assign to .question the value in question[index]', function() {
        room.selectRandomQuestion();
        expect(room.question).toEqual('test');
      });
      it('should call setState("playersAnswerQuestion")', function() {
        room.selectRandomQuestion();
        expect(room.setState).toHaveBeenCalledWith('playersAnswerQuestion');
      });
    });
});
<|endoftext|>


<commit_before>'use strict'

const http = require('http')
const querystring = require('querystring')

var methods = {}
var randomCache = []

process.on('unhandledRejection', () => {
  // This is a place holder to prevent missing Promise catch errors.
  // Since the user may use a callback instead of a Promise object.
})

/**
 * Copies an object without it being linked to the original.
 * @param {object} object Original object.
 * @return {object} Deep copied object.
 */
function deepCopy (object) {
  return JSON.parse(JSON.stringify(object))
}

/**
 * Retrieves a JSON parsed object.
 *
 * @param {string} url The full URL to retrieve from.
 * @param {function(error, object):void} callback
 * * argument[0] {error} error
 * * argument[1] {object} entry
 */
function get (url, callback) {
  // https://nodejs.org/dist/latest-v8.x/docs/api/http.html#http_http_get_options_callback
  http.get(url, (result) => {
    const contentType = result.headers['content-type']
    const statusCode = result.statusCode

    let error

    if (statusCode !== 200) {
      error = new Error(`Unable to send request for definitions. Status code: '${statusCode}'`)
      error.code = 'ERR_REQUEST_SEND'
    } else if (contentType.indexOf('application/json') === -1) {
      error = new Error(`Content retrieved isn't JSON. Content type: '${contentType}'`)
      error.code = 'ERR_RESPONSE_NOT_JSON'
    }

    if (error) {
      // Removes response data to clear up memory.
      result.resume()
      callback(error)
      return
    }

    result.setEncoding('utf8')

    let rawData = ''

    result.on('data', (buffer) => {
      rawData += buffer
    })

    result.on('end', () => {
      let data = null
      let error = null

      try {
        data = JSON.parse(rawData)
      } catch (unusedError) {
        // In case somehow the data got set to not null. This is more of a failsafe.
        data = null
        error = new Error('Failed to parse retrieved Urban Dictionary JSON.')
        error.code = 'ERR_JSON_PARSE'
      }

      callback(error, data)
    })
  })
}

/**
 * Asynchronously obtain Urban Dictionary term entry by defid.
 * @param {number} id Definition entry to retrieve.
 * @param {function(error, object):promise} callback
 * * argument[0] {error} error
 * * argument[1] {object} entry
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * @return {promise} Returns a promise object containing the 'entry' property.
 */
methods.defid = function defid (id, callback) {
  if (typeof id !== 'number') {
    let error = new TypeError('id has to be a number.')
    error.code = 'ERR_ID_NOT_NUMBER'

    throw error
  }

  return new Promise((resolve, reject) => {
    const query = querystring.stringify({'defid': id})

    get('http://api.urbandictionary.com/v0/define?' + query, (error, result) => {
      if (error) {
        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (!result.list[0]) {
        let error = new Error('defid ' + id + " doesn't exist or has been deleted.")
        error.code = 'ERR_DEFINITION_NOT_FOUND'

        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (typeof callback === 'function') {
        callback(null, result.list[0])
      }

      resolve(result.list[0])
    })
  })
}

/**
 * Asynchronously obtain a random definition from Urban Dictionary.
 *
 * When called, 10 definitions are downloaded at once and are output 1 at a time.
 * Once all 10 cache definitions are used, new ones are automatically downloaded.
 * @param {function(error, object):promise} callback
 * * argument[0] {error} error
 * * argument[1] {object} entry
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * @return {promise} Returns a promise object containing the 'entry' property.
 */
methods.random = function random (callback) {
  return new Promise((resolve, reject) => {
    if (!randomCache[0]) {
      get('http://api.urbandictionary.com/v0/random', (error, result) => {
        if (error) {
          if (typeof callback === 'function') {
            callback(error)
          }

          reject(error)
          return
        }

        randomCache = result.list
        let entry = deepCopy(randomCache[0])
        randomCache.splice(0, 1)

        if (typeof callback === 'function') {
          callback(null, entry)
        }

        resolve(entry)
      })
    } else {
      let entry = deepCopy(randomCache[0])
      randomCache.splice(0, 1)

      if (typeof callback === 'function') {
        callback(null, entry)
      }

      resolve(entry)
    }
  })
}

/**
 * Asynchronously obtain Urban Dictionary by term.
 * @param {number|string} word Definition to search for.
 * @param {function(error, array, array, array):promise} callback
 * * argument[0] {error} error
 * * argument[1] {array of object} entries
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * * argument[2] {array of string} tags Tags of related words.
 * * argument[3] {array of string} sounds Full link addresses to .mp3 and .wav files.
 * @return {promise} Returns a promise object containing entries, tags and sounds properties.
 */
methods.term = function term (word, callback) {
  if (typeof word !== 'string') {
    let error = new TypeError('word has to be a string.')
    error.code = 'ERR_WORD_NOT_STRING'

    throw error
  }

  return new Promise((resolve, reject) => {
    const query = querystring.stringify({'term': word})

    get('http://api.urbandictionary.com/v0/define?' + query, (error, result) => {
      if (error) {
        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (!result.list[0]) {
        let error = new Error(word + ' is undefined.')
        error.code = 'ERR_WORD_UNDEFINED'

        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (typeof callback === 'function') {
        callback(null, result.list, result.tags, result.sounds)
      }

      resolve({'entries': result.list, 'tags': result.tags, 'sounds': result.sounds})
    })
  })
}

module.exports = methods
<commit_msg>Small JSDoc improvement<commit_after>'use strict'

const http = require('http')
const querystring = require('querystring')

var methods = {}
var randomCache = []

process.on('unhandledRejection', () => {
  // This is a place holder to prevent missing Promise catch errors.
  // Since the user may use a callback instead of a Promise object.
})

/**
 * Copies an object without it being linked to the original.
 * @param {object} object Original object.
 * @return {object} Deep copied object.
 */
function deepCopy (object) {
  return JSON.parse(JSON.stringify(object))
}

/**
 * Retrieves a JSON parsed object.
 * @param {string} url The full URL to retrieve from.
 * @param {function(error, object):void} callback
 * * argument[0] {error} error
 * * argument[1] {object} Parsed JSON result.
 */
function get (url, callback) {
  // https://nodejs.org/dist/latest-v8.x/docs/api/http.html#http_http_get_options_callback
  http.get(url, (result) => {
    const contentType = result.headers['content-type']
    const statusCode = result.statusCode

    let error

    if (statusCode !== 200) {
      error = new Error(`Unable to send request for definitions. Status code: '${statusCode}'`)
      error.code = 'ERR_REQUEST_SEND'
    } else if (contentType.indexOf('application/json') === -1) {
      error = new Error(`Content retrieved isn't JSON. Content type: '${contentType}'`)
      error.code = 'ERR_RESPONSE_NOT_JSON'
    }

    if (error) {
      // Removes response data to clear up memory.
      result.resume()
      callback(error)
      return
    }

    result.setEncoding('utf8')

    let rawData = ''

    result.on('data', (buffer) => {
      rawData += buffer
    })

    result.on('end', () => {
      let data = null
      let error = null

      try {
        data = JSON.parse(rawData)
      } catch (unusedError) {
        // In case somehow the data got set to not null. This is more of a failsafe.
        data = null
        error = new Error('Failed to parse retrieved Urban Dictionary JSON.')
        error.code = 'ERR_JSON_PARSE'
      }

      callback(error, data)
    })
  })
}

/**
 * Asynchronously obtain Urban Dictionary term entry by defid.
 * @param {number} id Definition entry to retrieve.
 * @param {function(error, object):promise} callback
 * * argument[0] {error} error
 * * argument[1] {object} entry
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * @return {promise} Returns a promise object containing the 'entry' property.
 */
methods.defid = function defid (id, callback) {
  if (typeof id !== 'number') {
    let error = new TypeError('id has to be a number.')
    error.code = 'ERR_ID_NOT_NUMBER'

    throw error
  }

  return new Promise((resolve, reject) => {
    const query = querystring.stringify({'defid': id})

    get('http://api.urbandictionary.com/v0/define?' + query, (error, result) => {
      if (error) {
        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (!result.list[0]) {
        let error = new Error('defid ' + id + " doesn't exist or has been deleted.")
        error.code = 'ERR_DEFINITION_NOT_FOUND'

        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (typeof callback === 'function') {
        callback(null, result.list[0])
      }

      resolve(result.list[0])
    })
  })
}

/**
 * Asynchronously obtain a random definition from Urban Dictionary.
 *
 * When called, 10 definitions are downloaded at once and are output 1 at a time.
 * Once all 10 cache definitions are used, new ones are automatically downloaded.
 * @param {function(error, object):promise} callback
 * * argument[0] {error} error
 * * argument[1] {object} entry
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * @return {promise} Returns a promise object containing the 'entry' property.
 */
methods.random = function random (callback) {
  return new Promise((resolve, reject) => {
    if (!randomCache[0]) {
      get('http://api.urbandictionary.com/v0/random', (error, result) => {
        if (error) {
          if (typeof callback === 'function') {
            callback(error)
          }

          reject(error)
          return
        }

        randomCache = result.list
        let entry = deepCopy(randomCache[0])
        randomCache.splice(0, 1)

        if (typeof callback === 'function') {
          callback(null, entry)
        }

        resolve(entry)
      })
    } else {
      let entry = deepCopy(randomCache[0])
      randomCache.splice(0, 1)

      if (typeof callback === 'function') {
        callback(null, entry)
      }

      resolve(entry)
    }
  })
}

/**
 * Asynchronously obtain Urban Dictionary by term.
 * @param {number|string} word Definition to search for.
 * @param {function(error, array, array, array):promise} callback
 * * argument[0] {error} error
 * * argument[1] {array of object} entries
 * * * 'author' {string} Name of the poster.
 * * * 'current_vote' {string} Unknown. It only returns an empty string.
 * * * 'defid' {number} The unique definition entry ID.
 * * * 'definition' {string} The definition description.
 * * * 'example' {string} An example use of the definition.
 * * * 'permalink' {string} A shortened link to Urban Dictionary page of the definition.
 * * * 'thumbs_down' {number} Number of down votes.
 * * * 'thumbs_up' {number} Number of up votes.
 * * * 'word' {string} The word of the definition. Be aware that the casing might be different.
 * * argument[2] {array of string} tags Tags of related words.
 * * argument[3] {array of string} sounds Full link addresses to .mp3 and .wav files.
 * @return {promise} Returns a promise object containing entries, tags and sounds properties.
 */
methods.term = function term (word, callback) {
  if (typeof word !== 'string') {
    let error = new TypeError('word has to be a string.')
    error.code = 'ERR_WORD_NOT_STRING'

    throw error
  }

  return new Promise((resolve, reject) => {
    const query = querystring.stringify({'term': word})

    get('http://api.urbandictionary.com/v0/define?' + query, (error, result) => {
      if (error) {
        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (!result.list[0]) {
        let error = new Error(word + ' is undefined.')
        error.code = 'ERR_WORD_UNDEFINED'

        if (typeof callback === 'function') {
          callback(error)
        }

        reject(error)
        return
      }

      if (typeof callback === 'function') {
        callback(null, result.list, result.tags, result.sounds)
      }

      resolve({'entries': result.list, 'tags': result.tags, 'sounds': result.sounds})
    })
  })
}

module.exports = methods
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  'env': {
    es6: true
  },
  'extends': [
    'sidwood/rules/best-practices',
    'sidwood/rules/ecmascript-6',
    'sidwood/rules/nodejs-commonjs',
    'sidwood/rules/possible-errors',
    'sidwood/rules/strict-mode',
    'sidwood/rules/stylistic-issues',
    'sidwood/rules/variables'
  ],
  'globals': {
  },
  'parserOptions': {
    ecmaVersion: 6
  },
  'rules': {
  }
};
<commit_msg>Update ecmaVersion to 2018 in core config<commit_after>'use strict'

module.exports = {
  'env': {
    es6: true
  },
  'extends': [
    'sidwood/rules/best-practices',
    'sidwood/rules/ecmascript-6',
    'sidwood/rules/nodejs-commonjs',
    'sidwood/rules/possible-errors',
    'sidwood/rules/strict-mode',
    'sidwood/rules/stylistic-issues',
    'sidwood/rules/variables'
  ],
  'globals': {
  },
  'parserOptions': {
    ecmaVersion: 2018
  },
  'rules': {
  }
}
<|endoftext|>