<commit_before>var creep_upgrader = {

    /** @param {Creep} creep **/
    run: function(creep) {
        if (creep.ticksToLive <= creep.memory.deathWarn && creep.memory.priority != 'upgraderNearDeath') {
            creep.memory.priority = 'upgraderNearDeath';
        }

        if (!creep.memory.hasBoosted && creep.room.controller.level >= 6 && Memory.labList[creep.room.name].length >= 3 && !creep.memory.previousPriority) {
            var mineralCost = creep.getActiveBodyparts(WORK) * LAB_BOOST_MINERAL;
            var energyCost = creep.getActiveBodyparts(WORK) * LAB_BOOST_ENERGY;
            var upgradeLab = creep.room.find(FIND_MY_STRUCTURES, {
                filter: (structure) => (structure.structureType == STRUCTURE_LAB && structure.mineralType == RESOURCE_CATALYZED_GHODIUM_ACID)
            });
            if (upgradeLab.length && upgradeLab[0].mineralAmount >= mineralCost && upgradeLab[0].energy >= energyCost) {
                creep.travelTo(upgradeLab[0]);
                if (upgradeLab[0].boostCreep(creep) == OK) {
                    creep.memory.hasBoosted = true;
                } else {
                    creep.memory.hasBoosted = false;
                }
            } else {
                creep.memory.hasBoosted = true;
            }
        } else {
            if (!creep.memory.hasBoosted) {
                creep.memory.hasBoosted = true;
            }

            if (_.sum(creep.carry) > 0) {
                if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                    if (Game.flags[creep.room.name + "Controller"]) {
                        creep.travelTo(Game.flags[creep.room.name + "Controller"], {
                            maxRooms: 1
                        });
                    } else {
                        creep.travelTo(creep.room.controller, {
                            maxRooms: 1
                        });
                    }
                } else {
                    if (Game.time % 2 == 0) {
                        creep.say("\u261D\uD83D\uDE3C", true);
                    } else {
                        creep.say("\uD83D\uDC4C\uD83D\uDE39", true);
                    }

                    let talkingCreeps = creep.pos.findInRange(FIND_MY_CREEPS, 1, {
                        filter: (thisCreep) => (creep.id != thisCreep.id && thisCreep.saying)
                    })
                    if (talkingCreeps.length) {
                        let coords = talkingCreeps[0].saying.split(";");
                        if (coords.length == 2 && creep.pos.x == parseInt(coords[0]) && creep.pos.y == parseInt(coords[1])) {
                            //Standing in the way of a creep
                            let thisDirection = creep.pos.getDirectionTo(talkingCreeps[0].pos);
                            creep.move(thisDirection);
                            creep.say("\uD83D\uDCA6", true);
                        }
                    }
                }
            }

            if (_.sum(creep.carry) <= creep.getActiveBodyparts(WORK)) {
                var linkTarget = Game.getObjectById(creep.memory.linkSource);
                if (linkTarget && creep.withdraw(linkTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                    creep.travelTo(linkTarget, {
                        maxRooms: 1
                    });
                }
            }
        }
    }
};

module.exports = creep_upgrader;<commit_msg>Fix1<commit_after>var creep_upgrader = {

    /** @param {Creep} creep **/
    run: function(creep) {
        if (creep.ticksToLive <= creep.memory.deathWarn && creep.memory.priority != 'upgraderNearDeath') {
            creep.memory.priority = 'upgraderNearDeath';
        }

        if (!creep.memory.hasBoosted && creep.room.controller.level >= 6 && Memory.labList[creep.room.name].length >= 3 && !creep.memory.previousPriority) {
            var mineralCost = creep.getActiveBodyparts(WORK) * LAB_BOOST_MINERAL;
            var energyCost = creep.getActiveBodyparts(WORK) * LAB_BOOST_ENERGY;
            var upgradeLab = creep.room.find(FIND_MY_STRUCTURES, {
                filter: (structure) => (structure.structureType == STRUCTURE_LAB && structure.mineralType == RESOURCE_CATALYZED_GHODIUM_ACID)
            });
            if (upgradeLab.length && upgradeLab[0].mineralAmount >= mineralCost && upgradeLab[0].energy >= energyCost) {
                creep.travelTo(upgradeLab[0]);
                if (upgradeLab[0].boostCreep(creep) == OK) {
                    creep.memory.hasBoosted = true;
                } else {
                    creep.memory.hasBoosted = false;
                }
            } else {
                creep.memory.hasBoosted = true;
            }
        } else {
            if (!creep.memory.hasBoosted) {
                creep.memory.hasBoosted = true;
            }

            if (_.sum(creep.carry) > 0) {
                if (creep.upgradeController(creep.room.controller) == ERR_NOT_IN_RANGE) {
                    if (Game.flags[creep.room.name + "Controller"]) {
                        creep.travelTo(Game.flags[creep.room.name + "Controller"], {
                            maxRooms: 1
                        });
                    } else {
                        creep.travelTo(creep.room.controller, {
                            maxRooms: 1
                        });
                    }
                } else {
                    if (Game.time % 2 == 0) {
                        creep.say("\u261D\uD83D\uDE3C", true);
                    } else {
                        creep.say("\uD83D\uDC4C\uD83D\uDE39", true);
                    }

                    let talkingCreeps = creep.pos.findInRange(FIND_MY_CREEPS, 1, {
                        filter: (thisCreep) => (creep.id != thisCreep.id && thisCreep.saying && thisCreep.saying != "\u261D\uD83D\uDE3C" && thisCreep.saying != "\uD83D\uDC4C\uD83D\uDE39")
                    })
                    if (talkingCreeps.length) {
                        let coords = talkingCreeps[0].saying.split(";");
                        if (coords.length == 2 && creep.pos.x == parseInt(coords[0]) && creep.pos.y == parseInt(coords[1])) {
                            //Standing in the way of a creep
                            let thisDirection = creep.pos.getDirectionTo(talkingCreeps[0].pos);
                            creep.move(thisDirection);
                            creep.say("\uD83D\uDCA6", true);
                        }
                    }
                }
            }

            if (_.sum(creep.carry) <= creep.getActiveBodyparts(WORK)) {
                var linkTarget = Game.getObjectById(creep.memory.linkSource);
                if (linkTarget && creep.withdraw(linkTarget, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
                    creep.travelTo(linkTarget, {
                        maxRooms: 1
                    });
                }
            }
        }
    }
};

module.exports = creep_upgrader;<|endoftext|>


<commit_before>INTERVAL = 0;
BLOB_SIZE = 10;
BLOB_DRAW_INTERVAL = 50;
SCALEFACTOR = 1.0;
BACKGROUND_COLOR = "#1D1D1D";

/* Libraries used
jQuery: http://jquery.com/ */
var map = new Image();
map.src = "./img/map.png";
var blobs = [];
var currblobs = [];
var clicked = false;
var keysquares = [];

/* Fit the canvas properly inside the window
1000 * 550 are the "baseline" dimensions */
function doResize()
{
	var canvas = document.getElementById("canvas");
	if (window.innerWidth * 0.55 > window.innerHeight)
	{ // height will be the limiting factor
		canvas.height = window.innerHeight * 0.9;
		canvas.width = canvas.height / 0.55;
	}
	else
	{
		canvas.width = window.innerWidth * 0.95;
		canvas.height = canvas.width * 0.55;
	}
	SCALEFACTOR = canvas.width / 1000;
}

function main()
{
	// Set the background color
	document.body.style.backgroundColor = BACKGROUND_COLOR;

	// Initialize the clickable boxes in the key
	keysquares.push(new KeySquare(815, 365, "Angry"));
	keysquares.push(new KeySquare(865, 365, "Afraid"));
	keysquares.push(new KeySquare(915, 365, "Happy"));
	keysquares.push(new KeySquare(815, 415, "Impassive"));
	keysquares.push(new KeySquare(865, 415, "ALL"));
	keysquares.push(new KeySquare(915, 415, "Disgusted"));
	keysquares.push(new KeySquare(815, 465, "Confident"));
	keysquares.push(new KeySquare(865, 465, "Troubled"));
	keysquares.push(new KeySquare(915, 465, "Sad"));

	///// LISTENERS /////
	// Get rid of annoying text select
	canvas.addEventListener("selectstart", function(e)
	{
		e.preventDefault();
		return false;
	}, false);

	// Responsive to resizing
	var resizeTimer;
	$(window).resize(function()
	{
		clearTimeout(resizeTimer);
		resizeTimer = setTimeout(function()
		{
			doResize();
			var ctx = drawCanvas();
			drawBlobs(ctx);
		});
	});

	Parse.initialize("mEmM0UeRE8GX5hYcuI3Z8Yao4bT4Z7wTWyjOImvt", "G5gLmYiSdDo9YNHF56Rrom15e7VJyGYmUYlcu7f9");
	var MapPoint = Parse.Object.extend("MapPoint");

	// Click to choose a location
	canvas.addEventListener("click", function(e)
	{
		var x = e.pageX - canvas.offsetLeft;
		var y = e.pageY - canvas.offsetTop;
		x = x / canvas.width * 1000;
		y = y / canvas.height * 550;

		// Only let them click once for choosing location
		if (clicked) // Otherwise do the key
		{
			var found;
			for (var i in keysquares)
			{
				if (keysquares[i].contains(x, y))
				{
					found = keysquares[i];
					break;
				}
			}
			if (found == null)
				return;

			applyFilter(found.name);
			var ctx = drawCanvas();
			drawBlobs(ctx);
			return;
		}
		else
		{
			clicked = true;

			var query = new Parse.Query(MapPoint);
			// Sorts the results in descending order by the Created Date field
			query = query.addDescending("createdAt");

			query.first().then(function(object){
				object.set("x", x);
				object.set("y", y);
				console.log(object.get("x") + " " + object.get("y") + " " + object.get("color"));

				object.save(null, {
				  success: function(object) {
				    // Execute any logic that should take place after the object is saved.
				  },
				  error: function(object, error) {
				    // Execute any logic that should take place if the save fails.
				    // error is a Parse.Error with an error code and message.
				    alert("error occurred while saving");
				  }
				});

			});

			document.getElementById("toptext").innerHTML = "Please wait."

			getData(".*");
			var ctx = drawCanvas();

			// This gives enough time for the data to return
			setTimeout(function(){
				document.getElementById("toptext").innerHTML = "Please wait.."
				setTimeout(function(){
					document.getElementById("toptext").innerHTML = "Please wait..."
					setTimeout(function(){
						drawBlobsInterval(ctx, 0);
						document.getElementById("toptext").innerHTML = "This is how people around you feel!"
					}, 1000);
				}, 1000);
			}, 1000);

		}
		// console.log(data);
	});
	///// LISTENERS /////

	doResize();
	var ctx = drawCanvas();

	// for (var i in keysquares)
		// keysquares[i].draw(ctx);
}

function applyFilter(name)
{
	var filter = getColorRegex(name);
	console.log("Filtering for: " + filter);
	currblobs = [];
	for (var i in blobs)
	{
		if (blobs[i].name == null || blobs[i].name.match(filter))
			currblobs.push(blobs[i]);
	}
}

/* Get the existing data from wherever */
function getData()
{
	blobs = [];
	/* PARSE */
	Parse.initialize("mEmM0UeRE8GX5hYcuI3Z8Yao4bT4Z7wTWyjOImvt", "G5gLmYiSdDo9YNHF56Rrom15e7VJyGYmUYlcu7f9");
	var MapPoint = Parse.Object.extend("MapPoint");
	var query = new Parse.Query(MapPoint);
	query.each(function(object){
		blobs.push(new Blob(object.get("x"), object.get("y"), object.get("color"), object.get("name")));
  	});
  	currblobs = blobs;
}

/* Draws the map onto the canvas */
function drawCanvas()
{
	var canvas = document.getElementById("canvas");
	// console.log("drawCanvas called. width: " + canvas.width + " height: " + canvas.height);

	var ctx = canvas.getContext("2d");
	ctx.fillStyle = BACKGROUND_COLOR;
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	// ctx.strokeStyle = "rgb(0,0,0)";
	// ctx.strokeRect(0, 0, canvas.width, canvas.height);
	ctx.drawImage(map, 0, 0, canvas.width, canvas.height);

	return ctx;
}

/* Draws the blobs immediately, no delay */
function drawBlobs(ctx)
{
	for (var i in currblobs)
	{
		currblobs[i].draw(ctx);
	}
}

/* Draws the blobs one by one */
function drawBlobsInterval(ctx, i)
{
	setTimeout(function(){
		currblobs[i].draw(ctx);
		i++;
		if (i < currblobs.length)
			drawBlobsInterval(ctx, i);
	}, BLOB_DRAW_INTERVAL);
}

//// BLOB ////

function Blob(x, y, color, name)
{
	// console.log("created new blob");
	this.x = parseInt(x);
	this.y = parseInt(y);
	this.color = color;
	this.name = name;
}

/* Not really a blob anymore but I'm still calling it a blob. */
Blob.prototype.draw = function(ctx)
{
	// console.log("draw a blob: " + this.color);
	ctx.fillStyle = this.color;
	ctx.strokeStyle = "#FFFFFF";

	var path = new Path2D();
	path.arc(this.x * SCALEFACTOR, this.y * SCALEFACTOR, (BLOB_SIZE - 4) * SCALEFACTOR,
		0, Math.PI * 2);
	ctx.fill(path);
	ctx.stroke(path);

	var path = new Path2D();
	path.arc(this.x * SCALEFACTOR, this.y * SCALEFACTOR, (BLOB_SIZE - 1) * SCALEFACTOR,
		0.3 * Math.PI, 0.7 * Math.PI);
	path.lineTo((this.x - BLOB_SIZE - 1) * SCALEFACTOR, (this.y + BLOB_SIZE + 3) * SCALEFACTOR);
	path.lineTo((this.x + BLOB_SIZE + 1) * SCALEFACTOR, (this.y + BLOB_SIZE + 3) * SCALEFACTOR);
	path.closePath();
	ctx.fill(path);
	ctx.stroke(path);
}

//// KEYSQUARE ////

function KeySquare(x, y, name)
{
	this.x = x;
	this.y = y;
	this.name = name;
}

// For making sure bounding box is right
KeySquare.prototype.draw = function(ctx)
{
	ctx.strokeStyle = "#000000";
	ctx.strokeRect(this.x * SCALEFACTOR, this.y * SCALEFACTOR, 46 * SCALEFACTOR, 46 * SCALEFACTOR);
}

// Bounding box
KeySquare.prototype.contains = function(x, y)
{
	if (this.x > x || (this.x + 46) < x
		|| this.y > y || (this.y + 46) < y)
		return false;
	return true;
}

// Emotion name to color mappings
function getColorRegex(name)
{
	switch(name)
	{
		case "Angry": return "Angry|Impatient|Annoyed|Frustrated|Upset|Exasperated|Hostile|Wrathful|Enraged";
		case "Happy": return "Happy|Content|Satisfied|Pleased|Thankful|Glad|Joyful|Excited|Ecstatic";
		case "Sad": return "Sad|Dissatisfied|Lonely|Disappointed|Ashamed|Despondent|Rejected|Depressed|Inconsolable";
		case "Afraid": return "Afraid|Surprised|Nervous|Uneasy|Startled|Anxious|Scared|Dreadful|Terrified";
		case "Disgusted": return "Disgusted|Distasteful|Bitter|Jealous|Spiteful|Scornful|Disdainful|Loathsome|Hateful";
		case "Troubled": return "Troubled|Hesitant|Vulnerable|Restrained|Confused|Guilty|Distraught|Disturbed|Powerless";
		case "Confident": return "Confident|Hopeful|Positive|Courageous|Proud|Arrogant|Bold|Fearless|Powerful";
		case "Impassive": return "Impassive|Calm|Indifferent|Sleepy|Apathetic|Unmoved|Detached|Distant|Cold";
		case "ALL": return ".*";
	}
}<commit_msg>success thing<commit_after>INTERVAL = 0;
BLOB_SIZE = 10;
BLOB_DRAW_INTERVAL = 50;
SCALEFACTOR = 1.0;
BACKGROUND_COLOR = "#1D1D1D";

/* Libraries used
jQuery: http://jquery.com/ */
var map = new Image();
map.src = "./img/map.png";
var blobs = [];
var currblobs = [];
var clicked = false;
var keysquares = [];

/* Fit the canvas properly inside the window
1000 * 550 are the "baseline" dimensions */
function doResize()
{
	var canvas = document.getElementById("canvas");
	if (window.innerWidth * 0.55 > window.innerHeight)
	{ // height will be the limiting factor
		canvas.height = window.innerHeight * 0.9;
		canvas.width = canvas.height / 0.55;
	}
	else
	{
		canvas.width = window.innerWidth * 0.95;
		canvas.height = canvas.width * 0.55;
	}
	SCALEFACTOR = canvas.width / 1000;
}

function main()
{
	// Set the background color
	document.body.style.backgroundColor = BACKGROUND_COLOR;

	// Initialize the clickable boxes in the key
	keysquares.push(new KeySquare(815, 365, "Angry"));
	keysquares.push(new KeySquare(865, 365, "Afraid"));
	keysquares.push(new KeySquare(915, 365, "Happy"));
	keysquares.push(new KeySquare(815, 415, "Impassive"));
	keysquares.push(new KeySquare(865, 415, "ALL"));
	keysquares.push(new KeySquare(915, 415, "Disgusted"));
	keysquares.push(new KeySquare(815, 465, "Confident"));
	keysquares.push(new KeySquare(865, 465, "Troubled"));
	keysquares.push(new KeySquare(915, 465, "Sad"));

	///// LISTENERS /////
	// Get rid of annoying text select
	canvas.addEventListener("selectstart", function(e)
	{
		e.preventDefault();
		return false;
	}, false);

	// Responsive to resizing
	var resizeTimer;
	$(window).resize(function()
	{
		clearTimeout(resizeTimer);
		resizeTimer = setTimeout(function()
		{
			doResize();
			var ctx = drawCanvas();
			drawBlobs(ctx);
		});
	});

	Parse.initialize("mEmM0UeRE8GX5hYcuI3Z8Yao4bT4Z7wTWyjOImvt", "G5gLmYiSdDo9YNHF56Rrom15e7VJyGYmUYlcu7f9");
	var MapPoint = Parse.Object.extend("MapPoint");

	// Click to choose a location
	canvas.addEventListener("click", function(e)
	{
		var x = e.pageX - canvas.offsetLeft;
		var y = e.pageY - canvas.offsetTop;
		x = x / canvas.width * 1000;
		y = y / canvas.height * 550;

		// Only let them click once for choosing location
		if (clicked) // Otherwise do the key
		{
			var found;
			for (var i in keysquares)
			{
				if (keysquares[i].contains(x, y))
				{
					found = keysquares[i];
					break;
				}
			}
			if (found == null)
				return;

			applyFilter(found.name);
			var ctx = drawCanvas();
			drawBlobs(ctx);
			return;
		}
		else
		{
			clicked = true;

			var query = new Parse.Query(MapPoint);
			// Sorts the results in descending order by the Created Date field
			query = query.addDescending("createdAt");

			document.getElementById("toptext").innerHTML = "Please wait."

			query.first().then(function(object){
				object.set("x", x);
				object.set("y", y);
				console.log(object.get("x") + " " + object.get("y") + " " + object.get("color"));

				object.save(null, {
					success: function(object) {
					    // Execute any logic that should take place after the object is saved.
					    getData(".*");
						var ctx = drawCanvas();

						// This gives enough time for the data to return
						setTimeout(function(){
							document.getElementById("toptext").innerHTML = "Please wait.."
							setTimeout(function(){
								document.getElementById("toptext").innerHTML = "Please wait..."
								setTimeout(function(){
									drawBlobsInterval(ctx, 0);
									document.getElementById("toptext").innerHTML = "This is how people around you feel!"
								}, 1000);
							}, 1000);
						}, 1000);
					},
					error: function(object, error) {
				    	// Execute any logic that should take place if the save fails.
				    	// error is a Parse.Error with an error code and message.
				    	alert("error occurred while saving");
					}
				});
			});

			/*
			document.getElementById("toptext").innerHTML = "Please wait."

			getData(".*");
			var ctx = drawCanvas();

			// This gives enough time for the data to return
			setTimeout(function(){
				document.getElementById("toptext").innerHTML = "Please wait.."
				setTimeout(function(){
					document.getElementById("toptext").innerHTML = "Please wait..."
					setTimeout(function(){
						drawBlobsInterval(ctx, 0);
						document.getElementById("toptext").innerHTML = "This is how people around you feel!"
					}, 1000);
				}, 1000);
			}, 1000);
			*/
		}
		// console.log(data);
	});
	///// LISTENERS /////

	doResize();
	var ctx = drawCanvas();

	// for (var i in keysquares)
		// keysquares[i].draw(ctx);
	}

	function applyFilter(name)
	{
		var filter = getColorRegex(name);
		// console.log("Filtering for: " + filter);
		currblobs = [];
		for (var i in blobs)
		{
			if (blobs[i].name == null || blobs[i].name.match(filter))
				currblobs.push(blobs[i]);
		}
	}

	/* Get the existing data from wherever */
	function getData()
	{
		blobs = [];
		/* PARSE */
		Parse.initialize("mEmM0UeRE8GX5hYcuI3Z8Yao4bT4Z7wTWyjOImvt", "G5gLmYiSdDo9YNHF56Rrom15e7VJyGYmUYlcu7f9");
		var MapPoint = Parse.Object.extend("MapPoint");
		var query = new Parse.Query(MapPoint);
		query.each(function(object){
			blobs.push(new Blob(object.get("x"), object.get("y"), object.get("color"), object.get("name")));
		});
		currblobs = blobs;
	}

	/* Draws the map onto the canvas */
	function drawCanvas()
	{
		var canvas = document.getElementById("canvas");
	// console.log("drawCanvas called. width: " + canvas.width + " height: " + canvas.height);

	var ctx = canvas.getContext("2d");
	ctx.fillStyle = BACKGROUND_COLOR;
	ctx.fillRect(0, 0, canvas.width, canvas.height);
	// ctx.strokeStyle = "rgb(0,0,0)";
	// ctx.strokeRect(0, 0, canvas.width, canvas.height);
	ctx.drawImage(map, 0, 0, canvas.width, canvas.height);

	return ctx;
}

/* Draws the blobs immediately, no delay */
function drawBlobs(ctx)
{
	for (var i in currblobs)
	{
		currblobs[i].draw(ctx);
	}
}

/* Draws the blobs one by one */
function drawBlobsInterval(ctx, i)
{
	setTimeout(function(){
		currblobs[i].draw(ctx);
		i++;
		if (i < currblobs.length)
			drawBlobsInterval(ctx, i);
	}, BLOB_DRAW_INTERVAL);
}

//// BLOB ////

function Blob(x, y, color, name)
{
	// console.log("created new blob");
	this.x = parseInt(x);
	this.y = parseInt(y);
	this.color = color;
	this.name = name;
}

/* Not really a blob anymore but I'm still calling it a blob. */
Blob.prototype.draw = function(ctx)
{
	// console.log("draw a blob: " + this.color);
	ctx.fillStyle = this.color;
	ctx.strokeStyle = "#FFFFFF";

	var path = new Path2D();
	path.arc(this.x * SCALEFACTOR, this.y * SCALEFACTOR, (BLOB_SIZE - 4) * SCALEFACTOR,
		0, Math.PI * 2);
	ctx.fill(path);
	ctx.stroke(path);

	var path = new Path2D();
	path.arc(this.x * SCALEFACTOR, this.y * SCALEFACTOR, (BLOB_SIZE - 1) * SCALEFACTOR,
		0.3 * Math.PI, 0.7 * Math.PI);
	path.lineTo((this.x - BLOB_SIZE - 1) * SCALEFACTOR, (this.y + BLOB_SIZE + 3) * SCALEFACTOR);
	path.lineTo((this.x + BLOB_SIZE + 1) * SCALEFACTOR, (this.y + BLOB_SIZE + 3) * SCALEFACTOR);
	path.closePath();
	ctx.fill(path);
	ctx.stroke(path);
}

//// KEYSQUARE ////

function KeySquare(x, y, name)
{
	this.x = x;
	this.y = y;
	this.name = name;
}

// For making sure bounding box is right
KeySquare.prototype.draw = function(ctx)
{
	ctx.strokeStyle = "#000000";
	ctx.strokeRect(this.x * SCALEFACTOR, this.y * SCALEFACTOR, 46 * SCALEFACTOR, 46 * SCALEFACTOR);
}

// Bounding box
KeySquare.prototype.contains = function(x, y)
{
	if (this.x > x || (this.x + 46) < x
		|| this.y > y || (this.y + 46) < y)
		return false;
	return true;
}

// Emotion name to color mappings
function getColorRegex(name)
{
	switch(name)
	{
		case "Angry": return "Angry|Impatient|Annoyed|Frustrated|Upset|Exasperated|Hostile|Wrathful|Enraged";
		case "Happy": return "Happy|Content|Satisfied|Pleased|Thankful|Glad|Joyful|Excited|Ecstatic";
		case "Sad": return "Sad|Dissatisfied|Lonely|Disappointed|Ashamed|Despondent|Rejected|Depressed|Inconsolable";
		case "Afraid": return "Afraid|Surprised|Nervous|Uneasy|Startled|Anxious|Scared|Dreadful|Terrified";
		case "Disgusted": return "Disgusted|Distasteful|Bitter|Jealous|Spiteful|Scornful|Disdainful|Loathsome|Hateful";
		case "Troubled": return "Troubled|Hesitant|Vulnerable|Restrained|Confused|Guilty|Distraught|Disturbed|Powerless";
		case "Confident": return "Confident|Hopeful|Positive|Courageous|Proud|Arrogant|Bold|Fearless|Powerful";
		case "Impassive": return "Impassive|Calm|Indifferent|Sleepy|Apathetic|Unmoved|Detached|Distant|Cold";
		case "ALL": return ".*";
	}
}<|endoftext|>


<commit_before>"use strict";

const path = require("path");
const fsextra = require("fs-extra");
const sqlite3 = require("sqlite3");
const Helper = require("../helper");
const Msg = require("../models/msg");

const currentSchemaVersion = 1520239200;

const schema = [
	// Schema version #1
	"CREATE TABLE IF NOT EXISTS options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
	"CREATE TABLE IF NOT EXISTS messages (network TEXT, channel TEXT, time INTEGER, type TEXT, msg TEXT)",
	"CREATE INDEX IF NOT EXISTS network_channel ON messages (network, channel)",
	"CREATE INDEX IF NOT EXISTS time ON messages (time)",
];

class MessageStorage {
	constructor() {
		this.isEnabled = false;
	}

	enable(name) {
		const logsPath = path.join(Helper.getHomePath(), "logs");
		const sqlitePath = path.join(logsPath, `${name}.sqlite3`);

		try {
			fsextra.ensureDirSync(logsPath);
		} catch (e) {
			log.error("Unable to create logs directory", e);

			return;
		}

		this.isEnabled = true;

		this.database = new sqlite3.Database(sqlitePath);
		this.database.serialize(() => {
			schema.forEach((line) => this.database.run(line));

			this.database.get("SELECT value FROM options WHERE name = 'schema_version'", (err, row) => {
				if (err) {
					return log.error(`Failed to retrieve schema version: ${err}`);
				}

				// New table
				if (row === undefined) {
					this.database.serialize(() => this.database.run("INSERT INTO options (name, value) VALUES ('schema_version', ?)", currentSchemaVersion));

					return;
				}

				const storedSchemaVersion = parseInt(row.value, 10);

				if (storedSchemaVersion === currentSchemaVersion) {
					return;
				}

				if (storedSchemaVersion > currentSchemaVersion) {
					return log.error(`sqlite messages schema version is higher than expected (${storedSchemaVersion} > ${currentSchemaVersion}). Is The Lounge out of date?`);
				}

				log.info(`sqlite messages schema version is out of date (${storedSchemaVersion} < ${currentSchemaVersion}). Running migrations if any.`);

				this.database.serialize(() => this.database.run("UPDATE options SET value = ? WHERE name = 'schema_version'", currentSchemaVersion));
			});
		});
	}

	close(callback) {
		if (!this.isEnabled) {
			return;
		}

		this.isEnabled = false;

		this.database.close((err) => {
			if (err) {
				log.error(`Failed to close sqlite database: ${err}`);
			}

			if (callback) {
				callback(err);
			}
		});
	}

	index(network, channel, msg) {
		if (!this.isEnabled) {
			return;
		}

		const clonedMsg = Object.keys(msg).reduce((newMsg, prop) => {
			// id is regenerated when messages are retrieved
			// previews are not stored because storage is cleared on lounge restart
			// type and time are stored in a separate column
			if (prop !== "id" && prop !== "previews" && prop !== "type" && prop !== "time") {
				newMsg[prop] = msg[prop];
			}

			return newMsg;
		}, {});

		this.database.serialize(() => this.database.run(
			"INSERT INTO messages(network, channel, time, type, msg) VALUES(?, ?, ?, ?, ?)",
			network, channel.toLowerCase(), msg.time.getTime(), msg.type, JSON.stringify(clonedMsg)
		));
	}

	/**
	 * Load messages for given channel on a given network and resolve a promise with loaded messages.
	 *
	 * @param Network network - Network object where the channel is
	 * @param Chan channel - Channel object for which to load messages for
	 */
	getMessages(network, channel) {
		if (!this.isEnabled || Helper.config.maxHistory < 1) {
			return Promise.resolve([]);
		}

		return new Promise((resolve, reject) => {
			this.database.parallelize(() => this.database.all(
				"SELECT msg, type, time FROM messages WHERE network = ? AND channel = ? ORDER BY time DESC LIMIT ?",
				[network.uuid, channel.name.toLowerCase(), Helper.config.maxHistory],
				(err, rows) => {
					if (err) {
						return reject(err);
					}

					resolve(rows.map((row) => {
						const msg = JSON.parse(row.msg);
						msg.time = row.time;
						msg.type = row.type;

						return new Msg(msg);
					}).reverse());
				}
			));
		});
	}
}

module.exports = MessageStorage;
<commit_msg>Gracefully fail if sqlite3 module fails to load<commit_after>"use strict";

const path = require("path");
const fsextra = require("fs-extra");
const Helper = require("../helper");
const Msg = require("../models/msg");

let sqlite3;

try {
	sqlite3 = require("sqlite3");
} catch (e) {
	Helper.config.messageStorage = Helper.config.messageStorage.filter((item) => item !== "sqlite");

	log.error("Unable to load sqlite3 module. You might need to install it manually.");
}

const currentSchemaVersion = 1520239200;

const schema = [
	// Schema version #1
	"CREATE TABLE IF NOT EXISTS options (name TEXT, value TEXT, CONSTRAINT name_unique UNIQUE (name))",
	"CREATE TABLE IF NOT EXISTS messages (network TEXT, channel TEXT, time INTEGER, type TEXT, msg TEXT)",
	"CREATE INDEX IF NOT EXISTS network_channel ON messages (network, channel)",
	"CREATE INDEX IF NOT EXISTS time ON messages (time)",
];

class MessageStorage {
	constructor() {
		this.isEnabled = false;
	}

	enable(name) {
		const logsPath = path.join(Helper.getHomePath(), "logs");
		const sqlitePath = path.join(logsPath, `${name}.sqlite3`);

		try {
			fsextra.ensureDirSync(logsPath);
		} catch (e) {
			log.error("Unable to create logs directory", e);

			return;
		}

		this.isEnabled = true;

		this.database = new sqlite3.Database(sqlitePath);
		this.database.serialize(() => {
			schema.forEach((line) => this.database.run(line));

			this.database.get("SELECT value FROM options WHERE name = 'schema_version'", (err, row) => {
				if (err) {
					return log.error(`Failed to retrieve schema version: ${err}`);
				}

				// New table
				if (row === undefined) {
					this.database.serialize(() => this.database.run("INSERT INTO options (name, value) VALUES ('schema_version', ?)", currentSchemaVersion));

					return;
				}

				const storedSchemaVersion = parseInt(row.value, 10);

				if (storedSchemaVersion === currentSchemaVersion) {
					return;
				}

				if (storedSchemaVersion > currentSchemaVersion) {
					return log.error(`sqlite messages schema version is higher than expected (${storedSchemaVersion} > ${currentSchemaVersion}). Is The Lounge out of date?`);
				}

				log.info(`sqlite messages schema version is out of date (${storedSchemaVersion} < ${currentSchemaVersion}). Running migrations if any.`);

				this.database.serialize(() => this.database.run("UPDATE options SET value = ? WHERE name = 'schema_version'", currentSchemaVersion));
			});
		});
	}

	close(callback) {
		if (!this.isEnabled) {
			return;
		}

		this.isEnabled = false;

		this.database.close((err) => {
			if (err) {
				log.error(`Failed to close sqlite database: ${err}`);
			}

			if (callback) {
				callback(err);
			}
		});
	}

	index(network, channel, msg) {
		if (!this.isEnabled) {
			return;
		}

		const clonedMsg = Object.keys(msg).reduce((newMsg, prop) => {
			// id is regenerated when messages are retrieved
			// previews are not stored because storage is cleared on lounge restart
			// type and time are stored in a separate column
			if (prop !== "id" && prop !== "previews" && prop !== "type" && prop !== "time") {
				newMsg[prop] = msg[prop];
			}

			return newMsg;
		}, {});

		this.database.serialize(() => this.database.run(
			"INSERT INTO messages(network, channel, time, type, msg) VALUES(?, ?, ?, ?, ?)",
			network, channel.toLowerCase(), msg.time.getTime(), msg.type, JSON.stringify(clonedMsg)
		));
	}

	/**
	 * Load messages for given channel on a given network and resolve a promise with loaded messages.
	 *
	 * @param Network network - Network object where the channel is
	 * @param Chan channel - Channel object for which to load messages for
	 */
	getMessages(network, channel) {
		if (!this.isEnabled || Helper.config.maxHistory < 1) {
			return Promise.resolve([]);
		}

		return new Promise((resolve, reject) => {
			this.database.parallelize(() => this.database.all(
				"SELECT msg, type, time FROM messages WHERE network = ? AND channel = ? ORDER BY time DESC LIMIT ?",
				[network.uuid, channel.name.toLowerCase(), Helper.config.maxHistory],
				(err, rows) => {
					if (err) {
						return reject(err);
					}

					resolve(rows.map((row) => {
						const msg = JSON.parse(row.msg);
						msg.time = row.time;
						msg.type = row.type;

						return new Msg(msg);
					}).reverse());
				}
			));
		});
	}
}

module.exports = MessageStorage;
<|endoftext|>


<commit_before>/**
* Module that contains the animator implementations.
* Depends on core.js
*/
(function($) {
  if (typeof JSAV === "undefined") { return; }

  function backward() {
    if (this._undo.length === 0) { return; }
    var ops = this._undo.pop(); // get the operations in the step we're about to undo
    for (var i = ops.length - 1; i >= 0; i--) { // iterate the operations
      // operation contains: [target object, effect function, arguments, undo function]
      var prev = ops[i];
      prev[3].apply(prev[0], prev[2]);
    }
    this._redo.unshift(ops);
    // trigger an event on the container to update the counter
    this.container.trigger("updatecounter");
  }

  function forward() {
    if (this._redo.length === 0) { return; }
    var ops = this._redo.shift(); // get the operations in the step we're about to undo
    for (var i = 0; i < ops.length; i++) {
      // operation contains: [target object, effect function, arguments, undo function]
      var next = ops[i];
      if (typeof next[3] === "undefined" || !$.isFunction(next[3])) { // if no undo function
        next[3] = function() {
          var obj = next[0],
            state = obj.state();
          return function() { // we create one that will set the state of obj to its current state
            obj.state(state);
          };
        }();
      }
      next[1].apply(next[0], next[2]);
    }
    this._undo.push(ops);
    // trigger an event on the container to update the counter
    this.container.trigger("updatecounter");
  }

  function begin() {
    var prevFx = $.fx.off;
    $.fx.off = true;
    while (this._undo.length) {
      this.backward();
    }
    $.fx.off = false;
    return this;
  }

  function end() {
    var prevFx = $.fx.off;
    $.fx.off = true;
    while (this._redo.length) {
      this.forward();
    }
    $.fx.off = false;
    return this;
  }

  JSAV.init(function() {
    this._redo = []; // stack for operations to redo
    this._undo = []; // stack for operations to undo
    var that = this,
      $controls = $(".controls", this.container),
      playingCl = "jsavplaying"; // class used to mark controls when playing
    if ($controls.size() === 0) {
      return; // no controls, no need to proceed
    }

    // function for clearing the playing flag
    function clearPlaying() {
      // check to see if some elements are still animated
      if (!that.container.find(":animated").size()) {
        // if not, clear the playing flag
        $controls.removeClass(playingCl);
      } else {
        // if still animating, set a new timeout
        setTimeout(clearPlaying, that.SPEED/4);
      }
    }
    // reqister event handlers for the control buttons
    var beginHandler = function(e) {
      e.preventDefault();
      // if playing flag is set, don't respond
      if ($controls.hasClass(playingCl)) { return; }
      // set the playing flag, that is, a class on the controls
      $controls.addClass(playingCl);
      that.begin(); // go to beginning
      clearPlaying(); // clear the flag
    };
    var backwardHandler = function(e) {
      e.preventDefault();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.backward();
      // clear playing flag after a timeout for animations to end
      setTimeout(clearPlaying, that.SPEED + that.SPEED/4);
    };
    var forwardHandler = function(e) {
      e.preventDefault();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.forward();
      setTimeout(clearPlaying, that.SPEED + that.SPEED/4);
    };
    var endHandler = function(e) {
      e.preventDefault();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.end();
      clearPlaying();
    };
    $("Begin").click(beginHandler).appendTo($controls);
    $("Backward").click(backwardHandler).appendTo($controls);
    $("Forward").click(forwardHandler).appendTo($controls);
    $("End").click(endHandler).appendTo($controls);
    // bind the handlers to events to enable control by triggering events
    this.container.bind({ "forward": forwardHandler,
                          "backward": backwardHandler,
                          "begin": beginHandler,
                          "end": endHandler });

    // add slideshow counter if an element with class counter exists
    var counter = $(".counter", this.container);
    // register an event to be triggered on container to update the counter
    if (counter.size() > 0) {
      this.container.bind("updatecounter", function() {
        counter.text(that.currentStep() + 1 + " / " + (that.totalSteps() + 1));
      });
    }
  });

  // this function can be used to "decorate" effects to be applied when moving forward
  // in the animation
  function anim(effect, undo) {
    // returns a function that can be used to provide function calls that are applied later
    // when viewing the visualization
    return function() {
      var jsav = this; // this points to the objects whose function was decorated
      if (!jsav.hasOwnProperty("_redo")) { jsav = this.jsav; }
      var stackTop = jsav._redo[0];
      if (!stackTop) {
        stackTop = [];
        jsav._redo.push(stackTop);
      }
      if (jsav.options.animationMode == 'none') { // if not recording, apply immediately
        effect.apply(this, arguments);
      } else {
        // add to stack: [target object, effect function, arguments, undo function]
        stackTop.push([this, effect, arguments, undo]);
      }
      return this;
    };
  }

  JSAV.anim = anim;
  JSAV.ext.SPEED = 400;
  JSAV.ext.begin = begin;
  JSAV.ext.end = end;
  JSAV.ext.forward = forward;
  JSAV.ext.backward = backward;
  JSAV.ext.currentStep = function() {
    return this._undo.length;
  };
  JSAV.ext.totalSteps = function() {
    return this._undo.length + this._redo.length;
  };
  JSAV.ext.animInfo = function() {
    // get some "size" info about the animation, namely the number of steps
    // and the total number of effects (or operations) in the animation
    var info = { steps: this.totalSteps()},
      i,
      effects = 0;
    for (i = this._undo.length; i--; ) {
      effects += this._undo[i].length;
    }
    for (i = this._redo.length; i--; ) {
      effects += this._redo[i].length;
    }
    info.effects = effects;
    return info;
  };
  JSAV.ext.step = function(options) {
    if (this._redo.length === 0 || this._redo[0].length === 0) { // ignore step if no operations in it
      return this;
    }
    this.forward();
    this._redo.push([]); // add new empty step to oper. stack
    if (options && this.message && options.message) {
      this.message(options.message);
    }
    return this;
  };
  JSAV.ext.substep = function(options) {
    // TODO: implement substep
    return this.step(options);
  };
  JSAV.ext.stepdone = function() {return this;};
  JSAV.ext.recorded = function() {
    this.forward(); // apply the last steps
    this.begin();
    this.RECORD = false;
  };
})(jQuery);
<commit_msg>Control event handlers now stop event bubbling<commit_after>/**
* Module that contains the animator implementations.
* Depends on core.js
*/
(function($) {
  if (typeof JSAV === "undefined") { return; }

  function backward() {
    if (this._undo.length === 0) { return; }
    var ops = this._undo.pop(); // get the operations in the step we're about to undo
    for (var i = ops.length - 1; i >= 0; i--) { // iterate the operations
      // operation contains: [target object, effect function, arguments, undo function]
      var prev = ops[i];
      prev[3].apply(prev[0], prev[2]);
    }
    this._redo.unshift(ops);
    // trigger an event on the container to update the counter
    this.container.trigger("updatecounter");
  }

  function forward() {
    if (this._redo.length === 0) { return; }
    var ops = this._redo.shift(); // get the operations in the step we're about to undo
    for (var i = 0; i < ops.length; i++) {
      // operation contains: [target object, effect function, arguments, undo function]
      var next = ops[i];
      if (typeof next[3] === "undefined" || !$.isFunction(next[3])) { // if no undo function
        next[3] = function() {
          var obj = next[0],
            state = obj.state();
          return function() { // we create one that will set the state of obj to its current state
            obj.state(state);
          };
        }();
      }
      next[1].apply(next[0], next[2]);
    }
    this._undo.push(ops);
    // trigger an event on the container to update the counter
    this.container.trigger("updatecounter");
  }

  function begin() {
    var prevFx = $.fx.off;
    $.fx.off = true;
    while (this._undo.length) {
      this.backward();
    }
    $.fx.off = false;
    return this;
  }

  function end() {
    var prevFx = $.fx.off;
    $.fx.off = true;
    while (this._redo.length) {
      this.forward();
    }
    $.fx.off = false;
    return this;
  }

  JSAV.init(function() {
    this._redo = []; // stack for operations to redo
    this._undo = []; // stack for operations to undo
    var that = this,
      $controls = $(".controls", this.container),
      playingCl = "jsavplaying"; // class used to mark controls when playing
    if ($controls.size() === 0) {
      return; // no controls, no need to proceed
    }

    // function for clearing the playing flag
    function clearPlaying() {
      // check to see if some elements are still animated
      if (!that.container.find(":animated").size()) {
        // if not, clear the playing flag
        $controls.removeClass(playingCl);
      } else {
        // if still animating, set a new timeout
        setTimeout(clearPlaying, that.SPEED/4);
      }
    }
    // reqister event handlers for the control buttons
    var beginHandler = function(e) {
      e.preventDefault();
      e.stopPropagation();
      // if playing flag is set, don't respond
      if ($controls.hasClass(playingCl)) { return; }
      // set the playing flag, that is, a class on the controls
      $controls.addClass(playingCl);
      that.begin(); // go to beginning
      clearPlaying(); // clear the flag
    };
    var backwardHandler = function(e) {
      e.preventDefault();
      e.stopPropagation();
      e.stopPropagation();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.backward();
      // clear playing flag after a timeout for animations to end
      setTimeout(clearPlaying, that.SPEED + that.SPEED/4);
    };
    var forwardHandler = function(e) {
      e.preventDefault();
      e.stopPropagation();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.forward();
      setTimeout(clearPlaying, that.SPEED + that.SPEED/4);
    };
    var endHandler = function(e) {
      e.preventDefault();
      e.stopPropagation();
      if ($controls.hasClass(playingCl)) { return; }
      $controls.addClass(playingCl);
      that.end();
      clearPlaying();
    };
    $("Begin").click(beginHandler).appendTo($controls);
    $("Backward").click(backwardHandler).appendTo($controls);
    $("Forward").click(forwardHandler).appendTo($controls);
    $("End").click(endHandler).appendTo($controls);
    // bind the handlers to events to enable control by triggering events
    this.container.bind({ "forward": forwardHandler,
                          "backward": backwardHandler,
                          "begin": beginHandler,
                          "end": endHandler });

    // add slideshow counter if an element with class counter exists
    var counter = $(".counter", this.container);
    // register an event to be triggered on container to update the counter
    if (counter.size() > 0) {
      this.container.bind("updatecounter", function() {
        counter.text(that.currentStep() + 1 + " / " + (that.totalSteps() + 1));
      });
    }
  });

  // this function can be used to "decorate" effects to be applied when moving forward
  // in the animation
  function anim(effect, undo) {
    // returns a function that can be used to provide function calls that are applied later
    // when viewing the visualization
    return function() {
      var jsav = this; // this points to the objects whose function was decorated
      if (!jsav.hasOwnProperty("_redo")) { jsav = this.jsav; }
      var stackTop = jsav._redo[0];
      if (!stackTop) {
        stackTop = [];
        jsav._redo.push(stackTop);
      }
      if (jsav.options.animationMode == 'none') { // if not recording, apply immediately
        effect.apply(this, arguments);
      } else {
        // add to stack: [target object, effect function, arguments, undo function]
        stackTop.push([this, effect, arguments, undo]);
      }
      return this;
    };
  }

  JSAV.anim = anim;
  JSAV.ext.SPEED = 400;
  JSAV.ext.begin = begin;
  JSAV.ext.end = end;
  JSAV.ext.forward = forward;
  JSAV.ext.backward = backward;
  JSAV.ext.currentStep = function() {
    return this._undo.length;
  };
  JSAV.ext.totalSteps = function() {
    return this._undo.length + this._redo.length;
  };
  JSAV.ext.animInfo = function() {
    // get some "size" info about the animation, namely the number of steps
    // and the total number of effects (or operations) in the animation
    var info = { steps: this.totalSteps()},
      i,
      effects = 0;
    for (i = this._undo.length; i--; ) {
      effects += this._undo[i].length;
    }
    for (i = this._redo.length; i--; ) {
      effects += this._redo[i].length;
    }
    info.effects = effects;
    return info;
  };
  JSAV.ext.step = function(options) {
    if (this._redo.length === 0 || this._redo[0].length === 0) { // ignore step if no operations in it
      return this;
    }
    this.forward();
    this._redo.push([]); // add new empty step to oper. stack
    if (options && this.message && options.message) {
      this.message(options.message);
    }
    return this;
  };
  JSAV.ext.substep = function(options) {
    // TODO: implement substep
    return this.step(options);
  };
  JSAV.ext.stepdone = function() {return this;};
  JSAV.ext.recorded = function() {
    this.forward(); // apply the last steps
    this.begin();
    this.RECORD = false;
  };
})(jQuery);
<|endoftext|>


<commit_before>'use strict';

var Fluxxor = require('fluxxor')
var _ = require('lodash')
var uuid = require('../helpers/uuid')
var Immutable = require('immutable')

var StudentConstants = require('../constants/StudentConstants')
var ScheduleConstants = require('../constants/ScheduleConstants')
var StudyConstants = require('../constants/StudyConstants')

function randomChar() {
	// modified from http://stackoverflow.com/questions/10726909/random-alpha-numeric-string-in-javascript
	return Math.random().toString(36).slice(2, 3)
}

var StudentStore = Fluxxor.createStore({
	initialize: function(options) {
		console.log('StudentStore.initialize')
		this.students = Immutable.Map()
		this.active = {}

		if (options.students) {
			_.each(options.students, this.handleStudentCreation)
		}

		this.bindActions(
			// Student Constants
			StudentConstants.STUDENT_ENCODE,        this.handleStudentEncode,
			StudentConstants.STUDENT_DECODE,        this.handleStudentDecode,
			StudentConstants.STUDENT_UPDATE,        this.handleStudentUpdate,
			StudentConstants.STUDENT_CREATE,        this.handleStudentCreation,
			StudentConstants.STUDENT_DESTROY,       this.handleStudentDestroy,
			StudentConstants.STUDENT_UNDO,          this.handleStudentUndo,
			StudentConstants.STUDENT_TOGGLE_ACTIVE, this.handleStudentToggleActive,
			StudentConstants.STUDENT_SAVE,          this.handleStudentSave,

			// Schedule Constants
			ScheduleConstants.SCHEDULE_CREATE,           this.handleScheduleCreation,
			ScheduleConstants.SCHEDULE_DESTROY,          this.handleScheduleDestruction,
			ScheduleConstants.SCHEDULE_DESTROY_MULTIPLE, this.handleMultipleScheduleDestruction,
			ScheduleConstants.SCHEDULE_RENAME,           this.handleScheduleRename,
			ScheduleConstants.SCHEDULE_MOVE,             this.handleScheduleMove,
			ScheduleConstants.SCHEDULE_REORDER,          this.handleScheduleReorder,
			ScheduleConstants.SCHEDULE_TOGGLE_ACTIVATE,  this.handleScheduleToggleActivate,
			ScheduleConstants.SCHEDULE_COURSE_ADD,       this.handleScheduleCourseAdd,
			ScheduleConstants.SCHEDULE_COURSE_REMOVE,    this.handleScheduleCourseRemove,
			ScheduleConstants.SCHEDULE_COURSE_MOVE,      this.handleScheduleCourseMove,
			ScheduleConstants.SCHEDULE_UNDO,             this.handleScheduleUndo,
			ScheduleConstants.SCHEDULE_CHANGED,          this.handleScheduleChanged,
			ScheduleConstants.SCHEDULE_SHOW_DETAIL,      this.handleScheduleShowDetail,

			// Study Constants
			StudyConstants.STUDY_ADD,         this.handleStudyAdd,
			StudyConstants.STUDY_REMOVE,      this.handleStudyRemove,
			StudyConstants.STUDY_REORDER,     this.handleStudyReorder,
			StudyConstants.STUDY_UNDO,        this.handleStudyUndo,
			StudyConstants.STUDY_TOGGLE_OPEN, this.handleStudyToggleOpen

			// Fabrication Constants
			// Override Constants
		)

		this.emit('change')
	},
	getState: function() {
		console.log('StudentStore.getState')

		return {
			students: this.students,
			active: this.active
		}
	},

	// Student Actions
	handleStudentEncode: function() {},

	handleStudentDecode: function() {},

	handleStudentUpdate: function(student) {},

	handleStudentCreation: function(student) {
		console.log('StudentStore.createStudent')
		var genericStudent = {
			id: uuid(),
			name: 'Student ' + randomChar(),
			active: false,
			matriculation: 1874,
			graduation: 2016,
			creditsNeeded: 35,
			studies: {},
			schedules: {},
			overrides: {},
			fabrications: {},
		}

		var joinedStudent = _.merge(genericStudent, student)

		if (this.students.length > 0) {
			joinedStudent.active = false
		}

		joinedStudent.studies      = Immutable.OrderedMap(joinedStudent.studies)
		joinedStudent.schedules    = Immutable.OrderedMap(joinedStudent.schedules)
		joinedStudent.overrides    = Immutable.Map(joinedStudent.overrides)
		joinedStudent.fabrications = Immutable.Map(joinedStudent.fabrications)

		var immutableStudent = Immutable.fromJS(joinedStudent)

		this.students = this.students.set(joinedStudent.id, immutableStudent)

		this.findActiveStudent()

		this.emit('change')
	},

	handleStudentDestroy: function(student) {
		console.log('StudentStore.destroyStudent')
		this.students = this.students.delete(student.id)

		if (this.students.length > 0) {
			this.findActiveStudent()
		} else {
			this.handleStudentCreation()
		}

		this.emit('change')
	},

	handleStudentUndo: function() {
		this.findActiveStudent()
	},

	handleStudentToggleActive: function(student) {
		console.log('StudentStore.toggleStudentActive')
		// is there a possibility of a student being passed in that isn't an immutable object?
		var changedStudent = this.students.get(student.get('id'))
			.withMutations(function(student) {
				return student.set('active', !student.get('active'))
			})
		this.students = this.students.set(student.get('id'), changedStudent)

		this.findActiveStudent()

		this.emit('change')
	},

	handleStudentSave: function() {},

	// Schedule Actions
	handleScheduleCreation: function(args) {
		console.log('StudentStore.createSchedule')

		// args: {studentId, schedule}
		var studentId = args.studentId
		var schedule = args.schedule

		var genericSchedule = {
			id: uuid(),
			year: 0,
			semester: 0,
			title: 'Schedule ' + randomChar(),
			index: 1,
			clbids: [],
		}

		schedule.active = _.size(this.students.get(studentId).schedules) > 0 ? false : true

		var joinedSchedule = _.merge(genericSchedule, schedule)
		var immutableSchedule = Immutable.fromJS(joinedSchedule)
		this.students = this.students.updateIn([studentId, 'schedules'], function(schedules) {
			return schedules.set(joinedSchedule.id, immutableSchedule)
		})

		console.log(this.students.getIn([studentId, 'schedules', joinedSchedule.id]))
		this.emit('change')
	},

	handleScheduleDestruction: function(args, emitChange) {
		console.log('StudentStore.destroySchedule')
		emitChange = emitChange || false

		// args: {studentId, scheduleId}
		var studentId = args.studentId
		var scheduleId = args.scheduleId

		this.students = this.students.updateIn([studentId, 'schedules'], function(schedules) {
			return schedules.delete(scheduleId)
		})

		if (emitChange) {
			this.emit('change')
		}
	},

	handleMultipleScheduleDestruction: function(args) {
		console.log('StudentStore.destroyMultipleSchedules')

		// args: {studentId, schedule}
		var studentId = args.studentId
		var scheduleIds = args.scheduleIds

		_.each(scheduleIds, function(scheduleId) {
			this.handleScheduleDestruction({studentId: studentId, scheduleId: scheduleId}, false)
		}, this)
		this.emit('change')
	},

	handleScheduleRename: function() {},

	handleScheduleMove: function() {},

	handleScheduleReorder: function() {},

	handleScheduleToggleActivate: function() {},

	handleScheduleCourseAdd: function() {},

	handleScheduleCourseRemove: function() {},

	handleScheduleCourseMove: function() {},

	handleScheduleUndo: function() {},

	handleScheduleChanged: function() {},

	handleScheduleShowDetail: function() {},

	// Area of Study Actions
	handleStudyAdd: function() {},

	handleStudyRemove: function() {},

	handleStudyReorder: function() {},

	handleStudyUndo: function() {},

	handleStudyToggleOpen: function() {},

	findActiveStudent: function() {
		console.log('StudentStore.findActiveStudent')

		// try to find active student
		var activeStudentId = this.students.findKey(function(student) {
			return student.get('active')
		})

		// if there is an active student, put it in active
		if (activeStudentId) {
			this.active = this.students.get(activeStudentId).toJS()
		}

		// otherwise, make the next one active
		else if (this.students.length > 0) {
			this.handleStudentToggleActive(this.students.first())
		}

		// otherwise, make a student
		else {
			this.handleStudentCreation()
		}
	},
})

module.exports = StudentStore
<commit_msg>scheduleId must be a string<commit_after>'use strict';

var Fluxxor = require('fluxxor')
var _ = require('lodash')
var uuid = require('../helpers/uuid')
var Immutable = require('immutable')

var StudentConstants = require('../constants/StudentConstants')
var ScheduleConstants = require('../constants/ScheduleConstants')
var StudyConstants = require('../constants/StudyConstants')

function randomChar() {
	// modified from http://stackoverflow.com/questions/10726909/random-alpha-numeric-string-in-javascript
	return Math.random().toString(36).slice(2, 3)
}

var StudentStore = Fluxxor.createStore({
	initialize: function(options) {
		console.log('StudentStore.initialize')
		this.students = Immutable.Map()
		this.active = {}

		if (options.students) {
			_.each(options.students, this.handleStudentCreation)
		}

		this.bindActions(
			// Student Constants
			StudentConstants.STUDENT_ENCODE,        this.handleStudentEncode,
			StudentConstants.STUDENT_DECODE,        this.handleStudentDecode,
			StudentConstants.STUDENT_UPDATE,        this.handleStudentUpdate,
			StudentConstants.STUDENT_CREATE,        this.handleStudentCreation,
			StudentConstants.STUDENT_DESTROY,       this.handleStudentDestroy,
			StudentConstants.STUDENT_UNDO,          this.handleStudentUndo,
			StudentConstants.STUDENT_TOGGLE_ACTIVE, this.handleStudentToggleActive,
			StudentConstants.STUDENT_SAVE,          this.handleStudentSave,

			// Schedule Constants
			ScheduleConstants.SCHEDULE_CREATE,           this.handleScheduleCreation,
			ScheduleConstants.SCHEDULE_DESTROY,          this.handleScheduleDestruction,
			ScheduleConstants.SCHEDULE_DESTROY_MULTIPLE, this.handleMultipleScheduleDestruction,
			ScheduleConstants.SCHEDULE_RENAME,           this.handleScheduleRename,
			ScheduleConstants.SCHEDULE_MOVE,             this.handleScheduleMove,
			ScheduleConstants.SCHEDULE_REORDER,          this.handleScheduleReorder,
			ScheduleConstants.SCHEDULE_TOGGLE_ACTIVATE,  this.handleScheduleToggleActivate,
			ScheduleConstants.SCHEDULE_COURSE_ADD,       this.handleScheduleCourseAdd,
			ScheduleConstants.SCHEDULE_COURSE_REMOVE,    this.handleScheduleCourseRemove,
			ScheduleConstants.SCHEDULE_COURSE_MOVE,      this.handleScheduleCourseMove,
			ScheduleConstants.SCHEDULE_UNDO,             this.handleScheduleUndo,
			ScheduleConstants.SCHEDULE_CHANGED,          this.handleScheduleChanged,
			ScheduleConstants.SCHEDULE_SHOW_DETAIL,      this.handleScheduleShowDetail,

			// Study Constants
			StudyConstants.STUDY_ADD,         this.handleStudyAdd,
			StudyConstants.STUDY_REMOVE,      this.handleStudyRemove,
			StudyConstants.STUDY_REORDER,     this.handleStudyReorder,
			StudyConstants.STUDY_UNDO,        this.handleStudyUndo,
			StudyConstants.STUDY_TOGGLE_OPEN, this.handleStudyToggleOpen

			// Fabrication Constants
			// Override Constants
		)

		this.emit('change')
	},
	getState: function() {
		console.log('StudentStore.getState')

		return {
			students: this.students,
			active: this.active
		}
	},

	// Student Actions
	handleStudentEncode: function() {},

	handleStudentDecode: function() {},

	handleStudentUpdate: function(student) {},

	handleStudentCreation: function(student) {
		console.log('StudentStore.createStudent')
		var genericStudent = {
			id: uuid(),
			name: 'Student ' + randomChar(),
			active: false,
			matriculation: 1874,
			graduation: 2016,
			creditsNeeded: 35,
			studies: {},
			schedules: {},
			overrides: {},
			fabrications: {},
		}

		var joinedStudent = _.merge(genericStudent, student)

		if (this.students.length > 0) {
			joinedStudent.active = false
		}

		joinedStudent.studies      = Immutable.OrderedMap(joinedStudent.studies)
		joinedStudent.schedules    = Immutable.OrderedMap(joinedStudent.schedules)
		joinedStudent.overrides    = Immutable.Map(joinedStudent.overrides)
		joinedStudent.fabrications = Immutable.Map(joinedStudent.fabrications)

		var immutableStudent = Immutable.fromJS(joinedStudent)

		this.students = this.students.set(joinedStudent.id, immutableStudent)

		this.findActiveStudent()

		this.emit('change')
	},

	handleStudentDestroy: function(student) {
		console.log('StudentStore.destroyStudent')
		this.students = this.students.delete(student.id)

		if (this.students.length > 0) {
			this.findActiveStudent()
		} else {
			this.handleStudentCreation()
		}

		this.emit('change')
	},

	handleStudentUndo: function() {
		this.findActiveStudent()
	},

	handleStudentToggleActive: function(student) {
		console.log('StudentStore.toggleStudentActive')
		// is there a possibility of a student being passed in that isn't an immutable object?
		var changedStudent = this.students.get(student.get('id'))
			.withMutations(function(student) {
				return student.set('active', !student.get('active'))
			})
		this.students = this.students.set(student.get('id'), changedStudent)

		this.findActiveStudent()

		this.emit('change')
	},

	handleStudentSave: function() {},

	// Schedule Actions
	handleScheduleCreation: function(args) {
		console.log('StudentStore.createSchedule')

		// args: {studentId, schedule}
		var studentId = args.studentId
		var schedule = args.schedule

		var genericSchedule = {
			id: uuid(),
			year: 0,
			semester: 0,
			title: 'Schedule ' + randomChar(),
			index: 1,
			clbids: [],
		}

		schedule.active = _.size(this.students.get(studentId).schedules) > 0 ? false : true

		var joinedSchedule = _.merge(genericSchedule, schedule)
		var immutableSchedule = Immutable.fromJS(joinedSchedule)
		this.students = this.students.updateIn([studentId, 'schedules'], function(schedules) {
			return schedules.set(joinedSchedule.id, immutableSchedule)
		})

		console.log(this.students.getIn([studentId, 'schedules', joinedSchedule.id]))
		this.emit('change')
	},

	handleScheduleDestruction: function(args, emitChange) {
		console.log('StudentStore.destroySchedule')
		emitChange = emitChange || false

		// args: {studentId, scheduleId}
		var studentId = args.studentId
		var scheduleId = args.scheduleId.toString()

		this.students = this.students.updateIn([studentId, 'schedules'], function(schedules) {
			return schedules.remove(scheduleId)
		})

		if (emitChange) {
			this.emit('change')
		}
	},

	handleMultipleScheduleDestruction: function(args) {
		console.log('StudentStore.destroyMultipleSchedules')

		// args: {studentId, schedule}
		var studentId = args.studentId
		var scheduleIds = args.scheduleIds

		_.each(scheduleIds, function(scheduleId) {
			this.handleScheduleDestruction({studentId: studentId, scheduleId: scheduleId}, false)
		}, this)
		this.emit('change')
	},

	handleScheduleRename: function() {},

	handleScheduleMove: function() {},

	handleScheduleReorder: function() {},

	handleScheduleToggleActivate: function() {},

	handleScheduleCourseAdd: function() {},

	handleScheduleCourseRemove: function() {},

	handleScheduleCourseMove: function() {},

	handleScheduleUndo: function() {},

	handleScheduleChanged: function() {},

	handleScheduleShowDetail: function() {},

	// Area of Study Actions
	handleStudyAdd: function() {},

	handleStudyRemove: function() {},

	handleStudyReorder: function() {},

	handleStudyUndo: function() {},

	handleStudyToggleOpen: function() {},

	findActiveStudent: function() {
		console.log('StudentStore.findActiveStudent')

		// try to find active student
		var activeStudentId = this.students.findKey(function(student) {
			return student.get('active')
		})

		// if there is an active student, put it in active
		if (activeStudentId) {
			this.active = this.students.get(activeStudentId).toJS()
		}

		// otherwise, make the next one active
		else if (this.students.length > 0) {
			this.handleStudentToggleActive(this.students.first())
		}

		// otherwise, make a student
		else {
			this.handleStudentCreation()
		}
	},
})

module.exports = StudentStore
<|endoftext|>


<commit_before>
var DotNotationPointers = require("./DotNotationPointers")

// simple equality {a: 'a'}
// simple operator queries {b: {$gt: '3'}}
// basic and {c: 'c', d: 'd')}
// dot notation: {'e.x': 'ab'}
// array equality: {array: [1,2,3]}
// object equality: {obj: {x:1,y:2}} // order matters
// array contains: {array: 2}
// dot notation with array index: {'array.1.x': 'ab'}
// any obj embedded in array: {'array.x': 'ab'}
// combination of operators {e: {$gt: 3, $lt: 4}}
// function or string - equivalent of the $where operator
    // function using obj or this

// operators:
    // field operators
        // single-value
            // $gt, $gte, $lt, $lte, $ne
            // special (multiple operands)
                // $geoIntersects, $geoWithin
        // array
            // $nin, $all, $in,
        // no-value
            // $mod, $exists, $regex, $size, $nearSphere, $near
        // special
            // $elemMatch, $not
    // field-independant operators
        // no-value
           // $and, $or, $nor, $where, $comment
        // has value
            // $text
    // projection-operators
        // $, $elemMatch, $meta, $slice

var simpleComparators = {
    $gt: function(a,b) {return a>b},
    $gte:function(a,b) {return a>=b},
    $lt:function(a,b) {return a<b},
    $lte:function(a,b) {return a<=b},
    $ne:function(a,b) {return a!==b},

    $mod:function(docValue,operand) {return docValue%operand[0] === operand[1]},
    $regex:function(docValue,operand) {return typeof(docValue) === 'string' && docValue.match(RegExp(operand)) !== null},

    $exists:function(docValue,operand) {return (docValue !== undefined) === operand},

    $in:function(docVal,operand) {
        if(Array.isArray(docVal)) {
            return docVal.some(function(val) {
                return operand.indexOf(val) !== -1;
            });
        } else {
            return operand.indexOf(docVal) !== -1
        }
    },
    $nin:function(docVal,operand) {
        if(Array.isArray(docVal)) {
            return docVal.every(function(val) {
                return operand.indexOf(val) === -1;
            });
        } else {
            return operand.indexOf(docVal) === -1
        }
    },
    $all:function(docVal,operand) {
        return docVal instanceof Array && docVal.reduce(function(last,cur) {
            return last && operand.indexOf(cur) !== -1
        },true)
    },
}

var compoundOperatorComparators = {
    $and: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(!matches(parts[n].parts, document)) {
                return false
            }
        }
        // else
        return true
    },
    $or: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(matches(parts[n].parts, document)) {
                return true
            }
        }
        // else
        return false
    },
    $nor: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(matches(parts[n].parts, document)) {
                return false
            }
        }
        // else
        return true
    }
}

var matches = module.exports = function(parts, document, validate) {
    if(validate !== false)
        validateDocumentObject(document)

    for(var n=0; n<parts.length; n++) {
        var part = parts[n]
        if(!partMatches(part, document))
            return false
    }
    // else

    return true

}

function partMatches(part, document) {
    var pointers = DotNotationPointers(document, part.field)
    for(var p=0; p<pointers.length; p++) {
        var pointer = pointers[p]

        if(part.operator === undefined) { // equality
            if(!valueTest(pointer.val, part.operand, mongoEqual)) {
                continue; // this part doesn't match
            }
        } else if(part.operator in simpleComparators) {
            var test = valueTest(pointer.val, part.operand, simpleComparators[part.operator])
            if(!test)
                continue; // this part doesn't match
        } else if(part.operator in compoundOperatorComparators) {
            if(!compoundOperatorComparators[part.operator](document, part.parts)) {
                continue; // this part doesn't match
            }
        } else if(part.operator === '$not') {
            if(part.parts.length > 0) {
                if(matches(part.parts, document)) {
                    continue; // this part doesn't match
                }
            } else {
                if(valueTest(pointer.val, part.operand, mongoEqual) === true)
                    continue; // this part doesn't match
            }
        } else if(part.operator === '$size') {
            return pointer.val instanceof Array && pointer.val.length === part.operand

        } else if(part.operator === '$elemMatch') {
            var documentField = pointer.val
            if(documentField === undefined)
                continue; // this part doesn't match

            if(part.implicitField) {
                for(var n=0; n<part.parts.length; n++) {
                    part.parts[n].field = 'x' // some fake field so it can be tested against
                }
            }

            var anyMatched = false
            for(var n=0; n<documentField.length; n++) {
                if(part.implicitField) {
                    var documentToMatch = {x:documentField[n]}
                } else {
                    var documentToMatch = documentField[n]
                }

                if(matches(part.parts, documentToMatch)) {
                    anyMatched = true
                    break;
                }
            }
            if(!anyMatched)
                continue; // this part doesn't match

        } else if(part.operator === '$where') {
            if(part.field !== undefined) {
                var objectContext = pointer.val
            } else {
                var objectContext = document
            }

            if(!part.operand.call(objectContext))
                continue; // this part doesn't match
        } else if(part.operator === '$comment') {
            return true // ignore it

        } else {
            throw new Error("Unsupported operator: "+parts.operator)
        }
        // else
        return true
    }
    // else
    return false
}

// tests a document value against a query value, using a comparison function
// this handles array-contains behavior
function valueTest(documentValue, queryOperand, compare) {
    if(documentValue instanceof Array) {
        if(queryOperand instanceof Array) {
            if(!compare(documentValue, queryOperand)) return false
        } else {
            var test = documentValue.reduce(function(last, cur) {
                return last || compare(cur, queryOperand)
            },false)

            if(!test) return false
        }
    } else {
        return compare(documentValue, queryOperand)
    }
    // else
    return true
}

// matches any value, with mongo's special brand of very strict object equality and weird null matching
function mongoEqual(documentValue,queryOperand) {
    if(documentValue instanceof Array) {
        if(!(queryOperand instanceof Array))
            return false
        if(documentValue.length !== queryOperand.length) {
            return false
        } else {
            return documentValue.reduce(function(previousValue, currentValue, index) {
                return previousValue && mongoEqual(currentValue,queryOperand[index])
            }, true)
        }
    } else if(documentValue instanceof Object) {
        if(!(queryOperand instanceof Object))
            return false

        var aKeys = Object.keys(documentValue)
        var bKeys = Object.keys(queryOperand)

        if(aKeys.length !== bKeys.length) {
            return false
        } else {
            for(var n=0; n<aKeys.length; n++) {
                if(aKeys[n] !== bKeys[n]) return false

                var key = aKeys[n]
                var aVal = documentValue[key]
                var bVal = queryOperand[key]

                if(!mongoEqual(aVal,bVal)) {
                    return false
                }
            }
            // else
            return true
        }
    } else {
        if(queryOperand === null) {
            return documentValue === undefined || documentValue === null
        } else {
            return documentValue===queryOperand
        }
    }
}

function validateDocumentObject(document) {

    Object.keys(document).forEach(function(key) {
        if(key[0] === '$')
            throw new Error("Field names can't start with $")
        else if(key.indexOf('.') !== -1)
            throw new Error("Field names can't contain .")
        else if(document[key] instanceof Object) {
            validateDocumentObject(document[key])
        }
    });

}
<commit_msg>simplified matches function<commit_after>
var DotNotationPointers = require("./DotNotationPointers")

// simple equality {a: 'a'}
// simple operator queries {b: {$gt: '3'}}
// basic and {c: 'c', d: 'd')}
// dot notation: {'e.x': 'ab'}
// array equality: {array: [1,2,3]}
// object equality: {obj: {x:1,y:2}} // order matters
// array contains: {array: 2}
// dot notation with array index: {'array.1.x': 'ab'}
// any obj embedded in array: {'array.x': 'ab'}
// combination of operators {e: {$gt: 3, $lt: 4}}
// function or string - equivalent of the $where operator
    // function using obj or this

// operators:
    // field operators
        // single-value
            // $gt, $gte, $lt, $lte, $ne
            // special (multiple operands)
                // $geoIntersects, $geoWithin
        // array
            // $nin, $all, $in,
        // no-value
            // $mod, $exists, $regex, $size, $nearSphere, $near
        // special
            // $elemMatch, $not
    // field-independant operators
        // no-value
           // $and, $or, $nor, $where, $comment
        // has value
            // $text
    // projection-operators
        // $, $elemMatch, $meta, $slice

var simpleComparators = {
    $gt: function(a,b) {return a>b},
    $gte:function(a,b) {return a>=b},
    $lt:function(a,b) {return a<b},
    $lte:function(a,b) {return a<=b},
    $ne:function(a,b) {return a!==b},

    $mod:function(docValue,operand) {return docValue%operand[0] === operand[1]},
    $regex:function(docValue,operand) {return typeof(docValue) === 'string' && docValue.match(RegExp(operand)) !== null},

    $exists:function(docValue,operand) {return (docValue !== undefined) === operand},

    $in:function(docVal,operand) {
        if(Array.isArray(docVal)) {
            return docVal.some(function(val) {
                return operand.indexOf(val) !== -1;
            });
        } else {
            return operand.indexOf(docVal) !== -1
        }
    },
    $nin:function(docVal,operand) {
        if(Array.isArray(docVal)) {
            return docVal.every(function(val) {
                return operand.indexOf(val) === -1;
            });
        } else {
            return operand.indexOf(docVal) === -1
        }
    },
    $all:function(docVal,operand) {
        return docVal instanceof Array && docVal.reduce(function(last,cur) {
            return last && operand.indexOf(cur) !== -1
        },true)
    },
}

var compoundOperatorComparators = {
    $and: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(!matches(parts[n].parts, document)) {
                return false
            }
        }
        // else
        return true
    },
    $or: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(matches(parts[n].parts, document)) {
                return true
            }
        }
        // else
        return false
    },
    $nor: function(document, parts) {
        for(var n=0;  n<parts.length; n++) {
            if(matches(parts[n].parts, document)) {
                return false
            }
        }
        // else
        return true
    }
}

var matches = module.exports = function(parts, document, validate) {
    if(validate !== false)
        validateDocumentObject(document)

    return parts.every((part) => partMatches(part, document));
}

function partMatches(part, document) {
    var pointers = DotNotationPointers(document, part.field)
    for(var p=0; p<pointers.length; p++) {
        var pointer = pointers[p]

        if(part.operator === undefined) { // equality
            if(!valueTest(pointer.val, part.operand, mongoEqual)) {
                continue; // this part doesn't match
            }
        } else if(part.operator in simpleComparators) {
            var test = valueTest(pointer.val, part.operand, simpleComparators[part.operator])
            if(!test)
                continue; // this part doesn't match
        } else if(part.operator in compoundOperatorComparators) {
            if(!compoundOperatorComparators[part.operator](document, part.parts)) {
                continue; // this part doesn't match
            }
        } else if(part.operator === '$not') {
            if(part.parts.length > 0) {
                if(matches(part.parts, document)) {
                    continue; // this part doesn't match
                }
            } else {
                if(valueTest(pointer.val, part.operand, mongoEqual) === true)
                    continue; // this part doesn't match
            }
        } else if(part.operator === '$size') {
            return pointer.val instanceof Array && pointer.val.length === part.operand

        } else if(part.operator === '$elemMatch') {
            var documentField = pointer.val
            if(documentField === undefined)
                continue; // this part doesn't match

            if(part.implicitField) {
                for(var n=0; n<part.parts.length; n++) {
                    part.parts[n].field = 'x' // some fake field so it can be tested against
                }
            }

            var anyMatched = false
            for(var n=0; n<documentField.length; n++) {
                if(part.implicitField) {
                    var documentToMatch = {x:documentField[n]}
                } else {
                    var documentToMatch = documentField[n]
                }

                if(matches(part.parts, documentToMatch)) {
                    anyMatched = true
                    break;
                }
            }
            if(!anyMatched)
                continue; // this part doesn't match

        } else if(part.operator === '$where') {
            if(part.field !== undefined) {
                var objectContext = pointer.val
            } else {
                var objectContext = document
            }

            if(!part.operand.call(objectContext))
                continue; // this part doesn't match
        } else if(part.operator === '$comment') {
            return true // ignore it

        } else {
            throw new Error("Unsupported operator: "+parts.operator)
        }
        // else
        return true
    }
    // else
    return false
}

// tests a document value against a query value, using a comparison function
// this handles array-contains behavior
function valueTest(documentValue, queryOperand, compare) {
    if(documentValue instanceof Array) {
        if(queryOperand instanceof Array) {
            if(!compare(documentValue, queryOperand)) return false
        } else {
            var test = documentValue.reduce(function(last, cur) {
                return last || compare(cur, queryOperand)
            },false)

            if(!test) return false
        }
    } else {
        return compare(documentValue, queryOperand)
    }
    // else
    return true
}

// matches any value, with mongo's special brand of very strict object equality and weird null matching
function mongoEqual(documentValue,queryOperand) {
    if(documentValue instanceof Array) {
        if(!(queryOperand instanceof Array))
            return false
        if(documentValue.length !== queryOperand.length) {
            return false
        } else {
            return documentValue.reduce(function(previousValue, currentValue, index) {
                return previousValue && mongoEqual(currentValue,queryOperand[index])
            }, true)
        }
    } else if(documentValue instanceof Object) {
        if(!(queryOperand instanceof Object))
            return false

        var aKeys = Object.keys(documentValue)
        var bKeys = Object.keys(queryOperand)

        if(aKeys.length !== bKeys.length) {
            return false
        } else {
            for(var n=0; n<aKeys.length; n++) {
                if(aKeys[n] !== bKeys[n]) return false

                var key = aKeys[n]
                var aVal = documentValue[key]
                var bVal = queryOperand[key]

                if(!mongoEqual(aVal,bVal)) {
                    return false
                }
            }
            // else
            return true
        }
    } else {
        if(queryOperand === null) {
            return documentValue === undefined || documentValue === null
        } else {
            return documentValue===queryOperand
        }
    }
}

function validateDocumentObject(document) {

    Object.keys(document).forEach(function(key) {
        if(key[0] === '$')
            throw new Error("Field names can't start with $")
        else if(key.indexOf('.') !== -1)
            throw new Error("Field names can't contain .")
        else if(document[key] instanceof Object) {
            validateDocumentObject(document[key])
        }
    });

}
<|endoftext|>


<commit_before>module.exports = {
  docdash: {
    private: false,
    scripts: ['ltsv.css'],
    sort: false
  },
  opts: {
    destination: './docs',
    readme: './README.md',
    recurse: true,
    template: './node_modules/@mocha/docdash'
  },
  source: {
    excludePattern: 'node_modules/',
    include: ['.'],
    includePattern: '\\.mjs$'
  }
};
<commit_msg>changed to output private functions<commit_after>module.exports = {
  docdash: {
    scripts: ['ltsv.css'],
    sort: false
  },
  opts: {
    destination: './docs',
    private: true,
    readme: './README.md',
    recurse: true,
    template: './node_modules/@mocha/docdash'
  },
  source: {
    excludePattern: 'node_modules/',
    include: ['.'],
    includePattern: '\\.mjs$'
  }
};
<|endoftext|>